diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 54c9a7e481b..753f37f6dc9 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, MessageContext}; use lightning::blinded_path::payment::{BlindedPaymentPath, ReceiveTlvs}; use lightning::chain; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; @@ -138,8 +138,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 7b9a9ae1d00..cb4a690be91 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, MessageContext}; use lightning::blinded_path::payment::{BlindedPaymentPath, ReceiveTlvs}; use lightning::chain; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; @@ -171,8 +171,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 37668c1d801..45dd77f0dc4 100644 --- a/fuzz/src/invoice_request_deser.rs +++ b/fuzz/src/invoice_request_deser.rs @@ -103,6 +103,7 @@ fn build_response( htlc_minimum_msat: 1, }, payment_context, + custom_data: None, }; let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let intermediate_nodes = [PaymentForwardNode { diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index 10a667fb594..af93fd3e42e 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, MessageContext, OffersContext, ReceiveTlvs, }; use lightning::blinded_path::EmptyNodeIdLookUp; use lightning::ln::inbound_payment::ExpandedKey; @@ -100,7 +100,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!() @@ -112,7 +112,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 } @@ -161,7 +161,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 6151d810344..a45cab64015 100644 --- a/fuzz/src/refund_deser.rs +++ b/fuzz/src/refund_deser.rs @@ -80,6 +80,7 @@ fn build_response( htlc_minimum_msat: 1, }, payment_context, + custom_data: None, }; let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); let intermediate_nodes = [PaymentForwardNode { diff --git a/lightning-dns-resolver/src/lib.rs b/lightning-dns-resolver/src/lib.rs index 8f855cb5fb7..7851e350280 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::{BlindedMessagePath, MessageContext, ReceiveTlvs}; 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, + &self, recipient: PublicKey, recipient_tlvs: 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,7 +337,8 @@ mod test { let (msg, context) = payer.resolver.resolve_name(payment_id, name.clone(), &*payer_keys).unwrap(); - let query_context = MessageContext::DNSResolver(context); + let query_context = + ReceiveTlvs { context: Some(MessageContext::DNSResolver(context)), custom_data: None }; let reply_path = BlindedMessagePath::one_hop(payer_id, query_context, &*payer_keys, &secp_ctx).unwrap(); payer.pending_messages.lock().unwrap().push(( diff --git a/lightning/src/blinded_path/message.rs b/lightning/src/blinded_path/message.rs index 441a2c2a625..993aca61bde 100644 --- a/lightning/src/blinded_path/message.rs +++ b/lightning/src/blinded_path/message.rs @@ -55,13 +55,13 @@ 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, + 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 @@ -71,7 +71,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, @@ -90,7 +90,7 @@ impl BlindedMessagePath { secp_ctx, intermediate_nodes, recipient_node_id, - context, + recipient_tlvs, &blinding_secret, ) .map_err(|_| ())?, @@ -251,12 +251,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, + + /// 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 { @@ -280,6 +296,7 @@ impl Writeable for ReceiveTlvs { // TODO: write padding encode_tlv_stream!(writer, { (65537, self.context, option), + (65539, self.custom_data, option) }); Ok(()) } @@ -498,7 +515,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() @@ -515,7 +532,7 @@ pub(super) fn blinded_hops( .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 df7c29909f8..89f12453bda 100644 --- a/lightning/src/blinded_path/payment.rs +++ b/lightning/src/blinded_path/payment.rs @@ -325,6 +325,15 @@ pub struct UnauthenticatedReceiveTlvs { 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: Option>, } impl UnauthenticatedReceiveTlvs { @@ -490,6 +499,7 @@ impl Writeable for ReceiveTlvs { (65536, self.tlvs.payment_secret, required), (65537, self.tlvs.payment_context, required), (65539, self.authentication, required), + (65541, self.tlvs.custom_data, required) }); Ok(()) } @@ -501,6 +511,7 @@ impl Writeable for UnauthenticatedReceiveTlvs { (12, self.payment_constraints, required), (65536, self.payment_secret, required), (65537, self.payment_context, required), + (65541, self.custom_data, (default_value, Vec::new())), }); Ok(()) } @@ -529,6 +540,7 @@ impl Readable for BlindedPaymentTlvs { (65536, payment_secret, option), (65537, payment_context, option), (65539, authentication, option), + (65541, custom_data, option) }); let _padding: Option = _padding; @@ -552,6 +564,7 @@ impl Readable for BlindedPaymentTlvs { payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?, payment_constraints: payment_constraints.0.unwrap(), payment_context: payment_context.ok_or(DecodeError::InvalidValue)?, + custom_data: custom_data.ok_or(DecodeError::InvalidValue)?, }, authentication: authentication.ok_or(DecodeError::InvalidValue)?, })) @@ -794,6 +807,7 @@ mod tests { payment_secret: PaymentSecret([0; 32]), payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), + custom_data: None, }; let htlc_maximum_msat = 100_000; let blinded_payinfo = @@ -812,6 +826,7 @@ mod tests { payment_secret: PaymentSecret([0; 32]), payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), + custom_data: None, }; let blinded_payinfo = super::compute_payinfo(&[], &recv_tlvs, 4242, TEST_FINAL_CLTV as u16).unwrap(); @@ -869,6 +884,7 @@ mod tests { payment_secret: PaymentSecret([0; 32]), payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 3 }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), + custom_data: None, }; let htlc_maximum_msat = 100_000; let blinded_payinfo = super::compute_payinfo( @@ -928,6 +944,7 @@ mod tests { payment_secret: PaymentSecret([0; 32]), payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), + custom_data: None, }; let htlc_minimum_msat = 3798; assert!(super::compute_payinfo( @@ -997,6 +1014,7 @@ mod tests { payment_secret: PaymentSecret([0; 32]), payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), + custom_data: None, }; let blinded_payinfo = super::compute_payinfo( diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index b5dad96a979..eff9f695c59 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -705,15 +705,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. @@ -732,7 +732,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/async_payments_tests.rs b/lightning/src/ln/async_payments_tests.rs index 8d18938272c..d5726611def 100644 --- a/lightning/src/ln/async_payments_tests.rs +++ b/lightning/src/ln/async_payments_tests.rs @@ -7,7 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. -use crate::blinded_path::message::{MessageContext, OffersContext}; +use crate::blinded_path::message::{MessageContext, OffersContext, ReceiveTlvs}; use crate::blinded_path::payment::PaymentContext; use crate::blinded_path::payment::{AsyncBolt12OfferContext, BlindedPaymentTlvs}; use crate::chain::channelmonitor::{HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS}; @@ -106,11 +106,18 @@ fn create_static_invoice( always_online_counterparty: &Node, recipient: &Node, relative_expiry: Option, secp_ctx: &Secp256k1, ) -> (Offer, StaticInvoice) { + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Offers(OffersContext::InvoiceRequest { + nonce: Nonce([42; 16]), + })), + custom_data: None, + }; + let blinded_paths_to_always_online_node = always_online_counterparty .message_router .create_blinded_paths( always_online_counterparty.node.get_our_node_id(), - MessageContext::Offers(OffersContext::InvoiceRequest { nonce: Nonce([42; 16]) }), + recipient_tlvs, Vec::new(), &secp_ctx, ) @@ -212,11 +219,18 @@ fn static_invoice_unknown_required_features() { create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0); create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Offers(OffersContext::InvoiceRequest { + nonce: Nonce([42; 16]), + })), + custom_data: None, + }; + let blinded_paths_to_always_online_node = nodes[1] .message_router .create_blinded_paths( nodes[1].node.get_our_node_id(), - MessageContext::Offers(OffersContext::InvoiceRequest { nonce: Nonce([42; 16]) }), + recipient_tlvs, Vec::new(), &secp_ctx, ) @@ -759,6 +773,7 @@ fn reject_bad_payment_secret() { // We don't reach the point of checking the invreq nonce due to the invalid payment secret offer_nonce: Nonce([i; Nonce::LENGTH]), }), + None, u32::MAX, ) .unwrap(); @@ -813,11 +828,18 @@ fn invalid_async_receive_with_retry( create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0); create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0); + let recipient_tlvs = ReceiveTlvs { + context: Some(MessageContext::Offers(OffersContext::InvoiceRequest { + nonce: Nonce([42; 16]), + })), + custom_data: None, + }; + let blinded_paths_to_always_online_node = nodes[1] .message_router .create_blinded_paths( nodes[1].node.get_our_node_id(), - MessageContext::Offers(OffersContext::InvoiceRequest { nonce: Nonce([42; 16]) }), + recipient_tlvs, Vec::new(), &secp_ctx, ) diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index 0662d5f9395..ac6203b148c 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -79,6 +79,7 @@ pub fn blinded_payment_path( intro_node_min_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_minimum_msat), }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), + custom_data: None, }; let nonce = Nonce([42u8; 16]); @@ -165,6 +166,7 @@ fn do_one_hop_blinded_path(success: bool) { htlc_minimum_msat: chan_upd.htlc_minimum_msat, }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), + custom_data: None, }; let nonce = Nonce([42u8; 16]); let expanded_key = chanmon_cfgs[1].keys_manager.get_inbound_payment_key(); @@ -213,6 +215,7 @@ fn mpp_to_one_hop_blinded_path() { htlc_minimum_msat: chan_upd_1_3.htlc_minimum_msat, }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), + custom_data: None, }; let nonce = Nonce([42u8; 16]); let expanded_key = chanmon_cfgs[3].keys_manager.get_inbound_payment_key(); @@ -878,6 +881,8 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2], &chanmon_cfgs[2].keys_manager); + route_params.payment_params.max_path_length = 18; + let route = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck { let mut route = get_route(&nodes[0], &route_params).unwrap(); // Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards. @@ -1299,6 +1304,7 @@ fn custom_tlvs_to_blinded_path() { htlc_minimum_msat: chan_upd.htlc_minimum_msat, }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), + custom_data: Some(vec![43, 43]), }; let nonce = Nonce([42u8; 16]); let expanded_key = chanmon_cfgs[1].keys_manager.get_inbound_payment_key(); @@ -1315,7 +1321,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(); @@ -1327,12 +1334,14 @@ fn custom_tlvs_to_blinded_path() { let path = &[&nodes[1]]; let args = PassAlongPathArgs::new(&nodes[0], path, amt_msat, payment_hash, ev) + .with_user_custom_data(recipient_onion_fields.user_custom_data.clone().unwrap()) .with_payment_secret(payment_secret) - .with_custom_tlvs(recipient_onion_fields.custom_tlvs.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().unwrap()) + .with_sender_custom_tlvs(recipient_onion_fields.sender_custom_tlvs.clone()) ); } @@ -1353,6 +1362,7 @@ fn fails_receive_tlvs_authentication() { htlc_minimum_msat: chan_upd.htlc_minimum_msat, }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), + custom_data: None, }; let nonce = Nonce([42u8; 16]); let expanded_key = chanmon_cfgs[1].keys_manager.get_inbound_payment_key(); @@ -1384,6 +1394,7 @@ fn fails_receive_tlvs_authentication() { htlc_minimum_msat: chan_upd.htlc_minimum_msat, }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), + custom_data: None, }; let nonce = Nonce([43u8; 16]); let mut payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 5342cc649d3..c5f86b0e4a0 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}; 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::{AsyncBolt12OfferContext, BlindedPaymentPath, Bolt12OfferContext, Bolt12RefundContext, PaymentConstraints, PaymentContext, UnauthenticatedReceiveTlvs}; @@ -216,12 +216,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: Option>, /// Set if this HTLC is the final hop in a multi-hop blinded path. requires_blinded_error: bool, }, @@ -247,11 +253,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::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, these will ultimately bubble back up as + /// [`RecipientOnionFields::user_custom_data`]. + user_custom_data: Option>, /// 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 @@ -984,17 +995,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, { @@ -1011,10 +1022,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); } } @@ -6118,25 +6129,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, None) }, PendingHTLCRouting::ReceiveKeysend { payment_data, payment_preimage, payment_metadata, - incoming_cltv_expiry, custom_tlvs, requires_blinded_error: _, - has_recipient_created_payment_secret, payment_context, invoice_request, + incoming_cltv_expiry, sender_custom_tlvs, user_custom_data, + requires_blinded_error: _, has_recipient_created_payment_secret, payment_context, invoice_request, } => { 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, payment_context, None, onion_fields, @@ -7001,9 +7013,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 @@ -7011,12 +7023,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 @@ -7025,11 +7037,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); @@ -7037,7 +7049,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 { @@ -10108,7 +10120,7 @@ where ).map_err(|()| Bolt12SemanticError::InvalidAmount)?; let payment_paths = self.create_blinded_payment_paths( - amount_msat, payment_secret, payment_context, relative_expiry_secs + amount_msat, payment_secret, payment_context, None, relative_expiry_secs ).map_err(|()| Bolt12SemanticError::MissingPaths)?; let nonce = Nonce::from_entropy_source(entropy); @@ -10327,7 +10339,7 @@ where Ok((payment_hash, payment_secret)) => { let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {}); let payment_paths = self.create_blinded_payment_paths( - Some(amount_msats), payment_secret, payment_context, relative_expiry, + Some(amount_msats), payment_secret, payment_context, None, relative_expiry, ) .map_err(|_| Bolt12SemanticError::MissingPaths)?; @@ -10598,8 +10610,13 @@ where .map(|(node_id, _)| *node_id) .collect::>(); + 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(())) } @@ -10626,16 +10643,21 @@ where }) .collect::>(); + 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: Option, payment_secret: PaymentSecret, payment_context: PaymentContext, - relative_expiry_seconds: u32 + &self, amount_msats: Option, payment_secret: PaymentSecret, payment_context: PaymentContext, custom_data: Option>, + relative_expiry_seconds: u32, ) -> Result, ()> { let expanded_key = &self.inbound_payment_key; let entropy = &*self.entropy_source; @@ -10658,6 +10680,7 @@ where htlc_minimum_msat: 1, }, payment_context, + custom_data, }; let nonce = Nonce::from_entropy_source(entropy); let payee_tlvs = payee_tlvs.authenticate(nonce, expanded_key); @@ -10669,11 +10692,11 @@ where #[cfg(all(test, async_payments))] pub(super) fn test_create_blinded_payment_paths( - &self, amount_msats: Option, payment_secret: PaymentSecret, payment_context: PaymentContext, + &self, amount_msats: Option, payment_secret: PaymentSecret, payment_context: PaymentContext, custom_data: Option>, relative_expiry_seconds: u32 ) -> Result, ()> { self.create_blinded_payment_paths( - amount_msats, payment_secret, payment_context, relative_expiry_seconds + amount_msats, payment_secret, payment_context, custom_data, relative_expiry_seconds ) } @@ -12142,7 +12165,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; @@ -12231,7 +12255,7 @@ where invoice_request: invoice_request.fields(), }); let payment_paths = match self.create_blinded_payment_paths( - Some(amount_msats), payment_secret, payment_context, relative_expiry + Some(amount_msats), payment_secret, payment_context, None, relative_expiry ) { Ok(payment_paths) => payment_paths, Err(()) => { @@ -12285,7 +12309,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())), } @@ -12435,8 +12459,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 { @@ -12589,9 +12613,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, option), }, (2, ReceiveKeysend) => { (0, payment_preimage, required), @@ -12599,10 +12624,11 @@ 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, payment_context, option), (11, invoice_request, option), + (13, user_custom_data, option), }, ); #[cfg(trampoline)] @@ -12618,9 +12644,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, option), }, (2, ReceiveKeysend) => { (0, payment_preimage, required), @@ -12628,10 +12655,11 @@ 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, payment_context, option), (11, invoice_request, option), + (13, user_custom_data, option), }, (3, TrampolineForward) => { (0, incoming_shared_secret, required), @@ -15760,7 +15788,7 @@ mod tests { payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat, }), - custom_tlvs: Vec::new(), + sender_custom_tlvs: 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. @@ -15783,7 +15811,7 @@ mod tests { payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat, }), - custom_tlvs: Vec::new(), + sender_custom_tlvs: 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]), @@ -15808,7 +15836,7 @@ mod tests { payment_secret: PaymentSecret([0; 32]), total_msat: 100, }), - custom_tlvs: Vec::new(), + sender_custom_tlvs: 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 fa21967fdde..aa357cab173 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -2658,7 +2658,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: Option>, pub payment_metadata: Option>, pub expected_failure: Option, } @@ -2671,7 +2672,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: None, payment_metadata: None, expected_failure: None, } } pub fn without_clearing_recipient_events(mut self) -> Self { @@ -2695,8 +2696,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 = Some(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 { @@ -2714,7 +2719,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; @@ -2750,7 +2755,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, .. } => { @@ -2881,7 +2887,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: Option>, // Allow forwarding nodes to have taken 1 msat more fee than expected based on the downstream // fulfill amount. // @@ -2900,7 +2907,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: None, } } pub fn skip_last(mut self, skip_last: bool) -> Self { @@ -2919,8 +2926,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 = Some(user_custom_data); self } } @@ -2928,7 +2939,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); @@ -2948,7 +2960,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; }, @@ -2965,7 +2977,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 98c3a062bc4..75d2e55b4ce 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}; @@ -60,7 +60,7 @@ fn large_payment_metadata() { }), payment_metadata: None, keysend_preimage: None, - custom_tlvs: &Vec::new(), + sender_custom_tlvs: &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(); @@ -77,7 +77,8 @@ 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: None, }; 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); @@ -125,7 +126,8 @@ 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: None, }; let mut route_params_0_2 = route_0_2.route_params.clone().unwrap(); route_params_0_2.payment_params.max_path_length = 2; @@ -166,6 +168,7 @@ fn one_hop_blinded_path_with_custom_tlv() { htlc_minimum_msat: chan_upd_1_2.htlc_minimum_msat, }, payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}), + custom_data: None, }; let nonce = Nonce([42u8; 16]); let expanded_key = chanmon_cfgs[2].keys_manager.get_inbound_payment_key(); @@ -190,7 +193,7 @@ 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(), }.serialized_length(); let max_custom_tlv_len = 1300 - crate::util::ser::BigSize(CUSTOM_TLV_TYPE).serialized_length() // custom TLV type @@ -201,7 +204,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); @@ -211,16 +214,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); @@ -232,7 +235,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); @@ -241,11 +244,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) ); } @@ -297,7 +300,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); @@ -307,16 +310,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); @@ -340,7 +343,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); @@ -349,11 +352,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 5f8d270a60f..2b3304ee200 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1815,7 +1815,7 @@ mod fuzzy_internal_msgs { pub payment_data: Option, pub payment_metadata: Option>, pub keysend_preimage: Option, - pub custom_tlvs: Vec<(u64, Vec)>, + pub sender_custom_tlvs: Vec<(u64, Vec)>, pub sender_intended_htlc_amt_msat: u64, pub cltv_expiry_height: u32, } @@ -1837,7 +1837,8 @@ mod fuzzy_internal_msgs { pub intro_node_blinding_point: Option, pub keysend_preimage: Option, pub invoice_request: Option, - pub custom_tlvs: Vec<(u64, Vec)>, + pub sender_custom_tlvs: Vec<(u64, Vec)>, + pub user_custom_data: Option>, } pub enum InboundOnionPayload { @@ -1864,7 +1865,7 @@ 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)>, sender_intended_htlc_amt_msat: u64, cltv_expiry_height: u32, }, @@ -1879,7 +1880,7 @@ 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)>, invoice_request: Option<&'a InvoiceRequest>, } } @@ -1915,7 +1916,7 @@ 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)>, } } @@ -2796,20 +2797,20 @@ 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 } => { - // 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 keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode())); - let mut custom_tlvs: Vec<&(u64, Vec)> = custom_tlvs.iter().chain(keysend_tlv.iter()).collect(); - custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ); + let mut sender_custom_tlvs: Vec<&(u64, Vec)> = sender_custom_tlvs.iter().chain(keysend_tlv.iter()).collect(); + sender_custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ); _encode_varint_length_prefixed_tlv!(w, { (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required), (4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required), (8, payment_data, option), (16, payment_metadata.map(|m| WithoutLength(m)), option) - }, custom_tlvs.iter()); + }, sender_custom_tlvs.iter()); }, Self::BlindedForward { encrypted_tlvs, intro_node_blinding_point } => { _encode_varint_length_prefixed_tlv!(w, { @@ -2819,25 +2820,25 @@ 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 } => { - // 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 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 sender_custom_tlvs: Vec<&(u64, Vec)> = sender_custom_tlvs.iter() .chain(invoice_request_tlv.iter()) .chain(keysend_tlv.iter()) .collect(); - custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ); + sender_custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ); _encode_varint_length_prefixed_tlv!(w, { (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required), (4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required), (10, **encrypted_tlvs, required_vec), (12, intro_node_blinding_point, option), (18, HighZeroBytesDroppedBigSize(*total_msat), required) - }, custom_tlvs.iter()); + }, sender_custom_tlvs.iter()); }, } Ok(()) @@ -2879,7 +2880,7 @@ 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 } => { _encode_varint_length_prefixed_tlv!(w, { (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required), (4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required), @@ -2887,7 +2888,7 @@ impl<'a> Writeable for OutboundTrampolinePayload<'a> { (12, intro_node_blinding_point, option), (18, HighZeroBytesDroppedBigSize(*total_msat), required), (20, keysend_preimage, option) - }, custom_tlvs.iter()); + }, sender_custom_tlvs.iter()); } } Ok(()) @@ -2909,7 +2910,7 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPayload wh let mut total_msat = None; let mut keysend_preimage: Option = None; let mut invoice_request: Option = None; - let mut custom_tlvs = Vec::new(); + let mut sender_custom_tlvs = Vec::new(); let tlv_len = BigSize::read(r)?; let mut rd = FixedLengthReader::new(r, tlv_len.0); @@ -2929,7 +2930,7 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPayload wh if msg_type < 1 << 16 { return Ok(false) } let mut value = Vec::new(); msg_reader.read_to_limit(&mut value, u64::MAX)?; - custom_tlvs.push((msg_type, value)); + sender_custom_tlvs.push((msg_type, value)); Ok(true) }); @@ -2966,6 +2967,7 @@ 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(receive_tlvs) } => { let ReceiveTlvs { tlvs, authentication: (hmac, nonce) } = receive_tlvs; let expanded_key = node_signer.get_inbound_payment_key(); @@ -2974,7 +2976,7 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPayload wh } let UnauthenticatedReceiveTlvs { - payment_secret, payment_constraints, payment_context, + payment_secret, payment_constraints, payment_context, custom_data } = tlvs; if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) } Ok(Self::BlindedReceive(InboundOnionBlindedReceivePayload { @@ -2987,7 +2989,8 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPayload wh intro_node_blinding_point, keysend_preimage, invoice_request, - custom_tlvs, + sender_custom_tlvs, + user_custom_data: custom_data, })) }, } @@ -3015,7 +3018,7 @@ 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, })) } } @@ -4646,7 +4649,7 @@ mod tests { keysend_preimage: None, sender_intended_htlc_amt_msat: 0x0badf00d01020304, cltv_expiry_height: 0xffffffff, - custom_tlvs: &vec![], + sender_custom_tlvs: &vec![], }; let encoded_value = outbound_msg.encode(); let target_value = >::from_hex("1002080badf00d010203040404ffffffff").unwrap(); @@ -4674,7 +4677,7 @@ mod tests { keysend_preimage: None, sender_intended_htlc_amt_msat: 0x0badf00d01020304, cltv_expiry_height: 0xffffffff, - custom_tlvs: &vec![], + sender_custom_tlvs: &vec![], }; let encoded_value = outbound_msg.encode(); let target_value = >::from_hex("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap(); @@ -4690,18 +4693,18 @@ mod tests { sender_intended_htlc_amt_msat, cltv_expiry_height, payment_metadata: None, keysend_preimage: None, - custom_tlvs, + sender_custom_tlvs, }) = 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![]); } 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]), @@ -4711,7 +4714,7 @@ mod tests { payment_data: None, payment_metadata: None, keysend_preimage: None, - custom_tlvs: &bad_type_range_tlvs, + sender_custom_tlvs: &bad_type_range_tlvs, sender_intended_htlc_amt_msat: 0x0badf00d01020304, cltv_expiry_height: 0xffffffff, }; @@ -4722,20 +4725,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(InboundOnionReceivePayload { custom_tlvs, .. }) => assert!(custom_tlvs.is_empty()), + msgs::InboundOnionPayload::Receive(InboundOnionReceivePayload { 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]), ]; @@ -4743,7 +4746,7 @@ mod tests { payment_data: None, payment_metadata: None, keysend_preimage: None, - custom_tlvs: &expected_custom_tlvs, + sender_custom_tlvs: &expected_sender_custom_tlvs, sender_intended_htlc_amt_msat: 0x0badf00d01020304, cltv_expiry_height: 0xffffffff, }; @@ -4756,12 +4759,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!(); } @@ -4871,7 +4874,7 @@ mod tests { encrypted_tlvs: &>::from_hex("bcd747394fbd4d99588da075a623316e15a576df5bc785cccc7cd6ec7b398acce6faf520175f9ec920f2ef261cdb83dc28cc3a0eeb970107b3306489bf771ef5b1213bca811d345285405861d08a655b6c237fa247a8b4491beee20c878a60e9816492026d8feb9dafa84585b253978db6a0aa2945df5ef445c61e801fb82f43d5f00716baf9fc9b3de50bc22950a36bda8fc27bfb1242e5860c7e687438d4133e058770361a19b6c271a2a07788d34dccc27e39b9829b061a4d960eac4a2c2b0f4de506c24f9af3868c0aff6dda27281c").unwrap(), intro_node_blinding_point: None, keysend_preimage: None, - custom_tlvs: &vec![], + sender_custom_tlvs: &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 803441f09fe..dd6a18f186e 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 @@ pub(super) 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 @@ pub(super) 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 e3505e536de..0ab14661691 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -135,20 +135,20 @@ 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, invoice_request ) = match hop_data { msgs::InboundOnionPayload::Receive(msgs::InboundOnionReceivePayload { - payment_data, keysend_preimage, custom_tlvs, sender_intended_htlc_amt_msat, + payment_data, keysend_preimage, sender_custom_tlvs, 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, None, sender_intended_htlc_amt_msat, cltv_expiry_height, payment_metadata, None, false, keysend_preimage.is_none(), None), msgs::InboundOnionPayload::BlindedReceive(msgs::InboundOnionBlindedReceivePayload { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, payment_secret, intro_node_blinding_point, payment_constraints, payment_context, keysend_preimage, - custom_tlvs, invoice_request + sender_custom_tlvs, user_custom_data, invoice_request }) => { check_blinded_payment_constraints( sender_intended_htlc_amt_msat, cltv_expiry, &payment_constraints @@ -161,7 +161,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, invoice_request) } @@ -234,7 +234,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, payment_context, @@ -247,7 +248,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 { @@ -532,7 +534,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_route_tests.rs b/lightning/src/ln/onion_route_tests.rs index cee8bd1d301..f73a8b87cc8 100644 --- a/lightning/src/ln/onion_route_tests.rs +++ b/lightning/src/ln/onion_route_tests.rs @@ -1191,7 +1191,7 @@ fn test_trampoline_onion_payload_construction_vectors() { encrypted_tlvs: &>::from_hex("bcd747394fbd4d99588da075a623316e15a576df5bc785cccc7cd6ec7b398acce6faf520175f9ec920f2ef261cdb83dc28cc3a0eeb970107b3306489bf771ef5b1213bca811d345285405861d08a655b6c237fa247a8b4491beee20c878a60e9816492026d8feb9dafa84585b253978db6a0aa2945df5ef445c61e801fb82f43d5f00716baf9fc9b3de50bc22950a36bda8fc27bfb1242e5860c7e687438d4133e058770361a19b6c271a2a07788d34dccc27e39b9829b061a4d960eac4a2c2b0f4de506c24f9af3868c0aff6dda27281c").unwrap(), intro_node_blinding_point: None, keysend_preimage: None, - custom_tlvs: &vec![], + sender_custom_tlvs: &vec![], }; let eve_payload = trampoline_payload_eve.encode().to_lower_hex_string(); assert_eq!(eve_payload, "e4020408f0d18004030c35000ad1bcd747394fbd4d99588da075a623316e15a576df5bc785cccc7cd6ec7b398acce6faf520175f9ec920f2ef261cdb83dc28cc3a0eeb970107b3306489bf771ef5b1213bca811d345285405861d08a655b6c237fa247a8b4491beee20c878a60e9816492026d8feb9dafa84585b253978db6a0aa2945df5ef445c61e801fb82f43d5f00716baf9fc9b3de50bc22950a36bda8fc27bfb1242e5860c7e687438d4133e058770361a19b6c271a2a07788d34dccc27e39b9829b061a4d960eac4a2c2b0f4de506c24f9af3868c0aff6dda27281c120408f0d180"); diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 35de2403441..395cee341b0 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -205,7 +205,7 @@ impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundOnionPayload<'a> { .map(|payment_secret| msgs::FinalOnionHopData { payment_secret, total_msat }), payment_metadata: recipient_onion.payment_metadata.as_ref(), keysend_preimage, - custom_tlvs: &recipient_onion.custom_tlvs, + sender_custom_tlvs: &recipient_onion.sender_custom_tlvs, sender_intended_htlc_amt_msat, cltv_expiry_height, }) @@ -219,7 +219,7 @@ impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundOnionPayload<'a> { sender_intended_htlc_amt_msat: u64, total_msat: u64, cltv_expiry_height: u32, encrypted_tlvs: &'a Vec, intro_node_blinding_point: Option, keysend_preimage: Option, invoice_request: Option<&'a InvoiceRequest>, - custom_tlvs: &'a Vec<(u64, Vec)>, + sender_custom_tlvs: &'a Vec<(u64, Vec)>, ) -> Self { Self::BlindedReceive { sender_intended_htlc_amt_msat, @@ -229,7 +229,7 @@ impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundOnionPayload<'a> { intro_node_blinding_point, keysend_preimage, invoice_request, - custom_tlvs, + sender_custom_tlvs, } } @@ -272,7 +272,7 @@ impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundTrampolinePayload<'a> { sender_intended_htlc_amt_msat: u64, total_msat: u64, cltv_expiry_height: u32, encrypted_tlvs: &'a Vec, intro_node_blinding_point: Option, keysend_preimage: Option, _invoice_request: Option<&'a InvoiceRequest>, - custom_tlvs: &'a Vec<(u64, Vec)>, + sender_custom_tlvs: &'a Vec<(u64, Vec)>, ) -> Self { Self::BlindedReceive { sender_intended_htlc_amt_msat, @@ -281,7 +281,7 @@ impl<'a, 'b> OnionPayload<'a, 'b> for msgs::OutboundTrampolinePayload<'a> { encrypted_tlvs, intro_node_blinding_point, keysend_preimage, - custom_tlvs, + sender_custom_tlvs, } } @@ -539,7 +539,7 @@ where blinding_point.take(), *keysend_preimage, invoice_request, - &recipient_onion.custom_tlvs, + &recipient_onion.sender_custom_tlvs, ), ); } else { diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index eb57c8a94c4..8a34f8782c4 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: Option>, pending_amt_msat: u64, /// Used to track the fee paid. Present iff the payment was serialized on 0.0.103+. pending_fee_msat: Option, @@ -493,7 +494,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. /// @@ -650,14 +651,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: Option> } 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, option), }); impl RecipientOnionFields { @@ -665,7 +669,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: None } } /// Creates a new [`RecipientOnionFields`] with no fields. This generally does not create @@ -677,10 +681,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: None} } - /// 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(())`. @@ -688,11 +692,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 @@ -702,38 +706,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 = Some(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) -> &Option> { + &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 @@ -747,8 +772,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); @@ -943,7 +968,8 @@ impl OutboundPayments { let recipient_onion = RecipientOnionFields { payment_secret: None, payment_metadata: None, - custom_tlvs: vec![], + sender_custom_tlvs: vec![], + user_custom_data: None, }; let route = match self.find_initial_route( payment_id, payment_hash, &recipient_onion, keysend_preimage, invoice_request, @@ -1369,7 +1395,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(); @@ -1389,7 +1415,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(); @@ -1693,7 +1720,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: @@ -2310,7 +2338,8 @@ 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(), // only used for retries, and we'll never retry on startup + user_custom_data: None, // 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, @@ -2395,10 +2424,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, option), (not_written, retry_strategy, (static_value, None)), (not_written, attempts, (static_value, PaymentAttempts::new())), }, @@ -2468,25 +2498,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 66322cf107e..af0fbf811d2 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -3690,20 +3690,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]); @@ -3714,14 +3714,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: None, }; if spontaneous { nodes[0].node.send_spontaneous_payment( @@ -3746,24 +3747,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) => { @@ -3776,8 +3777,8 @@ 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 +fn test_retry_sender_custom_tlvs() { + // 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]); @@ -3797,9 +3798,9 @@ 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 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_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, @@ -3851,51 +3852,52 @@ 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_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_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) + 5 is reserved for user_custom_data. 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); @@ -3926,7 +3928,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: None, }; let session_privs = nodes[0].node.test_add_new_pending_payment(our_payment_hash, onion_fields.clone(), payment_id, &route).unwrap(); @@ -3948,7 +3951,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: None, + }; 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(); @@ -3982,14 +3987,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 { @@ -4054,7 +4059,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: None, }, payment_id, route_params.clone(), Retry::Attempts(1)).unwrap(); check_added_monitors!(nodes[0], 2); @@ -4267,7 +4273,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]); @@ -4281,7 +4287,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]); @@ -4309,12 +4315,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 aca1afbff39..14e5870cfc9 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -146,7 +146,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 } } @@ -165,11 +165,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/dns_resolution.rs b/lightning/src/onion_message/dns_resolution.rs index 0f6071e73a3..fd013db8637 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,7 +398,7 @@ 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)> { let DNSSECProof { name: answer_name, proof } = msg; let mut pending_resolves = self.pending_resolves.lock().unwrap(); diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 239625de9e4..d56cc98a88b 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -21,7 +21,7 @@ use super::offers::{OffersMessage, OffersMessageHandler}; use super::packet::{OnionMessageContents, Packet}; use crate::blinded_path::message::{ AsyncPaymentsContext, BlindedMessagePath, DNSResolverContext, MessageContext, - MessageForwardNode, OffersContext, + MessageForwardNode, OffersContext, ReceiveTlvs, }; use crate::blinded_path::EmptyNodeIdLookUp; use crate::events::{Event, EventsProvider}; @@ -81,7 +81,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 } @@ -107,7 +107,10 @@ 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)] @@ -154,6 +157,7 @@ struct TestCustomMessageHandler { struct OnHandleCustomMessage { expect: TestCustomMessage, include_reply_path: bool, + custom_data: Option>, } impl TestCustomMessageHandler { @@ -162,13 +166,25 @@ impl TestCustomMessageHandler { } fn expect_message(&self, message: TestCustomMessage) { - let expectation = OnHandleCustomMessage { expect: message, include_reply_path: false }; + let expectation = + OnHandleCustomMessage { expect: message, include_reply_path: false, custom_data: None }; self.expectations.lock().unwrap().push_back(expectation); } fn expect_message_and_response(&self, message: TestCustomMessage) { - let expectation = OnHandleCustomMessage { expect: message, include_reply_path: true }; - self.expectations.lock().unwrap().push_back(expectation); + self.expectations.lock().unwrap().push_back(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), + }); } fn get_next_expectation(&self) -> OnHandleCustomMessage { @@ -188,10 +204,12 @@ impl Drop for TestCustomMessageHandler { impl CustomOnionMessageHandler for TestCustomMessageHandler { type CustomMessage = TestCustomMessage; fn handle_custom_message( - &self, msg: Self::CustomMessage, context: Option>, responder: Option, + &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, @@ -206,7 +224,7 @@ impl CustomOnionMessageHandler for TestCustomMessageHandler { match responder { Some(responder) if expectation.include_reply_path => { let context = MessageContext::Custom(context.unwrap_or_else(Vec::new)); - let reply = responder.respond_with_reply_path(context); + let reply = responder.respond_with_reply_path(context, custom_data); Some((response, reply)) }, Some(responder) => Some((response, responder.respond())), @@ -406,10 +424,12 @@ fn one_blinded_hop() { let test_msg = TestCustomMessage::Pong; let secp_ctx = Secp256k1::new(); - let context = MessageContext::Custom(Vec::new()); + let recipient_tlvs = + ReceiveTlvs { context: Some(MessageContext::Custom(Vec::new())), custom_data: None }; let entropy = &*nodes[1].entropy_source; + let blinded_path = - BlindedMessagePath::new(&[], nodes[1].node_id, context, entropy, &secp_ctx).unwrap(); + BlindedMessagePath::new(&[], nodes[1].node_id, recipient_tlvs, entropy, &secp_ctx).unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; nodes[0].messenger.send_onion_message(test_msg, instructions).unwrap(); @@ -417,6 +437,33 @@ 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); @@ -425,11 +472,17 @@ 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 recipient_tlvs = + ReceiveTlvs { context: Some(MessageContext::Custom(Vec::new())), custom_data: None }; let entropy = &*nodes[4].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[4].node_id, context, entropy, &secp_ctx) - .unwrap(); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[4].node_id, + recipient_tlvs, + entropy, + &secp_ctx, + ) + .unwrap(); let path = OnionMessagePath { intermediate_nodes: vec![nodes[1].node_id, nodes[2].node_id], destination: Destination::BlindedPath(blinded_path), @@ -451,11 +504,17 @@ 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 recipient_tlvs = + ReceiveTlvs { context: Some(MessageContext::Custom(Vec::new())), custom_data: None }; let entropy = &*nodes[3].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[3].node_id, context, entropy, &secp_ctx) - .unwrap(); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[3].node_id, + recipient_tlvs, + entropy, + &secp_ctx, + ) + .unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -478,10 +537,11 @@ 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 recipient_tlvs = + ReceiveTlvs { context: Some(MessageContext::Custom(Vec::new())), custom_data: None }; let entropy = &*nodes[1].entropy_source; let reply_path = - BlindedMessagePath::new(&[], nodes[1].node_id, context, entropy, &secp_ctx).unwrap(); + BlindedMessagePath::new(&[], nodes[1].node_id, recipient_tlvs, entropy, &secp_ctx).unwrap(); // 4. Create a responder using the reply path for Alice. let responder = Some(Responder::new(reply_path)); @@ -489,7 +549,7 @@ fn async_response_over_one_blinded_hop() { // 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); + 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(); @@ -518,16 +578,17 @@ 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 recipient_tlvs = + ReceiveTlvs { context: Some(MessageContext::Custom(Vec::new())), custom_data: None }; let reply_path = - BlindedMessagePath::new(&[], bob.node_id, context, &*bob.entropy_source, &secp_ctx) + 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)); + alice.custom_message_handler.handle_custom_message(message, None, None, Some(responder)); let (msg, instructions) = response_instruction.unwrap(); assert_eq!( @@ -559,9 +620,10 @@ 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 recipient_tlvs = + ReceiveTlvs { context: Some(MessageContext::Custom(Vec::new())), custom_data: None }; let reply_path = - BlindedMessagePath::new(&[], bob.node_id, context, &*bob.entropy_source, &secp_ctx) + 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 @@ -570,7 +632,7 @@ fn async_response_with_reply_path_fails() { 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)); + alice.custom_message_handler.handle_custom_message(message, None, None, Some(responder)); let (msg, instructions) = response_instruction.unwrap(); assert_eq!( @@ -608,11 +670,17 @@ 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 recipient_tlvs = + ReceiveTlvs { context: Some(MessageContext::Custom(Vec::new())), custom_data: None }; let entropy = &*nodes[2].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, context, entropy, &secp_ctx) - .unwrap(); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[2].node_id, + recipient_tlvs, + entropy, + &secp_ctx, + ) + .unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -623,11 +691,17 @@ 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 recipient_tlvs = + ReceiveTlvs { context: Some(MessageContext::Custom(Vec::new())), custom_data: None }; let entropy = &*nodes[1].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[1].node_id, context, entropy, &secp_ctx) - .unwrap(); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[1].node_id, + recipient_tlvs, + entropy, + &secp_ctx, + ) + .unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -646,11 +720,17 @@ 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 recipient_tlvs = + ReceiveTlvs { context: Some(MessageContext::Custom(Vec::new())), custom_data: None }; let entropy = &*nodes[2].entropy_source; - let mut blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, context, entropy, &secp_ctx) - .unwrap(); + let mut blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[2].node_id, + recipient_tlvs, + entropy, + &secp_ctx, + ) + .unwrap(); blinded_path.clear_blinded_hops(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -675,11 +755,17 @@ 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 recipient_tlvs = + ReceiveTlvs { context: Some(MessageContext::Custom(Vec::new())), custom_data: None }; let entropy = &*nodes[0].entropy_source; - let reply_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[0].node_id, context, entropy, &secp_ctx) - .unwrap(); + let reply_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[0].node_id, + recipient_tlvs, + entropy, + &secp_ctx, + ) + .unwrap(); nodes[0] .messenger .send_onion_message_using_path(path, test_msg.clone(), Some(reply_path)) @@ -696,21 +782,33 @@ 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 recipient_tlvs = + ReceiveTlvs { context: Some(MessageContext::Custom(Vec::new())), custom_data: None }; let entropy = &*nodes[3].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[3].node_id, context, entropy, &secp_ctx) - .unwrap(); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[3].node_id, + recipient_tlvs, + entropy, + &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 recipient_tlvs = + ReceiveTlvs { context: Some(MessageContext::Custom(Vec::new())), custom_data: None }; let entropy = &*nodes[0].entropy_source; - let reply_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[0].node_id, context, entropy, &secp_ctx) - .unwrap(); + let reply_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[0].node_id, + recipient_tlvs, + entropy, + &secp_ctx, + ) + .unwrap(); let instructions = MessageSendInstructions::WithSpecifiedReplyPath { destination, reply_path }; nodes[0].messenger.send_onion_message(test_msg, instructions).unwrap(); @@ -804,11 +902,17 @@ fn requests_peer_connection_for_buffered_messages() { 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 entropy = &*nodes[0].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, context, entropy, &secp_ctx) - .unwrap(); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[2].node_id, + recipient_tlvs, + entropy, + &secp_ctx, + ) + .unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -846,11 +950,17 @@ fn drops_buffered_messages_waiting_for_peer_connection() { 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 entropy = &*nodes[0].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, context, entropy, &secp_ctx) - .unwrap(); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[2].node_id, + recipient_tlvs, + entropy, + &secp_ctx, + ) + .unwrap(); let destination = Destination::BlindedPath(blinded_path); let instructions = MessageSendInstructions::WithoutReplyPath { destination }; @@ -904,11 +1014,17 @@ fn intercept_offline_peer_oms() { 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 entropy = &*nodes[2].entropy_source; - let blinded_path = - BlindedMessagePath::new(&intermediate_nodes, nodes[2].node_id, context, entropy, &secp_ctx) - .unwrap(); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[2].node_id, + recipient_tlvs, + entropy, + &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 012b7978053..103dd98f1a8 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -186,7 +186,7 @@ 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}; @@ -213,7 +213,7 @@ 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!() /// # } @@ -268,8 +268,11 @@ 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); @@ -415,17 +418,19 @@ 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), } } } @@ -437,7 +442,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 { @@ -466,7 +471,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. @@ -491,7 +496,7 @@ pub trait MessageRouter { /// Creates [`BlindedMessagePath`]s to the `recipient` node. The nodes in `peers` are assumed to /// be direct peers with the `recipient`. fn create_blinded_paths( - &self, recipient: PublicKey, context: MessageContext, peers: Vec, + &self, recipient: PublicKey, recipient_tlvs: ReceiveTlvs, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()>; @@ -509,14 +514,14 @@ pub trait MessageRouter { /// The provided implementation simply delegates to [`MessageRouter::create_blinded_paths`], /// ignoring the short channel ids. fn create_compact_blinded_paths( - &self, recipient: PublicKey, context: MessageContext, peers: Vec, + &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) } } @@ -551,7 +556,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. @@ -591,7 +596,13 @@ where let paths = peer_info .into_iter() .map(|(peer, _, _)| { - BlindedMessagePath::new(&[peer], recipient, context.clone(), entropy, secp_ctx) + BlindedMessagePath::new( + &[peer], + recipient, + recipient_tlvs.clone(), + entropy, + secp_ctx, + ) }) .take(MAX_PATHS) .collect::, _>>(); @@ -600,8 +611,14 @@ where Ok(paths) if !paths.is_empty() => Ok(paths), _ => { if is_recipient_announced { - BlindedMessagePath::new(&[], recipient, context, &**entropy_source, secp_ctx) - .map(|path| vec![path]) + BlindedMessagePath::new( + &[], + recipient, + recipient_tlvs, + &**entropy_source, + secp_ctx, + ) + .map(|path| vec![path]) } else { Err(()) } @@ -659,15 +676,15 @@ where } pub(crate) fn create_blinded_paths( - network_graph: &G, recipient: PublicKey, context: MessageContext, peers: Vec, - entropy_source: &ES, secp_ctx: &Secp256k1, + 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, + recipient_tlvs, peers.into_iter(), entropy_source, secp_ctx, @@ -676,13 +693,13 @@ where } pub(crate) fn create_compact_blinded_paths( - 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, + recipient_tlvs, peers.into_iter(), entropy_source, secp_ctx, @@ -704,13 +721,13 @@ where } fn create_blinded_paths( - &self, recipient: PublicKey, context: MessageContext, peers: Vec, + &self, recipient: PublicKey, recipient_tlvs: ReceiveTlvs, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { Self::create_blinded_paths( &self.network_graph, recipient, - context, + recipient_tlvs, peers, &self.entropy_source, secp_ctx, @@ -718,13 +735,13 @@ where } fn create_compact_blinded_paths( - &self, recipient: PublicKey, context: MessageContext, peers: Vec, + &self, recipient: PublicKey, recipient_tlvs: ReceiveTlvs, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { Self::create_compact_blinded_paths( &self.network_graph, recipient, - context, + recipient_tlvs, peers, &self.entropy_source, secp_ctx, @@ -871,7 +888,8 @@ 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 @@ -896,7 +914,12 @@ 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, + ), } /// Creates an [`OnionMessage`] with the given `contents` for sending to the destination of @@ -1067,25 +1090,25 @@ where Ok(( Payload::Receive { message, - control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { context }), + control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { context, custom_data }), reply_path, }, None, )) => match (&message, &context) { - (_, None) => Ok(PeeledOnion::Receive(message, None, reply_path)), + (_, None) => 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!( @@ -1312,10 +1335,14 @@ where MessageSendInstructions::WithSpecifiedReplyPath { destination, reply_path } => { (destination, Some(reply_path)) }, - MessageSendInstructions::WithReplyPath { destination, context } + MessageSendInstructions::WithReplyPath { + destination, + reply_data: (context, custom_data), + } | MessageSendInstructions::ForReply { - instructions: ResponseInstruction { destination, context: Some(context) }, - } => match self.create_blinded_path(context) { + instructions: + ResponseInstruction { destination, reply_data: (Some(context), custom_data) }, + } => match self.create_blinded_path(context, custom_data) { Ok(reply_path) => (destination, Some(reply_path)), Err(err) => { log_trace!( @@ -1329,7 +1356,7 @@ where }, MessageSendInstructions::WithoutReplyPath { destination } | MessageSendInstructions::ForReply { - instructions: ResponseInstruction { destination, context: None }, + instructions: ResponseInstruction { destination, reply_data: (None, _) }, } => (destination, None), }; @@ -1387,7 +1414,7 @@ where } fn create_blinded_path( - &self, context: MessageContext, + &self, context: MessageContext, custom_data: Option>, ) -> Result { let recipient = self .node_signer @@ -1404,8 +1431,10 @@ where .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) } @@ -1821,7 +1850,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: {:?}", @@ -1840,8 +1869,12 @@ 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); } @@ -1895,7 +1928,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 { @@ -1906,8 +1939,12 @@ 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); } @@ -2270,7 +2307,10 @@ fn packet_payloads_and_keys< } else { payloads.push(( Payload::Receive { - control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { context: None }), + control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { + context: None, + custom_data: None, + }), reply_path: reply_path.take(), message, }, diff --git a/lightning/src/onion_message/offers.rs b/lightning/src/onion_message/offers.rs index 1d0f9c7b664..d4ffa9dfec3 100644 --- a/lightning/src/onion_message/offers.rs +++ b/lightning/src/onion_message/offers.rs @@ -44,7 +44,8 @@ pub trait OffersMessageHandler { /// /// [`OnionMessenger`]: crate::onion_message::messenger::OnionMessenger fn handle_message( - &self, message: OffersMessage, context: Option, 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 961ae71a2d9..9e9a811293b 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -341,6 +341,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; @@ -360,7 +361,7 @@ impl Readable for ControlTlvs { next_blinding_override, }) } else if valid_recv_fmt { - ControlTlvs::Receive(ReceiveTlvs { context }) + 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 59e4dc7decd..a30ccb56ceb 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -737,7 +737,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 9d02452010d..6debfb273cf 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -7,7 +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; use crate::blinded_path::message::{BlindedMessagePath, MessageForwardNode}; use crate::blinded_path::payment::{BlindedPaymentPath, ReceiveTlvs}; use crate::chain; @@ -331,17 +331,17 @@ impl<'a> MessageRouter for TestMessageRouter<'a> { } fn create_blinded_paths( - &self, recipient: PublicKey, context: MessageContext, peers: Vec, + &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, peers: Vec, - secp_ctx: &Secp256k1, + &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) } }