diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index d2e35cd45cd..fcc8dc3cad2 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -14,7 +14,7 @@ use lightning::offers::invoice_request::UnsignedInvoiceRequest; use lightning::util::test_channel_signer::TestChannelSigner; use lightning::util::logger::Logger; use lightning::util::ser::{Readable, Writeable, Writer}; -use lightning::onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessagePath, OnionMessenger}; +use lightning::onion_message::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, PendingOnionMessage}; use crate::utils::test_logger; @@ -84,7 +84,7 @@ struct TestCustomMessage {} const CUSTOM_MESSAGE_TYPE: u64 = 4242; const CUSTOM_MESSAGE_CONTENTS: [u8; 32] = [42; 32]; -impl CustomOnionMessageContents for TestCustomMessage { +impl OnionMessageContents for TestCustomMessage { fn tlv_type(&self) -> u64 { CUSTOM_MESSAGE_TYPE } @@ -108,6 +108,9 @@ impl CustomOnionMessageHandler for TestCustomMessageHandler { buffer.read_to_end(&mut buf)?; return Ok(Some(TestCustomMessage {})) } + fn release_pending_custom_messages(&self) -> Vec> { + vec![] + } } pub struct VecWriter(pub Vec); @@ -208,19 +211,6 @@ mod tests { #[test] fn test_no_onion_message_breakage() { - let one_hop_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e01ae0276020000000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000000000000000000000e0101022a0000000000000000000000000000014551231950b75fc4402da1732fc9bebf00109500000000000000000000000000000004106d000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005600000000000000000000000000000000000000000000000000000000000000"; - let logger = TrackingLogger { lines: Mutex::new(HashMap::new()) }; - super::do_test(&::hex::decode(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 path_id None and a reply_path".to_string())), Some(&1)); - assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), - "Responding to onion message with path_id None".to_string())), Some(&1)); - assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), - "Failed responding to onion message with path_id None: TooFewBlindedHops".to_string())), Some(&1)); - } - let two_unblinded_hops_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e0135043304210202020202020202020202020202020202020202020202020202020202020202026d000000000000000000000000000000eb0000000000000000000000000000000000000000000000000000000000000036041096000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000000000000000"; let logger = TrackingLogger { lines: Mutex::new(HashMap::new()) }; super::do_test(&::hex::decode(two_unblinded_hops_om).unwrap(), &logger); diff --git a/lightning/src/blinded_path/mod.rs b/lightning/src/blinded_path/mod.rs index 89bf7ce5d2f..1415836c01f 100644 --- a/lightning/src/blinded_path/mod.rs +++ b/lightning/src/blinded_path/mod.rs @@ -56,15 +56,22 @@ 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 + ) -> Result { + Self::new_for_message(&[recipient_node_id], entropy_source, secp_ctx) + } + /// Create a blinded path for an onion message, to be forwarded along `node_pks`. The last node /// pubkey in `node_pks` will be the destination node. /// - /// Errors if less than two hops are provided or if `node_pk`(s) are invalid. + /// Errors if no hops are provided or if `node_pk`(s) are invalid. // TODO: make all payloads the same size with padding + add dummy hops - pub fn new_for_message - (node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1) -> Result - { - if node_pks.len() < 2 { return Err(()) } + pub fn new_for_message( + node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1 + ) -> Result { + if node_pks.is_empty() { return Err(()) } let blinding_secret_bytes = entropy_source.get_secure_random_bytes(); let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted"); let introduction_node_id = node_pks[0]; diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index c8c736c1f71..ca55fde31c5 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -1846,12 +1846,6 @@ pub trait MessageSendEventsProvider { fn get_and_clear_pending_msg_events(&self) -> Vec; } -/// A trait indicating an object may generate onion messages to send -pub trait OnionMessageProvider { - /// Gets the next pending onion message for the peer with the given node id. - fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option; -} - /// A trait indicating an object may generate events. /// /// Events are processed by passing an [`EventHandler`] to [`process_pending_events`]. diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index d1a1208c809..113d6902155 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -30,6 +30,7 @@ use bitcoin::secp256k1::{SecretKey,PublicKey}; use bitcoin::secp256k1::Secp256k1; use bitcoin::{LockTime, secp256k1, Sequence}; +use crate::blinded_path::BlindedPath; use crate::chain; use crate::chain::{Confirm, ChannelMonitorUpdateStatus, Watch, BestBlock}; use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator}; @@ -55,6 +56,9 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError}; use crate::ln::outbound_payment; use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs}; use crate::ln::wire::Encode; +use crate::offers::offer::{DerivedMetadata, OfferBuilder}; +use crate::offers::parse::Bolt12SemanticError; +use crate::offers::refund::RefundBuilder; use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner}; use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate}; use crate::util::wakers::{Future, Notifier}; @@ -4791,6 +4795,10 @@ where /// with the current [`ChannelConfig`]. /// * Removing peers which have disconnected but and no longer have any channels. /// * Force-closing and removing channels which have not completed establishment in a timely manner. + /// * Forgetting about stale outbound payments, either those that have already been fulfilled + /// or those awaiting an invoice that hasn't been delivered in the necessary amount of time. + /// The latter is determined using the system clock in `std` and the block time minus two + /// hours in `no-std`. /// /// Note that this may cause reentrancy through [`chain::Watch::update_channel`] calls or feerate /// estimate fetches. @@ -5019,7 +5027,18 @@ where self.finish_close_channel(shutdown_res); } - self.pending_outbound_payments.remove_stale_payments(&self.pending_events); + #[cfg(feature = "std")] + let duration_since_epoch = std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH) + .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH"); + #[cfg(not(feature = "std"))] + let duration_since_epoch = Duration::from_secs( + self.highest_seen_timestamp.load(Ordering::Acquire).saturating_sub(7200) as u64 + ); + + self.pending_outbound_payments.remove_stale_payments( + duration_since_epoch, &self.pending_events + ); // Technically we don't need to do this here, but if we have holding cell entries in a // channel that need freeing, it's better to do that here and block a background task @@ -7108,6 +7127,71 @@ where } } + /// Creates an [`OfferBuilder`] such that the [`Offer`] it builds is recognized by the + /// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. The offer will + /// not have an expiration unless otherwise set on the builder. + /// + /// Uses a one-hop [`BlindedPath`] for the offer with [`ChannelManager::get_our_node_id`] as the + /// introduction node and a derived signing pubkey for recipient privacy. As such, currently, + /// the node must be announced. Otherwise, there is no way to find a path to the introduction + /// node in order to send the [`InvoiceRequest`]. + /// + /// [`Offer`]: crate::offers::offer::Offer + /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest + pub fn create_offer_builder( + &self, description: String + ) -> OfferBuilder { + let node_id = self.get_our_node_id(); + let expanded_key = &self.inbound_payment_key; + let entropy = &*self.entropy_source; + let secp_ctx = &self.secp_ctx; + let path = self.create_one_hop_blinded_path(); + + OfferBuilder::deriving_signing_pubkey(description, node_id, expanded_key, entropy, secp_ctx) + .chain_hash(self.chain_hash) + .path(path) + } + + /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the + /// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund. The builder will + /// have the provided expiration set. Any changes to the expiration on the returned builder will + /// not be honored by [`ChannelManager`]. + /// + /// The provided `payment_id` is used to ensure that only one invoice is paid for the refund. + /// + /// Uses a one-hop [`BlindedPath`] for the refund with [`ChannelManager::get_our_node_id`] as + /// the introduction node and a derived payer id for sender privacy. As such, currently, the + /// node must be announced. Otherwise, there is no way to find a path to the introduction node + /// in order to send the [`Bolt12Invoice`]. + /// + /// [`Refund`]: crate::offers::refund::Refund + /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice + pub fn create_refund_builder( + &self, description: String, amount_msats: u64, absolute_expiry: Duration, + payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option + ) -> Result, Bolt12SemanticError> { + let node_id = self.get_our_node_id(); + let expanded_key = &self.inbound_payment_key; + let entropy = &*self.entropy_source; + let secp_ctx = &self.secp_ctx; + let path = self.create_one_hop_blinded_path(); + + let builder = RefundBuilder::deriving_payer_id( + description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id + )? + .chain_hash(self.chain_hash) + .absolute_expiry(absolute_expiry) + .path(path); + + self.pending_outbound_payments + .add_new_awaiting_invoice( + payment_id, absolute_expiry, retry_strategy, max_total_routing_fee_msat, + ) + .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?; + + Ok(builder) + } + /// Gets a payment secret and payment hash for use in an invoice given to a third party wishing /// to pay us. /// @@ -7208,6 +7292,14 @@ where inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key) } + /// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction + /// node. + fn create_one_hop_blinded_path(&self) -> BlindedPath { + let entropy_source = self.entropy_source.deref(); + let secp_ctx = &self.secp_ctx; + BlindedPath::one_hop_for_message(self.get_our_node_id(), entropy_source, secp_ctx).unwrap() + } + /// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids /// are used when constructing the phantom invoice's route hints. /// diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index f86dc74befe..477524a9d54 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -49,7 +49,7 @@ use core::str::FromStr; use crate::io::{self, Cursor, Read}; use crate::io_extras::read_to_end; -use crate::events::{MessageSendEventsProvider, OnionMessageProvider}; +use crate::events::MessageSendEventsProvider; use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter; use crate::util::logger; use crate::util::ser::{LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize}; @@ -1497,10 +1497,14 @@ pub trait RoutingMessageHandler : MessageSendEventsProvider { fn provided_init_features(&self, their_node_id: &PublicKey) -> InitFeatures; } -/// A trait to describe an object that can receive onion messages. -pub trait OnionMessageHandler : OnionMessageProvider { +/// A handler for received [`OnionMessage`]s and for providing generated ones to send. +pub trait OnionMessageHandler { /// Handle an incoming `onion_message` message from the given peer. fn handle_onion_message(&self, peer_node_id: &PublicKey, msg: &OnionMessage); + + /// Returns the next pending onion message for the peer with the given node id. + fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option; + /// Called when a connection is established with a peer. Can be used to track which peers /// advertise onion message support and are online. /// @@ -1508,6 +1512,7 @@ pub trait OnionMessageHandler : OnionMessageProvider { /// with us. Implementors should be somewhat conservative about doing so, however, as other /// message handlers may still wish to communicate with this peer. fn peer_connected(&self, their_node_id: &PublicKey, init: &Init, inbound: bool) -> Result<(), ()>; + /// Indicates a connection to the peer failed/an existing connection was lost. Allows handlers to /// drop and refuse to forward onion messages to this peer. fn peer_disconnected(&self, their_node_id: &PublicKey); diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index d6d9f7aaca0..19faad07bbd 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -29,6 +29,7 @@ use crate::util::ser::ReadableArgs; use core::fmt::{self, Display, Formatter}; use core::ops::Deref; +use core::time::Duration; use crate::prelude::*; use crate::sync::Mutex; @@ -39,12 +40,6 @@ use crate::sync::Mutex; /// [`ChannelManager::timer_tick_occurred`]: crate::ln::channelmanager::ChannelManager::timer_tick_occurred pub(crate) const IDEMPOTENCY_TIMEOUT_TICKS: u8 = 7; -/// The number of ticks of [`ChannelManager::timer_tick_occurred`] until an invoice request without -/// a response is timed out. -/// -/// [`ChannelManager::timer_tick_occurred`]: crate::ln::channelmanager::ChannelManager::timer_tick_occurred -const INVOICE_REQUEST_TIMEOUT_TICKS: u8 = 3; - /// Stores the session_priv for each part of a payment that is still pending. For versions 0.0.102 /// and later, also stores information for retrying the payment. pub(crate) enum PendingOutboundPayment { @@ -52,7 +47,7 @@ pub(crate) enum PendingOutboundPayment { session_privs: HashSet<[u8; 32]>, }, AwaitingInvoice { - timer_ticks_without_response: u8, + absolute_expiry: Duration, retry_strategy: Retry, max_total_routing_fee_msat: Option, }, @@ -1273,16 +1268,16 @@ impl OutboundPayments { (payment, onion_session_privs) } - #[allow(unused)] pub(super) fn add_new_awaiting_invoice( - &self, payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option + &self, payment_id: PaymentId, absolute_expiry: Duration, retry_strategy: Retry, + max_total_routing_fee_msat: Option ) -> Result<(), ()> { let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap(); match pending_outbounds.entry(payment_id) { hash_map::Entry::Occupied(_) => Err(()), hash_map::Entry::Vacant(entry) => { entry.insert(PendingOutboundPayment::AwaitingInvoice { - timer_ticks_without_response: 0, + absolute_expiry, retry_strategy, max_total_routing_fee_msat, }); @@ -1511,14 +1506,15 @@ impl OutboundPayments { } pub(super) fn remove_stale_payments( - &self, pending_events: &Mutex)>>) + &self, duration_since_epoch: Duration, + pending_events: &Mutex)>>) { let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap(); #[cfg(not(invreqfailed))] let pending_events = pending_events.lock().unwrap(); #[cfg(invreqfailed)] let mut pending_events = pending_events.lock().unwrap(); - pending_outbound_payments.retain(|payment_id, payment| { + pending_outbound_payments.retain(|payment_id, payment| match payment { // If an outbound payment was completed, and no pending HTLCs remain, we should remove it // from the map. However, if we did that immediately when the last payment HTLC is claimed, // this could race the user making a duplicate send_payment call and our idempotency @@ -1526,7 +1522,7 @@ impl OutboundPayments { // removal. This should be more than sufficient to ensure the idempotency of any // `send_payment` calls that were made at the same time the `PaymentSent` event was being // processed. - if let PendingOutboundPayment::Fulfilled { session_privs, timer_ticks_without_htlcs, .. } = payment { + PendingOutboundPayment::Fulfilled { session_privs, timer_ticks_without_htlcs, .. } => { let mut no_remaining_entries = session_privs.is_empty(); if no_remaining_entries { for (ev, _) in pending_events.iter() { @@ -1550,9 +1546,9 @@ impl OutboundPayments { *timer_ticks_without_htlcs = 0; true } - } else if let PendingOutboundPayment::AwaitingInvoice { timer_ticks_without_response, .. } = payment { - *timer_ticks_without_response += 1; - if *timer_ticks_without_response <= INVOICE_REQUEST_TIMEOUT_TICKS { + }, + PendingOutboundPayment::AwaitingInvoice { absolute_expiry, .. } => { + if duration_since_epoch < *absolute_expiry { true } else { #[cfg(invreqfailed)] @@ -1561,7 +1557,8 @@ impl OutboundPayments { ); false } - } else { true } + }, + _ => true, }); } @@ -1778,7 +1775,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment, (2, payment_hash, required), }, (5, AwaitingInvoice) => { - (0, timer_ticks_without_response, required), + (0, absolute_expiry, required), (2, retry_strategy, required), (4, max_total_routing_fee_msat, option), }, @@ -1794,14 +1791,14 @@ mod tests { use bitcoin::network::constants::Network; use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; + use core::time::Duration; + use crate::events::{Event, PathFailure, PaymentFailureReason}; use crate::ln::PaymentHash; use crate::ln::channelmanager::{PaymentId, RecipientOnionFields}; use crate::ln::features::{ChannelFeatures, NodeFeatures}; use crate::ln::msgs::{ErrorAction, LightningError}; use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, Retry, RetryableSendFailure}; - #[cfg(invreqfailed)] - use crate::ln::outbound_payment::INVOICE_REQUEST_TIMEOUT_TICKS; use crate::offers::invoice::DEFAULT_RELATIVE_EXPIRY; use crate::offers::offer::OfferBuilder; use crate::offers::test_utils::*; @@ -2011,20 +2008,28 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); + let absolute_expiry = 100; + let tick_interval = 10; assert!(!outbound_payments.has_pending_payments()); assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok() + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), None + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); - for _ in 0..INVOICE_REQUEST_TIMEOUT_TICKS { - outbound_payments.remove_stale_payments(&pending_events); + for seconds_since_epoch in (0..absolute_expiry).step_by(tick_interval) { + let duration_since_epoch = Duration::from_secs(seconds_since_epoch); + outbound_payments.remove_stale_payments(duration_since_epoch, &pending_events); + assert!(outbound_payments.has_pending_payments()); assert!(pending_events.lock().unwrap().is_empty()); } - outbound_payments.remove_stale_payments(&pending_events); + let duration_since_epoch = Duration::from_secs(absolute_expiry); + outbound_payments.remove_stale_payments(duration_since_epoch, &pending_events); + assert!(!outbound_payments.has_pending_payments()); assert!(!pending_events.lock().unwrap().is_empty()); assert_eq!( @@ -2034,13 +2039,16 @@ mod tests { assert!(pending_events.lock().unwrap().is_empty()); assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok() + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry + 1), Retry::Attempts(0), None + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None) - .is_err() + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry + 1), Retry::Attempts(0), None + ).is_err() ); } @@ -2050,10 +2058,13 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); + let absolute_expiry = 100; assert!(!outbound_payments.has_pending_payments()); assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok() + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), None + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); @@ -2081,9 +2092,12 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); + let absolute_expiry = 100; assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok() + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), None + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); @@ -2129,6 +2143,7 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); + let absolute_expiry = 100; let invoice = OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) @@ -2140,9 +2155,11 @@ mod tests { .build().unwrap() .sign(recipient_sign).unwrap(); - assert!(outbound_payments.add_new_awaiting_invoice( - payment_id, Retry::Attempts(0), Some(invoice.amount_msats() / 100 + 50_000)) - .is_ok() + assert!( + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), + Some(invoice.amount_msats() / 100 + 50_000) + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); @@ -2185,6 +2202,7 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); + let absolute_expiry = 100; let invoice = OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) @@ -2196,9 +2214,11 @@ mod tests { .build().unwrap() .sign(recipient_sign).unwrap(); - assert!(outbound_payments.add_new_awaiting_invoice( - payment_id, Retry::Attempts(0), Some(invoice.amount_msats() / 100 + 50_000)) - .is_ok() + assert!( + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), + Some(invoice.amount_msats() / 100 + 50_000) + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); @@ -2241,6 +2261,7 @@ mod tests { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); + let absolute_expiry = 100; let invoice = OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) @@ -2292,7 +2313,9 @@ mod tests { assert!(pending_events.lock().unwrap().is_empty()); assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), Some(1234)).is_ok() + outbound_payments.add_new_awaiting_invoice( + payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), Some(1234) + ).is_ok() ); assert!(outbound_payments.has_pending_payments()); diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index 8e91023b1b1..fc574251f56 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -19,7 +19,7 @@ use bitcoin::blockdata::constants::ChainHash; use bitcoin::secp256k1::{self, Secp256k1, SecretKey, PublicKey}; use crate::sign::{KeysManager, NodeSigner, Recipient}; -use crate::events::{MessageSendEvent, MessageSendEventsProvider, OnionMessageProvider}; +use crate::events::{MessageSendEvent, MessageSendEventsProvider}; use crate::ln::ChannelId; use crate::ln::features::{InitFeatures, NodeFeatures}; use crate::ln::msgs; @@ -29,7 +29,7 @@ use crate::util::ser::{VecWriter, Writeable, Writer}; use crate::ln::peer_channel_encryptor::{PeerChannelEncryptor,NextNoiseStep}; use crate::ln::wire; use crate::ln::wire::{Encode, Type}; -use crate::onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, OffersMessage, OffersMessageHandler, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; +use crate::onion_message::{CustomOnionMessageHandler, OffersMessage, OffersMessageHandler, OnionMessageContents, PendingOnionMessage, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; use crate::routing::gossip::{NetworkGraph, P2PGossipSync, NodeId, NodeAlias}; use crate::util::atomic_counter::AtomicCounter; use crate::util::logger::Logger; @@ -107,11 +107,9 @@ impl RoutingMessageHandler for IgnoringMessageHandler { } fn processing_queue_high(&self) -> bool { false } } -impl OnionMessageProvider for IgnoringMessageHandler { - fn next_onion_message_for_peer(&self, _peer_node_id: PublicKey) -> Option { None } -} impl OnionMessageHandler for IgnoringMessageHandler { fn handle_onion_message(&self, _their_node_id: &PublicKey, _msg: &msgs::OnionMessage) {} + fn next_onion_message_for_peer(&self, _peer_node_id: PublicKey) -> Option { None } fn peer_connected(&self, _their_node_id: &PublicKey, _init: &msgs::Init, _inbound: bool) -> Result<(), ()> { Ok(()) } fn peer_disconnected(&self, _their_node_id: &PublicKey) {} fn provided_node_features(&self) -> NodeFeatures { NodeFeatures::empty() } @@ -131,9 +129,12 @@ impl CustomOnionMessageHandler for IgnoringMessageHandler { fn read_custom_message(&self, _msg_type: u64, _buffer: &mut R) -> Result, msgs::DecodeError> where Self: Sized { Ok(None) } + fn release_pending_custom_messages(&self) -> Vec> { + vec![] + } } -impl CustomOnionMessageContents for Infallible { +impl OnionMessageContents for Infallible { fn tlv_type(&self) -> u64 { unreachable!(); } } diff --git a/lightning/src/offers/offer.rs b/lightning/src/offers/offer.rs index 60621b9dc74..ab95d5b192f 100644 --- a/lightning/src/offers/offer.rs +++ b/lightning/src/offers/offer.rs @@ -13,6 +13,8 @@ //! published as a QR code to be scanned by a customer. The customer uses the offer to request an //! invoice from the merchant to be paid. //! +//! # Example +//! //! ``` //! extern crate bitcoin; //! extern crate core; @@ -65,6 +67,14 @@ //! # Ok(()) //! # } //! ``` +//! +//! # Note +//! +//! If constructing an [`Offer`] for use with a [`ChannelManager`], use +//! [`ChannelManager::create_offer_builder`] instead of [`OfferBuilder::new`]. +//! +//! [`ChannelManager`]: crate::ln::channelmanager::ChannelManager +//! [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder use bitcoin::blockdata::constants::ChainHash; use bitcoin::network::constants::Network; @@ -132,6 +142,14 @@ impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> { /// while the offer is valid. /// /// Use a different pubkey per offer to avoid correlating offers. + /// + /// # Note + /// + /// If constructing an [`Offer`] for use with a [`ChannelManager`], use + /// [`ChannelManager::create_offer_builder`] instead of [`OfferBuilder::new`]. + /// + /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager + /// [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder pub fn new(description: String, signing_pubkey: PublicKey) -> Self { OfferBuilder { offer: OfferContents { @@ -191,9 +209,18 @@ impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> { /// See [`Offer::chains`] on how this relates to the payment currency. /// /// Successive calls to this method will add another chain hash. - pub fn chain(mut self, network: Network) -> Self { + pub fn chain(self, network: Network) -> Self { + self.chain_hash(ChainHash::using_genesis_block(network)) + } + + /// Adds the [`ChainHash`] to [`Offer::chains`]. If not called, the chain hash of + /// [`Network::Bitcoin`] is assumed to be the only one supported. + /// + /// See [`Offer::chains`] on how this relates to the payment currency. + /// + /// Successive calls to this method will add another chain hash. + pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Self { let chains = self.offer.chains.get_or_insert_with(Vec::new); - let chain = ChainHash::using_genesis_block(network); if !chains.contains(&chain) { chains.push(chain); } diff --git a/lightning/src/offers/parse.rs b/lightning/src/offers/parse.rs index e9477086ee9..c85c2f326b0 100644 --- a/lightning/src/offers/parse.rs +++ b/lightning/src/offers/parse.rs @@ -179,6 +179,8 @@ pub enum Bolt12SemanticError { MissingPayerMetadata, /// A payer id was expected but was missing. MissingPayerId, + /// The payment id for a refund or request is already in use. + DuplicatePaymentId, /// Blinded paths were expected but were missing. MissingPaths, /// The blinded payinfo given does not match the number of blinded path hops. diff --git a/lightning/src/offers/refund.rs b/lightning/src/offers/refund.rs index 4b4572b4df9..ecafb2bb5c7 100644 --- a/lightning/src/offers/refund.rs +++ b/lightning/src/offers/refund.rs @@ -18,6 +18,8 @@ //! [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest //! [`Offer`]: crate::offers::offer::Offer //! +//! # Example +//! //! ``` //! extern crate bitcoin; //! extern crate core; @@ -70,6 +72,14 @@ //! # Ok(()) //! # } //! ``` +//! +//! # Note +//! +//! If constructing a [`Refund`] for use with a [`ChannelManager`], use +//! [`ChannelManager::create_refund_builder`] instead of [`RefundBuilder::new`]. +//! +//! [`ChannelManager`]: crate::ln::channelmanager::ChannelManager +//! [`ChannelManager::create_refund_builder`]: crate::ln::channelmanager::ChannelManager::create_refund_builder use bitcoin::blockdata::constants::ChainHash; use bitcoin::network::constants::Network; @@ -120,6 +130,14 @@ impl<'a> RefundBuilder<'a, secp256k1::SignOnly> { /// /// Additionally, sets the required [`Refund::description`], [`Refund::payer_metadata`], and /// [`Refund::amount_msats`]. + /// + /// # Note + /// + /// If constructing a [`Refund`] for use with a [`ChannelManager`], use + /// [`ChannelManager::create_refund_builder`] instead of [`RefundBuilder::new`]. + /// + /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager + /// [`ChannelManager::create_refund_builder`]: crate::ln::channelmanager::ChannelManager::create_refund_builder pub fn new( description: String, metadata: Vec, payer_id: PublicKey, amount_msats: u64 ) -> Result { @@ -206,8 +224,16 @@ impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> { /// called, [`Network::Bitcoin`] is assumed. /// /// Successive calls to this method will override the previous setting. - pub fn chain(mut self, network: Network) -> Self { - self.refund.chain = Some(ChainHash::using_genesis_block(network)); + pub fn chain(self, network: Network) -> Self { + self.chain_hash(ChainHash::using_genesis_block(network)) + } + + /// Sets the [`Refund::chain`] of the given [`ChainHash`] for paying an invoice. If not called, + /// [`Network::Bitcoin`] is assumed. + /// + /// Successive calls to this method will override the previous setting. + pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Self { + self.refund.chain = Some(chain); self } diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index c2d90ad60f9..0193001b30c 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -10,13 +10,12 @@ //! Onion message testing and test utilities live here. use crate::blinded_path::BlindedPath; -use crate::events::OnionMessageProvider; use crate::ln::features::InitFeatures; use crate::ln::msgs::{self, DecodeError, OnionMessageHandler}; use crate::sign::{NodeSigner, Recipient}; use crate::util::ser::{FixedLengthReader, LengthReadable, Writeable, Writer}; use crate::util::test_utils; -use super::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, SendError}; +use super::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, PendingOnionMessage, SendError}; use bitcoin::network::constants::Network; use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; @@ -78,7 +77,7 @@ const CUSTOM_RESPONSE_MESSAGE_TYPE: u64 = 4343; const CUSTOM_REQUEST_MESSAGE_CONTENTS: [u8; 32] = [42; 32]; const CUSTOM_RESPONSE_MESSAGE_CONTENTS: [u8; 32] = [43; 32]; -impl CustomOnionMessageContents for TestCustomMessage { +impl OnionMessageContents for TestCustomMessage { fn tlv_type(&self) -> u64 { match self { TestCustomMessage::Request => CUSTOM_REQUEST_MESSAGE_TYPE, @@ -149,6 +148,9 @@ impl CustomOnionMessageHandler for TestCustomMessageHandler { _ => Ok(None), } } + fn release_pending_custom_messages(&self) -> Vec> { + vec![] + } } fn create_nodes(num_messengers: u8) -> Vec { @@ -195,9 +197,9 @@ fn pass_along_path(path: &Vec) { } #[test] -fn one_hop() { +fn one_unblinded_hop() { let nodes = create_nodes(2); - let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = TestCustomMessage::Response; let path = OnionMessagePath { intermediate_nodes: vec![], @@ -211,7 +213,7 @@ fn one_hop() { #[test] fn two_unblinded_hops() { let nodes = create_nodes(3); - let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = TestCustomMessage::Response; let path = OnionMessagePath { intermediate_nodes: vec![nodes[1].get_node_pk()], @@ -222,10 +224,26 @@ fn two_unblinded_hops() { pass_along_path(&nodes); } +#[test] +fn one_blinded_hop() { + let nodes = create_nodes(2); + let test_msg = TestCustomMessage::Response; + + let secp_ctx = Secp256k1::new(); + let blinded_path = BlindedPath::new_for_message(&[nodes[1].get_node_pk()], &*nodes[1].keys_manager, &secp_ctx).unwrap(); + let path = OnionMessagePath { + intermediate_nodes: vec![], + destination: Destination::BlindedPath(blinded_path), + }; + nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap(); + nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response); + pass_along_path(&nodes); +} + #[test] fn two_unblinded_two_blinded() { let nodes = create_nodes(5); - let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = TestCustomMessage::Response; let secp_ctx = Secp256k1::new(); let blinded_path = BlindedPath::new_for_message(&[nodes[3].get_node_pk(), nodes[4].get_node_pk()], &*nodes[4].keys_manager, &secp_ctx).unwrap(); @@ -242,7 +260,7 @@ fn two_unblinded_two_blinded() { #[test] fn three_blinded_hops() { let nodes = create_nodes(4); - let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = TestCustomMessage::Response; let secp_ctx = Secp256k1::new(); let blinded_path = BlindedPath::new_for_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap(); @@ -260,7 +278,7 @@ fn three_blinded_hops() { fn too_big_packet_error() { // Make sure we error as expected if a packet is too big to send. let nodes = create_nodes(2); - let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response); + let test_msg = TestCustomMessage::Response; let hop_node_id = nodes[1].get_node_pk(); let hops = vec![hop_node_id; 400]; @@ -286,7 +304,7 @@ fn we_are_intro_node() { destination: Destination::BlindedPath(blinded_path), }; - nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg.clone()), None).unwrap(); + nodes[0].messenger.send_onion_message(path, test_msg.clone(), None).unwrap(); nodes[2].custom_message_handler.expect_message(TestCustomMessage::Response); pass_along_path(&nodes); @@ -296,7 +314,7 @@ fn we_are_intro_node() { intermediate_nodes: vec![], destination: Destination::BlindedPath(blinded_path), }; - nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), None).unwrap(); + nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap(); nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response); nodes.remove(2); pass_along_path(&nodes); @@ -316,18 +334,7 @@ fn invalid_blinded_path_error() { intermediate_nodes: vec![], destination: Destination::BlindedPath(blinded_path), }; - let err = nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg.clone()), None).unwrap_err(); - assert_eq!(err, SendError::TooFewBlindedHops); - - // 1 hop - let mut blinded_path = BlindedPath::new_for_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap(); - blinded_path.blinded_hops.remove(0); - assert_eq!(blinded_path.blinded_hops.len(), 1); - let path = OnionMessagePath { - intermediate_nodes: vec![], - destination: Destination::BlindedPath(blinded_path), - }; - let err = nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), None).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(path, test_msg.clone(), None).unwrap_err(); assert_eq!(err, SendError::TooFewBlindedHops); } @@ -343,7 +350,7 @@ fn reply_path() { destination: Destination::Node(nodes[3].get_node_pk()), }; let reply_path = BlindedPath::new_for_message(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap(); - nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg.clone()), Some(reply_path)).unwrap(); + nodes[0].messenger.send_onion_message(path, test_msg.clone(), Some(reply_path)).unwrap(); nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request); pass_along_path(&nodes); // Make sure the last node successfully decoded the reply path. @@ -359,7 +366,7 @@ fn reply_path() { }; let reply_path = BlindedPath::new_for_message(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap(); - nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), Some(reply_path)).unwrap(); + nodes[0].messenger.send_onion_message(path, test_msg, Some(reply_path)).unwrap(); nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request); pass_along_path(&nodes); @@ -374,7 +381,7 @@ fn invalid_custom_message_type() { let nodes = create_nodes(2); struct InvalidCustomMessage{} - impl CustomOnionMessageContents for InvalidCustomMessage { + impl OnionMessageContents for InvalidCustomMessage { fn tlv_type(&self) -> u64 { // Onion message contents must have a TLV >= 64. 63 @@ -385,7 +392,7 @@ fn invalid_custom_message_type() { fn write(&self, _w: &mut W) -> Result<(), io::Error> { unreachable!() } } - let test_msg = OnionMessageContents::Custom(InvalidCustomMessage {}); + let test_msg = InvalidCustomMessage {}; let path = OnionMessagePath { intermediate_nodes: vec![], destination: Destination::Node(nodes[1].get_node_pk()), @@ -403,9 +410,9 @@ fn peer_buffer_full() { destination: Destination::Node(nodes[1].get_node_pk()), }; for _ in 0..188 { // Based on MAX_PER_PEER_BUFFER_SIZE in OnionMessenger - nodes[0].messenger.send_onion_message(path.clone(), OnionMessageContents::Custom(test_msg.clone()), None).unwrap(); + nodes[0].messenger.send_onion_message(path.clone(), test_msg.clone(), None).unwrap(); } - let err = nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), None).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap_err(); assert_eq!(err, SendError::BufferFull); } @@ -426,7 +433,7 @@ fn many_hops() { intermediate_nodes, destination: Destination::Node(nodes[num_nodes-1].get_node_pk()), }; - nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), None).unwrap(); + nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap(); nodes[num_nodes-1].custom_message_handler.expect_message(TestCustomMessage::Response); pass_along_path(&nodes); } diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 1a0145441e2..8960058fa9f 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -19,25 +19,38 @@ use crate::blinded_path::BlindedPath; use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, ReceiveTlvs}; use crate::blinded_path::utils; use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient}; -use crate::events::OnionMessageProvider; use crate::ln::features::{InitFeatures, NodeFeatures}; -use crate::ln::msgs::{self, OnionMessageHandler}; +use crate::ln::msgs::{self, OnionMessage, OnionMessageHandler}; use crate::ln::onion_utils; use crate::ln::peer_handler::IgnoringMessageHandler; -pub use super::packet::{CustomOnionMessageContents, OnionMessageContents}; +pub use super::packet::OnionMessageContents; +use super::packet::ParsedOnionMessageContents; use super::offers::OffersMessageHandler; use super::packet::{BIG_PACKET_HOP_DATA_LEN, ForwardControlTlvs, Packet, Payload, ReceiveControlTlvs, SMALL_PACKET_HOP_DATA_LEN}; use crate::util::logger::Logger; use crate::util::ser::Writeable; +use core::fmt; use core::ops::Deref; use crate::io; use crate::sync::{Arc, Mutex}; use crate::prelude::*; -/// A sender, receiver and forwarder of onion messages. In upcoming releases, this object will be -/// used to retrieve invoices and fulfill invoice requests from [offers]. Currently, only sending -/// and receiving custom onion messages is supported. +/// A sender, receiver and forwarder of [`OnionMessage`]s. +/// +/// # Handling Messages +/// +/// `OnionMessenger` implements [`OnionMessageHandler`], making it responsible for either forwarding +/// messages to peers or delegating to the appropriate handler for the message type. Currently, the +/// available handlers are: +/// * [`OffersMessageHandler`], for responding to [`InvoiceRequest`]s and paying [`Bolt12Invoice`]s +/// * [`CustomOnionMessageHandler`], for handling user-defined message types +/// +/// # Sending Messages +/// +/// [`OnionMessage`]s are sent initially using [`OnionMessenger::send_onion_message`]. When handling +/// a message, the matched handler may return a response message which `OnionMessenger` will send +/// on its behalf. /// /// # Example /// @@ -48,7 +61,7 @@ use crate::prelude::*; /// # use lightning::blinded_path::BlindedPath; /// # use lightning::sign::KeysManager; /// # use lightning::ln::peer_handler::IgnoringMessageHandler; -/// # use lightning::onion_message::{CustomOnionMessageContents, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger}; +/// # use lightning::onion_message::{OnionMessageContents, Destination, MessageRouter, OnionMessagePath, OnionMessenger}; /// # use lightning::util::logger::{Logger, Record}; /// # use lightning::util::ser::{Writeable, Writer}; /// # use lightning::io; @@ -89,7 +102,7 @@ use crate::prelude::*; /// // Write your custom onion message to `w` /// } /// } -/// impl CustomOnionMessageContents for YourCustomMessage { +/// impl OnionMessageContents for YourCustomMessage { /// fn tlv_type(&self) -> u64 { /// # let your_custom_message_type = 42; /// your_custom_message_type @@ -101,8 +114,7 @@ use crate::prelude::*; /// destination: Destination::Node(destination_node_id), /// }; /// let reply_path = None; -/// # let your_custom_message = YourCustomMessage {}; -/// let message = OnionMessageContents::Custom(your_custom_message); +/// # let message = YourCustomMessage {}; /// onion_messenger.send_onion_message(path, message, reply_path); /// /// // Create a blinded path to yourself, for someone to send an onion message to. @@ -116,13 +128,12 @@ use crate::prelude::*; /// destination: Destination::BlindedPath(blinded_path), /// }; /// let reply_path = None; -/// # let your_custom_message = YourCustomMessage {}; -/// let message = OnionMessageContents::Custom(your_custom_message); +/// # let message = YourCustomMessage {}; /// onion_messenger.send_onion_message(path, message, reply_path); /// ``` /// -/// [offers]: -/// [`OnionMessenger`]: crate::onion_message::OnionMessenger +/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest +/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice pub struct OnionMessenger where ES::Target: EntropySource, @@ -135,20 +146,31 @@ where entropy_source: ES, node_signer: NS, logger: L, - pending_messages: Mutex>>, + pending_messages: Mutex>>, secp_ctx: Secp256k1, message_router: MR, offers_handler: OMH, custom_handler: CMH, } -/// A trait defining behavior for routing an [`OnionMessage`]. +/// An [`OnionMessage`] for [`OnionMessenger`] to send. /// -/// [`OnionMessage`]: msgs::OnionMessage +/// These are obtained when released from [`OnionMessenger`]'s handlers after which they are +/// enqueued for sending. +pub struct PendingOnionMessage { + /// The message contents to send in an [`OnionMessage`]. + pub contents: T, + + /// The destination of the message. + pub destination: Destination, + + /// A reply path to include in the [`OnionMessage`] for a response. + pub reply_path: Option, +} + +/// A trait defining behavior for routing an [`OnionMessage`]. pub trait MessageRouter { /// Returns a route for sending an [`OnionMessage`] to the given [`Destination`]. - /// - /// [`OnionMessage`]: msgs::OnionMessage fn find_path( &self, sender: PublicKey, peers: Vec, destination: Destination ) -> Result; @@ -165,7 +187,7 @@ impl MessageRouter for DefaultMessageRouter { } } -/// A path for sending an [`msgs::OnionMessage`]. +/// A path for sending an [`OnionMessage`]. #[derive(Clone)] pub struct OnionMessagePath { /// Nodes on the path between the sender and the destination. @@ -203,8 +225,8 @@ pub enum SendError { /// Because implementations such as Eclair will drop onion messages where the message packet /// exceeds 32834 bytes, we refuse to send messages where the packet exceeds this size. TooBigPacket, - /// The provided [`Destination`] was an invalid [`BlindedPath`], due to having fewer than two - /// blinded hops. + /// The provided [`Destination`] was an invalid [`BlindedPath`] due to not having any blinded + /// hops. TooFewBlindedHops, /// Our next-hop peer was offline or does not support onion message forwarding. InvalidFirstHop, @@ -236,43 +258,53 @@ pub enum SendError { pub trait CustomOnionMessageHandler { /// The message known to the handler. To support multiple message types, you may want to make this /// an enum with a variant for each supported message. - type CustomMessage: CustomOnionMessageContents; + type CustomMessage: OnionMessageContents; /// 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, msg: Self::CustomMessage) -> Option; /// Read a custom message of type `message_type` from `buffer`, returning `Ok(None)` if the /// message type is unknown. fn read_custom_message(&self, message_type: u64, buffer: &mut R) -> Result, msgs::DecodeError>; + + /// Releases any [`Self::CustomMessage`]s that need to be sent. + /// + /// Typically, this is used for messages initiating a message flow rather than in response to + /// another message. The latter should use the return value of [`Self::handle_custom_message`]. + fn release_pending_custom_messages(&self) -> Vec>; } /// A processed incoming onion message, containing either a Forward (another onion message) /// or a Receive payload with decrypted contents. -pub enum PeeledOnion { +pub enum PeeledOnion { /// Forwarded onion, with the next node id and a new onion - Forward(PublicKey, msgs::OnionMessage), + Forward(PublicKey, OnionMessage), /// Received onion message, with decrypted contents, path_id, and reply path - Receive(OnionMessageContents, Option<[u8; 32]>, Option) + Receive(ParsedOnionMessageContents, Option<[u8; 32]>, Option) } -/// Create an onion message with contents `message` to the destination of `path`. -/// Returns (introduction_node_id, onion_msg) -pub fn create_onion_message( +/// Creates an [`OnionMessage`] with the given `contents` for sending to the destination of +/// `path`. +/// +/// Returns both the node id of the peer to send the message to and the message itself. +pub fn create_onion_message( entropy_source: &ES, node_signer: &NS, secp_ctx: &Secp256k1, - path: OnionMessagePath, message: OnionMessageContents, reply_path: Option, -) -> Result<(PublicKey, msgs::OnionMessage), SendError> + path: OnionMessagePath, contents: T, reply_path: Option, +) -> Result<(PublicKey, OnionMessage), SendError> where ES::Target: EntropySource, NS::Target: NodeSigner, { let OnionMessagePath { intermediate_nodes, mut destination } = path; if let Destination::BlindedPath(BlindedPath { ref blinded_hops, .. }) = destination { - if blinded_hops.len() < 2 { + if blinded_hops.is_empty() { return Err(SendError::TooFewBlindedHops); } } - if message.tlv_type() < 64 { return Err(SendError::InvalidMessage) } + if contents.tlv_type() < 64 { return Err(SendError::InvalidMessage) } // If we are sending straight to a blinded path and we are the introduction node, we need to // advance the blinded path by 1 hop so the second hop is the new introduction node. @@ -289,8 +321,8 @@ where let blinding_secret_bytes = entropy_source.get_secure_random_bytes(); let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted"); - let (introduction_node_id, blinding_point) = if intermediate_nodes.len() != 0 { - (intermediate_nodes[0], PublicKey::from_secret_key(&secp_ctx, &blinding_secret)) + let (first_node_id, blinding_point) = if let Some(first_node_id) = intermediate_nodes.first() { + (*first_node_id, PublicKey::from_secret_key(&secp_ctx, &blinding_secret)) } else { match destination { Destination::Node(pk) => (pk, PublicKey::from_secret_key(&secp_ctx, &blinding_secret)), @@ -299,24 +331,26 @@ where } }; let (packet_payloads, packet_keys) = packet_payloads_and_keys( - &secp_ctx, &intermediate_nodes, destination, message, reply_path, &blinding_secret) + &secp_ctx, &intermediate_nodes, destination, contents, reply_path, &blinding_secret) .map_err(|e| SendError::Secp256k1(e))?; let prng_seed = entropy_source.get_secure_random_bytes(); let onion_routing_packet = construct_onion_message_packet( packet_payloads, packet_keys, prng_seed).map_err(|()| SendError::TooBigPacket)?; - Ok((introduction_node_id, msgs::OnionMessage { + Ok((first_node_id, OnionMessage { blinding_point, onion_routing_packet })) } -/// Decode one layer of an incoming onion message -/// Returns either a Forward (another onion message), or Receive (decrypted content) -pub fn peel_onion( - node_signer: NS, secp_ctx: &Secp256k1, logger: L, custom_handler: CMH, - msg: &msgs::OnionMessage, +/// Decode one layer of an incoming [`OnionMessage`]. +/// +/// Returns either the next layer of the onion for forwarding or the decrypted content for the +/// receiver. +pub fn peel_onion_message( + msg: &OnionMessage, secp_ctx: &Secp256k1, node_signer: NS, logger: L, + custom_handler: CMH, ) -> Result::Target as CustomOnionMessageHandler>::CustomMessage>, ()> where NS::Target: NodeSigner, @@ -350,7 +384,7 @@ where onion_decode_ss, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac, (control_tlvs_ss, custom_handler.deref(), logger.deref()) ) { - Ok((Payload::Receive::<<::Target as CustomOnionMessageHandler>::CustomMessage> { + Ok((Payload::Receive::::Target as CustomOnionMessageHandler>::CustomMessage>> { message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path, }, None)) => { Ok(PeeledOnion::Receive(message, path_id, reply_path)) @@ -376,7 +410,7 @@ where hop_data: new_packet_bytes, hmac: next_hop_hmac, }; - let onion_message = msgs::OnionMessage { + let onion_message = OnionMessage { blinding_point: match next_blinding_override { Some(blinding_point) => blinding_point, None => { @@ -437,21 +471,20 @@ where } } - /// Send an onion message with contents `message` to the destination of `path`. + /// Sends an [`OnionMessage`] with the given `contents` for sending to the destination of + /// `path`. /// /// See [`OnionMessenger`] for example usage. - pub fn send_onion_message( - &self, path: OnionMessagePath, message: OnionMessageContents, - reply_path: Option + pub fn send_onion_message( + &self, path: OnionMessagePath, contents: T, reply_path: Option ) -> Result<(), SendError> { - let (introduction_node_id, onion_msg) = create_onion_message( - &self.entropy_source, &self.node_signer, &self.secp_ctx, - path, message, reply_path + let (first_node_id, onion_msg) = create_onion_message( + &self.entropy_source, &self.node_signer, &self.secp_ctx, path, contents, reply_path )?; let mut pending_per_peer_msgs = self.pending_messages.lock().unwrap(); - if outbound_buffer_full(&introduction_node_id, &pending_per_peer_msgs) { return Err(SendError::BufferFull) } - match pending_per_peer_msgs.entry(introduction_node_id) { + if outbound_buffer_full(&first_node_id, &pending_per_peer_msgs) { return Err(SendError::BufferFull) } + match pending_per_peer_msgs.entry(first_node_id) { hash_map::Entry::Vacant(_) => Err(SendError::InvalidFirstHop), hash_map::Entry::Occupied(mut e) => { e.get_mut().push_back(onion_msg); @@ -460,58 +493,54 @@ where } } - fn respond_with_onion_message( - &self, response: OnionMessageContents, path_id: Option<[u8; 32]>, - reply_path: Option + fn handle_onion_message_response( + &self, response: Option, reply_path: Option, log_suffix: fmt::Arguments + ) { + if let Some(response) = response { + match reply_path { + Some(reply_path) => { + self.find_path_and_enqueue_onion_message( + response, Destination::BlindedPath(reply_path), None, log_suffix + ); + }, + None => { + log_trace!(self.logger, "Missing reply path {}", log_suffix); + }, + } + } + } + + fn find_path_and_enqueue_onion_message( + &self, contents: T, destination: Destination, reply_path: Option, + log_suffix: fmt::Arguments ) { let sender = match self.node_signer.get_node_id(Recipient::Node) { Ok(node_id) => node_id, Err(_) => { - log_warn!( - self.logger, "Unable to retrieve node id when responding to onion message with \ - path_id {:02x?}", path_id - ); + log_warn!(self.logger, "Unable to retrieve node id {}", log_suffix); return; } }; let peers = self.pending_messages.lock().unwrap().keys().copied().collect(); - - let destination = match reply_path { - Some(reply_path) => Destination::BlindedPath(reply_path), - None => { - log_trace!( - self.logger, "Missing reply path when responding to onion message with path_id \ - {:02x?}", path_id - ); - return; - }, - }; - let path = match self.message_router.find_path(sender, peers, destination) { Ok(path) => path, Err(()) => { - log_trace!( - self.logger, "Failed to find path when responding to onion message with \ - path_id {:02x?}", path_id - ); + log_trace!(self.logger, "Failed to find path {}", log_suffix); return; }, }; - log_trace!(self.logger, "Responding to onion message with path_id {:02x?}", path_id); + log_trace!(self.logger, "Sending onion message {}", log_suffix); - if let Err(e) = self.send_onion_message(path, response, None) { - log_trace!( - self.logger, "Failed responding to onion message with path_id {:02x?}: {:?}", - path_id, e - ); + if let Err(e) = self.send_onion_message(path, contents, reply_path) { + log_trace!(self.logger, "Failed sending onion message {}: {:?}", log_suffix, e); return; } } #[cfg(test)] - pub(super) fn release_pending_msgs(&self) -> HashMap> { + pub(super) fn release_pending_msgs(&self) -> HashMap> { let mut pending_msgs = self.pending_messages.lock().unwrap(); let mut msgs = HashMap::new(); // We don't want to disconnect the peers by removing them entirely from the original map, so we @@ -523,7 +552,7 @@ where } } -fn outbound_buffer_full(peer_node_id: &PublicKey, buffer: &HashMap>) -> bool { +fn outbound_buffer_full(peer_node_id: &PublicKey, buffer: &HashMap>) -> bool { const MAX_TOTAL_BUFFER_SIZE: usize = (1 << 20) * 128; const MAX_PER_PEER_BUFFER_SIZE: usize = (1 << 10) * 256; let mut total_buffered_bytes = 0; @@ -556,29 +585,34 @@ where OMH::Target: OffersMessageHandler, CMH::Target: CustomOnionMessageHandler, { - /// Handle an incoming onion message. Currently, if a message was destined for us we will log, but - /// soon we'll delegate the onion message to a handler that can generate invoices or send - /// payments. - fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &msgs::OnionMessage) { - match peel_onion( - &*self.node_signer, &self.secp_ctx, &*self.logger, &*self.custom_handler, msg + fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &OnionMessage) { + match peel_onion_message( + msg, &self.secp_ctx, &*self.node_signer, &*self.logger, &*self.custom_handler ) { Ok(PeeledOnion::Receive(message, path_id, reply_path)) => { log_trace!(self.logger, "Received an onion message with path_id {:02x?} and {} reply_path", path_id, if reply_path.is_some() { "a" } else { "no" }); - let response = match message { - OnionMessageContents::Offers(msg) => { - self.offers_handler.handle_message(msg) - .map(|msg| OnionMessageContents::Offers(msg)) + + match message { + ParsedOnionMessageContents::Offers(msg) => { + let response = self.offers_handler.handle_message(msg); + self.handle_onion_message_response( + response, reply_path, format_args!( + "when responding to Offers onion message with path_id {:02x?}", + path_id + ) + ); }, - OnionMessageContents::Custom(msg) => { - self.custom_handler.handle_custom_message(msg) - .map(|msg| OnionMessageContents::Custom(msg)) + ParsedOnionMessageContents::Custom(msg) => { + let response = self.custom_handler.handle_custom_message(msg); + self.handle_onion_message_response( + response, reply_path, format_args!( + "when responding to Custom onion message with path_id {:02x?}", + path_id + ) + ); }, - }; - if let Some(response) = response { - self.respond_with_onion_message(response, path_id, reply_path); } }, Ok(PeeledOnion::Forward(next_node_id, onion_message)) => { @@ -632,19 +666,27 @@ where features.set_onion_messages_optional(); features } -} -impl OnionMessageProvider -for OnionMessenger -where - ES::Target: EntropySource, - NS::Target: NodeSigner, - L::Target: Logger, - MR::Target: MessageRouter, - OMH::Target: OffersMessageHandler, - CMH::Target: CustomOnionMessageHandler, -{ - fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option { + // Before returning any messages to send for the peer, this method will see if any messages were + // enqueued in the handler by users, find a path to the corresponding blinded path's introduction + // node, and then enqueue the message for sending to the first peer in the full path. + fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option { + // Enqueue any initiating `OffersMessage`s to send. + for message in self.offers_handler.release_pending_messages() { + let PendingOnionMessage { contents, destination, reply_path } = message; + self.find_path_and_enqueue_onion_message( + contents, destination, reply_path, format_args!("when sending OffersMessage") + ); + } + + // Enqueue any initiating `CustomMessage`s to send. + for message in self.custom_handler.release_pending_custom_messages() { + let PendingOnionMessage { contents, destination, reply_path } = message; + self.find_path_and_enqueue_onion_message( + contents, destination, reply_path, format_args!("when sending CustomMessage") + ); + } + let mut pending_msgs = self.pending_messages.lock().unwrap(); if let Some(msgs) = pending_msgs.get_mut(&peer_node_id) { return msgs.pop_front() @@ -689,9 +731,9 @@ pub type SimpleRefOnionMessenger<'a, 'b, 'c, L> = OnionMessenger< /// Construct onion packet payloads and keys for sending an onion message along the given /// `unblinded_path` to the given `destination`. -fn packet_payloads_and_keys( - secp_ctx: &Secp256k1, unblinded_path: &[PublicKey], destination: Destination, - message: OnionMessageContents, mut reply_path: Option, session_priv: &SecretKey +fn packet_payloads_and_keys( + secp_ctx: &Secp256k1, unblinded_path: &[PublicKey], destination: Destination, message: T, + mut reply_path: Option, session_priv: &SecretKey ) -> Result<(Vec<(Payload, [u8; 32])>, Vec), secp256k1::Error> { let num_hops = unblinded_path.len() + destination.num_hops(); let mut payloads = Vec::with_capacity(num_hops); @@ -767,7 +809,7 @@ fn packet_payloads_and_keys(payloads: Vec<(Payload, [u8; 32])>, onion_keys: Vec, prng_seed: [u8; 32]) -> Result { +fn construct_onion_message_packet(payloads: Vec<(Payload, [u8; 32])>, onion_keys: Vec, prng_seed: [u8; 32]) -> Result { // Spec rationale: // "`len` allows larger messages to be sent than the standard 1300 bytes allowed for an HTLC // onion, but this should be used sparingly as it is reduces anonymity set, hence the diff --git a/lightning/src/onion_message/mod.rs b/lightning/src/onion_message/mod.rs index fb2943425dd..be800822e4d 100644 --- a/lightning/src/onion_message/mod.rs +++ b/lightning/src/onion_message/mod.rs @@ -27,7 +27,7 @@ mod packet; mod functional_tests; // Re-export structs so they can be imported with just the `onion_message::` module prefix. -pub use self::messenger::{CustomOnionMessageContents, CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger, PeeledOnion, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; +pub use self::messenger::{CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger, PeeledOnion, PendingOnionMessage, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; pub use self::offers::{OffersMessage, OffersMessageHandler}; -pub use self::packet::Packet; +pub use self::packet::{Packet, ParsedOnionMessageContents}; pub(crate) use self::packet::ControlTlvs; diff --git a/lightning/src/onion_message/offers.rs b/lightning/src/onion_message/offers.rs index de373bda1bc..254db7b81bd 100644 --- a/lightning/src/onion_message/offers.rs +++ b/lightning/src/onion_message/offers.rs @@ -16,6 +16,8 @@ use crate::offers::invoice_error::InvoiceError; use crate::offers::invoice_request::InvoiceRequest; use crate::offers::invoice::Bolt12Invoice; use crate::offers::parse::Bolt12ParseError; +use crate::onion_message::OnionMessageContents; +use crate::onion_message::messenger::PendingOnionMessage; use crate::util::logger::Logger; use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer}; @@ -32,7 +34,17 @@ const INVOICE_ERROR_TLV_TYPE: u64 = 68; pub trait OffersMessageHandler { /// Handles the given message by either responding with an [`Bolt12Invoice`], sending a payment, /// or replying with an error. + /// + /// The returned [`OffersMessage`], if any, is enqueued to be sent by [`OnionMessenger`]. + /// + /// [`OnionMessenger`]: crate::onion_message::OnionMessenger fn handle_message(&self, message: OffersMessage) -> Option; + + /// Releases any [`OffersMessage`]s that need to be sent. + /// + /// Typically, this is used for messages initiating a payment flow rather than in response to + /// another message. The latter should use the return value of [`Self::handle_message`]. + fn release_pending_messages(&self) -> Vec> { vec![] } } /// Possible BOLT 12 Offers messages sent and received via an [`OnionMessage`]. @@ -63,15 +75,6 @@ impl OffersMessage { } } - /// The TLV record type for the message as used in an `onionmsg_tlv` TLV stream. - pub fn tlv_type(&self) -> u64 { - match self { - OffersMessage::InvoiceRequest(_) => INVOICE_REQUEST_TLV_TYPE, - OffersMessage::Invoice(_) => INVOICE_TLV_TYPE, - OffersMessage::InvoiceError(_) => INVOICE_ERROR_TLV_TYPE, - } - } - fn parse(tlv_type: u64, bytes: Vec) -> Result { match tlv_type { INVOICE_REQUEST_TLV_TYPE => Ok(Self::InvoiceRequest(InvoiceRequest::try_from(bytes)?)), @@ -81,6 +84,16 @@ impl OffersMessage { } } +impl OnionMessageContents for OffersMessage { + fn tlv_type(&self) -> u64 { + match self { + OffersMessage::InvoiceRequest(_) => INVOICE_REQUEST_TLV_TYPE, + OffersMessage::Invoice(_) => INVOICE_TLV_TYPE, + OffersMessage::InvoiceError(_) => INVOICE_ERROR_TLV_TYPE, + } + } +} + impl Writeable for OffersMessage { fn write(&self, w: &mut W) -> Result<(), io::Error> { match self { diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index d19bd30edd2..19ca6eb963b 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -103,51 +103,52 @@ impl LengthReadable for Packet { /// Onion message payloads contain "control" TLVs and "data" TLVs. Control TLVs are used to route /// the onion message from hop to hop and for path verification, whereas data TLVs contain the onion /// message content itself, such as an invoice request. -pub(super) enum Payload { +pub(super) enum Payload { /// This payload is for an intermediate hop. Forward(ForwardControlTlvs), /// This payload is for the final hop. Receive { control_tlvs: ReceiveControlTlvs, reply_path: Option, - message: OnionMessageContents, + message: T, } } +/// The contents of an [`OnionMessage`] as read from the wire. +/// +/// [`OnionMessage`]: crate::ln::msgs::OnionMessage #[derive(Debug)] -/// The contents of an onion message. In the context of offers, this would be the invoice, invoice -/// request, or invoice error. -pub enum OnionMessageContents { +pub enum ParsedOnionMessageContents { /// A message related to BOLT 12 Offers. Offers(OffersMessage), /// A custom onion message specified by the user. Custom(T), } -impl OnionMessageContents { +impl OnionMessageContents for ParsedOnionMessageContents { /// Returns the type that was used to decode the message payload. /// /// This is not exported to bindings users as methods on non-cloneable enums are not currently exportable - pub fn tlv_type(&self) -> u64 { + fn tlv_type(&self) -> u64 { match self { - &OnionMessageContents::Offers(ref msg) => msg.tlv_type(), - &OnionMessageContents::Custom(ref msg) => msg.tlv_type(), + &ParsedOnionMessageContents::Offers(ref msg) => msg.tlv_type(), + &ParsedOnionMessageContents::Custom(ref msg) => msg.tlv_type(), } } } /// This is not exported to bindings users as methods on non-cloneable enums are not currently exportable -impl Writeable for OnionMessageContents { +impl Writeable for ParsedOnionMessageContents { fn write(&self, w: &mut W) -> Result<(), io::Error> { match self { - OnionMessageContents::Offers(msg) => Ok(msg.write(w)?), - OnionMessageContents::Custom(msg) => Ok(msg.write(w)?), + ParsedOnionMessageContents::Offers(msg) => Ok(msg.write(w)?), + ParsedOnionMessageContents::Custom(msg) => Ok(msg.write(w)?), } } } -/// The contents of a custom onion message. -pub trait CustomOnionMessageContents: Writeable { +/// The contents of an onion message. +pub trait OnionMessageContents: Writeable { /// Returns the TLV type identifying the message contents. MUST be >= 64. fn tlv_type(&self) -> u64; } @@ -173,7 +174,7 @@ pub(super) enum ReceiveControlTlvs { } // Uses the provided secret to simultaneously encode and encrypt the unblinded control TLVs. -impl Writeable for (Payload, [u8; 32]) { +impl Writeable for (Payload, [u8; 32]) { fn write(&self, w: &mut W) -> Result<(), io::Error> { match &self.0 { Payload::Forward(ForwardControlTlvs::Blinded(encrypted_bytes)) => { @@ -212,8 +213,8 @@ impl Writeable for (Payload, [u8; 32]) { } // Uses the provided secret to simultaneously decode and decrypt the control TLVs and data TLV. -impl -ReadableArgs<(SharedSecret, &H, &L)> for Payload<::CustomMessage> { +impl ReadableArgs<(SharedSecret, &H, &L)> +for Payload::CustomMessage>> { fn read(r: &mut R, args: (SharedSecret, &H, &L)) -> Result { let (encrypted_tlvs_ss, handler, logger) = args; @@ -236,12 +237,12 @@ ReadableArgs<(SharedSecret, &H, &L)> for Payload< { let msg = OffersMessage::read(msg_reader, (tlv_type, logger))?; - message = Some(OnionMessageContents::Offers(msg)); + message = Some(ParsedOnionMessageContents::Offers(msg)); Ok(true) }, _ => match handler.read_custom_message(msg_type, msg_reader)? { Some(msg) => { - message = Some(OnionMessageContents::Custom(msg)); + message = Some(ParsedOnionMessageContents::Custom(msg)); Ok(true) }, None => Ok(false), diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 4c8a31bd735..104f4c93c38 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -90,6 +90,7 @@ pub trait Router { &self, payer: &PublicKey, route_params: &RouteParameters, first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs ) -> Result; + /// Finds a [`Route`] for a payment between the given `payer` and a payee. /// /// The `payee` and the payment's value are given in [`RouteParameters::payment_params`]