diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 39ed7300c69..7d660f459ea 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -33,6 +33,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hashes::Hash as TraitImport; use bitcoin::WPubkeyHash; +use lightning::blinded_path::message::MessageContext; use lightning::blinded_path::payment::ReceiveTlvs; use lightning::blinded_path::BlindedPath; use lightning::chain; @@ -138,7 +139,8 @@ impl MessageRouter for FuzzRouter { } fn create_blinded_paths( - &self, _recipient: PublicKey, _peers: Vec, _secp_ctx: &Secp256k1, + &self, _recipient: PublicKey, _context: MessageContext, _peers: Vec, + _secp_ctx: &Secp256k1, ) -> Result, ()> { unreachable!() } diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 86f4ef4de4c..9c45c66e1a4 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -30,6 +30,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hashes::Hash as _; use bitcoin::WPubkeyHash; +use lightning::blinded_path::message::MessageContext; use lightning::blinded_path::payment::ReceiveTlvs; use lightning::blinded_path::BlindedPath; use lightning::chain; @@ -175,7 +176,8 @@ impl MessageRouter for FuzzRouter { } fn create_blinded_paths( - &self, _recipient: PublicKey, _peers: Vec, _secp_ctx: &Secp256k1, + &self, _recipient: PublicKey, _context: MessageContext, _peers: Vec, + _secp_ctx: &Secp256k1, ) -> Result, ()> { unreachable!() } diff --git a/fuzz/src/invoice_request_deser.rs b/fuzz/src/invoice_request_deser.rs index 642da58236f..54b842febc9 100644 --- a/fuzz/src/invoice_request_deser.rs +++ b/fuzz/src/invoice_request_deser.rs @@ -10,7 +10,7 @@ use crate::utils::test_logger; use bitcoin::secp256k1::{self, Keypair, Parity, PublicKey, Secp256k1, SecretKey}; use core::convert::TryFrom; -use lightning::blinded_path::message::ForwardNode; +use lightning::blinded_path::message::{ForwardNode, MessageContext, OffersContext}; use lightning::blinded_path::BlindedPath; use lightning::ln::features::BlindedHopFeatures; use lightning::ln::PaymentHash; @@ -87,10 +87,22 @@ fn build_response( ], ]; let paths = vec![ - BlindedPath::new_for_message(&intermediate_nodes[0], pubkey(42), &entropy_source, secp_ctx) - .unwrap(), - BlindedPath::new_for_message(&intermediate_nodes[1], pubkey(42), &entropy_source, secp_ctx) - .unwrap(), + BlindedPath::new_for_message( + &intermediate_nodes[0], + pubkey(42), + MessageContext::Offers(OffersContext::Unknown {}), + &entropy_source, + secp_ctx, + ) + .unwrap(), + BlindedPath::new_for_message( + &intermediate_nodes[1], + pubkey(42), + MessageContext::Offers(OffersContext::Unknown {}), + &entropy_source, + secp_ctx, + ) + .unwrap(), ]; let payinfo = vec![ diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index e60d13d57a9..05ee7526faa 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -6,6 +6,7 @@ use bitcoin::secp256k1::ecdsa::RecoverableSignature; use bitcoin::secp256k1::schnorr; use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey}; +use lightning::blinded_path::message::{MessageContext, OffersContext}; use lightning::blinded_path::{BlindedPath, EmptyNodeIdLookUp}; use lightning::ln::features::InitFeatures; use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler}; @@ -94,7 +95,8 @@ impl MessageRouter for TestMessageRouter { } fn create_blinded_paths( - &self, _recipient: PublicKey, _peers: Vec, _secp_ctx: &Secp256k1, + &self, _recipient: PublicKey, _context: MessageContext, _peers: Vec, + _secp_ctx: &Secp256k1, ) -> Result, ()> { unreachable!() } @@ -104,7 +106,7 @@ struct TestOffersMessageHandler {} impl OffersMessageHandler for TestOffersMessageHandler { fn handle_message( - &self, _message: OffersMessage, _responder: Option, + &self, _message: OffersMessage, _context: OffersContext, _responder: Option, ) -> ResponseInstruction { ResponseInstruction::NoResponse } @@ -152,7 +154,8 @@ struct TestCustomMessageHandler {} impl CustomOnionMessageHandler for TestCustomMessageHandler { type CustomMessage = TestCustomMessage; fn handle_custom_message( - &self, message: Self::CustomMessage, responder: Option, + &self, message: Self::CustomMessage, _context: Option>, + responder: Option, ) -> ResponseInstruction { match responder { Some(responder) => responder.respond(message), @@ -342,13 +345,18 @@ mod tests { super::do_test(&>::from_hex(one_hop_om).unwrap(), &logger); { let log_entries = logger.lines.lock().unwrap(); + assert_eq!( + log_entries.get(&( + "lightning::onion_message::messenger".to_string(), + "Received an onion message with a reply_path: Custom(TestCustomMessage)" + .to_string() + )), + Some(&1) + ); assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), - "Received an onion message with path_id None and a reply_path: Custom(TestCustomMessage)" - .to_string())), Some(&1)); - assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), - "Constructing onion message when responding with Custom Message to an onion message with path_id None: TestCustomMessage".to_string())), Some(&1)); + "Constructing onion message when responding with Custom Message to an onion message: TestCustomMessage".to_string())), Some(&1)); assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), - "Buffered onion message when responding with Custom Message to an onion message with path_id None".to_string())), Some(&1)); + "Buffered onion message when responding with Custom Message to an onion message".to_string())), Some(&1)); } let two_unblinded_hops_om = "\ diff --git a/fuzz/src/refund_deser.rs b/fuzz/src/refund_deser.rs index 11907b6d696..8e9e6442f47 100644 --- a/fuzz/src/refund_deser.rs +++ b/fuzz/src/refund_deser.rs @@ -10,7 +10,7 @@ use crate::utils::test_logger; use bitcoin::secp256k1::{self, Keypair, PublicKey, Secp256k1, SecretKey}; use core::convert::TryFrom; -use lightning::blinded_path::message::ForwardNode; +use lightning::blinded_path::message::{ForwardNode, MessageContext, OffersContext}; use lightning::blinded_path::BlindedPath; use lightning::ln::features::BlindedHopFeatures; use lightning::ln::PaymentHash; @@ -76,10 +76,22 @@ fn build_response( ], ]; let paths = vec![ - BlindedPath::new_for_message(&intermediate_nodes[0], pubkey(42), &entropy_source, secp_ctx) - .unwrap(), - BlindedPath::new_for_message(&intermediate_nodes[1], pubkey(42), &entropy_source, secp_ctx) - .unwrap(), + BlindedPath::new_for_message( + &intermediate_nodes[0], + pubkey(42), + MessageContext::Offers(OffersContext::Unknown {}), + &entropy_source, + secp_ctx, + ) + .unwrap(), + BlindedPath::new_for_message( + &intermediate_nodes[1], + pubkey(42), + MessageContext::Offers(OffersContext::Unknown {}), + &entropy_source, + secp_ctx, + ) + .unwrap(), ]; let payinfo = vec![ diff --git a/lightning/src/blinded_path/message.rs b/lightning/src/blinded_path/message.rs index bdbb4be4541..06d535a3527 100644 --- a/lightning/src/blinded_path/message.rs +++ b/lightning/src/blinded_path/message.rs @@ -20,6 +20,7 @@ use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode, NextMessage use crate::blinded_path::utils; use crate::io; use crate::io::Cursor; +use crate::ln::channelmanager::PaymentId; use crate::ln::onion_utils; use crate::onion_message::packet::ControlTlvs; use crate::sign::{NodeSigner, Recipient}; @@ -52,10 +53,10 @@ pub(crate) struct ForwardTlvs { /// Similar to [`ForwardTlvs`], but these TLVs are for the final node. pub(crate) struct ReceiveTlvs { - /// If `path_id` is `Some`, it is used to identify the blinded path that this onion message is + /// 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(crate) path_id: Option<[u8; 32]>, + pub context: Option } impl Writeable for ForwardTlvs { @@ -78,16 +79,62 @@ impl Writeable for ReceiveTlvs { fn write(&self, writer: &mut W) -> Result<(), io::Error> { // TODO: write padding encode_tlv_stream!(writer, { - (6, self.path_id, option), + (65537, self.context, option), }); Ok(()) } } +/// Represents additional data included by the recipient in a [`BlindedPath`]. +/// +/// This data is encrypted by the recipient and remains invisible to anyone else. +/// It is included in the [`BlindedPath`], making it accessible again to the recipient +/// whenever the [`BlindedPath`] is used. +/// The recipient can authenticate the message and utilize it for further processing +/// if needed. +#[derive(Clone, Debug)] +pub enum MessageContext { + /// Represents the data specific to [`OffersMessage`] + /// + /// [`OffersMessage`]: crate::onion_message::offers::OffersMessage + Offers(OffersContext), + /// Represents custom data received in a Custom Onion Message. + Custom(Vec), +} + +/// Contains the data specific to [`OffersMessage`] +/// +/// [`OffersMessage`]: crate::onion_message::offers::OffersMessage +#[derive(Clone, Debug)] +pub enum OffersContext { + /// Represents an unknown BOLT12 payment context. + /// This variant is used when a message is sent without + /// using a [`BlindedPath`] or over one created prior to + /// LDK version 0.0.124. + Unknown {}, + /// Represents an outbound BOLT12 payment context. + OutboundPayment { + /// Payment ID of the outbound BOLT12 payment. + payment_id: PaymentId + }, +} + +impl_writeable_tlv_based_enum!(MessageContext, ; + (0, Offers), + (1, Custom), +); + +impl_writeable_tlv_based_enum!(OffersContext, + (0, Unknown) => {}, + (1, OutboundPayment) => { + (0, payment_id, required), + }, +;); + /// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`. pub(super) fn blinded_hops( secp_ctx: &Secp256k1, intermediate_nodes: &[ForwardNode], recipient_node_id: PublicKey, - session_priv: &SecretKey + context: MessageContext, session_priv: &SecretKey ) -> Result, secp256k1::Error> { let pks = intermediate_nodes.iter().map(|node| &node.node_id) .chain(core::iter::once(&recipient_node_id)); @@ -99,7 +146,7 @@ pub(super) fn blinded_hops( None => NextMessageHop::NodeId(*pubkey), }) .map(|next_hop| ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None })) - .chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { path_id: None }))); + .chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs{ context: Some(context) }))); utils::construct_blinded_hops(secp_ctx, pks, tlvs, session_priv) } diff --git a/lightning/src/blinded_path/mod.rs b/lightning/src/blinded_path/mod.rs index 2d3d085bddf..db90e165942 100644 --- a/lightning/src/blinded_path/mod.rs +++ b/lightning/src/blinded_path/mod.rs @@ -14,6 +14,7 @@ pub mod message; pub(crate) mod utils; use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey}; +use message::MessageContext; use core::ops::Deref; use crate::ln::msgs::DecodeError; @@ -123,9 +124,9 @@ pub struct BlindedHop { impl BlindedPath { /// Create a one-hop blinded path for a message. pub fn one_hop_for_message( - recipient_node_id: PublicKey, entropy_source: ES, secp_ctx: &Secp256k1 + recipient_node_id: PublicKey, context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1 ) -> Result where ES::Target: EntropySource { - Self::new_for_message(&[], recipient_node_id, entropy_source, secp_ctx) + Self::new_for_message(&[], recipient_node_id, context, entropy_source, secp_ctx) } /// Create a blinded path for an onion message, to be forwarded along `node_pks`. The last node @@ -135,7 +136,7 @@ impl BlindedPath { // TODO: make all payloads the same size with padding + add dummy hops pub fn new_for_message( intermediate_nodes: &[message::ForwardNode], recipient_node_id: PublicKey, - entropy_source: ES, secp_ctx: &Secp256k1 + context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1 ) -> Result where ES::Target: EntropySource { let introduction_node = IntroductionNode::NodeId( intermediate_nodes.first().map_or(recipient_node_id, |n| n.node_id) @@ -147,7 +148,8 @@ impl BlindedPath { introduction_node, blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret), blinded_hops: message::blinded_hops( - secp_ctx, intermediate_nodes, recipient_node_id, &blinding_secret, + secp_ctx, intermediate_nodes, recipient_node_id, + context, &blinding_secret, ).map_err(|_| ())?, }) } diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 19dcd25fe80..89f967c4f70 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -31,6 +31,7 @@ use bitcoin::secp256k1::{SecretKey,PublicKey}; use bitcoin::secp256k1::Secp256k1; use bitcoin::{secp256k1, Sequence}; +use crate::blinded_path::message::{MessageContext, OffersContext}; use crate::blinded_path::{BlindedPath, NodeIdLookUp}; use crate::blinded_path::message::ForwardNode; use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentConstraints, PaymentContext, ReceiveTlvs}; @@ -8620,7 +8621,7 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => { let entropy = &*$self.entropy_source; let secp_ctx = &$self.secp_ctx; - let path = $self.create_blinded_path_using_absolute_expiry(absolute_expiry) + let path = $self.create_blinded_path_using_absolute_expiry(OffersContext::Unknown {}, absolute_expiry) .map_err(|_| Bolt12SemanticError::MissingPaths)?; let builder = OfferBuilder::deriving_signing_pubkey( node_id, expanded_key, entropy, secp_ctx @@ -8692,7 +8693,8 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => { let entropy = &*$self.entropy_source; let secp_ctx = &$self.secp_ctx; - let path = $self.create_blinded_path_using_absolute_expiry(Some(absolute_expiry)) + let context = OffersContext::OutboundPayment { payment_id }; + let path = $self.create_blinded_path_using_absolute_expiry(context, Some(absolute_expiry)) .map_err(|_| Bolt12SemanticError::MissingPaths)?; let builder = RefundBuilder::deriving_payer_id( node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id @@ -8815,7 +8817,9 @@ where Some(payer_note) => builder.payer_note(payer_note), }; let invoice_request = builder.build_and_sign()?; - let reply_path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?; + + let context = OffersContext::OutboundPayment { payment_id }; + let reply_path = self.create_blinded_path(context).map_err(|_| Bolt12SemanticError::MissingPaths)?; let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); @@ -8915,7 +8919,7 @@ where )?; let builder: InvoiceBuilder = builder.into(); let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?; - let reply_path = self.create_blinded_path() + let reply_path = self.create_blinded_path(OffersContext::Unknown {}) .map_err(|_| Bolt12SemanticError::MissingPaths)?; let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap(); @@ -9048,15 +9052,15 @@ where /// respectively, based on the given `absolute_expiry` as seconds since the Unix epoch. See /// [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. fn create_blinded_path_using_absolute_expiry( - &self, absolute_expiry: Option + &self, context: OffersContext, absolute_expiry: Option, ) -> Result { let now = self.duration_since_epoch(); let max_short_lived_absolute_expiry = now.saturating_add(MAX_SHORT_LIVED_RELATIVE_EXPIRY); if absolute_expiry.unwrap_or(Duration::MAX) <= max_short_lived_absolute_expiry { - self.create_compact_blinded_path() + self.create_compact_blinded_path(context) } else { - self.create_blinded_path() + self.create_blinded_path(context) } } @@ -9076,7 +9080,7 @@ where /// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`]. /// /// Errors if the `MessageRouter` errors or returns an empty `Vec`. - fn create_blinded_path(&self) -> Result { + fn create_blinded_path(&self, context: OffersContext) -> Result { let recipient = self.get_our_node_id(); let secp_ctx = &self.secp_ctx; @@ -9089,14 +9093,14 @@ where .collect::>(); self.router - .create_blinded_paths(recipient, peers, secp_ctx) + .create_blinded_paths(recipient, MessageContext::Offers(context), peers, secp_ctx) .and_then(|paths| paths.into_iter().next().ok_or(())) } /// Creates a blinded path by delegating to [`MessageRouter::create_compact_blinded_paths`]. /// /// Errors if the `MessageRouter` errors or returns an empty `Vec`. - fn create_compact_blinded_path(&self) -> Result { + fn create_compact_blinded_path(&self, context: OffersContext) -> Result { let recipient = self.get_our_node_id(); let secp_ctx = &self.secp_ctx; @@ -9116,7 +9120,7 @@ where .collect::>(); self.router - .create_compact_blinded_paths(recipient, peers, secp_ctx) + .create_compact_blinded_paths(recipient, MessageContext::Offers(context), peers, secp_ctx) .and_then(|paths| paths.into_iter().next().ok_or(())) } @@ -10498,10 +10502,17 @@ where R::Target: Router, L::Target: Logger, { - fn handle_message(&self, message: OffersMessage, responder: Option) -> ResponseInstruction { + fn handle_message(&self, message: OffersMessage, context: OffersContext, responder: Option) -> ResponseInstruction { let secp_ctx = &self.secp_ctx; let expanded_key = &self.inbound_payment_key; + let abandon_if_payment = |context| { + match context { + OffersContext::OutboundPayment { payment_id } => self.abandon_payment(payment_id), + _ => {}, + } + }; + match message { OffersMessage::InvoiceRequest(invoice_request) => { let responder = match responder { @@ -10614,12 +10625,21 @@ where }; match result { - Ok(()) => ResponseInstruction::NoResponse, - Err(e) => match responder { - Some(responder) => responder.respond(OffersMessage::InvoiceError(e)), + Ok(_) => ResponseInstruction::NoResponse, + Err(err) => match responder { + Some(responder) => { + abandon_if_payment(context); + responder.respond(OffersMessage::InvoiceError(err)) + }, None => { - log_trace!(self.logger, "No reply path for sending invoice error: {:?}", e); - ResponseInstruction::NoResponse + abandon_if_payment(context); + log_trace!( + self.logger, + "An error response was generated, but there is no reply_path specified \ + for sending the response. Error: {}", + err + ); + return ResponseInstruction::NoResponse; }, }, } @@ -10636,6 +10656,7 @@ where } }, OffersMessage::InvoiceError(invoice_error) => { + abandon_if_payment(context); log_trace!(self.logger, "Received invoice_error: {}", invoice_error); ResponseInstruction::NoResponse }, diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index a77d91e89c7..096bcf9633c 100644 --- a/lightning/src/ln/max_payment_path_len_tests.rs +++ b/lightning/src/ln/max_payment_path_len_tests.rs @@ -13,7 +13,7 @@ use bitcoin::secp256k1::{Secp256k1, PublicKey}; use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode}; use crate::blinded_path::payment::{PaymentConstraints, PaymentContext, ReceiveTlvs}; -use crate::events::MessageSendEventsProvider; +use crate::events::{Event, MessageSendEventsProvider}; use crate::ln::PaymentSecret; use crate::ln::blinded_payment_tests::get_blinded_route_parameters; use crate::ln::channelmanager::PaymentId; @@ -389,4 +389,13 @@ fn bolt12_invoice_too_large_blinded_paths() { nodes[0].onion_messenger.handle_onion_message(&nodes[1].node.get_our_node_id(), &invoice_om); // TODO: assert on the invoice error once we support replying to invoice OMs with failure info nodes[0].logger.assert_log_contains("lightning::ln::channelmanager", "Failed paying invoice: OnionPacketSizeExceeded", 1); + + let events = nodes[0].node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + match events[0] { + Event::PaymentFailed { payment_id: id, .. } => { + assert_eq!(id, payment_id) + }, + _ => panic!("Unexpected event"), + } } diff --git a/lightning/src/ln/offers_tests.rs b/lightning/src/ln/offers_tests.rs index 405ab87be3f..83ff8ce6d5c 100644 --- a/lightning/src/ln/offers_tests.rs +++ b/lightning/src/ln/offers_tests.rs @@ -1412,6 +1412,14 @@ fn fails_sending_invoice_without_blinded_payment_paths_for_offer() { let invoice_error = extract_invoice_error(david, &onion_message); assert_eq!(invoice_error, InvoiceError::from(Bolt12SemanticError::MissingPaths)); + + // Confirm that david drops this failed payment from his pending outbound payments. + match get_event!(david, Event::InvoiceRequestFailed) { + Event::InvoiceRequestFailed { payment_id: pay_id } => { + assert_eq!(pay_id, payment_id) + }, + _ => panic!("No Event::InvoiceRequestFailed"), + } } #[test] diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index 1b75755fac2..7a46bee9d73 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -18,6 +18,7 @@ use bitcoin::blockdata::constants::ChainHash; use bitcoin::secp256k1::{self, Secp256k1, SecretKey, PublicKey}; +use crate::blinded_path::message::OffersContext; use crate::sign::{NodeSigner, Recipient}; use crate::events::{MessageSendEvent, MessageSendEventsProvider}; use crate::ln::types::ChannelId; @@ -145,7 +146,7 @@ impl OnionMessageHandler for IgnoringMessageHandler { } impl OffersMessageHandler for IgnoringMessageHandler { - fn handle_message(&self, _message: OffersMessage, _responder: Option) -> ResponseInstruction { + fn handle_message(&self, _message: OffersMessage, _context: OffersContext, _responder: Option) -> ResponseInstruction { ResponseInstruction::NoResponse } } @@ -159,7 +160,7 @@ impl AsyncPaymentsMessageHandler for IgnoringMessageHandler { } impl CustomOnionMessageHandler for IgnoringMessageHandler { type CustomMessage = Infallible; - fn handle_custom_message(&self, _message: Self::CustomMessage, _responder: Option) -> ResponseInstruction { + fn handle_custom_message(&self, _message: Self::CustomMessage, _context: Option>, _responder: Option) -> ResponseInstruction { // Since we always return `None` in the read the handle method should never be called. unreachable!(); } diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 40b6177921f..371b4f5879d 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -10,7 +10,7 @@ //! Onion message testing and test utilities live here. use crate::blinded_path::{BlindedPath, EmptyNodeIdLookUp}; -use crate::blinded_path::message::ForwardNode; +use crate::blinded_path::message::{ForwardNode, MessageContext, OffersContext}; use crate::events::{Event, EventsProvider}; use crate::ln::features::{ChannelFeatures, InitFeatures}; use crate::ln::msgs::{self, DecodeError, OnionMessageHandler}; @@ -76,7 +76,7 @@ impl Drop for MessengerNode { struct TestOffersMessageHandler {} impl OffersMessageHandler for TestOffersMessageHandler { - fn handle_message(&self, _message: OffersMessage, _responder: Option) -> ResponseInstruction { + fn handle_message(&self, _message: OffersMessage, _context: OffersContext, _responder: Option) -> ResponseInstruction { ResponseInstruction::NoResponse } } @@ -174,7 +174,7 @@ impl Drop for TestCustomMessageHandler { impl CustomOnionMessageHandler for TestCustomMessageHandler { type CustomMessage = TestCustomMessage; - fn handle_custom_message(&self, msg: Self::CustomMessage, responder: Option) -> ResponseInstruction { + fn handle_custom_message(&self, msg: Self::CustomMessage, context: Option>, responder: Option) -> ResponseInstruction { let expectation = self.get_next_expectation(); assert_eq!(msg, expectation.expect); @@ -190,7 +190,7 @@ impl CustomOnionMessageHandler for TestCustomMessageHandler { match responder { Some(responder) if expectation.include_reply_path => { - responder.respond_with_reply_path(response) + responder.respond_with_reply_path(response, MessageContext::Custom(context.unwrap_or_else(Vec::new))) }, Some(responder) => responder.respond(response), None => ResponseInstruction::NoResponse, @@ -372,7 +372,8 @@ fn one_blinded_hop() { let test_msg = TestCustomMessage::Pong; let secp_ctx = Secp256k1::new(); - let blinded_path = BlindedPath::new_for_message(&[], nodes[1].node_id, &*nodes[1].entropy_source, &secp_ctx).unwrap(); + let context = MessageContext::Custom(Vec::new()); + let blinded_path = BlindedPath::new_for_message(&[], nodes[1].node_id, context, &*nodes[1].entropy_source, &secp_ctx).unwrap(); let destination = Destination::BlindedPath(blinded_path); nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap(); nodes[1].custom_message_handler.expect_message(TestCustomMessage::Pong); @@ -386,7 +387,8 @@ fn two_unblinded_two_blinded() { let secp_ctx = Secp256k1::new(); let intermediate_nodes = [ForwardNode { node_id: nodes[3].node_id, short_channel_id: None }]; - let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[4].node_id, &*nodes[4].entropy_source, &secp_ctx).unwrap(); + let context = MessageContext::Custom(Vec::new()); + let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[4].node_id, context, &*nodes[4].entropy_source, &secp_ctx).unwrap(); let path = OnionMessagePath { intermediate_nodes: vec![nodes[1].node_id, nodes[2].node_id], destination: Destination::BlindedPath(blinded_path), @@ -408,7 +410,8 @@ fn three_blinded_hops() { ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, ForwardNode { node_id: nodes[2].node_id, short_channel_id: None }, ]; - let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[3].node_id, &*nodes[3].entropy_source, &secp_ctx).unwrap(); + let context = MessageContext::Custom(Vec::new()); + let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[3].node_id, context, &*nodes[3].entropy_source, &secp_ctx).unwrap(); let destination = Destination::BlindedPath(blinded_path); nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap(); @@ -427,18 +430,18 @@ fn async_response_over_one_blinded_hop() { // 2. Define the message sent from Bob to Alice. let message = TestCustomMessage::Ping; - let path_id = Some([2; 32]); // 3. Simulate the creation of a Blinded Reply path provided by Bob. let secp_ctx = Secp256k1::new(); - let reply_path = BlindedPath::new_for_message(&[], nodes[1].node_id, &*nodes[1].entropy_source, &secp_ctx).unwrap(); + let context = MessageContext::Custom(Vec::new()); + let reply_path = BlindedPath::new_for_message(&[], nodes[1].node_id, context, &*nodes[1].entropy_source, &secp_ctx).unwrap(); // 4. Create a responder using the reply path for Alice. - let responder = Some(Responder::new(reply_path, path_id)); + let responder = Some(Responder::new(reply_path)); // 5. Expect Alice to receive the message and create a response instruction for it. alice.custom_message_handler.expect_message(message.clone()); - let response_instruction = nodes[0].custom_message_handler.handle_custom_message(message, responder); + let response_instruction = nodes[0].custom_message_handler.handle_custom_message(message, None, responder); // 6. Simulate Alice asynchronously responding back to Bob with a response. assert_eq!( @@ -466,13 +469,13 @@ 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 path_id = Some([2; 32]); - let reply_path = BlindedPath::new_for_message(&[], bob.node_id, &*bob.entropy_source, &secp_ctx).unwrap(); + let context = MessageContext::Custom(Vec::new()); + let reply_path = BlindedPath::new_for_message(&[], bob.node_id, context, &*bob.entropy_source, &secp_ctx).unwrap(); // Alice asynchronously responds to Bob, expecting a response back from him. - let responder = Responder::new(reply_path, path_id); + 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, Some(responder)); + let response_instruction = alice.custom_message_handler.handle_custom_message(message, None, Some(responder)); assert_eq!( alice.messenger.handle_onion_message_response(response_instruction), @@ -503,15 +506,15 @@ 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 path_id = Some([2; 32]); - let reply_path = BlindedPath::new_for_message(&[], bob.node_id, &*bob.entropy_source, &secp_ctx).unwrap(); + let context = MessageContext::Custom(Vec::new()); + let reply_path = BlindedPath::new_for_message(&[], bob.node_id, context, &*bob.entropy_source, &secp_ctx).unwrap(); // Alice tries to asynchronously respond to Bob, but fails because the nodes are unannounced and // disconnected. Thus, a reply path could no be created for the response. disconnect_peers(alice, bob); - let responder = Responder::new(reply_path, path_id); + 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, Some(responder)); + let response_instruction = alice.custom_message_handler.handle_custom_message(message, None, Some(responder)); assert_eq!( alice.messenger.handle_onion_message_response(response_instruction), @@ -548,7 +551,8 @@ fn we_are_intro_node() { ForwardNode { node_id: nodes[0].node_id, short_channel_id: None }, ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, ]; - let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[2].node_id, &*nodes[2].entropy_source, &secp_ctx).unwrap(); + let context = MessageContext::Custom(Vec::new()); + let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[2].node_id, context, &*nodes[2].entropy_source, &secp_ctx).unwrap(); let destination = Destination::BlindedPath(blinded_path); nodes[0].messenger.send_onion_message(test_msg.clone(), destination, None).unwrap(); @@ -557,7 +561,8 @@ fn we_are_intro_node() { // Try with a two-hop blinded path where we are the introduction node. let intermediate_nodes = [ForwardNode { node_id: nodes[0].node_id, short_channel_id: None }]; - let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[1].node_id, &*nodes[1].entropy_source, &secp_ctx).unwrap(); + let context = MessageContext::Custom(Vec::new()); + let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[1].node_id, context, &*nodes[1].entropy_source, &secp_ctx).unwrap(); let destination = Destination::BlindedPath(blinded_path); nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap(); nodes[1].custom_message_handler.expect_message(TestCustomMessage::Pong); @@ -573,7 +578,8 @@ fn invalid_blinded_path_error() { let secp_ctx = Secp256k1::new(); let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }]; - let mut blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[2].node_id, &*nodes[2].entropy_source, &secp_ctx).unwrap(); + let context = MessageContext::Custom(Vec::new()); + let mut blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[2].node_id, context, &*nodes[2].entropy_source, &secp_ctx).unwrap(); blinded_path.blinded_hops.clear(); let destination = Destination::BlindedPath(blinded_path); let err = nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap_err(); @@ -596,7 +602,8 @@ fn reply_path() { ForwardNode { node_id: nodes[2].node_id, short_channel_id: None }, ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, ]; - let reply_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[0].node_id, &*nodes[0].entropy_source, &secp_ctx).unwrap(); + let context = MessageContext::Custom(Vec::new()); + let reply_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[0].node_id, context, &*nodes[0].entropy_source, &secp_ctx).unwrap(); nodes[0].messenger.send_onion_message_using_path(path, test_msg.clone(), Some(reply_path)).unwrap(); nodes[3].custom_message_handler.expect_message(TestCustomMessage::Ping); pass_along_path(&nodes); @@ -610,13 +617,15 @@ fn reply_path() { ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, ForwardNode { node_id: nodes[2].node_id, short_channel_id: None }, ]; - let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[3].node_id, &*nodes[3].entropy_source, &secp_ctx).unwrap(); + let context = MessageContext::Custom(Vec::new()); + let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[3].node_id, context, &*nodes[3].entropy_source, &secp_ctx).unwrap(); let destination = Destination::BlindedPath(blinded_path); let intermediate_nodes = [ ForwardNode { node_id: nodes[2].node_id, short_channel_id: None }, ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, ]; - let reply_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[0].node_id, &*nodes[0].entropy_source, &secp_ctx).unwrap(); + let context = MessageContext::Custom(Vec::new()); + let reply_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[0].node_id, context, &*nodes[0].entropy_source, &secp_ctx).unwrap(); nodes[0].messenger.send_onion_message(test_msg, destination, Some(reply_path)).unwrap(); nodes[3].custom_message_handler.expect_message(TestCustomMessage::Ping); @@ -697,8 +706,9 @@ fn requests_peer_connection_for_buffered_messages() { add_channel_to_graph(&nodes[0], &nodes[1], &secp_ctx, 42); let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }]; + let context = MessageContext::Custom(Vec::new()); let blinded_path = BlindedPath::new_for_message( - &intermediate_nodes, nodes[2].node_id, &*nodes[0].entropy_source, &secp_ctx + &intermediate_nodes, nodes[2].node_id, context, &*nodes[0].entropy_source, &secp_ctx ).unwrap(); let destination = Destination::BlindedPath(blinded_path); @@ -735,8 +745,9 @@ fn drops_buffered_messages_waiting_for_peer_connection() { add_channel_to_graph(&nodes[0], &nodes[1], &secp_ctx, 42); let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }]; + let context = MessageContext::Custom(Vec::new()); let blinded_path = BlindedPath::new_for_message( - &intermediate_nodes, nodes[2].node_id, &*nodes[0].entropy_source, &secp_ctx + &intermediate_nodes, nodes[2].node_id, context, &*nodes[0].entropy_source, &secp_ctx ).unwrap(); let destination = Destination::BlindedPath(blinded_path); @@ -785,8 +796,9 @@ fn intercept_offline_peer_oms() { let message = TestCustomMessage::Pong; let secp_ctx = Secp256k1::new(); let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }]; + let context = MessageContext::Custom(Vec::new()); let blinded_path = BlindedPath::new_for_message( - &intermediate_nodes, nodes[2].node_id, &*nodes[2].entropy_source, &secp_ctx + &intermediate_nodes, nodes[2].node_id, context, &*nodes[2].entropy_source, &secp_ctx ).unwrap(); let destination = Destination::BlindedPath(blinded_path); diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 21012bd7fed..28f1bc79253 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -16,7 +16,7 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey}; use crate::blinded_path::{BlindedPath, IntroductionNode, NextMessageHop, NodeIdLookUp}; -use crate::blinded_path::message::{advance_path_by_one, ForwardNode, ForwardTlvs, ReceiveTlvs}; +use crate::blinded_path::message::{advance_path_by_one, ForwardNode, ForwardTlvs, MessageContext, OffersContext, ReceiveTlvs}; use crate::blinded_path::utils; use crate::events::{Event, EventHandler, EventsProvider}; use crate::sign::{EntropySource, NodeSigner, Recipient}; @@ -145,7 +145,7 @@ for OnionMessenger where /// # use bitcoin::hashes::hex::FromHex; /// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey, self}; /// # use lightning::blinded_path::{BlindedPath, EmptyNodeIdLookUp}; -/// # use lightning::blinded_path::message::ForwardNode; +/// # use lightning::blinded_path::message::{ForwardNode, MessageContext}; /// # use lightning::sign::{EntropySource, KeysManager}; /// # use lightning::ln::peer_handler::IgnoringMessageHandler; /// # use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath, OnionMessenger}; @@ -172,7 +172,7 @@ for OnionMessenger where /// # }) /// # } /// # fn create_blinded_paths( -/// # &self, _recipient: PublicKey, _peers: Vec, _secp_ctx: &Secp256k1 +/// # &self, _recipient: PublicKey, _context: MessageContext, _peers: Vec, _secp_ctx: &Secp256k1 /// # ) -> Result, ()> { /// # unreachable!() /// # } @@ -225,7 +225,8 @@ for OnionMessenger where /// ForwardNode { node_id: hop_node_id3, short_channel_id: None }, /// ForwardNode { node_id: hop_node_id4, short_channel_id: None }, /// ]; -/// let blinded_path = BlindedPath::new_for_message(&hops, your_node_id, &keys_manager, &secp_ctx).unwrap(); +/// let context = MessageContext::Custom(Vec::new()); +/// let blinded_path = BlindedPath::new_for_message(&hops, your_node_id, context, &keys_manager, &secp_ctx).unwrap(); /// /// // Send a custom onion message to a blinded path. /// let destination = Destination::BlindedPath(blinded_path); @@ -344,20 +345,17 @@ impl OnionMessageRecipient { pub struct Responder { /// The path along which a response can be sent. reply_path: BlindedPath, - path_id: Option<[u8; 32]> } impl_writeable_tlv_based!(Responder, { (0, reply_path, required), - (2, path_id, option), }); impl Responder { /// Creates a new [`Responder`] instance with the provided reply path. - pub(super) fn new(reply_path: BlindedPath, path_id: Option<[u8; 32]>) -> Self { + pub(super) fn new(reply_path: BlindedPath) -> Self { Responder { reply_path, - path_id, } } @@ -368,19 +366,17 @@ impl Responder { ResponseInstruction::WithoutReplyPath(OnionMessageResponse { message: response, reply_path: self.reply_path, - path_id: self.path_id, }) } /// Creates a [`ResponseInstruction::WithReplyPath`] for a given response. /// /// Use when the recipient needs to send back a reply to us. - pub fn respond_with_reply_path(self, response: T) -> ResponseInstruction { + pub fn respond_with_reply_path(self, response: T, context: MessageContext) -> ResponseInstruction { ResponseInstruction::WithReplyPath(OnionMessageResponse { message: response, reply_path: self.reply_path, - path_id: self.path_id, - }) + }, context) } } @@ -388,14 +384,13 @@ impl Responder { pub struct OnionMessageResponse { message: T, reply_path: BlindedPath, - path_id: Option<[u8; 32]>, } /// `ResponseInstruction` represents instructions for responding to received messages. pub enum ResponseInstruction { /// Indicates that a response should be sent including a reply path for /// the recipient to respond back. - WithReplyPath(OnionMessageResponse), + WithReplyPath(OnionMessageResponse, MessageContext), /// Indicates that a response should be sent without including a reply path /// for the recipient to respond back. WithoutReplyPath(OnionMessageResponse), @@ -447,7 +442,7 @@ pub trait MessageRouter { fn create_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, context: MessageContext, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()>; /// Creates compact [`BlindedPath`]s to the `recipient` node. The nodes in `peers` are assumed @@ -466,13 +461,14 @@ pub trait MessageRouter { fn create_compact_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, context: MessageContext, + peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { let peers = peers .into_iter() .map(|ForwardNode { node_id, short_channel_id: _ }| node_id) .collect(); - self.create_blinded_paths(recipient, peers, secp_ctx) + self.create_blinded_paths(recipient, context, peers, secp_ctx) } } @@ -507,7 +503,8 @@ where I: ExactSizeIterator, T: secp256k1::Signing + secp256k1::Verification >( - &self, recipient: PublicKey, peers: I, secp_ctx: &Secp256k1, compact_paths: bool + &self, recipient: PublicKey, context: MessageContext, peers: I, + secp_ctx: &Secp256k1, compact_paths: bool ) -> Result, ()> { // Limit the number of blinded paths that are computed. const MAX_PATHS: usize = 3; @@ -546,7 +543,7 @@ where let paths = peer_info.into_iter() .map(|(peer, _, _)| { - BlindedPath::new_for_message(&[peer], recipient, &*self.entropy_source, secp_ctx) + BlindedPath::new_for_message(&[peer], recipient, context.clone(), &*self.entropy_source, secp_ctx) }) .take(MAX_PATHS) .collect::, _>>(); @@ -555,7 +552,7 @@ where Ok(paths) if !paths.is_empty() => Ok(paths), _ => { if is_recipient_announced { - BlindedPath::one_hop_for_message(recipient, &*self.entropy_source, secp_ctx) + BlindedPath::one_hop_for_message(recipient, context, &*self.entropy_source, secp_ctx) .map(|path| vec![path]) } else { Err(()) @@ -614,20 +611,22 @@ where fn create_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, context: MessageContext, + peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { let peers = peers .into_iter() .map(|node_id| ForwardNode { node_id, short_channel_id: None }); - self.create_blinded_paths_from_iter(recipient, peers, secp_ctx, false) + self.create_blinded_paths_from_iter(recipient, context, peers, secp_ctx, false) } fn create_compact_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, context: MessageContext, + peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { - self.create_blinded_paths_from_iter(recipient, peers.into_iter(), secp_ctx, true) + self.create_blinded_paths_from_iter(recipient, context, peers.into_iter(), secp_ctx, true) } } @@ -774,7 +773,7 @@ pub trait CustomOnionMessageHandler { /// Called with the custom message that was received, returning a response to send, if any. /// /// The returned [`Self::CustomMessage`], if any, is enqueued to be sent by [`OnionMessenger`]. - fn handle_custom_message(&self, message: Self::CustomMessage, responder: Option) -> ResponseInstruction; + fn handle_custom_message(&self, message: Self::CustomMessage, context: Option>, responder: Option) -> ResponseInstruction; /// Read a custom message of type `message_type` from `buffer`, returning `Ok(None)` if the /// message type is unknown. @@ -801,8 +800,8 @@ pub trait CustomOnionMessageHandler { pub enum PeeledOnion { /// Forwarded onion, with the next node id and a new onion Forward(NextMessageHop, OnionMessage), - /// Received onion message, with decrypted contents, path_id, and reply path - Receive(ParsedOnionMessageContents, Option<[u8; 32]>, Option) + /// Received onion message, with decrypted contents, context, and reply path + Receive(ParsedOnionMessageContents, Option, Option) } @@ -952,9 +951,23 @@ where (control_tlvs_ss, custom_handler.deref(), logger.deref()) ) { Ok((Payload::Receive::::Target as CustomOnionMessageHandler>::CustomMessage>> { - message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path, + message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { context }), reply_path, }, None)) => { - Ok(PeeledOnion::Receive(message, path_id, reply_path)) + match (&message, &context) { + (_, None) => { + Ok(PeeledOnion::Receive(message, None, reply_path)) + } + (ParsedOnionMessageContents::Offers(_), Some(MessageContext::Offers(_))) => { + Ok(PeeledOnion::Receive(message, context, reply_path)) + } + (ParsedOnionMessageContents::Custom(_), Some(MessageContext::Custom(_))) => { + Ok(PeeledOnion::Receive(message, context, reply_path)) + } + _ => { + log_trace!(logger, "Received message was sent on a blinded path with the wrong context."); + Err(()) + } + } }, Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs { next_hop, next_blinding_override @@ -1157,7 +1170,7 @@ where .map_err(|_| SendError::PathNotFound) } - fn create_blinded_path(&self) -> Result { + fn create_blinded_path(&self, context: MessageContext) -> Result { let recipient = self.node_signer .get_node_id(Recipient::Node) .map_err(|_| SendError::GetNodeIdFailed)?; @@ -1170,7 +1183,7 @@ where .collect::>(); self.message_router - .create_blinded_paths(recipient, peers, secp_ctx) + .create_blinded_paths(recipient, context, peers, secp_ctx) .and_then(|paths| paths.into_iter().next().ok_or(())) .map_err(|_| SendError::PathNotFound) } @@ -1259,22 +1272,21 @@ where pub fn handle_onion_message_response( &self, response: ResponseInstruction ) -> Result, SendError> { - let (response, create_reply_path) = match response { - ResponseInstruction::WithReplyPath(response) => (response, true), - ResponseInstruction::WithoutReplyPath(response) => (response, false), + let (response, context) = match response { + ResponseInstruction::WithReplyPath(response, context) => (response, Some(context)), + ResponseInstruction::WithoutReplyPath(response) => (response, None), ResponseInstruction::NoResponse => return Ok(None), }; let message_type = response.message.msg_type(); - let reply_path = if create_reply_path { - match self.create_blinded_path() { + let reply_path = if let Some(context) = context { + match self.create_blinded_path(context) { Ok(reply_path) => Some(reply_path), Err(err) => { log_trace!( self.logger, - "Failed to create reply path when responding with {} to an onion message \ - with path_id {:02x?}: {:?}", - message_type, response.path_id, err + "Failed to create reply path when responding with {} to an onion message: {:?}", + message_type, err ); return Err(err); } @@ -1284,9 +1296,8 @@ where self.find_path_and_enqueue_onion_message( response.message, Destination::BlindedPath(response.reply_path), reply_path, format_args!( - "when responding with {} to an onion message with path_id {:02x?}", + "when responding with {} to an onion message", message_type, - response.path_id ) ).map(|result| Some(result)) } @@ -1440,18 +1451,24 @@ 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, path_id, reply_path)) => { + Ok(PeeledOnion::Receive(message, context, reply_path)) => { log_trace!( logger, - "Received an onion message with path_id {:02x?} and {} reply_path: {:?}", - path_id, if reply_path.is_some() { "a" } else { "no" }, message); + "Received an onion message with {} reply_path: {:?}", + if reply_path.is_some() { "a" } else { "no" }, message); - let responder = reply_path.map( - |reply_path| Responder::new(reply_path, path_id) - ); + let responder = reply_path.map(Responder::new); match message { ParsedOnionMessageContents::Offers(msg) => { - let response_instructions = self.offers_handler.handle_message(msg, responder); + let context = match context { + None => OffersContext::Unknown {}, + Some(MessageContext::Offers(context)) => context, + Some(MessageContext::Custom(_)) => { + debug_assert!(false, "Shouldn't have triggered this case."); + return + } + }; + let response_instructions = self.offers_handler.handle_message(msg, context, responder); let _ = self.handle_onion_message_response(response_instructions); }, #[cfg(async_payments)] @@ -1466,7 +1483,15 @@ where self.async_payments_handler.release_held_htlc(msg); }, ParsedOnionMessageContents::Custom(msg) => { - let response_instructions = self.custom_handler.handle_custom_message(msg, responder); + let context = match context { + None => None, + Some(MessageContext::Custom(data)) => Some(data), + Some(MessageContext::Offers(_)) => { + debug_assert!(false, "Shouldn't have triggered this case."); + return + } + }; + let response_instructions = self.custom_handler.handle_custom_message(msg, context, responder); let _ = self.handle_onion_message_response(response_instructions); }, } @@ -1737,7 +1762,7 @@ fn packet_payloads_and_keys) -> ResponseInstruction; + fn handle_message(&self, message: OffersMessage, context: OffersContext, responder: Option) -> 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 fca7dd6a91a..49b3f6642ab 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -319,8 +319,8 @@ impl Readable for ControlTlvs { (1, _padding, option), (2, short_channel_id, option), (4, next_node_id, option), - (6, path_id, option), (8, next_blinding_override, option), + (65537, context, option), }); let _padding: Option = _padding; @@ -331,7 +331,7 @@ impl Readable for ControlTlvs { (None, None) => None, }; - let valid_fwd_fmt = next_hop.is_some() && path_id.is_none(); + let valid_fwd_fmt = next_hop.is_some(); let valid_recv_fmt = next_hop.is_none() && next_blinding_override.is_none(); let payload_fmt = if valid_fwd_fmt { @@ -341,7 +341,7 @@ impl Readable for ControlTlvs { }) } else if valid_recv_fmt { ControlTlvs::Receive(ReceiveTlvs { - path_id, + context, }) } else { return Err(DecodeError::InvalidValue) diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 8062f598392..46d79854a67 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -12,7 +12,7 @@ use bitcoin::secp256k1::{PublicKey, Secp256k1, self}; use crate::blinded_path::{BlindedHop, BlindedPath, Direction, IntroductionNode}; -use crate::blinded_path::message; +use crate::blinded_path::message::{self, MessageContext}; use crate::blinded_path::payment::{ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs, self}; use crate::ln::{PaymentHash, PaymentPreimage}; use crate::ln::channel_state::ChannelDetails; @@ -193,17 +193,17 @@ impl< G: Deref> + Clone, L: Deref, ES: Deref, S: Deref, fn create_blinded_paths< T: secp256k1::Signing + secp256k1::Verification > ( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, context: MessageContext, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { - self.message_router.create_blinded_paths(recipient, peers, secp_ctx) + self.message_router.create_blinded_paths(recipient, context, peers, secp_ctx) } fn create_compact_blinded_paths< T: secp256k1::Signing + secp256k1::Verification > ( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, context: MessageContext, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { - self.message_router.create_compact_blinded_paths(recipient, peers, secp_ctx) + self.message_router.create_compact_blinded_paths(recipient, context, peers, secp_ctx) } } diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 4c4df2688a5..f734f7f94d4 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -7,6 +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::BlindedPath; use crate::blinded_path::message::ForwardNode; use crate::blinded_path::payment::ReceiveTlvs; @@ -263,17 +264,19 @@ impl<'a> MessageRouter for TestRouter<'a> { fn create_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, context: MessageContext, + peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { - self.router.create_blinded_paths(recipient, peers, secp_ctx) + self.router.create_blinded_paths(recipient, context, peers, secp_ctx) } fn create_compact_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, context: MessageContext, + peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { - self.router.create_compact_blinded_paths(recipient, peers, secp_ctx) + self.router.create_compact_blinded_paths(recipient, context, peers, secp_ctx) } } @@ -306,15 +309,17 @@ impl<'a> MessageRouter for TestMessageRouter<'a> { } fn create_blinded_paths( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, context: MessageContext, + peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { - self.inner.create_blinded_paths(recipient, peers, secp_ctx) + self.inner.create_blinded_paths(recipient, context, peers, secp_ctx) } fn create_compact_blinded_paths( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, context: MessageContext, + peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { - self.inner.create_compact_blinded_paths(recipient, peers, secp_ctx) + self.inner.create_compact_blinded_paths(recipient, context, peers, secp_ctx) } }