diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 8fdd7b03e21..a2495d7db49 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -33,7 +33,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hashes::Hash as TraitImport; use bitcoin::WPubkeyHash; -use lightning::blinded_path::message::{BlindedMessagePath, MessageContext}; +use lightning::blinded_path::message::{self, BlindedMessagePath}; use lightning::blinded_path::payment::{BlindedPaymentPath, ReceiveTlvs}; use lightning::chain; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; @@ -141,8 +141,8 @@ impl MessageRouter for FuzzRouter { } fn create_blinded_paths( - &self, _recipient: PublicKey, _context: MessageContext, _peers: Vec, - _secp_ctx: &Secp256k1, + &self, _recipient: PublicKey, _recipient_tlvs: message::ReceiveTlvs, + _peers: Vec, _secp_ctx: &Secp256k1, ) -> Result, ()> { unreachable!() } diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index f9570841ab2..5ba87a8e2f9 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -30,7 +30,7 @@ use bitcoin::hashes::Hash as _; use bitcoin::hex::FromHex; use bitcoin::WPubkeyHash; -use lightning::blinded_path::message::{BlindedMessagePath, MessageContext}; +use lightning::blinded_path::message::{self, BlindedMessagePath}; use lightning::blinded_path::payment::{BlindedPaymentPath, ReceiveTlvs}; use lightning::chain; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; @@ -176,8 +176,8 @@ impl MessageRouter for FuzzRouter { } fn create_blinded_paths( - &self, _recipient: PublicKey, _context: MessageContext, _peers: Vec, - _secp_ctx: &Secp256k1, + &self, _recipient: PublicKey, _recipient_tlvs: message::ReceiveTlvs, + _peers: Vec, _secp_ctx: &Secp256k1, ) -> Result, ()> { unreachable!() } diff --git a/fuzz/src/invoice_request_deser.rs b/fuzz/src/invoice_request_deser.rs index d5a43ae46ec..6be9dfeba3c 100644 --- a/fuzz/src/invoice_request_deser.rs +++ b/fuzz/src/invoice_request_deser.rs @@ -99,6 +99,7 @@ fn build_response( htlc_minimum_msat: 1, }, payment_context, + custom_data: Vec::new(), }; let intermediate_nodes = [PaymentForwardNode { tlvs: ForwardTlvs { diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index 59255d0a0e9..fc3742cfadb 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -6,7 +6,7 @@ use bitcoin::secp256k1::schnorr; use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey}; use lightning::blinded_path::message::{ - AsyncPaymentsContext, BlindedMessagePath, MessageContext, OffersContext, + AsyncPaymentsContext, BlindedMessagePath, OffersContext, ReceiveTlvs, }; use lightning::blinded_path::EmptyNodeIdLookUp; use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler}; @@ -99,7 +99,7 @@ impl MessageRouter for TestMessageRouter { } fn create_blinded_paths( - &self, _recipient: PublicKey, _context: MessageContext, _peers: Vec, + &self, _recipient: PublicKey, _recipient_tlvs: ReceiveTlvs, _peers: Vec, _secp_ctx: &Secp256k1, ) -> Result, ()> { unreachable!() @@ -111,7 +111,7 @@ struct TestOffersMessageHandler {} impl OffersMessageHandler for TestOffersMessageHandler { fn handle_message( &self, _message: OffersMessage, _context: Option, - _responder: Option, + _custom_data: Option>, _responder: Option, ) -> Option<(OffersMessage, ResponseInstruction)> { None } @@ -129,7 +129,11 @@ impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler { }; Some((ReleaseHeldHtlc {}, responder.respond())) } - fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {} + fn handle_release_held_htlc( + &self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext, + _custom_data: Option>, + ) { + } } #[derive(Debug)] @@ -159,7 +163,7 @@ impl CustomOnionMessageHandler for TestCustomMessageHandler { type CustomMessage = TestCustomMessage; fn handle_custom_message( &self, message: Self::CustomMessage, _context: Option>, - responder: Option, + _custom_data: Option>, responder: Option, ) -> Option<(Self::CustomMessage, ResponseInstruction)> { match responder { Some(responder) => Some((message, responder.respond())), diff --git a/fuzz/src/refund_deser.rs b/fuzz/src/refund_deser.rs index d8d9881ef97..9311aecfbc9 100644 --- a/fuzz/src/refund_deser.rs +++ b/fuzz/src/refund_deser.rs @@ -76,6 +76,7 @@ fn build_response( htlc_minimum_msat: 1, }, payment_context, + custom_data: Vec::new(), }; let intermediate_nodes = [PaymentForwardNode { tlvs: ForwardTlvs { diff --git a/lightning-dns-resolver/src/lib.rs b/lightning-dns-resolver/src/lib.rs index 8f855cb5fb7..581e7f21584 100644 --- a/lightning-dns-resolver/src/lib.rs +++ b/lightning-dns-resolver/src/lib.rs @@ -107,9 +107,11 @@ impl DNSResolverMessageHandler for OMDomainResolver where PH::Target: DNSResolverMessageHandler, { - fn handle_dnssec_proof(&self, proof: DNSSECProof, context: DNSResolverContext) { + fn handle_dnssec_proof( + &self, proof: DNSSECProof, context: DNSResolverContext, custom_data: Option>, + ) { if let Some(proof_handler) = &self.proof_handler { - proof_handler.handle_dnssec_proof(proof, context); + proof_handler.handle_dnssec_proof(proof, context, custom_data); } } @@ -159,7 +161,7 @@ mod test { use bitcoin::secp256k1::{self, PublicKey, Secp256k1}; use bitcoin::Block; - use lightning::blinded_path::message::{BlindedMessagePath, MessageContext}; + use lightning::blinded_path::message::{self, BlindedMessagePath, MessageContext}; use lightning::blinded_path::NodeIdLookUp; use lightning::events::{Event, PaymentPurpose}; use lightning::ln::channelmanager::{PaymentId, Retry}; @@ -225,11 +227,13 @@ mod test { } fn create_blinded_paths( - &self, recipient: PublicKey, context: MessageContext, _peers: Vec, - secp_ctx: &Secp256k1, + &self, recipient: PublicKey, recipient_tlvs: message::ReceiveTlvs, + _peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { let keys = KeysManager::new(&[0; 32], 42, 43); - Ok(vec![BlindedMessagePath::one_hop(recipient, context, &keys, secp_ctx).unwrap()]) + Ok(vec![ + BlindedMessagePath::one_hop(recipient, recipient_tlvs, &keys, secp_ctx).unwrap() + ]) } } impl Deref for DirectlyConnectedRouter { @@ -251,8 +255,11 @@ mod test { panic!(); } - fn handle_dnssec_proof(&self, msg: DNSSECProof, context: DNSResolverContext) { - let mut proof = self.resolver.handle_dnssec_proof_for_uri(msg, context).unwrap(); + fn handle_dnssec_proof( + &self, msg: DNSSECProof, context: DNSResolverContext, custom_data: Option>, + ) { + let mut proof = + self.resolver.handle_dnssec_proof_for_uri(msg, context, custom_data).unwrap(); assert_eq!(proof.0.len(), 1); let payment = proof.0.pop().unwrap(); let mut result = Some((payment.0, payment.1, proof.1)); @@ -330,9 +337,12 @@ mod test { let (msg, context) = payer.resolver.resolve_name(payment_id, name.clone(), &*payer_keys).unwrap(); - let query_context = MessageContext::DNSResolver(context); + let recipient_tlvs = message::ReceiveTlvs { + context: Some(MessageContext::DNSResolver(context)), + custom_data: None, + }; let reply_path = - BlindedMessagePath::one_hop(payer_id, query_context, &*payer_keys, &secp_ctx).unwrap(); + BlindedMessagePath::one_hop(payer_id, recipient_tlvs, &*payer_keys, &secp_ctx).unwrap(); payer.pending_messages.lock().unwrap().push(( DNSResolverMessage::DNSSECQuery(msg), MessageSendInstructions::WithSpecifiedReplyPath { diff --git a/lightning/src/blinded_path/message.rs b/lightning/src/blinded_path/message.rs index 4d96434dd63..2e16709f691 100644 --- a/lightning/src/blinded_path/message.rs +++ b/lightning/src/blinded_path/message.rs @@ -55,9 +55,9 @@ impl Readable for BlindedMessagePath { impl BlindedMessagePath { /// Create a one-hop blinded path for a message. pub fn one_hop( - recipient_node_id: PublicKey, context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1 + recipient_node_id: PublicKey, recipient_tlvs: ReceiveTlvs, entropy_source: ES, secp_ctx: &Secp256k1 ) -> Result where ES::Target: EntropySource { - Self::new(&[], recipient_node_id, context, entropy_source, secp_ctx) + Self::new(&[], recipient_node_id, recipient_tlvs, entropy_source, secp_ctx) } /// Create a path for an onion message, to be forwarded along `node_pks`. The last node @@ -67,7 +67,7 @@ impl BlindedMessagePath { // TODO: make all payloads the same size with padding + add dummy hops pub fn new( intermediate_nodes: &[MessageForwardNode], recipient_node_id: PublicKey, - context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1, + recipient_tlvs: ReceiveTlvs, entropy_source: ES, secp_ctx: &Secp256k1, ) -> Result where ES::Target: EntropySource { let introduction_node = IntroductionNode::NodeId( intermediate_nodes.first().map_or(recipient_node_id, |n| n.node_id) @@ -80,7 +80,7 @@ impl BlindedMessagePath { blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret), blinded_hops: blinded_hops( secp_ctx, intermediate_nodes, recipient_node_id, - context, &blinding_secret, + recipient_tlvs, &blinding_secret, ).map_err(|_| ())?, })) } @@ -236,12 +236,28 @@ pub(crate) struct ForwardTlvs { pub(crate) next_blinding_override: Option, } -/// Similar to [`ForwardTlvs`], but these TLVs are for the final node. -pub(crate) struct ReceiveTlvs { +/// TLVs to encode in the final onion message packet's hop data. These TLVs are specific to the +/// recipient node and provide information necessary for final processing of the message. +/// When provided in a blinded route, they are encoded into [`BlindedHop::encrypted_payload`]. +#[derive(Clone)] +pub struct ReceiveTlvs { /// If `context` is `Some`, it is used to identify the blinded path that this onion message is /// sending to. This is useful for receivers to check that said blinded path is being used in /// the right context. - pub context: Option + pub context: Option, + + /// Custom data set by the user. If `custom_data` is `Some`, it will be provided to the message + /// recipient when the blinded path is used. + /// + /// This field allows encoding custom data intended to be provided back when the blinded path is used. + /// + /// ## Note on Forward Compatibility: + /// Users can encode any kind of data into the `Vec` bytes here. However, they should ensure + /// that the data is structured in a forward-compatible manner. This is especially important as + /// `ReceiveTlvs` created in one version of the software may still appear in messages received + /// shortly after a software upgrade. Proper forward compatibility helps prevent data loss or + /// misinterpretation in future versions. + pub custom_data: Option>, } impl Writeable for ForwardTlvs { @@ -265,6 +281,7 @@ impl Writeable for ReceiveTlvs { // TODO: write padding encode_tlv_stream!(writer, { (65537, self.context, option), + (65539, self.custom_data, option) }); Ok(()) } @@ -456,7 +473,7 @@ impl_writeable_tlv_based!(DNSResolverContext, { /// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`. pub(super) fn blinded_hops( secp_ctx: &Secp256k1, intermediate_nodes: &[MessageForwardNode], - recipient_node_id: PublicKey, context: MessageContext, session_priv: &SecretKey, + recipient_node_id: PublicKey, recipient_tlvs: ReceiveTlvs, session_priv: &SecretKey, ) -> Result, secp256k1::Error> { let pks = intermediate_nodes.iter().map(|node| node.node_id) .chain(core::iter::once(recipient_node_id)); @@ -468,7 +485,7 @@ pub(super) fn blinded_hops( None => NextMessageHop::NodeId(pubkey), }) .map(|next_hop| ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None })) - .chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs{ context: Some(context) }))); + .chain(core::iter::once(ControlTlvs::Receive(recipient_tlvs))); let path = pks.zip(tlvs); diff --git a/lightning/src/blinded_path/payment.rs b/lightning/src/blinded_path/payment.rs index 62ce7dec186..ff55ba5bf01 100644 --- a/lightning/src/blinded_path/payment.rs +++ b/lightning/src/blinded_path/payment.rs @@ -260,6 +260,15 @@ pub struct ReceiveTlvs { pub payment_constraints: PaymentConstraints, /// Context for the receiver of this payment. pub payment_context: PaymentContext, + /// Custom data set by the user. And is returned back when the blinded path is used. + /// + /// ## Note on Forward Compatibility: + /// Users can encode any kind of data into the `Vec` bytes here. However, they should ensure + /// that the data is structured in a forward-compatible manner. This is especially important as + /// `ReceiveTlvs` created in one version of the software may still appear in payments received + /// shortly after a software upgrade. Proper forward compatibility helps prevent data loss or + /// misinterpretation in future versions. + pub custom_data: Vec, } /// Data to construct a [`BlindedHop`] for sending a payment over. @@ -404,7 +413,8 @@ impl Writeable for ReceiveTlvs { encode_tlv_stream!(w, { (12, self.payment_constraints, required), (65536, self.payment_secret, required), - (65537, self.payment_context, required) + (65537, self.payment_context, required), + (65539, self.custom_data, (default_value, Vec::new())), }); Ok(()) } @@ -432,6 +442,7 @@ impl Readable for BlindedPaymentTlvs { (14, features, (option, encoding: (BlindedHopFeatures, WithoutLength))), (65536, payment_secret, option), (65537, payment_context, (default_value, PaymentContext::unknown())), + (65539, custom_data, (default_value, Vec::new())) }); let _padding: Option = _padding; @@ -452,6 +463,7 @@ impl Readable for BlindedPaymentTlvs { payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?, payment_constraints: payment_constraints.0.unwrap(), payment_context: payment_context.0.unwrap(), + custom_data: custom_data.0.unwrap(), })) } } @@ -683,6 +695,7 @@ mod tests { htlc_minimum_msat: 1, }, payment_context: PaymentContext::unknown(), + custom_data: Vec::new(), }; let htlc_maximum_msat = 100_000; let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, 12).unwrap(); @@ -702,6 +715,7 @@ mod tests { htlc_minimum_msat: 1, }, payment_context: PaymentContext::unknown(), + custom_data: Vec::new(), }; let blinded_payinfo = super::compute_payinfo(&[], &recv_tlvs, 4242, TEST_FINAL_CLTV as u16).unwrap(); assert_eq!(blinded_payinfo.fee_base_msat, 0); @@ -758,6 +772,7 @@ mod tests { htlc_minimum_msat: 3, }, payment_context: PaymentContext::unknown(), + custom_data: Vec::new(), }; let htlc_maximum_msat = 100_000; let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, TEST_FINAL_CLTV as u16).unwrap(); @@ -811,6 +826,7 @@ mod tests { htlc_minimum_msat: 1, }, payment_context: PaymentContext::unknown(), + custom_data: Vec::new(), }; let htlc_minimum_msat = 3798; assert!(super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_minimum_msat - 1, TEST_FINAL_CLTV as u16).is_err()); @@ -868,6 +884,7 @@ mod tests { htlc_minimum_msat: 1, }, payment_context: PaymentContext::unknown(), + custom_data: Vec::new(), }; let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, 10_000, TEST_FINAL_CLTV as u16).unwrap(); diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index 92991f4b228..0ce23a65995 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -698,15 +698,15 @@ pub enum Event { /// [`ChannelManager::fail_htlc_backwards`] or [`ChannelManager::fail_htlc_backwards_with_reason`] /// to free up resources for this HTLC and avoid network congestion. /// - /// If [`Event::PaymentClaimable::onion_fields`] is `Some`, and includes custom TLVs with even type + /// If [`Event::PaymentClaimable::onion_fields`] is `Some`, and includes sender custom TLVs with even type /// numbers, you should use [`ChannelManager::fail_htlc_backwards_with_reason`] with /// [`FailureCode::InvalidOnionPayload`] if you fail to understand and handle the contents, or - /// [`ChannelManager::claim_funds_with_known_custom_tlvs`] upon successful handling. - /// If you don't intend to check for custom TLVs, you can simply use - /// [`ChannelManager::claim_funds`], which will automatically fail back even custom TLVs. + /// [`ChannelManager::claim_funds_with_known_sender_custom_tlvs`] upon successful handling. + /// If you don't intend to check for sender custom TLVs, you can simply use + /// [`ChannelManager::claim_funds`], which will automatically fail back even sender custom TLVs. /// /// If you fail to call [`ChannelManager::claim_funds`], - /// [`ChannelManager::claim_funds_with_known_custom_tlvs`], + /// [`ChannelManager::claim_funds_with_known_sender_custom_tlvs`], /// [`ChannelManager::fail_htlc_backwards`], or /// [`ChannelManager::fail_htlc_backwards_with_reason`] within the HTLC's timeout, the HTLC will /// be automatically failed. @@ -725,7 +725,7 @@ pub enum Event { /// returning `Err(ReplayEvent ())`) and will be persisted across restarts. /// /// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds - /// [`ChannelManager::claim_funds_with_known_custom_tlvs`]: crate::ln::channelmanager::ChannelManager::claim_funds_with_known_custom_tlvs + /// [`ChannelManager::claim_funds_with_known_sender_custom_tlvs`]: crate::ln::channelmanager::ChannelManager::claim_funds_with_known_sender_custom_tlvs /// [`FailureCode::InvalidOnionPayload`]: crate::ln::channelmanager::FailureCode::InvalidOnionPayload /// [`ChannelManager::fail_htlc_backwards`]: crate::ln::channelmanager::ChannelManager::fail_htlc_backwards /// [`ChannelManager::fail_htlc_backwards_with_reason`]: crate::ln::channelmanager::ChannelManager::fail_htlc_backwards_with_reason diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index 302dc87a1e2..f4907233885 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -77,6 +77,7 @@ fn blinded_payment_path( intro_node_min_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_minimum_msat), }, payment_context: PaymentContext::unknown(), + custom_data: Vec::new(), }; let mut secp_ctx = Secp256k1::new(); BlindedPaymentPath::new( @@ -123,6 +124,7 @@ fn do_one_hop_blinded_path(success: bool) { htlc_minimum_msat: chan_upd.htlc_minimum_msat, }, payment_context: PaymentContext::unknown(), + custom_data: Vec::new(), }; let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( @@ -167,6 +169,7 @@ fn mpp_to_one_hop_blinded_path() { htlc_minimum_msat: chan_upd_1_3.htlc_minimum_msat, }, payment_context: PaymentContext::unknown(), + custom_data: Vec::new(), }; let blinded_path = BlindedPaymentPath::new( &[], nodes[3].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16, @@ -1376,6 +1379,7 @@ fn custom_tlvs_to_blinded_path() { htlc_minimum_msat: chan_upd.htlc_minimum_msat, }, payment_context: PaymentContext::unknown(), + custom_data: vec![43, 43], }; let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( @@ -1389,7 +1393,8 @@ fn custom_tlvs_to_blinded_path() { ); let recipient_onion_fields = RecipientOnionFields::spontaneous_empty() - .with_custom_tlvs(vec![((1 << 16) + 1, vec![42, 42])]) + .with_user_custom_data(vec![43, 43]) + .with_sender_custom_tlvs(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(); @@ -1402,11 +1407,13 @@ fn custom_tlvs_to_blinded_path() { let path = &[&nodes[1]]; let args = PassAlongPathArgs::new(&nodes[0], path, amt_msat, payment_hash, ev) .with_payment_secret(payment_secret) - .with_custom_tlvs(recipient_onion_fields.custom_tlvs.clone()); + .with_user_custom_data(recipient_onion_fields.user_custom_data.clone()) + .with_sender_custom_tlvs(recipient_onion_fields.sender_custom_tlvs.clone()); do_pass_along_path(args); claim_payment_along_route( ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1]]], payment_preimage) - .with_custom_tlvs(recipient_onion_fields.custom_tlvs.clone()) + .with_user_custom_data(recipient_onion_fields.user_custom_data.clone()) + .with_sender_custom_tlvs(recipient_onion_fields.sender_custom_tlvs.clone()) ); } diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index a58863ab1a2..5ec8d10efd4 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -33,7 +33,7 @@ use bitcoin::secp256k1::Secp256k1; use bitcoin::{secp256k1, Sequence, Weight}; use crate::events::FundingInfo; -use crate::blinded_path::message::{AsyncPaymentsContext, MessageContext, OffersContext}; +use crate::blinded_path::message::{self, AsyncPaymentsContext, MessageContext, OffersContext}; use crate::blinded_path::NodeIdLookUp; use crate::blinded_path::message::{BlindedMessagePath, MessageForwardNode}; use crate::blinded_path::payment::{BlindedPaymentPath, Bolt12OfferContext, Bolt12RefundContext, PaymentConstraints, PaymentContext, ReceiveTlvs}; @@ -189,12 +189,18 @@ pub enum PendingHTLCRouting { /// provide the onion shared secret used to decrypt the next level of forwarding /// instructions. phantom_shared_secret: Option<[u8; 32]>, - /// Custom TLVs which were set by the sender. + /// Sender custom TLVs which were set by the sender. /// /// For HTLCs received by LDK, this will ultimately be exposed in /// [`Event::PaymentClaimable::onion_fields`] as - /// [`RecipientOnionFields::custom_tlvs`]. - custom_tlvs: Vec<(u64, Vec)>, + /// [`RecipientOnionFields::sender_custom_tlvs`]. + sender_custom_tlvs: Vec<(u64, Vec)>, + /// Custom TLVs set by the receiver in the blinded path used to reach them. + /// + /// For HTLCs received by LDK, this will be exposed in + /// [`Event::PaymentClaimable::onion_fields`] as + /// [`RecipientOnionFields::user_custom_data`]. + user_custom_data: Vec, /// Set if this HTLC is the final hop in a multi-hop blinded path. requires_blinded_error: bool, }, @@ -220,11 +226,16 @@ pub enum PendingHTLCRouting { /// /// Used to track when we should expire pending HTLCs that go unclaimed. incoming_cltv_expiry: u32, - /// Custom TLVs which were set by the sender. + /// Sender custom TLVs which were set by the sender. + /// + /// For HTLCs received by LDK, these will ultimately bubble back up as + /// [`RecipientOnionFields::sender_custom_tlvs`]. + sender_custom_tlvs: Vec<(u64, Vec)>, + /// Custom TLVs set by the receiver in the blinded path used to reach them. /// /// For HTLCs received by LDK, these will ultimately bubble back up as - /// [`RecipientOnionFields::custom_tlvs`]. - custom_tlvs: Vec<(u64, Vec)>, + /// [`RecipientOnionFields::user_custom_data`]. + user_custom_data: Vec, /// Set if this HTLC is the final hop in a multi-hop blinded path. requires_blinded_error: bool, /// Set if we are receiving a keysend to a blinded path, meaning we created the @@ -912,17 +923,17 @@ struct ClaimablePayments { impl ClaimablePayments { /// Moves a payment from [`Self::claimable_payments`] to [`Self::pending_claiming_payments`]. /// - /// If `custom_tlvs_known` is false and custom even TLVs are set by the sender, the set of + /// If `sender_custom_tlvs_known` is false and custom even TLVs are set by the sender, the set of /// pending HTLCs will be returned in the `Err` variant of this method. They MUST then be /// failed by the caller as they will not be in either [`Self::claimable_payments`] or /// [`Self::pending_claiming_payments`]. /// - /// If `custom_tlvs_known` is true, and a matching payment is found, it will always be moved. + /// If `sender_custom_tlvs_known` is true, and a matching payment is found, it will always be moved. /// /// If no payment is found, `Err(Vec::new())` is returned. fn begin_claiming_payment( &mut self, payment_hash: PaymentHash, node_signer: &S, logger: &L, - inbound_payment_id_secret: &[u8; 32], custom_tlvs_known: bool, + inbound_payment_id_secret: &[u8; 32], sender_custom_tlvs_known: bool, ) -> Result<(Vec, ClaimingPayment), Vec> where L::Target: Logger, S::Target: NodeSigner, { @@ -939,10 +950,10 @@ impl ClaimablePayments { } } - if let Some(RecipientOnionFields { custom_tlvs, .. }) = &payment.onion_fields { - if !custom_tlvs_known && custom_tlvs.iter().any(|(typ, _)| typ % 2 == 0) { + if let Some(RecipientOnionFields { sender_custom_tlvs, .. }) = &payment.onion_fields { + if !sender_custom_tlvs_known && sender_custom_tlvs.iter().any(|(typ, _)| typ % 2 == 0) { log_info!(logger, "Rejecting payment with payment hash {} as we cannot accept payment with unknown even TLVs: {}", - &payment_hash, log_iter!(custom_tlvs.iter().map(|(typ, _)| typ).filter(|typ| *typ % 2 == 0))); + &payment_hash, log_iter!(sender_custom_tlvs.iter().map(|(typ, _)| typ).filter(|typ| *typ % 2 == 0))); return Err(payment.htlcs); } } @@ -6051,25 +6062,26 @@ where ) = match routing { PendingHTLCRouting::Receive { payment_data, payment_metadata, payment_context, - incoming_cltv_expiry, phantom_shared_secret, custom_tlvs, - requires_blinded_error: _ + incoming_cltv_expiry, phantom_shared_secret, sender_custom_tlvs, + user_custom_data, requires_blinded_error: _ } => { let _legacy_hop_data = Some(payment_data.clone()); let onion_fields = RecipientOnionFields { payment_secret: Some(payment_data.payment_secret), - payment_metadata, custom_tlvs }; + payment_metadata, sender_custom_tlvs, user_custom_data }; (incoming_cltv_expiry, OnionPayload::Invoice { _legacy_hop_data }, Some(payment_data), payment_context, phantom_shared_secret, onion_fields, true) }, PendingHTLCRouting::ReceiveKeysend { payment_data, payment_preimage, payment_metadata, - incoming_cltv_expiry, custom_tlvs, requires_blinded_error: _, - has_recipient_created_payment_secret, + incoming_cltv_expiry, sender_custom_tlvs, user_custom_data, + requires_blinded_error: _, has_recipient_created_payment_secret, } => { let onion_fields = RecipientOnionFields { payment_secret: payment_data.as_ref().map(|data| data.payment_secret), payment_metadata, - custom_tlvs, + sender_custom_tlvs, + user_custom_data }; (incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage), payment_data, None, None, onion_fields, has_recipient_created_payment_secret) @@ -6893,9 +6905,9 @@ where /// event matches your expectation. If you fail to do so and call this method, you may provide /// the sender "proof-of-payment" when they did not fulfill the full expected payment. /// - /// This function will fail the payment if it has custom TLVs with even type numbers, as we - /// will assume they are unknown. If you intend to accept even custom TLVs, you should use - /// [`claim_funds_with_known_custom_tlvs`]. + /// This function will fail the payment if it has sender custom TLVs with even type numbers, as we + /// will assume they are unknown. If you intend to accept even sender custom TLVs, you should use + /// [`claim_funds_with_known_sender_custom_tlvs`]. /// /// [`Event::PaymentClaimable`]: crate::events::Event::PaymentClaimable /// [`Event::PaymentClaimable::claim_deadline`]: crate::events::Event::PaymentClaimable::claim_deadline @@ -6903,12 +6915,12 @@ where /// [`process_pending_events`]: EventsProvider::process_pending_events /// [`create_inbound_payment`]: Self::create_inbound_payment /// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash - /// [`claim_funds_with_known_custom_tlvs`]: Self::claim_funds_with_known_custom_tlvs + /// [`claim_funds_with_known_sender_custom_tlvs`]: Self::claim_funds_with_known_sender_custom_tlvs pub fn claim_funds(&self, payment_preimage: PaymentPreimage) { self.claim_payment_internal(payment_preimage, false); } - /// This is a variant of [`claim_funds`] that allows accepting a payment with custom TLVs with + /// This is a variant of [`claim_funds`] that allows accepting a payment with sender custom TLVs with /// even type numbers. /// /// # Note @@ -6917,11 +6929,11 @@ where /// claim, otherwise you may unintentionally agree to some protocol you do not understand. /// /// [`claim_funds`]: Self::claim_funds - pub fn claim_funds_with_known_custom_tlvs(&self, payment_preimage: PaymentPreimage) { + pub fn claim_funds_with_known_sender_custom_tlvs(&self, payment_preimage: PaymentPreimage) { self.claim_payment_internal(payment_preimage, true); } - fn claim_payment_internal(&self, payment_preimage: PaymentPreimage, custom_tlvs_known: bool) { + fn claim_payment_internal(&self, payment_preimage: PaymentPreimage, sender_custom_tlvs_known: bool) { let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).to_byte_array()); let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); @@ -6929,7 +6941,7 @@ where let (sources, claiming_payment) = { let res = self.claimable_payments.lock().unwrap().begin_claiming_payment( payment_hash, &self.node_signer, &self.logger, &self.inbound_payment_id_secret, - custom_tlvs_known, + sender_custom_tlvs_known, ); match res { @@ -10181,7 +10193,7 @@ where Ok((payment_hash, payment_secret)) => { let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {}); let payment_paths = self.create_blinded_payment_paths( - amount_msats, payment_secret, payment_context + amount_msats, payment_secret, payment_context, None ) .map_err(|_| Bolt12SemanticError::MissingPaths)?; @@ -10452,8 +10464,14 @@ where .map(|(node_id, _)| *node_id) .collect::>(); + // todo: Introduce ability to add custom_data here. + let recipient_tlvs = message::ReceiveTlvs { + context: Some(context), + custom_data: None, + }; + self.message_router - .create_blinded_paths(recipient, context, peers, secp_ctx) + .create_blinded_paths(recipient, recipient_tlvs, peers, secp_ctx) .and_then(|paths| (!paths.is_empty()).then(|| paths).ok_or(())) } @@ -10480,15 +10498,21 @@ where }) .collect::>(); + // todo: Introduce ability to add custom_data here. + let recipient_tlvs = message::ReceiveTlvs { + context: Some(MessageContext::Offers(context)), + custom_data: None, + }; + self.message_router - .create_compact_blinded_paths(recipient, MessageContext::Offers(context), peers, secp_ctx) + .create_compact_blinded_paths(recipient, recipient_tlvs, peers, secp_ctx) .and_then(|paths| (!paths.is_empty()).then(|| paths).ok_or(())) } /// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to /// [`Router::create_blinded_payment_paths`]. fn create_blinded_payment_paths( - &self, amount_msats: u64, payment_secret: PaymentSecret, payment_context: PaymentContext + &self, amount_msats: u64, payment_secret: PaymentSecret, payment_context: PaymentContext, custom_data: Option> ) -> Result, ()> { let secp_ctx = &self.secp_ctx; @@ -10503,6 +10527,7 @@ where htlc_minimum_msat: 1, }, payment_context, + custom_data: custom_data.unwrap_or_default() }; self.router.create_blinded_payment_paths( payee_node_id, first_hops, payee_tlvs, amount_msats, secp_ctx @@ -11952,7 +11977,8 @@ where L::Target: Logger, { fn handle_message( - &self, message: OffersMessage, context: Option, responder: Option, + // todo: Allow user to analyse `custom_data`. + &self, message: OffersMessage, context: Option, _custom_data: Option>, responder: Option, ) -> Option<(OffersMessage, ResponseInstruction)> { let secp_ctx = &self.secp_ctx; let expanded_key = &self.inbound_payment_key; @@ -12041,7 +12067,7 @@ where invoice_request: invoice_request.fields(), }); let payment_paths = match self.create_blinded_payment_paths( - amount_msats, payment_secret, payment_context + amount_msats, payment_secret, payment_context, None ) { Ok(payment_paths) => payment_paths, Err(()) => { @@ -12095,7 +12121,7 @@ where let nonce = Nonce::from_entropy_source(&*self.entropy_source); let hmac = payment_hash.hmac_for_offer_payment(nonce, expanded_key); let context = MessageContext::Offers(OffersContext::InboundPayment { payment_hash, nonce, hmac }); - Some((OffersMessage::Invoice(invoice), responder.respond_with_reply_path(context))) + Some((OffersMessage::Invoice(invoice), responder.respond_with_reply_path(context, None))) }, Err(error) => Some((OffersMessage::InvoiceError(error.into()), responder.respond())), } @@ -12189,8 +12215,9 @@ where None } - fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) { + fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext, _custom_data: Option>) { #[cfg(async_payments)] { + // todo: Introduce ability to analyse `custom_data` let AsyncPaymentsContext::OutboundPayment { payment_id, hmac, nonce } = _context; if payment_id.verify_for_async_payment(hmac, nonce, &self.inbound_payment_key).is_err() { return } if let Err(e) = self.send_payment_for_static_invoice(payment_id) { @@ -12226,8 +12253,8 @@ where None } - fn handle_dnssec_proof(&self, message: DNSSECProof, context: DNSResolverContext) { - let offer_opt = self.hrn_resolver.handle_dnssec_proof_for_offer(message, context); + fn handle_dnssec_proof(&self, message: DNSSECProof, context: DNSResolverContext, custom_data: Option>) { + let offer_opt = self.hrn_resolver.handle_dnssec_proof_for_offer(message, context, custom_data); #[cfg_attr(not(feature = "_test_utils"), allow(unused_mut))] if let Some((completed_requests, mut offer)) = offer_opt { for (name, payment_id) in completed_requests { @@ -12375,9 +12402,10 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting, (1, phantom_shared_secret, option), (2, incoming_cltv_expiry, required), (3, payment_metadata, option), - (5, custom_tlvs, optional_vec), + (5, sender_custom_tlvs, optional_vec), (7, requires_blinded_error, (default_value, false)), (9, payment_context, option), + (11, user_custom_data, optional_vec), }, (2, ReceiveKeysend) => { (0, payment_preimage, required), @@ -12385,8 +12413,9 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting, (2, incoming_cltv_expiry, required), (3, payment_metadata, option), (4, payment_data, option), // Added in 0.0.116 - (5, custom_tlvs, optional_vec), + (5, sender_custom_tlvs, optional_vec), (7, has_recipient_created_payment_secret, (default_value, false)), + (9, user_custom_data, optional_vec), }, ); @@ -15351,7 +15380,7 @@ mod tests { payment_data: Some(msgs::FinalOnionHopData { payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat, }), - custom_tlvs: Vec::new(), + sender_custom_tlvs: Vec::new(), user_custom_data: Vec::new(), }; // Check that if the amount we received + the penultimate hop extra fee is less than the sender // intended amount, we fail the payment. @@ -15373,7 +15402,7 @@ mod tests { payment_data: Some(msgs::FinalOnionHopData { payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat, }), - custom_tlvs: Vec::new(), + sender_custom_tlvs: Vec::new(), user_custom_data: Vec::new(), }; let current_height: u32 = node[0].node.best_block.read().unwrap().height; assert!(create_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]), @@ -15397,7 +15426,7 @@ mod tests { payment_data: Some(msgs::FinalOnionHopData { payment_secret: PaymentSecret([0; 32]), total_msat: 100, }), - custom_tlvs: Vec::new(), + sender_custom_tlvs: Vec::new(), user_custom_data: Vec::new(), }, [0; 32], PaymentHash([0; 32]), 100, 23, None, true, None, current_height); // Should not return an error as this condition: diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 6c6b24bce7c..a88c1c0d9a7 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -2633,7 +2633,8 @@ pub struct PassAlongPathArgs<'a, 'b, 'c, 'd> { pub clear_recipient_events: bool, pub expected_preimage: Option, pub is_probe: bool, - pub custom_tlvs: Vec<(u64, Vec)>, + pub sender_custom_tlvs: Vec<(u64, Vec)>, + pub user_custom_data: Vec, pub payment_metadata: Option>, pub expected_failure: Option, } @@ -2646,7 +2647,7 @@ impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> { Self { origin_node, expected_path, recv_value, payment_hash, payment_secret: None, event, payment_claimable_expected: true, clear_recipient_events: true, expected_preimage: None, - is_probe: false, custom_tlvs: Vec::new(), payment_metadata: None, expected_failure: None, + is_probe: false, sender_custom_tlvs: Vec::new(), user_custom_data: Vec::new(), payment_metadata: None, expected_failure: None, } } pub fn without_clearing_recipient_events(mut self) -> Self { @@ -2670,8 +2671,12 @@ impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> { self.expected_preimage = Some(payment_preimage); self } - pub fn with_custom_tlvs(mut self, custom_tlvs: Vec<(u64, Vec)>) -> Self { - self.custom_tlvs = custom_tlvs; + pub fn with_user_custom_data(mut self, custom_tlvs: Vec) -> Self { + self.user_custom_data = custom_tlvs; + self + } + pub fn with_sender_custom_tlvs(mut self, sender_custom_tlvs: Vec<(u64, Vec)>) -> Self { + self.sender_custom_tlvs = sender_custom_tlvs; self } pub fn with_payment_metadata(mut self, payment_metadata: Vec) -> Self { @@ -2689,7 +2694,7 @@ pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option let PassAlongPathArgs { origin_node, expected_path, recv_value, payment_hash: our_payment_hash, payment_secret: our_payment_secret, event: ev, payment_claimable_expected, - clear_recipient_events, expected_preimage, is_probe, custom_tlvs, payment_metadata, + clear_recipient_events, expected_preimage, is_probe, sender_custom_tlvs, user_custom_data, payment_metadata, expected_failure } = args; @@ -2723,7 +2728,8 @@ pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option assert_eq!(our_payment_hash, *payment_hash); assert_eq!(node.node.get_our_node_id(), receiver_node_id.unwrap()); assert!(onion_fields.is_some()); - assert_eq!(onion_fields.as_ref().unwrap().custom_tlvs, custom_tlvs); + assert_eq!(onion_fields.as_ref().unwrap().sender_custom_tlvs, sender_custom_tlvs); + assert_eq!(onion_fields.as_ref().unwrap().user_custom_data, user_custom_data); assert_eq!(onion_fields.as_ref().unwrap().payment_metadata, payment_metadata); match &purpose { PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => { @@ -2842,7 +2848,8 @@ pub struct ClaimAlongRouteArgs<'a, 'b, 'c, 'd> { pub expected_min_htlc_overpay: Vec, pub skip_last: bool, pub payment_preimage: PaymentPreimage, - pub custom_tlvs: Vec<(u64, Vec)>, + pub sender_custom_tlvs: Vec<(u64, Vec)>, + pub user_custom_data: Vec, // Allow forwarding nodes to have taken 1 msat more fee than expected based on the downstream // fulfill amount. // @@ -2861,7 +2868,7 @@ impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> { Self { origin_node, expected_paths, expected_extra_fees: vec![0; expected_paths.len()], expected_min_htlc_overpay: vec![0; expected_paths.len()], skip_last: false, payment_preimage, - allow_1_msat_fee_overpay: false, custom_tlvs: vec![], + allow_1_msat_fee_overpay: false, sender_custom_tlvs: vec![], user_custom_data: vec![] } } pub fn skip_last(mut self, skip_last: bool) -> Self { @@ -2880,8 +2887,12 @@ impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> { self.allow_1_msat_fee_overpay = true; self } - pub fn with_custom_tlvs(mut self, custom_tlvs: Vec<(u64, Vec)>) -> Self { - self.custom_tlvs = custom_tlvs; + pub fn with_sender_custom_tlvs(mut self, sender_custom_tlvs: Vec<(u64, Vec)>) -> Self { + self.sender_custom_tlvs = sender_custom_tlvs; + self + } + pub fn with_user_custom_data(mut self, user_custom_data: Vec) -> Self { + self.user_custom_data = user_custom_data; self } } @@ -2889,7 +2900,8 @@ impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> { pub fn pass_claimed_payment_along_route(args: ClaimAlongRouteArgs) -> u64 { let ClaimAlongRouteArgs { origin_node, expected_paths, expected_extra_fees, expected_min_htlc_overpay, skip_last, - payment_preimage: our_payment_preimage, allow_1_msat_fee_overpay, custom_tlvs, + payment_preimage: our_payment_preimage, allow_1_msat_fee_overpay, sender_custom_tlvs, + user_custom_data } = args; let claim_event = expected_paths[0].last().unwrap().node.get_and_clear_pending_events(); assert_eq!(claim_event.len(), 1); @@ -2909,7 +2921,7 @@ pub fn pass_claimed_payment_along_route(args: ClaimAlongRouteArgs) -> u64 { assert_eq!(preimage, our_payment_preimage); assert_eq!(htlcs.len(), expected_paths.len()); // One per path. assert_eq!(htlcs.iter().map(|h| h.value_msat).sum::(), amount_msat); - assert_eq!(onion_fields.as_ref().unwrap().custom_tlvs, custom_tlvs); + assert_eq!(onion_fields.as_ref().unwrap().sender_custom_tlvs, sender_custom_tlvs); check_claimed_htlcs_match_route(origin_node, expected_paths, htlcs); fwd_amt_msat = amount_msat; }, @@ -2926,7 +2938,8 @@ pub fn pass_claimed_payment_along_route(args: ClaimAlongRouteArgs) -> u64 { assert_eq!(&payment_hash.0, &Sha256::hash(&our_payment_preimage.0)[..]); assert_eq!(htlcs.len(), expected_paths.len()); // One per path. assert_eq!(htlcs.iter().map(|h| h.value_msat).sum::(), amount_msat); - assert_eq!(onion_fields.as_ref().unwrap().custom_tlvs, custom_tlvs); + assert_eq!(onion_fields.as_ref().unwrap().sender_custom_tlvs, sender_custom_tlvs); + assert_eq!(onion_fields.as_ref().unwrap().user_custom_data, user_custom_data); check_claimed_htlcs_match_route(origin_node, expected_paths, htlcs); fwd_amt_msat = amount_msat; } diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index af30003ee5d..2420f67ba52 100644 --- a/lightning/src/ln/max_payment_path_len_tests.rs +++ b/lightning/src/ln/max_payment_path_len_tests.rs @@ -7,7 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. -//! Tests for calculating the maximum length of a path based on the payment metadata, custom TLVs, +//! Tests for calculating the maximum length of a path based on the payment metadata, sender custom TLVs, //! and/or blinded paths present. use bitcoin::secp256k1::{Secp256k1, PublicKey}; @@ -58,7 +58,8 @@ fn large_payment_metadata() { }), payment_metadata: None, keysend_preimage: None, - custom_tlvs: &Vec::new(), + sender_custom_tlvs: &Vec::new(), + user_custom_data: &Vec::new(), sender_intended_htlc_amt_msat: MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, cltv_expiry_height: nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, }.serialized_length(); @@ -75,7 +76,7 @@ fn large_payment_metadata() { let mut recipient_onion_max_md_size = RecipientOnionFields { payment_secret: Some(payment_secret), payment_metadata: Some(payment_metadata.clone()), - custom_tlvs: Vec::new(), + sender_custom_tlvs: Vec::new(), user_custom_data: Vec::new(), }; nodes[0].node.send_payment(payment_hash, recipient_onion_max_md_size.clone(), PaymentId(payment_hash.0), route_0_1.route_params.clone().unwrap(), Retry::Attempts(0)).unwrap(); check_added_monitors!(nodes[0], 1); @@ -123,7 +124,7 @@ fn large_payment_metadata() { let mut recipient_onion_allows_2_hops = RecipientOnionFields { payment_secret: Some(payment_secret_2), payment_metadata: Some(vec![42; max_metadata_len - INTERMED_PAYLOAD_LEN_ESTIMATE]), - custom_tlvs: Vec::new(), + sender_custom_tlvs: Vec::new(), user_custom_data: Vec::new(), }; let mut route_params_0_2 = route_0_2.route_params.clone().unwrap(); route_params_0_2.payment_params.max_path_length = 2; @@ -164,6 +165,7 @@ fn one_hop_blinded_path_with_custom_tlv() { htlc_minimum_msat: chan_upd_1_2.htlc_minimum_msat, }, payment_context: PaymentContext::unknown(), + custom_data: Vec::new(), }; let mut secp_ctx = Secp256k1::new(); let blinded_path = BlindedPaymentPath::new( @@ -185,7 +187,8 @@ fn one_hop_blinded_path_with_custom_tlv() { intro_node_blinding_point: Some(blinded_path.blinding_point()), keysend_preimage: None, invoice_request: None, - custom_tlvs: &Vec::new() + sender_custom_tlvs: &Vec::new(), + user_custom_data: &Vec::new() }.serialized_length(); let max_custom_tlv_len = 1300 - crate::util::ser::BigSize(CUSTOM_TLV_TYPE).serialized_length() // custom TLV type @@ -196,7 +199,7 @@ fn one_hop_blinded_path_with_custom_tlv() { // Check that we can send the maximum custom TLV with 1 blinded hop. let recipient_onion_max_custom_tlv_size = RecipientOnionFields::spontaneous_empty() - .with_custom_tlvs(vec![(CUSTOM_TLV_TYPE, vec![42; max_custom_tlv_len])]) + .with_sender_custom_tlvs(vec![(CUSTOM_TLV_TYPE, vec![42; max_custom_tlv_len])]) .unwrap(); nodes[1].node.send_payment(payment_hash, recipient_onion_max_custom_tlv_size.clone(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[1], 1); @@ -206,16 +209,16 @@ fn one_hop_blinded_path_with_custom_tlv() { let path = &[&nodes[2]]; let args = PassAlongPathArgs::new(&nodes[1], path, amt_msat, payment_hash, events.pop().unwrap()) .with_payment_secret(payment_secret) - .with_custom_tlvs(recipient_onion_max_custom_tlv_size.custom_tlvs.clone()); + .with_sender_custom_tlvs(recipient_onion_max_custom_tlv_size.sender_custom_tlvs.clone()); do_pass_along_path(args); claim_payment_along_route( ClaimAlongRouteArgs::new(&nodes[1], &[&[&nodes[2]]], payment_preimage) - .with_custom_tlvs(recipient_onion_max_custom_tlv_size.custom_tlvs.clone()) + .with_sender_custom_tlvs(recipient_onion_max_custom_tlv_size.sender_custom_tlvs.clone()) ); // If 1 byte is added to the custom TLV value, we'll fail to send prior to pathfinding. let mut recipient_onion_too_large_custom_tlv = recipient_onion_max_custom_tlv_size.clone(); - recipient_onion_too_large_custom_tlv.custom_tlvs[0].1.push(42); + recipient_onion_too_large_custom_tlv.sender_custom_tlvs[0].1.push(42); let err = nodes[1].node.send_payment(payment_hash, recipient_onion_too_large_custom_tlv, PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap_err(); assert_eq!(err, RetryableSendFailure::OnionPacketSizeExceeded); @@ -227,7 +230,7 @@ fn one_hop_blinded_path_with_custom_tlv() { // If we remove enough custom TLV bytes to allow for 1 intermediate unblinded hop, we're now able // to send nodes[0] -> nodes[2]. let mut recipient_onion_allows_2_hops = recipient_onion_max_custom_tlv_size.clone(); - recipient_onion_allows_2_hops.custom_tlvs[0].1.resize(max_custom_tlv_len - INTERMED_PAYLOAD_LEN_ESTIMATE, 0); + recipient_onion_allows_2_hops.sender_custom_tlvs[0].1.resize(max_custom_tlv_len - INTERMED_PAYLOAD_LEN_ESTIMATE, 0); nodes[0].node.send_payment(payment_hash, recipient_onion_allows_2_hops.clone(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); @@ -236,11 +239,11 @@ fn one_hop_blinded_path_with_custom_tlv() { let path = &[&nodes[1], &nodes[2]]; let args = PassAlongPathArgs::new(&nodes[0], path, amt_msat, payment_hash, events.pop().unwrap()) .with_payment_secret(payment_secret) - .with_custom_tlvs(recipient_onion_allows_2_hops.custom_tlvs.clone()); + .with_sender_custom_tlvs(recipient_onion_allows_2_hops.sender_custom_tlvs.clone()); do_pass_along_path(args); claim_payment_along_route( ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage) - .with_custom_tlvs(recipient_onion_allows_2_hops.custom_tlvs) + .with_sender_custom_tlvs(recipient_onion_allows_2_hops.sender_custom_tlvs) ); } @@ -291,7 +294,7 @@ fn blinded_path_with_custom_tlv() { // Check that we can send the maximum custom TLV size with 0 intermediate unblinded hops. let recipient_onion_max_custom_tlv_size = RecipientOnionFields::spontaneous_empty() - .with_custom_tlvs(vec![(CUSTOM_TLV_TYPE, vec![42; max_custom_tlv_len])]) + .with_sender_custom_tlvs(vec![(CUSTOM_TLV_TYPE, vec![42; max_custom_tlv_len])]) .unwrap(); nodes[1].node.send_payment(payment_hash, recipient_onion_max_custom_tlv_size.clone(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[1], 1); @@ -301,16 +304,16 @@ fn blinded_path_with_custom_tlv() { let path = &[&nodes[2], &nodes[3]]; let args = PassAlongPathArgs::new(&nodes[1], path, amt_msat, payment_hash, events.pop().unwrap()) .with_payment_secret(payment_secret) - .with_custom_tlvs(recipient_onion_max_custom_tlv_size.custom_tlvs.clone()); + .with_sender_custom_tlvs(recipient_onion_max_custom_tlv_size.sender_custom_tlvs.clone()); do_pass_along_path(args); claim_payment_along_route( ClaimAlongRouteArgs::new(&nodes[1], &[&[&nodes[2], &nodes[3]]], payment_preimage) - .with_custom_tlvs(recipient_onion_max_custom_tlv_size.custom_tlvs.clone()) + .with_sender_custom_tlvs(recipient_onion_max_custom_tlv_size.sender_custom_tlvs.clone()) ); // If 1 byte is added to the custom TLV value, we'll fail to send prior to pathfinding. let mut recipient_onion_too_large_custom_tlv = recipient_onion_max_custom_tlv_size.clone(); - recipient_onion_too_large_custom_tlv.custom_tlvs[0].1.push(42); + recipient_onion_too_large_custom_tlv.sender_custom_tlvs[0].1.push(42); let err = nodes[1].node.send_payment(payment_hash, recipient_onion_too_large_custom_tlv.clone(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap_err(); assert_eq!(err, RetryableSendFailure::OnionPacketSizeExceeded); @@ -334,7 +337,7 @@ fn blinded_path_with_custom_tlv() { // If we remove enough custom TLV bytes to allow for 1 intermediate unblinded hop, we're now able // to send nodes[0] -> nodes[3]. let mut recipient_onion_allows_2_hops = recipient_onion_max_custom_tlv_size.clone(); - recipient_onion_allows_2_hops.custom_tlvs[0].1.resize(max_custom_tlv_len - INTERMED_PAYLOAD_LEN_ESTIMATE, 0); + recipient_onion_allows_2_hops.sender_custom_tlvs[0].1.resize(max_custom_tlv_len - INTERMED_PAYLOAD_LEN_ESTIMATE, 0); nodes[0].node.send_payment(payment_hash, recipient_onion_allows_2_hops.clone(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap(); check_added_monitors(&nodes[0], 1); @@ -343,11 +346,11 @@ fn blinded_path_with_custom_tlv() { let path = &[&nodes[1], &nodes[2], &nodes[3]]; let args = PassAlongPathArgs::new(&nodes[0], path, amt_msat, payment_hash, events.pop().unwrap()) .with_payment_secret(payment_secret) - .with_custom_tlvs(recipient_onion_allows_2_hops.custom_tlvs.clone()); + .with_sender_custom_tlvs(recipient_onion_allows_2_hops.sender_custom_tlvs.clone()); do_pass_along_path(args); claim_payment_along_route( ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3]]], payment_preimage) - .with_custom_tlvs(recipient_onion_allows_2_hops.custom_tlvs) + .with_sender_custom_tlvs(recipient_onion_allows_2_hops.sender_custom_tlvs) ); } diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index c20d2027209..c6bf4195c58 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1769,7 +1769,8 @@ mod fuzzy_internal_msgs { payment_data: Option, payment_metadata: Option>, keysend_preimage: Option, - custom_tlvs: Vec<(u64, Vec)>, + sender_custom_tlvs: Vec<(u64, Vec)>, + user_custom_data: Vec, sender_intended_htlc_amt_msat: u64, cltv_expiry_height: u32, }, @@ -1790,7 +1791,8 @@ mod fuzzy_internal_msgs { payment_context: PaymentContext, intro_node_blinding_point: Option, keysend_preimage: Option, - custom_tlvs: Vec<(u64, Vec)>, + sender_custom_tlvs: Vec<(u64, Vec)>, + user_custom_data: Vec, } } @@ -1812,7 +1814,8 @@ mod fuzzy_internal_msgs { payment_data: Option, payment_metadata: Option<&'a Vec>, keysend_preimage: Option, - custom_tlvs: &'a Vec<(u64, Vec)>, + sender_custom_tlvs: &'a Vec<(u64, Vec)>, + user_custom_data: &'a Vec, sender_intended_htlc_amt_msat: u64, cltv_expiry_height: u32, }, @@ -1827,7 +1830,8 @@ mod fuzzy_internal_msgs { encrypted_tlvs: &'a Vec, intro_node_blinding_point: Option, // Set if the introduction node of the blinded path is the final node keysend_preimage: Option, - custom_tlvs: &'a Vec<(u64, Vec)>, + sender_custom_tlvs: &'a Vec<(u64, Vec)>, + user_custom_data: &'a Vec, invoice_request: Option<&'a InvoiceRequest>, } } @@ -1866,7 +1870,8 @@ mod fuzzy_internal_msgs { encrypted_tlvs: &'a Vec, intro_node_blinding_point: Option, // Set if the introduction node of the blinded path is the final node keysend_preimage: Option, - custom_tlvs: &'a Vec<(u64, Vec)>, + sender_custom_tlvs: &'a Vec<(u64, Vec)>, + user_custom_data: &'a Vec, } } @@ -2739,13 +2744,14 @@ impl<'a> Writeable for OutboundOnionPayload<'a> { }, Self::Receive { ref payment_data, ref payment_metadata, ref keysend_preimage, sender_intended_htlc_amt_msat, - cltv_expiry_height, ref custom_tlvs, + cltv_expiry_height, ref sender_custom_tlvs, ref user_custom_data } => { - // We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`] + // We need to update [`ln::outbound_payment::RecipientOnionFields::with_sender_custom_tlvs`] // to reject any reserved types in the experimental range if new ones are ever // standardized. + let user_custom_data = (!user_custom_data.is_empty()).then(|| (65539, user_custom_data.to_vec())); let keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode())); - let mut custom_tlvs: Vec<&(u64, Vec)> = custom_tlvs.iter().chain(keysend_tlv.iter()).collect(); + let mut custom_tlvs: Vec<&(u64, Vec)> = sender_custom_tlvs.iter().chain(keysend_tlv.iter()).chain(user_custom_data.iter()).collect(); custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ); _encode_varint_length_prefixed_tlv!(w, { (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required), @@ -2762,14 +2768,16 @@ impl<'a> Writeable for OutboundOnionPayload<'a> { }, Self::BlindedReceive { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, encrypted_tlvs, - intro_node_blinding_point, keysend_preimage, ref invoice_request, ref custom_tlvs, + intro_node_blinding_point, keysend_preimage, ref invoice_request, ref sender_custom_tlvs, ref user_custom_data } => { - // We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`] + // We need to update [`ln::outbound_payment::RecipientOnionFields::with_sender_custom_tlvs`] // to reject any reserved types in the experimental range if new ones are ever // standardized. + let user_custom_data = (!user_custom_data.is_empty()).then(|| (65539, user_custom_data.to_vec())); let invoice_request_tlv = invoice_request.map(|invreq| (77_777, invreq.encode())); // TODO: update TLV type once the async payments spec is merged let keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode())); - let mut custom_tlvs: Vec<&(u64, Vec)> = custom_tlvs.iter() + let mut custom_tlvs: Vec<&(u64, Vec)> = sender_custom_tlvs.iter() + .chain(user_custom_data.iter()) .chain(invoice_request_tlv.iter()) .chain(keysend_tlv.iter()) .collect(); @@ -2822,7 +2830,11 @@ impl<'a> Writeable for OutboundTrampolinePayload<'a> { (12, intro_node_blinding_point, option) }); }, - Self::BlindedReceive { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, encrypted_tlvs, intro_node_blinding_point, keysend_preimage, custom_tlvs } => { + Self::BlindedReceive { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, encrypted_tlvs, intro_node_blinding_point, keysend_preimage, sender_custom_tlvs, user_custom_data} => { + let user_custom_data = (!user_custom_data.is_empty()).then(|| (65539, user_custom_data.to_vec())); + let custom_tlvs: Vec<&(u64, Vec)> = sender_custom_tlvs.iter() + .chain(user_custom_data.iter()) + .collect(); _encode_varint_length_prefixed_tlv!(w, { (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required), (4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required), @@ -2874,6 +2886,12 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPayload wh Ok(true) }); + let (user_custom_data, sender_custom_tlvs): (Vec<(u64, Vec)>, Vec<(u64, Vec)>) = custom_tlvs + .into_iter() + .partition(|(tlv_type, _)| *tlv_type == 65539); + + let user_custom_data = user_custom_data.into_iter().next().map(|(_, data)| data).unwrap_or_else(Vec::new); + if amt.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) } if intro_node_blinding_point.is_some() && update_add_blinding_point.is_some() { return Err(DecodeError::InvalidValue) @@ -2907,9 +2925,11 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPayload wh next_blinding_override, }) }, + // Note: The custom data in the receive tlvs is not used here. ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(ReceiveTlvs { - payment_secret, payment_constraints, payment_context + payment_secret, payment_constraints, payment_context, custom_data })} => { + debug_assert_eq!(custom_data, user_custom_data, "The custom TLVs in ReceiveTlvs must match the ones read from serialization."); if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) } Ok(Self::BlindedReceive { sender_intended_htlc_amt_msat: amt.ok_or(DecodeError::InvalidValue)?, @@ -2920,7 +2940,8 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPayload wh payment_context, intro_node_blinding_point, keysend_preimage, - custom_tlvs, + sender_custom_tlvs, + user_custom_data, }) }, } @@ -2948,7 +2969,8 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPayload wh keysend_preimage, sender_intended_htlc_amt_msat: amt.ok_or(DecodeError::InvalidValue)?, cltv_expiry_height: cltv_value.ok_or(DecodeError::InvalidValue)?, - custom_tlvs, + sender_custom_tlvs, + user_custom_data, }) } } @@ -4559,7 +4581,8 @@ mod tests { keysend_preimage: None, sender_intended_htlc_amt_msat: 0x0badf00d01020304, cltv_expiry_height: 0xffffffff, - custom_tlvs: &vec![], + sender_custom_tlvs: &vec![], + user_custom_data: &vec![], }; let encoded_value = outbound_msg.encode(); let target_value = >::from_hex("1002080badf00d010203040404ffffffff").unwrap(); @@ -4587,7 +4610,8 @@ mod tests { keysend_preimage: None, sender_intended_htlc_amt_msat: 0x0badf00d01020304, cltv_expiry_height: 0xffffffff, - custom_tlvs: &vec![], + sender_custom_tlvs: &vec![], + user_custom_data: &vec![], }; let encoded_value = outbound_msg.encode(); let target_value = >::from_hex("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap(); @@ -4603,18 +4627,20 @@ mod tests { sender_intended_htlc_amt_msat, cltv_expiry_height, payment_metadata: None, keysend_preimage: None, - custom_tlvs, + sender_custom_tlvs, + user_custom_data, } = inbound_msg { assert_eq!(payment_secret, expected_payment_secret); assert_eq!(sender_intended_htlc_amt_msat, 0x0badf00d01020304); assert_eq!(cltv_expiry_height, 0xffffffff); - assert_eq!(custom_tlvs, vec![]); + assert_eq!(sender_custom_tlvs, vec![]); + assert_eq!(user_custom_data, vec![]); } else { panic!(); } } #[test] - fn encoding_final_onion_hop_data_with_bad_custom_tlvs() { - // If custom TLVs have type number within the range reserved for protocol, treat them as if + fn encoding_final_onion_hop_data_with_bad_sender_custom_tlvs() { + // If sender custom TLVs have type number within the range reserved for protocol, treat them as if // they're unknown let bad_type_range_tlvs = vec![ ((1 << 16) - 4, vec![42]), @@ -4624,7 +4650,8 @@ mod tests { payment_data: None, payment_metadata: None, keysend_preimage: None, - custom_tlvs: &bad_type_range_tlvs, + sender_custom_tlvs: &bad_type_range_tlvs, + user_custom_data: &vec![], sender_intended_htlc_amt_msat: 0x0badf00d01020304, cltv_expiry_height: 0xffffffff, }; @@ -4635,20 +4662,20 @@ mod tests { ((1 << 16) - 3, vec![42]), ((1 << 16) - 1, vec![42; 32]), ]; - if let msgs::OutboundOnionPayload::Receive { ref mut custom_tlvs, .. } = msg { - *custom_tlvs = &good_type_range_tlvs; + if let msgs::OutboundOnionPayload::Receive { ref mut sender_custom_tlvs, .. } = msg { + *sender_custom_tlvs = &good_type_range_tlvs; } let encoded_value = msg.encode(); let inbound_msg = ReadableArgs::read(&mut Cursor::new(&encoded_value[..]), (None, &node_signer)).unwrap(); match inbound_msg { - msgs::InboundOnionPayload::Receive { custom_tlvs, .. } => assert!(custom_tlvs.is_empty()), + msgs::InboundOnionPayload::Receive { sender_custom_tlvs, .. } => assert!(sender_custom_tlvs.is_empty()), _ => panic!(), } } #[test] - fn encoding_final_onion_hop_data_with_custom_tlvs() { - let expected_custom_tlvs = vec![ + fn encoding_final_onion_hop_data_with_sender_custom_tlvs() { + let expected_sender_custom_tlvs = vec![ (5482373483, vec![0x12, 0x34]), (5482373487, vec![0x42u8; 8]), ]; @@ -4656,7 +4683,8 @@ mod tests { payment_data: None, payment_metadata: None, keysend_preimage: None, - custom_tlvs: &expected_custom_tlvs, + sender_custom_tlvs: &expected_sender_custom_tlvs, + user_custom_data: &vec![], sender_intended_htlc_amt_msat: 0x0badf00d01020304, cltv_expiry_height: 0xffffffff, }; @@ -4669,12 +4697,12 @@ mod tests { payment_data: None, payment_metadata: None, keysend_preimage: None, - custom_tlvs, + sender_custom_tlvs, sender_intended_htlc_amt_msat, cltv_expiry_height: outgoing_cltv_value, .. } = inbound_msg { - assert_eq!(custom_tlvs, expected_custom_tlvs); + assert_eq!(sender_custom_tlvs, expected_sender_custom_tlvs); assert_eq!(sender_intended_htlc_amt_msat, 0x0badf00d01020304); assert_eq!(outgoing_cltv_value, 0xffffffff); } else { panic!(); } @@ -4784,7 +4812,8 @@ mod tests { encrypted_tlvs: &>::from_hex("bcd747394fbd4d99588da075a623316e15a576df5bc785cccc7cd6ec7b398acce6faf520175f9ec920f2ef261cdb83dc28cc3a0eeb970107b3306489bf771ef5b1213bca811d345285405861d08a655b6c237fa247a8b4491beee20c878a60e9816492026d8feb9dafa84585b253978db6a0aa2945df5ef445c61e801fb82f43d5f00716baf9fc9b3de50bc22950a36bda8fc27bfb1242e5860c7e687438d4133e058770361a19b6c271a2a07788d34dccc27e39b9829b061a4d960eac4a2c2b0f4de506c24f9af3868c0aff6dda27281c").unwrap(), intro_node_blinding_point: None, keysend_preimage: None, - custom_tlvs: &vec![], + sender_custom_tlvs: &vec![], + user_custom_data: &vec![], }; let eve_payload = trampoline_payload_eve.encode().to_lower_hex_string(); assert_eq!(eve_payload, "e4020408f0d18004030c35000ad1bcd747394fbd4d99588da075a623316e15a576df5bc785cccc7cd6ec7b398acce6faf520175f9ec920f2ef261cdb83dc28cc3a0eeb970107b3306489bf771ef5b1213bca811d345285405861d08a655b6c237fa247a8b4491beee20c878a60e9816492026d8feb9dafa84585b253978db6a0aa2945df5ef445c61e801fb82f43d5f00716baf9fc9b3de50bc22950a36bda8fc27bfb1242e5860c7e687438d4133e058770361a19b6c271a2a07788d34dccc27e39b9829b061a4d960eac4a2c2b0f4de506c24f9af3868c0aff6dda27281c120408f0d180"); diff --git a/lightning/src/ln/offers_tests.rs b/lightning/src/ln/offers_tests.rs index a7eeebf9848..6978d8caa04 100644 --- a/lightning/src/ln/offers_tests.rs +++ b/lightning/src/ln/offers_tests.rs @@ -193,8 +193,8 @@ fn claim_bolt12_payment<'a, 'b, 'c>( fn extract_offer_nonce<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, message: &OnionMessage) -> Nonce { match node.onion_messenger.peel_onion_message(message) { - Ok(PeeledOnion::Receive(_, Some(MessageContext::Offers(OffersContext::InvoiceRequest { nonce })), _)) => nonce, - Ok(PeeledOnion::Receive(_, context, _)) => panic!("Unexpected onion message context: {:?}", context), + Ok(PeeledOnion::Receive(_, Some(MessageContext::Offers(OffersContext::InvoiceRequest { nonce })), _, _)) => nonce, + Ok(PeeledOnion::Receive(_, context, _, _)) => panic!("Unexpected onion message context: {:?}", context), Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"), Err(e) => panic!("Failed to process onion message {:?}", e), } @@ -204,7 +204,7 @@ fn extract_invoice_request<'a, 'b, 'c>( node: &Node<'a, 'b, 'c>, message: &OnionMessage ) -> (InvoiceRequest, BlindedMessagePath) { match node.onion_messenger.peel_onion_message(message) { - Ok(PeeledOnion::Receive(message, _, reply_path)) => match message { + Ok(PeeledOnion::Receive(message, _, _, reply_path)) => match message { ParsedOnionMessageContents::Offers(offers_message) => match offers_message { OffersMessage::InvoiceRequest(invoice_request) => (invoice_request, reply_path.unwrap()), OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice), @@ -221,7 +221,7 @@ fn extract_invoice_request<'a, 'b, 'c>( fn extract_invoice<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, message: &OnionMessage) -> (Bolt12Invoice, BlindedMessagePath) { match node.onion_messenger.peel_onion_message(message) { - Ok(PeeledOnion::Receive(message, _, reply_path)) => match message { + Ok(PeeledOnion::Receive(message, _, _, reply_path)) => match message { ParsedOnionMessageContents::Offers(offers_message) => match offers_message { OffersMessage::InvoiceRequest(invoice_request) => panic!("Unexpected invoice_request: {:?}", invoice_request), OffersMessage::Invoice(invoice) => (invoice, reply_path.unwrap()), @@ -240,7 +240,7 @@ fn extract_invoice_error<'a, 'b, 'c>( node: &Node<'a, 'b, 'c>, message: &OnionMessage ) -> InvoiceError { match node.onion_messenger.peel_onion_message(message) { - Ok(PeeledOnion::Receive(message, _, _)) => match message { + Ok(PeeledOnion::Receive(message, _, _, _)) => match message { ParsedOnionMessageContents::Offers(offers_message) => match offers_message { OffersMessage::InvoiceRequest(invoice_request) => panic!("Unexpected invoice_request: {:?}", invoice_request), OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice), diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index 193cdd1582a..1fc22383ca2 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -134,19 +134,19 @@ pub(super) fn create_recv_pending_htlc_info( counterparty_skimmed_fee_msat: Option, current_height: u32 ) -> Result { let ( - payment_data, keysend_preimage, custom_tlvs, onion_amt_msat, onion_cltv_expiry, + payment_data, keysend_preimage, sender_custom_tlvs, user_custom_data, onion_amt_msat, onion_cltv_expiry, payment_metadata, payment_context, requires_blinded_error, has_recipient_created_payment_secret ) = match hop_data { msgs::InboundOnionPayload::Receive { - payment_data, keysend_preimage, custom_tlvs, sender_intended_htlc_amt_msat, + payment_data, keysend_preimage, sender_custom_tlvs, user_custom_data, sender_intended_htlc_amt_msat, cltv_expiry_height, payment_metadata, .. } => - (payment_data, keysend_preimage, custom_tlvs, sender_intended_htlc_amt_msat, + (payment_data, keysend_preimage, sender_custom_tlvs, user_custom_data, sender_intended_htlc_amt_msat, cltv_expiry_height, payment_metadata, None, false, keysend_preimage.is_none()), msgs::InboundOnionPayload::BlindedReceive { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, payment_secret, intro_node_blinding_point, payment_constraints, payment_context, keysend_preimage, - custom_tlvs + sender_custom_tlvs, user_custom_data } => { check_blinded_payment_constraints( sender_intended_htlc_amt_msat, cltv_expiry, &payment_constraints @@ -159,7 +159,7 @@ pub(super) fn create_recv_pending_htlc_info( } })?; let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat }; - (Some(payment_data), keysend_preimage, custom_tlvs, + (Some(payment_data), keysend_preimage, sender_custom_tlvs, user_custom_data, sender_intended_htlc_amt_msat, cltv_expiry_height, None, Some(payment_context), intro_node_blinding_point.is_none(), true) } @@ -232,7 +232,8 @@ pub(super) fn create_recv_pending_htlc_info( payment_preimage, payment_metadata, incoming_cltv_expiry: onion_cltv_expiry, - custom_tlvs, + sender_custom_tlvs, + user_custom_data, requires_blinded_error, has_recipient_created_payment_secret, } @@ -243,7 +244,8 @@ pub(super) fn create_recv_pending_htlc_info( payment_context, incoming_cltv_expiry: onion_cltv_expiry, phantom_shared_secret, - custom_tlvs, + sender_custom_tlvs, + user_custom_data, requires_blinded_error, } } else { @@ -530,7 +532,7 @@ mod tests { ) = payment_onion_args(bob_pk, charlie_pk); // Ensure the onion will not fit all the payloads by adding a large custom TLV. - recipient_onion.custom_tlvs.push((13377331, vec![0; 1156])); + recipient_onion.sender_custom_tlvs.push((13377331, vec![0; 1156])); let path = Path { hops, blinded_tail: None, }; let onion_keys = super::onion_utils::construct_onion_keys(&secp_ctx, &path, &session_priv).unwrap(); diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 960209c0e0a..53ff6abd985 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -267,7 +267,8 @@ where intro_node_blinding_point: blinding_point.take(), keysend_preimage: *keysend_preimage, invoice_request, - custom_tlvs: &recipient_onion.custom_tlvs, + sender_custom_tlvs: &recipient_onion.sender_custom_tlvs, + user_custom_data: &recipient_onion.user_custom_data, }, ); } else { @@ -289,7 +290,8 @@ where }), payment_metadata: recipient_onion.payment_metadata.as_ref(), keysend_preimage: *keysend_preimage, - custom_tlvs: &recipient_onion.custom_tlvs, + sender_custom_tlvs: &recipient_onion.sender_custom_tlvs, + user_custom_data: &recipient_onion.user_custom_data, sender_intended_htlc_amt_msat: value_msat, cltv_expiry_height: cltv, }, diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 1b546ac72ac..5594e86b84f 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -104,7 +104,8 @@ pub(crate) enum PendingOutboundPayment { payment_metadata: Option>, keysend_preimage: Option, invoice_request: Option, - custom_tlvs: Vec<(u64, Vec)>, + sender_custom_tlvs: Vec<(u64, Vec)>, + user_custom_data: 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, @@ -470,7 +471,7 @@ pub enum RetryableSendFailure { /// [`Event::PaymentSent`]: crate::events::Event::PaymentSent /// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed DuplicatePayment, - /// The [`RecipientOnionFields::payment_metadata`], [`RecipientOnionFields::custom_tlvs`], or + /// The [`RecipientOnionFields::payment_metadata`], [`RecipientOnionFields::sender_custom_tlvs`], or /// [`BlindedPaymentPath`]s provided are too large and caused us to exceed the maximum onion /// packet size of 1300 bytes. /// @@ -613,14 +614,17 @@ pub struct RecipientOnionFields { /// [`Self::payment_secret`] and while nearly all lightning senders support secrets, metadata /// may not be supported as universally. pub payment_metadata: Option>, - /// See [`Self::custom_tlvs`] for more info. - pub(super) custom_tlvs: Vec<(u64, Vec)>, + /// See [`Self::sender_custom_tlvs`] for more info. + pub(super) sender_custom_tlvs: Vec<(u64, Vec)>, + /// See [`Self::user_custom_data`] for more info. + pub(super) user_custom_data: Vec } impl_writeable_tlv_based!(RecipientOnionFields, { (0, payment_secret, option), - (1, custom_tlvs, optional_vec), + (1, sender_custom_tlvs, optional_vec), (2, payment_metadata, option), + (3, user_custom_data, optional_vec), }); impl RecipientOnionFields { @@ -628,7 +632,7 @@ impl RecipientOnionFields { /// set of onion fields for today's BOLT11 invoices - most nodes require a [`PaymentSecret`] /// but do not require or provide any further data. pub fn secret_only(payment_secret: PaymentSecret) -> Self { - Self { payment_secret: Some(payment_secret), payment_metadata: None, custom_tlvs: Vec::new() } + Self { payment_secret: Some(payment_secret), payment_metadata: None, sender_custom_tlvs: Vec::new(), user_custom_data: Vec::new() } } /// Creates a new [`RecipientOnionFields`] with no fields. This generally does not create @@ -640,10 +644,10 @@ 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() } + Self { payment_secret: None, payment_metadata: None, sender_custom_tlvs: Vec::new(), user_custom_data: Vec::new()} } - /// Creates a new [`RecipientOnionFields`] from an existing one, adding custom TLVs. Each + /// Creates a new [`RecipientOnionFields`] from an existing one, adding sender custom TLVs. Each /// TLV is provided as a `(u64, Vec)` for the type number and serialized value /// respectively. TLV type numbers must be unique and within the range /// reserved for custom types, i.e. >= 2^16, otherwise this method will return `Err(())`. @@ -651,11 +655,11 @@ impl RecipientOnionFields { /// This method will also error for types in the experimental range which have been /// standardized within the protocol, which only includes 5482373484 (keysend) for now. /// - /// See [`Self::custom_tlvs`] for more info. - pub fn with_custom_tlvs(mut self, mut custom_tlvs: Vec<(u64, Vec)>) -> Result { - custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ); + /// See [`Self::sender_custom_tlvs`] for more info. + pub fn with_sender_custom_tlvs(mut self, mut sender_custom_tlvs: Vec<(u64, Vec)>) -> Result { + sender_custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ); let mut prev_type = None; - for (typ, _) in custom_tlvs.iter() { + for (typ, _) in sender_custom_tlvs.iter() { if *typ < 1 << 16 { return Err(()); } if *typ == 5482373484 { return Err(()); } // keysend if *typ == 77_777 { return Err(()); } // invoice requests for async payments @@ -665,38 +669,59 @@ impl RecipientOnionFields { } prev_type = Some(*typ); } - self.custom_tlvs = custom_tlvs; + self.sender_custom_tlvs = sender_custom_tlvs; Ok(self) } - /// Gets the custom TLVs that will be sent or have been received. + /// Creates a new [`RecipientOnionFields`] from an existing one, adding user custom data. + /// + /// See [`Self::user_custom_data`] for more info. + pub fn with_user_custom_data(mut self, custom_data: Vec) -> Self { + self.user_custom_data = custom_data; + self + } + + /// Gets the user custom data that will be sent or have been received. /// - /// Custom TLVs allow sending extra application-specific data with a payment. They provide - /// additional flexibility on top of payment metadata, as while other implementations may - /// require `payment_metadata` to reflect metadata provided in an invoice, custom TLVs - /// do not have this restriction. + /// user custom data allow receiving back extra application-specific + /// data that was set by the receiver in the Blinded Path used by the sender + /// to reach them. + /// + /// This provides additional flexibility to users by enabling them to include + /// extra data they want to receive back, which can be used for authentication + /// or other purposes. + pub fn user_custom_data(&self) -> &Vec { + &self.user_custom_data + } + + /// Gets the sender custom TLVs that will be sent or have been received. + /// + /// Sender custom TLVs allow sending extra application-specific data with a payment. + /// They provide additional flexibility on top of payment metadata, as while other + /// implementations may require `payment_metadata` to reflect metadata provided in + /// an invoice, custom TLVs do not have this restriction. /// /// Note that if this field is non-empty, it will contain strictly increasing TLVs, each /// represented by a `(u64, Vec)` for its type number and serialized value respectively. - /// This is validated when setting this field using [`Self::with_custom_tlvs`]. + /// This is validated when setting this field using [`Self::with_sender_custom_tlvs`]. #[cfg(not(c_bindings))] - pub fn custom_tlvs(&self) -> &Vec<(u64, Vec)> { - &self.custom_tlvs + pub fn sender_custom_tlvs(&self) -> &Vec<(u64, Vec)> { + &self.sender_custom_tlvs } - /// Gets the custom TLVs that will be sent or have been received. + /// Gets the sender custom TLVs that will be sent or have been received. /// - /// Custom TLVs allow sending extra application-specific data with a payment. They provide + /// Sender custom TLVs allow sending extra application-specific data with a payment. They provide /// additional flexibility on top of payment metadata, as while other implementations may - /// require `payment_metadata` to reflect metadata provided in an invoice, custom TLVs + /// require `payment_metadata` to reflect metadata provided in an invoice, sender custom TLVs /// do not have this restriction. /// /// Note that if this field is non-empty, it will contain strictly increasing TLVs, each /// represented by a `(u64, Vec)` for its type number and serialized value respectively. - /// This is validated when setting this field using [`Self::with_custom_tlvs`]. + /// This is validated when setting this field using [`Self::with_sender_custom_tlvs`]. #[cfg(c_bindings)] - pub fn custom_tlvs(&self) -> Vec<(u64, Vec)> { - self.custom_tlvs.clone() + pub fn sender_custom_tlvs(&self) -> Vec<(u64, Vec)> { + self.sender_custom_tlvs.clone() } /// When we have received some HTLC(s) towards an MPP payment, as we receive further HTLC(s) we @@ -710,8 +735,8 @@ impl RecipientOnionFields { if self.payment_secret != further_htlc_fields.payment_secret { return Err(()); } if self.payment_metadata != further_htlc_fields.payment_metadata { return Err(()); } - let tlvs = &mut self.custom_tlvs; - let further_tlvs = &mut further_htlc_fields.custom_tlvs; + let tlvs = &mut self.sender_custom_tlvs; + let further_tlvs = &mut further_htlc_fields.sender_custom_tlvs; let even_tlvs = tlvs.iter().filter(|(typ, _)| *typ % 2 == 0); let further_even_tlvs = further_tlvs.iter().filter(|(typ, _)| *typ % 2 == 0); @@ -944,7 +969,8 @@ impl OutboundPayments { let recipient_onion = RecipientOnionFields { payment_secret: None, payment_metadata: None, - custom_tlvs: vec![], + sender_custom_tlvs: vec![], + user_custom_data: vec![], }; let route = match self.find_initial_route( payment_id, payment_hash, &recipient_onion, keysend_preimage, invoice_request, @@ -1361,7 +1387,7 @@ impl OutboundPayments { match payment.get() { PendingOutboundPayment::Retryable { total_msat, keysend_preimage, payment_secret, payment_metadata, - custom_tlvs, pending_amt_msat, invoice_request, .. + sender_custom_tlvs, user_custom_data, pending_amt_msat, invoice_request, .. } => { const RETRY_OVERFLOW_PERCENTAGE: u64 = 10; let retry_amt_msat = route.get_total_amount(); @@ -1381,7 +1407,8 @@ impl OutboundPayments { let recipient_onion = RecipientOnionFields { payment_secret: *payment_secret, payment_metadata: payment_metadata.clone(), - custom_tlvs: custom_tlvs.clone(), + sender_custom_tlvs: sender_custom_tlvs.clone(), + user_custom_data: user_custom_data.clone(), }; let keysend_preimage = *keysend_preimage; let invoice_request = invoice_request.clone(); @@ -1625,7 +1652,8 @@ impl OutboundPayments { payment_metadata: recipient_onion.payment_metadata, keysend_preimage, invoice_request, - custom_tlvs: recipient_onion.custom_tlvs, + sender_custom_tlvs: recipient_onion.sender_custom_tlvs, + user_custom_data: recipient_onion.user_custom_data, starting_block_height: best_block_height, total_msat: route.get_total_amount(), remaining_max_total_routing_fee_msat: @@ -2251,7 +2279,7 @@ impl OutboundPayments { payment_metadata: None, // only used for retries, and we'll never retry on startup 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 - custom_tlvs: Vec::new(), // only used for retries, and we'll never retry on startup + sender_custom_tlvs: Vec::new(), user_custom_data: 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, @@ -2335,10 +2363,11 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment, (6, total_msat, required), (7, payment_metadata, option), (8, pending_amt_msat, required), - (9, custom_tlvs, optional_vec), + (9, sender_custom_tlvs, optional_vec), (10, starting_block_height, required), (11, remaining_max_total_routing_fee_msat, option), (13, invoice_request, option), + (15, user_custom_data, optional_vec), (not_written, retry_strategy, (static_value, None)), (not_written, attempts, (static_value, PaymentAttempts::new())), }, @@ -2409,25 +2438,25 @@ mod tests { use alloc::collections::VecDeque; #[test] - fn test_recipient_onion_fields_with_custom_tlvs() { + fn test_recipient_onion_fields_with_sender_custom_tlvs() { let onion_fields = RecipientOnionFields::spontaneous_empty(); let bad_type_range_tlvs = vec![ (0, vec![42]), (1, vec![42; 32]), ]; - assert!(onion_fields.clone().with_custom_tlvs(bad_type_range_tlvs).is_err()); + assert!(onion_fields.clone().with_sender_custom_tlvs(bad_type_range_tlvs).is_err()); let keysend_tlv = vec![ (5482373484, vec![42; 32]), ]; - assert!(onion_fields.clone().with_custom_tlvs(keysend_tlv).is_err()); + assert!(onion_fields.clone().with_sender_custom_tlvs(keysend_tlv).is_err()); let good_tlvs = vec![ ((1 << 16) + 1, vec![42]), ((1 << 16) + 3, vec![42; 32]), ]; - assert!(onion_fields.with_custom_tlvs(good_tlvs).is_ok()); + assert!(onion_fields.with_sender_custom_tlvs(good_tlvs).is_ok()); } #[test] diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index a6cd55ba056..9e5c09a9620 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -3670,20 +3670,20 @@ fn claim_from_closed_chan() { } #[test] -fn test_custom_tlvs_basic() { - do_test_custom_tlvs(false, false, false); - do_test_custom_tlvs(true, false, false); +fn test_sender_custom_tlvs_basic() { + do_test_sender_custom_tlvs(false, false, false); + do_test_sender_custom_tlvs(true, false, false); } #[test] -fn test_custom_tlvs_explicit_claim() { - // Test that when receiving even custom TLVs the user must explicitly accept in case they +fn test_sender_custom_tlvs_explicit_claim() { + // Test that when receiving even sender custom TLVs the user must explicitly accept in case they // are unknown. - do_test_custom_tlvs(false, true, false); - do_test_custom_tlvs(false, true, true); + do_test_sender_custom_tlvs(false, true, false); + do_test_sender_custom_tlvs(false, true, true); } -fn do_test_custom_tlvs(spontaneous: bool, even_tlvs: bool, known_tlvs: bool) { +fn do_test_sender_custom_tlvs(spontaneous: bool, even_tlvs: bool, known_tlvs: bool) { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None; 2]); @@ -3694,14 +3694,15 @@ fn do_test_custom_tlvs(spontaneous: bool, even_tlvs: bool, known_tlvs: bool) { let amt_msat = 100_000; let (mut route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(&nodes[0], &nodes[1], amt_msat); let payment_id = PaymentId(our_payment_hash.0); - let custom_tlvs = vec![ + let sender_custom_tlvs = vec![ (if even_tlvs { 5482373482 } else { 5482373483 }, vec![1, 2, 3, 4]), (5482373487, vec![0x42u8; 16]), ]; let onion_fields = RecipientOnionFields { payment_secret: if spontaneous { None } else { Some(our_payment_secret) }, payment_metadata: None, - custom_tlvs: custom_tlvs.clone() + sender_custom_tlvs: sender_custom_tlvs.clone(), + user_custom_data: Vec::new(), }; if spontaneous { nodes[0].node.send_spontaneous_payment(&route, Some(our_payment_preimage), onion_fields, payment_id).unwrap(); @@ -3723,24 +3724,24 @@ fn do_test_custom_tlvs(spontaneous: bool, even_tlvs: bool, known_tlvs: bool) { assert_eq!(events.len(), 1); match events[0] { Event::PaymentClaimable { ref onion_fields, .. } => { - assert_eq!(onion_fields.clone().unwrap().custom_tlvs().clone(), custom_tlvs); + assert_eq!(onion_fields.clone().unwrap().sender_custom_tlvs().clone(), sender_custom_tlvs); }, _ => panic!("Unexpected event"), } match (known_tlvs, even_tlvs) { (true, _) => { - nodes[1].node.claim_funds_with_known_custom_tlvs(our_payment_preimage); + nodes[1].node.claim_funds_with_known_sender_custom_tlvs(our_payment_preimage); let expected_total_fee_msat = pass_claimed_payment_along_route( ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1]]], our_payment_preimage) - .with_custom_tlvs(custom_tlvs) + .with_sender_custom_tlvs(sender_custom_tlvs) ); expect_payment_sent!(&nodes[0], our_payment_preimage, Some(expected_total_fee_msat)); }, (false, false) => { claim_payment_along_route( ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1]]], our_payment_preimage) - .with_custom_tlvs(custom_tlvs) + .with_sender_custom_tlvs(sender_custom_tlvs) ); }, (false, true) => { @@ -3754,7 +3755,7 @@ fn do_test_custom_tlvs(spontaneous: bool, even_tlvs: bool, known_tlvs: bool) { #[test] fn test_retry_custom_tlvs() { - // Test that custom TLVs are successfully sent on retries + // Test that sender custom TLVs are successfully sent on retries 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]); @@ -3774,9 +3775,10 @@ fn test_retry_custom_tlvs() { let payment_id = PaymentId(payment_hash.0); let mut route_params = route.route_params.clone().unwrap(); - let custom_tlvs = vec![((1 << 16) + 1, vec![0x42u8; 16])]; + let sender_custom_tlvs = vec![((1 << 16) + 1, vec![0x42u8; 16])]; + let user_custom_data = vec![0x43u8; 16]; let onion_fields = RecipientOnionFields::secret_only(payment_secret); - let onion_fields = onion_fields.with_custom_tlvs(custom_tlvs.clone()).unwrap(); + let onion_fields = onion_fields.with_user_custom_data(user_custom_data.clone()).with_sender_custom_tlvs(sender_custom_tlvs.clone()).unwrap(); nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); nodes[0].node.send_payment(payment_hash, onion_fields, @@ -3828,51 +3830,54 @@ fn test_retry_custom_tlvs() { let path = &[&nodes[1], &nodes[2]]; let args = PassAlongPathArgs::new(&nodes[0], path, 1_000_000, payment_hash, events.pop().unwrap()) .with_payment_secret(payment_secret) - .with_custom_tlvs(custom_tlvs.clone()); + .with_user_custom_data(user_custom_data.clone()) + .with_sender_custom_tlvs(sender_custom_tlvs.clone()); do_pass_along_path(args); claim_payment_along_route( ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage) - .with_custom_tlvs(custom_tlvs) + .with_user_custom_data(user_custom_data.clone()) + .with_sender_custom_tlvs(sender_custom_tlvs) ); } #[test] -fn test_custom_tlvs_consistency() { +fn test_sender_custom_tlvs_consistency() { let even_type_1 = 1 << 16; let odd_type_1 = (1 << 16)+ 1; let even_type_2 = (1 << 16) + 2; - let odd_type_2 = (1 << 16) + 3; + // (1<<16) + 3 is reserved for user_custom_data. + let odd_type_2 = (1 << 16) + 5; let value_1 = || vec![1, 2, 3, 4]; let differing_value_1 = || vec![1, 2, 3, 5]; let value_2 = || vec![42u8; 16]; // Drop missing odd tlvs - do_test_custom_tlvs_consistency( + do_test_sender_custom_tlvs_consistency( vec![(odd_type_1, value_1()), (odd_type_2, value_2())], vec![(odd_type_1, value_1())], Some(vec![(odd_type_1, value_1())]), ); // Drop non-matching odd tlvs - do_test_custom_tlvs_consistency( + do_test_sender_custom_tlvs_consistency( vec![(odd_type_1, value_1()), (odd_type_2, value_2())], vec![(odd_type_1, differing_value_1()), (odd_type_2, value_2())], Some(vec![(odd_type_2, value_2())]), ); // Fail missing even tlvs - do_test_custom_tlvs_consistency( + do_test_sender_custom_tlvs_consistency( vec![(odd_type_1, value_1()), (even_type_2, value_2())], vec![(odd_type_1, value_1())], None, ); // Fail non-matching even tlvs - do_test_custom_tlvs_consistency( + do_test_sender_custom_tlvs_consistency( vec![(even_type_1, value_1()), (odd_type_2, value_2())], vec![(even_type_1, differing_value_1()), (odd_type_2, value_2())], None, ); } -fn do_test_custom_tlvs_consistency(first_tlvs: Vec<(u64, Vec)>, second_tlvs: Vec<(u64, Vec)>, +fn do_test_sender_custom_tlvs_consistency(first_tlvs: Vec<(u64, Vec)>, second_tlvs: Vec<(u64, Vec)>, expected_receive_tlvs: Option)>>) { let chanmon_cfgs = create_chanmon_cfgs(4); @@ -3903,7 +3908,8 @@ fn do_test_custom_tlvs_consistency(first_tlvs: Vec<(u64, Vec)>, second_tlvs: let onion_fields = RecipientOnionFields { payment_secret: Some(our_payment_secret), payment_metadata: None, - custom_tlvs: first_tlvs + sender_custom_tlvs: first_tlvs, + user_custom_data: Vec::new(), }; let session_privs = nodes[0].node.test_add_new_pending_payment(our_payment_hash, onion_fields.clone(), payment_id, &route).unwrap(); @@ -3925,7 +3931,9 @@ fn do_test_custom_tlvs_consistency(first_tlvs: Vec<(u64, Vec)>, second_tlvs: let onion_fields = RecipientOnionFields { payment_secret: Some(our_payment_secret), payment_metadata: None, - custom_tlvs: second_tlvs + sender_custom_tlvs: second_tlvs, + user_custom_data: Vec::new(), + }; nodes[0].node.test_send_payment_along_path(&route.paths[1], &our_payment_hash, onion_fields.clone(), amt_msat, cur_height, payment_id, &None, session_privs[1]).unwrap(); @@ -3959,14 +3967,14 @@ fn do_test_custom_tlvs_consistency(first_tlvs: Vec<(u64, Vec)>, second_tlvs: assert_eq!(events.len(), 1); match events[0] { Event::PaymentClaimable { ref onion_fields, .. } => { - assert_eq!(onion_fields.clone().unwrap().custom_tlvs, expected_tlvs); + assert_eq!(onion_fields.clone().unwrap().sender_custom_tlvs, expected_tlvs); }, _ => panic!("Unexpected event"), } do_claim_payment_along_route( ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], our_payment_preimage) - .with_custom_tlvs(expected_tlvs) + .with_sender_custom_tlvs(expected_tlvs) ); expect_payment_sent(&nodes[0], our_payment_preimage, Some(Some(2000)), true, true); } else { @@ -4031,7 +4039,8 @@ fn do_test_payment_metadata_consistency(do_reload: bool, do_modify: bool) { // Send the MPP payment, delivering the updated commitment state to nodes[1]. nodes[0].node.send_payment(payment_hash, RecipientOnionFields { - payment_secret: Some(payment_secret), payment_metadata: Some(payment_metadata), custom_tlvs: vec![], + payment_secret: Some(payment_secret), payment_metadata: Some(payment_metadata), sender_custom_tlvs: vec![], + user_custom_data: vec![], }, payment_id, route_params.clone(), Retry::Attempts(1)).unwrap(); check_added_monitors!(nodes[0], 2); @@ -4238,7 +4247,7 @@ fn test_htlc_forward_considers_anchor_outputs_value() { } #[test] -fn peel_payment_onion_custom_tlvs() { +fn peel_payment_onion_sender_custom_tlvs() { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); @@ -4252,7 +4261,7 @@ fn peel_payment_onion_custom_tlvs() { 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() - .with_custom_tlvs(vec![(414141, vec![42; 1200])]).unwrap(); + .with_sender_custom_tlvs(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"); let keysend_preimage = PaymentPreimage([42; 32]); @@ -4280,12 +4289,12 @@ fn peel_payment_onion_custom_tlvs() { assert_eq!(peeled_onion.incoming_amt_msat, Some(amt_msat)); match peeled_onion.routing { PendingHTLCRouting::ReceiveKeysend { - payment_data, payment_metadata, custom_tlvs, .. + payment_data, payment_metadata, sender_custom_tlvs, .. } => { #[cfg(not(c_bindings))] - assert_eq!(&custom_tlvs, recipient_onion.custom_tlvs()); + assert_eq!(&sender_custom_tlvs, recipient_onion.sender_custom_tlvs()); #[cfg(c_bindings)] - assert_eq!(custom_tlvs, recipient_onion.custom_tlvs()); + assert_eq!(sender_custom_tlvs, recipient_onion.sender_custom_tlvs()); assert!(payment_metadata.is_none()); assert!(payment_data.is_none()); }, diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index f561ef18edc..b03cfb0f22a 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -143,7 +143,7 @@ impl OnionMessageHandler for IgnoringMessageHandler { } impl OffersMessageHandler for IgnoringMessageHandler { - fn handle_message(&self, _message: OffersMessage, _context: Option, _responder: Option) -> Option<(OffersMessage, ResponseInstruction)> { + fn handle_message(&self, _message: OffersMessage, _context: Option, _custom_data: Option>, _responder: Option) -> Option<(OffersMessage, ResponseInstruction)> { None } } @@ -153,7 +153,7 @@ impl AsyncPaymentsMessageHandler for IgnoringMessageHandler { ) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> { None } - fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {} + fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext, _custom_data: Option>) {} } impl DNSResolverMessageHandler for IgnoringMessageHandler { fn handle_dnssec_query( @@ -161,11 +161,11 @@ impl DNSResolverMessageHandler for IgnoringMessageHandler { ) -> Option<(DNSResolverMessage, ResponseInstruction)> { None } - fn handle_dnssec_proof(&self, _message: DNSSECProof, _context: DNSResolverContext) {} + fn handle_dnssec_proof(&self, _message: DNSSECProof, _context: DNSResolverContext, _custom_data: Option>) {} } impl CustomOnionMessageHandler for IgnoringMessageHandler { type CustomMessage = Infallible; - fn handle_custom_message(&self, _message: Infallible, _context: Option>, _responder: Option) -> Option<(Infallible, ResponseInstruction)> { + fn handle_custom_message(&self, _message: Infallible, _context: Option>, _custom_data: Option>, _responder: Option) -> Option<(Infallible, ResponseInstruction)> { // Since we always return `None` in the read the handle method should never be called. unreachable!(); } diff --git a/lightning/src/onion_message/async_payments.rs b/lightning/src/onion_message/async_payments.rs index d81010e5d5f..7d3262a84cf 100644 --- a/lightning/src/onion_message/async_payments.rs +++ b/lightning/src/onion_message/async_payments.rs @@ -33,7 +33,10 @@ pub trait AsyncPaymentsMessageHandler { /// Handle a [`ReleaseHeldHtlc`] message. If authentication of the message succeeds, an HTLC /// should be released to the corresponding payee. - fn handle_release_held_htlc(&self, message: ReleaseHeldHtlc, context: AsyncPaymentsContext); + fn handle_release_held_htlc( + &self, message: ReleaseHeldHtlc, context: AsyncPaymentsContext, + custom_data: Option>, + ); /// Release any [`AsyncPaymentsMessage`]s that need to be sent. /// diff --git a/lightning/src/onion_message/dns_resolution.rs b/lightning/src/onion_message/dns_resolution.rs index 0f6071e73a3..8cefdc7fb77 100644 --- a/lightning/src/onion_message/dns_resolution.rs +++ b/lightning/src/onion_message/dns_resolution.rs @@ -67,7 +67,9 @@ pub trait DNSResolverMessageHandler { /// Handle a [`DNSSECProof`] message (in response to a [`DNSSECQuery`] we presumably sent). /// /// With this, we should be able to validate the DNS record we requested. - fn handle_dnssec_proof(&self, message: DNSSECProof, context: DNSResolverContext); + fn handle_dnssec_proof( + &self, message: DNSSECProof, context: DNSResolverContext, custom_data: Option>, + ); /// Gets the node feature flags which this handler itself supports. Useful for setting the /// `dns_resolver` flag if this handler supports returning [`DNSSECProof`] messages in response @@ -362,9 +364,10 @@ impl OMNameResolver { /// If an [`Offer`] is found, it, as well as the [`PaymentId`] and original `name` passed to /// [`Self::resolve_name`] are returned. pub fn handle_dnssec_proof_for_offer( - &self, msg: DNSSECProof, context: DNSResolverContext, + &self, msg: DNSSECProof, context: DNSResolverContext, custom_data: Option>, ) -> Option<(Vec<(HumanReadableName, PaymentId)>, Offer)> { - let (completed_requests, uri) = self.handle_dnssec_proof_for_uri(msg, context)?; + let (completed_requests, uri) = + self.handle_dnssec_proof_for_uri(msg, context, custom_data)?; if let Some((_onchain, params)) = uri.split_once("?") { for param in params.split("&") { let (k, v) = if let Some(split) = param.split_once("=") { @@ -395,8 +398,9 @@ impl OMNameResolver { /// This method is useful for those who handle bitcoin: URIs already, handling more than just /// BOLT12 [`Offer`]s. pub fn handle_dnssec_proof_for_uri( - &self, msg: DNSSECProof, context: DNSResolverContext, + &self, msg: DNSSECProof, context: DNSResolverContext, _custom_data: Option>, ) -> Option<(Vec<(HumanReadableName, PaymentId)>, String)> { + // todo: Introduce ability to analyse `custom_data` let DNSSECProof { name: answer_name, proof } = msg; let mut pending_resolves = self.pending_resolves.lock().unwrap(); if let hash_map::Entry::Occupied(entry) = pending_resolves.entry(answer_name) { diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index f9d73f05ff3..7ddc9271b6d 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -10,7 +10,7 @@ //! Onion message testing and test utilities live here. use crate::blinded_path::EmptyNodeIdLookUp; -use crate::blinded_path::message::{AsyncPaymentsContext, BlindedMessagePath, DNSResolverContext, MessageForwardNode, MessageContext, OffersContext}; +use crate::blinded_path::message::{AsyncPaymentsContext, BlindedMessagePath, DNSResolverContext, MessageContext, MessageForwardNode, OffersContext, ReceiveTlvs}; use crate::events::{Event, EventsProvider}; use crate::types::features::{ChannelFeatures, InitFeatures}; use crate::ln::msgs::{self, DecodeError, OnionMessageHandler}; @@ -76,7 +76,7 @@ impl Drop for MessengerNode { struct TestOffersMessageHandler {} impl OffersMessageHandler for TestOffersMessageHandler { - fn handle_message(&self, _message: OffersMessage, _context: Option, _responder: Option) -> Option<(OffersMessage, ResponseInstruction)> { + fn handle_message(&self, _message: OffersMessage, _context: Option, _custom_data: Option>, _responder: Option) -> Option<(OffersMessage, ResponseInstruction)> { None } } @@ -89,7 +89,7 @@ impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler { ) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> { None } - fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {} + fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext, _custom_data: Option>) {} } struct TestDNSResolverMessageHandler {} @@ -100,7 +100,7 @@ impl DNSResolverMessageHandler for TestDNSResolverMessageHandler { ) -> Option<(DNSResolverMessage, ResponseInstruction)> { None } - fn handle_dnssec_proof(&self, _message: DNSSECProof, _context: DNSResolverContext) {} + fn handle_dnssec_proof(&self, _message: DNSSECProof, _context: DNSResolverContext, custom_data: Option>) {} } #[derive(Clone, Debug, PartialEq)] @@ -147,6 +147,7 @@ struct TestCustomMessageHandler { struct OnHandleCustomMessage { expect: TestCustomMessage, include_reply_path: bool, + custom_data: Option>, } impl TestCustomMessageHandler { @@ -159,6 +160,7 @@ impl TestCustomMessageHandler { OnHandleCustomMessage { expect: message, include_reply_path: false, + custom_data: None, } ); } @@ -168,6 +170,17 @@ impl TestCustomMessageHandler { OnHandleCustomMessage { expect: message, include_reply_path: true, + custom_data: None + } + ); + } + + fn expect_message_with_custom_data(&self, message: TestCustomMessage, custom_data: Vec) { + self.expectations.lock().unwrap().push_back( + OnHandleCustomMessage { + expect: message, + include_reply_path: false, + custom_data: Some(custom_data), } ); } @@ -188,9 +201,10 @@ impl Drop for TestCustomMessageHandler { impl CustomOnionMessageHandler for TestCustomMessageHandler { type CustomMessage = TestCustomMessage; - fn handle_custom_message(&self, msg: Self::CustomMessage, context: Option>, responder: Option) -> Option<(Self::CustomMessage, ResponseInstruction)> { + fn handle_custom_message(&self, msg: Self::CustomMessage, context: Option>, custom_data: Option>, responder: Option) -> Option<(Self::CustomMessage, ResponseInstruction)> { let expectation = self.get_next_expectation(); assert_eq!(msg, expectation.expect); + assert_eq!(custom_data, expectation.custom_data); let response = match msg { TestCustomMessage::Ping => TestCustomMessage::Pong, @@ -204,7 +218,7 @@ impl CustomOnionMessageHandler for TestCustomMessageHandler { match responder { Some(responder) if expectation.include_reply_path => { - Some((response, responder.respond_with_reply_path(MessageContext::Custom(context.unwrap_or_else(Vec::new))))) + Some((response, responder.respond_with_reply_path(MessageContext::Custom(context.unwrap_or_else(Vec::new)), custom_data))) }, Some(responder) => Some((response, responder.respond())), None => None @@ -390,8 +404,11 @@ fn one_blinded_hop() { let test_msg = TestCustomMessage::Pong; let secp_ctx = Secp256k1::new(); - let context = MessageContext::Custom(Vec::new()); - let blinded_path = BlindedMessagePath::new(&[], nodes[1].node_id, context, &*nodes[1].entropy_source, &secp_ctx).unwrap(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; + let blinded_path = BlindedMessagePath::new(&[], nodes[1].node_id, recipient_tlvs, &*nodes[1].entropy_source, &secp_ctx).unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; nodes[0].messenger.send_onion_message(test_msg, instructions).unwrap(); @@ -399,6 +416,24 @@ fn one_blinded_hop() { pass_along_path(&nodes); } +#[test] +fn one_blinded_hop_with_custom_data() { + let nodes = create_nodes(2); + let test_msg = TestCustomMessage::Pong; + + let secp_ctx = Secp256k1::new(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: Some(vec![42; 42]), + }; + let blinded_path = BlindedMessagePath::new(&[], nodes[1].node_id, recipient_tlvs, &*nodes[1].entropy_source, &secp_ctx).unwrap(); + let destination = Destination::BlindedPath(blinded_path); + let instructions = MessageSendInstructions::WithoutReplyPath { destination }; + nodes[0].messenger.send_onion_message(test_msg, instructions).unwrap(); + nodes[1].custom_message_handler.expect_message_with_custom_data(TestCustomMessage::Pong, vec![42; 42]); + pass_along_path(&nodes); +} + #[test] fn two_unblinded_two_blinded() { let nodes = create_nodes(5); @@ -406,8 +441,11 @@ fn two_unblinded_two_blinded() { let secp_ctx = Secp256k1::new(); let intermediate_nodes = [MessageForwardNode { node_id: nodes[3].node_id, short_channel_id: None }]; - let context = MessageContext::Custom(Vec::new()); - let blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[4].node_id, context, &*nodes[4].entropy_source, &secp_ctx).unwrap(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; + let blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[4].node_id, recipient_tlvs, &*nodes[4].entropy_source, &secp_ctx).unwrap(); let path = OnionMessagePath { intermediate_nodes: vec![nodes[1].node_id, nodes[2].node_id], destination: Destination::BlindedPath(blinded_path), @@ -429,8 +467,11 @@ fn three_blinded_hops() { MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None }, ]; - let context = MessageContext::Custom(Vec::new()); - let blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[3].node_id, context, &*nodes[3].entropy_source, &secp_ctx).unwrap(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; + let blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[3].node_id, recipient_tlvs, &*nodes[3].entropy_source, &secp_ctx).unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -453,15 +494,18 @@ fn async_response_over_one_blinded_hop() { // 3. Simulate the creation of a Blinded Reply path provided by Bob. let secp_ctx = Secp256k1::new(); - let context = MessageContext::Custom(Vec::new()); - let reply_path = BlindedMessagePath::new(&[], nodes[1].node_id, context, &*nodes[1].entropy_source, &secp_ctx).unwrap(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; + let reply_path = BlindedMessagePath::new(&[], nodes[1].node_id, recipient_tlvs, &*nodes[1].entropy_source, &secp_ctx).unwrap(); // 4. Create a responder using the reply path for Alice. let responder = Some(Responder::new(reply_path)); // 5. Expect Alice to receive the message and create a response instruction for it. alice.custom_message_handler.expect_message(message.clone()); - let response_instruction = nodes[0].custom_message_handler.handle_custom_message(message, None, responder); + let response_instruction = nodes[0].custom_message_handler.handle_custom_message(message, None, None, responder); // 6. Simulate Alice asynchronously responding back to Bob with a response. let (msg, instructions) = response_instruction.unwrap(); @@ -490,13 +534,16 @@ fn async_response_with_reply_path_succeeds() { // Alice receives a message from Bob with an added reply_path for responding back. let message = TestCustomMessage::Ping; - let context = MessageContext::Custom(Vec::new()); - let reply_path = BlindedMessagePath::new(&[], bob.node_id, context, &*bob.entropy_source, &secp_ctx).unwrap(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; + let reply_path = BlindedMessagePath::new(&[], bob.node_id, recipient_tlvs, &*bob.entropy_source, &secp_ctx).unwrap(); // Alice asynchronously responds to Bob, expecting a response back from him. let responder = Responder::new(reply_path); alice.custom_message_handler.expect_message_and_response(message.clone()); - let response_instruction = alice.custom_message_handler.handle_custom_message(message, None, Some(responder)); + let response_instruction = alice.custom_message_handler.handle_custom_message(message, None, None, Some(responder)); let (msg, instructions) = response_instruction.unwrap(); assert_eq!( @@ -528,15 +575,18 @@ fn async_response_with_reply_path_fails() { // Alice receives a message from Bob with an added reply_path for responding back. let message = TestCustomMessage::Ping; - let context = MessageContext::Custom(Vec::new()); - let reply_path = BlindedMessagePath::new(&[], bob.node_id, context, &*bob.entropy_source, &secp_ctx).unwrap(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; + let reply_path = BlindedMessagePath::new(&[], bob.node_id, recipient_tlvs, &*bob.entropy_source, &secp_ctx).unwrap(); // Alice tries to asynchronously respond to Bob, but fails because the nodes are unannounced and // disconnected. Thus, a reply path could no be created for the response. disconnect_peers(alice, bob); let responder = Responder::new(reply_path); alice.custom_message_handler.expect_message_and_response(message.clone()); - let response_instruction = alice.custom_message_handler.handle_custom_message(message, None, Some(responder)); + let response_instruction = alice.custom_message_handler.handle_custom_message(message, None, None, Some(responder)); let (msg, instructions) = response_instruction.unwrap(); assert_eq!( @@ -574,8 +624,11 @@ fn we_are_intro_node() { MessageForwardNode { node_id: nodes[0].node_id, short_channel_id: None }, MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, ]; - let context = MessageContext::Custom(Vec::new()); - let blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, context, &*nodes[2].entropy_source, &secp_ctx).unwrap(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; + let blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, recipient_tlvs, &*nodes[2].entropy_source, &secp_ctx).unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -585,8 +638,11 @@ fn we_are_intro_node() { // Try with a two-hop blinded path where we are the introduction node. let intermediate_nodes = [MessageForwardNode { node_id: nodes[0].node_id, short_channel_id: None }]; - let context = MessageContext::Custom(Vec::new()); - let blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[1].node_id, context, &*nodes[1].entropy_source, &secp_ctx).unwrap(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; + let blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[1].node_id, recipient_tlvs, &*nodes[1].entropy_source, &secp_ctx).unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -604,8 +660,11 @@ fn invalid_blinded_path_error() { let secp_ctx = Secp256k1::new(); let intermediate_nodes = [MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }]; - let context = MessageContext::Custom(Vec::new()); - let mut blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, context, &*nodes[2].entropy_source, &secp_ctx).unwrap(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; + let mut blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, recipient_tlvs, &*nodes[2].entropy_source, &secp_ctx).unwrap(); blinded_path.clear_blinded_hops(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -630,8 +689,11 @@ fn reply_path() { MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None }, MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, ]; - let context = MessageContext::Custom(Vec::new()); - let reply_path = BlindedMessagePath::new(&intermediate_nodes, nodes[0].node_id, context, &*nodes[0].entropy_source, &secp_ctx).unwrap(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; + let reply_path = BlindedMessagePath::new(&intermediate_nodes, nodes[0].node_id, recipient_tlvs, &*nodes[0].entropy_source, &secp_ctx).unwrap(); nodes[0].messenger.send_onion_message_using_path(path, test_msg.clone(), Some(reply_path)).unwrap(); nodes[3].custom_message_handler.expect_message(TestCustomMessage::Ping); pass_along_path(&nodes); @@ -645,15 +707,21 @@ fn reply_path() { MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None }, ]; - let context = MessageContext::Custom(Vec::new()); - let blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[3].node_id, context, &*nodes[3].entropy_source, &secp_ctx).unwrap(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; + let blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[3].node_id, recipient_tlvs, &*nodes[3].entropy_source, &secp_ctx).unwrap(); let destination = Destination::BlindedPath(blinded_path); let intermediate_nodes = [ MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None }, MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, ]; - let context = MessageContext::Custom(Vec::new()); - let reply_path = BlindedMessagePath::new(&intermediate_nodes, nodes[0].node_id, context, &*nodes[0].entropy_source, &secp_ctx).unwrap(); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; + let reply_path = BlindedMessagePath::new(&intermediate_nodes, nodes[0].node_id, recipient_tlvs, &*nodes[0].entropy_source, &secp_ctx).unwrap(); let instructions = MessageSendInstructions::WithSpecifiedReplyPath { destination, reply_path }; nodes[0].messenger.send_onion_message(test_msg, instructions).unwrap(); @@ -744,9 +812,12 @@ fn requests_peer_connection_for_buffered_messages() { add_channel_to_graph(&nodes[0], &nodes[1], &secp_ctx, 42); let intermediate_nodes = [MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }]; - let context = MessageContext::Custom(Vec::new()); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; let blinded_path = BlindedMessagePath::new( - &intermediate_nodes, nodes[2].node_id, context, &*nodes[0].entropy_source, &secp_ctx + &intermediate_nodes, nodes[2].node_id, recipient_tlvs, &*nodes[0].entropy_source, &secp_ctx ).unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -784,9 +855,12 @@ fn drops_buffered_messages_waiting_for_peer_connection() { add_channel_to_graph(&nodes[0], &nodes[1], &secp_ctx, 42); let intermediate_nodes = [MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }]; - let context = MessageContext::Custom(Vec::new()); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; let blinded_path = BlindedMessagePath::new( - &intermediate_nodes, nodes[2].node_id, context, &*nodes[0].entropy_source, &secp_ctx + &intermediate_nodes, nodes[2].node_id, recipient_tlvs, &*nodes[0].entropy_source, &secp_ctx ).unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -836,9 +910,12 @@ fn intercept_offline_peer_oms() { let message = TestCustomMessage::Pong; let secp_ctx = Secp256k1::new(); let intermediate_nodes = [MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }]; - let context = MessageContext::Custom(Vec::new()); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Custom(Vec::new())), + custom_data: None, + }; let blinded_path = BlindedMessagePath::new( - &intermediate_nodes, nodes[2].node_id, context, &*nodes[2].entropy_source, &secp_ctx + &intermediate_nodes, nodes[2].node_id, recipient_tlvs, &*nodes[2].entropy_source, &secp_ctx ).unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index cb4fd105811..5233c729477 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -156,7 +156,7 @@ for OnionMessenger where /// # use bitcoin::hex::FromHex; /// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey, self}; /// # use lightning::blinded_path::EmptyNodeIdLookUp; -/// # use lightning::blinded_path::message::{BlindedMessagePath, MessageForwardNode, MessageContext}; +/// # use lightning::blinded_path::message::{BlindedMessagePath, MessageForwardNode, MessageContext, ReceiveTlvs}; /// # use lightning::sign::{EntropySource, KeysManager}; /// # use lightning::ln::peer_handler::IgnoringMessageHandler; /// # use lightning::onion_message::messenger::{Destination, MessageRouter, MessageSendInstructions, OnionMessagePath, OnionMessenger}; @@ -183,7 +183,7 @@ for OnionMessenger where /// # }) /// # } /// # fn create_blinded_paths( -/// # &self, _recipient: PublicKey, _context: MessageContext, _peers: Vec, _secp_ctx: &Secp256k1 +/// # &self, _recipient: PublicKey, _recipient_tlvs: ReceiveTlvs, _peers: Vec, _secp_ctx: &Secp256k1 /// # ) -> Result, ()> { /// # unreachable!() /// # } @@ -238,8 +238,11 @@ for OnionMessenger where /// MessageForwardNode { node_id: hop_node_id3, short_channel_id: None }, /// MessageForwardNode { node_id: hop_node_id4, short_channel_id: None }, /// ]; -/// let context = MessageContext::Custom(Vec::new()); -/// let blinded_path = BlindedMessagePath::new(&hops, your_node_id, context, &keys_manager, &secp_ctx).unwrap(); +/// let recipient_tlvs = ReceiveTlvs { +/// context: Some(MessageContext::Custom(Vec::new())), +/// custom_data: None, +/// }; +/// let blinded_path = BlindedMessagePath::new(&hops, your_node_id, recipient_tlvs, &keys_manager, &secp_ctx).unwrap(); /// /// // Send a custom onion message to a blinded path. /// let destination = Destination::BlindedPath(blinded_path); @@ -380,17 +383,17 @@ impl Responder { pub fn respond(self) -> ResponseInstruction { ResponseInstruction { destination: Destination::BlindedPath(self.reply_path), - context: None, + reply_data: (None, None) } } /// Creates a [`ResponseInstruction`] for responding including a reply path. /// /// Use when the recipient needs to send back a reply to us. - pub fn respond_with_reply_path(self, context: MessageContext) -> ResponseInstruction { + pub fn respond_with_reply_path(self, context: MessageContext, custom_data: Option>) -> ResponseInstruction { ResponseInstruction { destination: Destination::BlindedPath(self.reply_path), - context: Some(context), + reply_data: (Some(context), custom_data), } } } @@ -402,7 +405,7 @@ pub struct ResponseInstruction { /// [`Destination`] rather than an explicit [`BlindedMessagePath`] simplifies the logic in /// [`OnionMessenger::send_onion_message_internal`] somewhat. destination: Destination, - context: Option, + reply_data: (Option, Option>) } impl ResponseInstruction { @@ -431,7 +434,7 @@ pub enum MessageSendInstructions { destination: Destination, /// The context to include in the reply path we'll give the recipient so they can respond /// to us. - context: MessageContext, + reply_data: (MessageContext, Option>), }, /// Indicates that a message should be sent without including a reply path, preventing the /// recipient from responding. @@ -458,7 +461,7 @@ pub trait MessageRouter { fn create_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - &self, recipient: PublicKey, context: MessageContext, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, recipient_tlvs: ReceiveTlvs, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()>; /// Creates compact [`BlindedMessagePath`]s to the `recipient` node. The nodes in `peers` are @@ -477,14 +480,14 @@ pub trait MessageRouter { fn create_compact_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - &self, recipient: PublicKey, context: MessageContext, + &self, recipient: PublicKey, recipient_tlvs: ReceiveTlvs, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { let peers = peers .into_iter() .map(|MessageForwardNode { node_id, short_channel_id: _ }| node_id) .collect(); - self.create_blinded_paths(recipient, context, peers, secp_ctx) + self.create_blinded_paths(recipient, recipient_tlvs, peers, secp_ctx) } } @@ -519,7 +522,7 @@ where I: ExactSizeIterator, T: secp256k1::Signing + secp256k1::Verification >( - network_graph: &G, recipient: PublicKey, context: MessageContext, peers: I, + network_graph: &G, recipient: PublicKey, recipient_tlvs: ReceiveTlvs, peers: I, entropy_source: &ES, secp_ctx: &Secp256k1, compact_paths: bool, ) -> Result, ()> { // Limit the number of blinded paths that are computed. @@ -559,7 +562,7 @@ where let paths = peer_info.into_iter() .map(|(peer, _, _)| { - BlindedMessagePath::new(&[peer], recipient, context.clone(), &**entropy_source, secp_ctx) + BlindedMessagePath::new(&[peer], recipient, recipient_tlvs.clone(), &**entropy_source, secp_ctx) }) .take(MAX_PATHS) .collect::, _>>(); @@ -568,7 +571,7 @@ where Ok(paths) if !paths.is_empty() => Ok(paths), _ => { if is_recipient_announced { - BlindedMessagePath::new(&[], recipient, context, &**entropy_source, secp_ctx) + BlindedMessagePath::new(&[], recipient, recipient_tlvs, &**entropy_source, secp_ctx) .map(|path| vec![path]) } else { Err(()) @@ -621,22 +624,22 @@ where pub(crate) fn create_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - network_graph: &G, recipient: PublicKey, context: MessageContext, + network_graph: &G, recipient: PublicKey, recipient_tlvs: ReceiveTlvs, peers: Vec, entropy_source: &ES, secp_ctx: &Secp256k1, ) -> Result, ()> { let peers = peers .into_iter() .map(|node_id| MessageForwardNode { node_id, short_channel_id: None }); - Self::create_blinded_paths_from_iter(network_graph, recipient, context, peers.into_iter(), entropy_source, secp_ctx, false) + Self::create_blinded_paths_from_iter(network_graph, recipient, recipient_tlvs, peers.into_iter(), entropy_source, secp_ctx, false) } pub(crate) fn create_compact_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - network_graph: &G, recipient: PublicKey, context: MessageContext, + network_graph: &G, recipient: PublicKey, recipient_tlvs: ReceiveTlvs, peers: Vec, entropy_source: &ES, secp_ctx: &Secp256k1, ) -> Result, ()> { - Self::create_blinded_paths_from_iter(network_graph, recipient, context, peers.into_iter(), entropy_source, secp_ctx, true) + Self::create_blinded_paths_from_iter(network_graph, recipient, recipient_tlvs, peers.into_iter(), entropy_source, secp_ctx, true) } } @@ -654,17 +657,17 @@ where fn create_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - &self, recipient: PublicKey, context: MessageContext, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, recipient_tlvs: ReceiveTlvs, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { - Self::create_blinded_paths(&self.network_graph, recipient, context, peers, &self.entropy_source, secp_ctx) + Self::create_blinded_paths(&self.network_graph, recipient, recipient_tlvs, peers, &self.entropy_source, secp_ctx) } fn create_compact_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - &self, recipient: PublicKey, context: MessageContext, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, recipient_tlvs: ReceiveTlvs, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { - Self::create_compact_blinded_paths(&self.network_graph, recipient, context, peers, &self.entropy_source, secp_ctx) + Self::create_compact_blinded_paths(&self.network_graph, recipient, recipient_tlvs, peers, &self.entropy_source, secp_ctx) } } @@ -813,7 +816,7 @@ pub trait CustomOnionMessageHandler { /// /// The returned [`Self::CustomMessage`], if any, is enqueued to be sent by [`OnionMessenger`]. fn handle_custom_message( - &self, message: Self::CustomMessage, context: Option>, responder: Option + &self, message: Self::CustomMessage, context: Option>, custom_data: Option>, responder: Option ) -> Option<(Self::CustomMessage, ResponseInstruction)>; /// Read a custom message of type `message_type` from `buffer`, returning `Ok(None)` if the @@ -834,7 +837,7 @@ pub enum PeeledOnion { /// Forwarded onion, with the next node id and a new onion Forward(NextMessageHop, OnionMessage), /// Received onion message, with decrypted contents, context, and reply path - Receive(ParsedOnionMessageContents, Option, Option) + Receive(ParsedOnionMessageContents, Option, Option>, Option) } @@ -984,24 +987,24 @@ where (control_tlvs_ss, custom_handler.deref(), logger.deref()) ) { Ok((Payload::Receive::::Target as CustomOnionMessageHandler>::CustomMessage>> { - message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { context }), reply_path, + message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { context , custom_data}), reply_path, }, None)) => { match (&message, &context) { (_, None) => { - Ok(PeeledOnion::Receive(message, None, reply_path)) + Ok(PeeledOnion::Receive(message, None, custom_data, reply_path)) } (ParsedOnionMessageContents::Offers(_), Some(MessageContext::Offers(_))) => { - Ok(PeeledOnion::Receive(message, context, reply_path)) + Ok(PeeledOnion::Receive(message, context, custom_data, reply_path)) } #[cfg(async_payments)] (ParsedOnionMessageContents::AsyncPayments(_), Some(MessageContext::AsyncPayments(_))) => { - Ok(PeeledOnion::Receive(message, context, reply_path)) + Ok(PeeledOnion::Receive(message, context, custom_data, reply_path)) } (ParsedOnionMessageContents::Custom(_), Some(MessageContext::Custom(_))) => { - Ok(PeeledOnion::Receive(message, context, reply_path)) + Ok(PeeledOnion::Receive(message, context, custom_data, reply_path)) } (ParsedOnionMessageContents::DNSResolver(_), Some(MessageContext::DNSResolver(_))) => { - Ok(PeeledOnion::Receive(message, context, reply_path)) + Ok(PeeledOnion::Receive(message, context, custom_data, reply_path)) } _ => { log_trace!(logger, "Received message was sent on a blinded path with the wrong context."); @@ -1190,10 +1193,10 @@ where let (destination, reply_path) = match instructions { MessageSendInstructions::WithSpecifiedReplyPath { destination, reply_path } => (destination, Some(reply_path)), - MessageSendInstructions::WithReplyPath { destination, context } - |MessageSendInstructions::ForReply { instructions: ResponseInstruction { destination, context: Some(context) } } => + MessageSendInstructions::WithReplyPath { destination, reply_data: (context, custom_data) } + |MessageSendInstructions::ForReply { instructions: ResponseInstruction { destination, reply_data: (Some(context), custom_data) } } => { - match self.create_blinded_path(context) { + match self.create_blinded_path(context, custom_data) { Ok(reply_path) => (destination, Some(reply_path)), Err(err) => { log_trace!( @@ -1206,7 +1209,7 @@ where } }, MessageSendInstructions::WithoutReplyPath { destination } - |MessageSendInstructions::ForReply { instructions: ResponseInstruction { destination, context: None } } => + |MessageSendInstructions::ForReply { instructions: ResponseInstruction { destination, reply_data: (None, _) } } => (destination, None), }; @@ -1258,7 +1261,7 @@ where .map_err(|_| SendError::PathNotFound) } - fn create_blinded_path(&self, context: MessageContext) -> Result { + fn create_blinded_path(&self, context: MessageContext, custom_data: Option>) -> Result { let recipient = self.node_signer .get_node_id(Recipient::Node) .map_err(|_| SendError::GetNodeIdFailed)?; @@ -1270,8 +1273,13 @@ where .map(|(node_id, _ )| *node_id) .collect::>(); + let recipient_tlvs = ReceiveTlvs { + context: Some(context), + custom_data, + }; + self.message_router - .create_blinded_paths(recipient, context, peers, secp_ctx) + .create_blinded_paths(recipient, recipient_tlvs, peers, secp_ctx) .and_then(|paths| paths.into_iter().next().ok_or(())) .map_err(|_| SendError::PathNotFound) } @@ -1616,7 +1624,7 @@ where fn handle_onion_message(&self, peer_node_id: PublicKey, msg: &OnionMessage) { let logger = WithContext::from(&self.logger, Some(peer_node_id), None, None); match self.peel_onion_message(msg) { - Ok(PeeledOnion::Receive(message, context, reply_path)) => { + Ok(PeeledOnion::Receive(message, context, custom_data, reply_path)) => { log_trace!( logger, "Received an onion message with {} reply_path: {:?}", @@ -1633,7 +1641,7 @@ where return } }; - let response_instructions = self.offers_handler.handle_message(msg, context, responder); + let response_instructions = self.offers_handler.handle_message(msg, context, custom_data, responder); if let Some((msg, instructions)) = response_instructions { let _ = self.handle_onion_message_response(msg, instructions); } @@ -1657,7 +1665,7 @@ where }, None => return, }; - self.async_payments_handler.handle_release_held_htlc(msg, context); + self.async_payments_handler.handle_release_held_htlc(msg, context, custom_data); }, ParsedOnionMessageContents::DNSResolver(DNSResolverMessage::DNSSECQuery(msg)) => { let response_instructions = self.dns_resolver_handler.handle_dnssec_query(msg, responder); @@ -1670,7 +1678,7 @@ where Some(MessageContext::DNSResolver(context)) => context, _ => return, }; - self.dns_resolver_handler.handle_dnssec_proof(msg, context); + self.dns_resolver_handler.handle_dnssec_proof(msg, context, custom_data); }, ParsedOnionMessageContents::Custom(msg) => { let context = match context { @@ -1681,7 +1689,7 @@ where return } }; - let response_instructions = self.custom_handler.handle_custom_message(msg, context, responder); + let response_instructions = self.custom_handler.handle_custom_message(msg, context, custom_data, responder); if let Some((msg, instructions)) = response_instructions { let _ = self.handle_onion_message_response(msg, instructions); } @@ -2013,7 +2021,7 @@ fn packet_payloads_and_keys, responder: Option, + &self, message: OffersMessage, context: Option, custom_data: Option>, responder: Option, ) -> Option<(OffersMessage, ResponseInstruction)>; /// Releases any [`OffersMessage`]s that need to be sent. diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 8ec85a6bed7..9b42f2850c1 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -347,6 +347,7 @@ impl Readable for ControlTlvs { (4, next_node_id, option), (8, next_blinding_override, option), (65537, context, option), + (65539, custom_data, option) }); let _padding: Option = _padding; @@ -368,6 +369,7 @@ impl Readable for ControlTlvs { } else if valid_recv_fmt { ControlTlvs::Receive(ReceiveTlvs { context, + custom_data }) } else { return Err(DecodeError::InvalidValue) diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index b91b1b47d6e..9f684771d6e 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -670,7 +670,7 @@ const DEFAULT_MAX_CHANNEL_SATURATION_POW_HALF: u8 = 2; const MEDIAN_HOP_CLTV_EXPIRY_DELTA: u32 = 40; /// Estimated maximum number of hops that can be included in a payment path. May be inaccurate if -/// payment metadata, custom TLVs, or blinded paths are included in the payment. +/// payment metadata, sender custom TLVs, or blinded paths are included in the payment. // During routing, we only consider paths shorter than our maximum length estimate. // In the TLV onion format, there is no fixed maximum length, but the `hop_payloads` // field is always 1300 bytes. As the `tlv_payload` for each hop may vary in length, we have to diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 813b481c9cf..159fc24220d 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -7,8 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. -use crate::blinded_path::message::MessageContext; -use crate::blinded_path::message::{BlindedMessagePath, MessageForwardNode}; +use crate::blinded_path::message::{self, BlindedMessagePath, MessageForwardNode}; use crate::blinded_path::payment::{BlindedPaymentPath, ReceiveTlvs}; use crate::chain; use crate::chain::WatchedOutput; @@ -291,17 +290,17 @@ impl<'a> MessageRouter for TestMessageRouter<'a> { } fn create_blinded_paths( - &self, recipient: PublicKey, context: MessageContext, + &self, recipient: PublicKey, recipient_tlvs: message::ReceiveTlvs, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { - self.inner.create_blinded_paths(recipient, context, peers, secp_ctx) + self.inner.create_blinded_paths(recipient, recipient_tlvs, peers, secp_ctx) } fn create_compact_blinded_paths( - &self, recipient: PublicKey, context: MessageContext, + &self, recipient: PublicKey, recipient_tlvs: message::ReceiveTlvs, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { - self.inner.create_compact_blinded_paths(recipient, context, peers, secp_ctx) + self.inner.create_compact_blinded_paths(recipient, recipient_tlvs, peers, secp_ctx) } }