From 99b949bcaedee299a535c1da1fafb0a552529509 Mon Sep 17 00:00:00 2001 From: Ian Slane Date: Fri, 19 Jul 2024 13:00:35 -0600 Subject: [PATCH] Add `Bolt12CreationError` error type to `ChannelManager` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduced the Bolt12CreationError error type for bolt12 errors that occur outside the builder. This error type is used in the ChannelManger functions. I moved, DuplicatePayment, and InsufficientLiquidity out of Bolt12SemanticsErrors into the new error type as well. Additionally, I updated the code to replace occurrences where we replaced Bolt12SemanticsErrors with the new Bolt12CreationError type throughout the relevant files. --- lightning/src/ln/channelmanager.rs | 60 +++++++++++++++++++----------- lightning/src/ln/offers_tests.rs | 24 ++++++------ lightning/src/offers/parse.rs | 4 -- 3 files changed, 51 insertions(+), 37 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index fef6a4a809d..e2a23d5116b 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -1700,10 +1700,9 @@ where /// /// ``` /// # use lightning::events::{Event, EventsProvider, PaymentPurpose}; -/// # use lightning::ln::channelmanager::AChannelManager; -/// # use lightning::offers::parse::Bolt12SemanticError; +/// # use lightning::ln::channelmanager::{AChannelManager, Bolt12CreationError}; /// # -/// # fn example(channel_manager: T) -> Result<(), Bolt12SemanticError> { +/// # fn example(channel_manager: T) -> Result<(), Bolt12CreationError> { /// # let channel_manager = channel_manager.get_cm(); /// # let absolute_expiry = None; /// let offer = channel_manager @@ -1805,13 +1804,12 @@ where /// ``` /// # use core::time::Duration; /// # use lightning::events::{Event, EventsProvider}; -/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, Retry}; -/// # use lightning::offers::parse::Bolt12SemanticError; +/// # use lightning::ln::channelmanager::{AChannelManager, Bolt12CreationError, PaymentId, RecentPaymentDetails, Retry}; /// # /// # fn example( /// # channel_manager: T, amount_msats: u64, absolute_expiry: Duration, retry: Retry, /// # max_total_routing_fee_msat: Option -/// # ) -> Result<(), Bolt12SemanticError> { +/// # ) -> Result<(), Bolt12CreationError> { /// # let channel_manager = channel_manager.get_cm(); /// let payment_id = PaymentId([42; 32]); /// let refund = channel_manager @@ -2526,6 +2524,26 @@ pub enum RecentPaymentDetails { }, } +/// Error during creation and handling of BOLT 12 related payments. +#[derive(Debug, Clone, PartialEq)] +pub enum Bolt12CreationError { + /// Error from BOLT 12 semantic checks. + InvalidSemantics(Bolt12SemanticError), + /// The payment id for a refund or request is already in use. + DuplicatePaymentId, + /// There is insufficient liquidity to complete the payment. + InsufficientLiquidity, + /// Failed to create a blinded path. + BlindedPathCreationFailed, +} + +impl From for Bolt12CreationError { + fn from(err: Bolt12SemanticError) -> Self { + Bolt12CreationError::InvalidSemantics(err) + } +} + + /// Route hints used in constructing invoices for [phantom node payents]. /// /// [phantom node payments]: crate::sign::PhantomKeysManager @@ -8801,7 +8819,7 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => { /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest pub fn create_offer_builder( &$self, absolute_expiry: Option - ) -> Result<$builder, Bolt12SemanticError> { + ) -> Result<$builder, Bolt12CreationError> { let node_id = $self.get_our_node_id(); let expanded_key = &$self.inbound_payment_key; let entropy = &*$self.entropy_source; @@ -8811,7 +8829,7 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => { let context = OffersContext::InvoiceRequest { nonce }; let path = $self.create_blinded_paths_using_absolute_expiry(context, absolute_expiry) .and_then(|paths| paths.into_iter().next().ok_or(())) - .map_err(|_| Bolt12SemanticError::MissingPaths)?; + .map_err(|_| Bolt12CreationError::BlindedPathCreationFailed)?; let builder = OfferBuilder::deriving_signing_pubkey(node_id, expanded_key, nonce, secp_ctx) .chain_hash($self.chain_hash) .path(path); @@ -8874,7 +8892,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => { pub fn create_refund_builder( &$self, amount_msats: u64, absolute_expiry: Duration, payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option - ) -> Result<$builder, Bolt12SemanticError> { + ) -> Result<$builder, Bolt12CreationError> { let node_id = $self.get_our_node_id(); let expanded_key = &$self.inbound_payment_key; let entropy = &*$self.entropy_source; @@ -8884,7 +8902,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => { let context = OffersContext::OutboundPayment { payment_id, nonce }; let path = $self.create_blinded_paths_using_absolute_expiry(context, Some(absolute_expiry)) .and_then(|paths| paths.into_iter().next().ok_or(())) - .map_err(|_| Bolt12SemanticError::MissingPaths)?; + .map_err(|_| Bolt12CreationError::BlindedPathCreationFailed)?; let builder = RefundBuilder::deriving_payer_id( node_id, expanded_key, nonce, secp_ctx, amount_msats, payment_id @@ -8900,7 +8918,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => { .add_new_awaiting_invoice( payment_id, expiration, retry_strategy, max_total_routing_fee_msat, ) - .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?; + .map_err(|_| Bolt12CreationError::DuplicatePaymentId)?; Ok(builder.into()) } @@ -8991,7 +9009,7 @@ where &self, offer: &Offer, quantity: Option, amount_msats: Option, payer_note: Option, payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option - ) -> Result<(), Bolt12SemanticError> { + ) -> Result<(), Bolt12CreationError> { let expanded_key = &self.inbound_payment_key; let entropy = &*self.entropy_source; let secp_ctx = &self.secp_ctx; @@ -9018,7 +9036,7 @@ where let context = OffersContext::OutboundPayment { payment_id, nonce }; let reply_paths = self.create_blinded_paths(context) - .map_err(|_| Bolt12SemanticError::MissingPaths)?; + .map_err(|_| Bolt12CreationError::BlindedPathCreationFailed)?; let total_liquidity: u64 = self.list_usable_channels().iter().map(|channel| channel.next_outbound_htlc_limit_msat).sum(); let total_amount_msats = match invoice_request.amount_msats() { @@ -9032,7 +9050,7 @@ where if let Some(amount) = total_amount_msats { if amount > total_liquidity { log_error!(self.logger, "Insufficient liquidity for payment with payment id: {}", payment_id); - return Err(Bolt12SemanticError::InsufficientLiquidity); + return Err(Bolt12CreationError::InsufficientLiquidity); } } @@ -9043,7 +9061,7 @@ where .add_new_awaiting_invoice( payment_id, expiration, retry_strategy, max_total_routing_fee_msat ) - .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?; + .map_err(|_| Bolt12CreationError::DuplicatePaymentId)?; let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap(); if !offer.paths().is_empty() { @@ -9070,7 +9088,7 @@ where } } else { debug_assert!(false); - return Err(Bolt12SemanticError::MissingSigningPubkey); + return Err(Bolt12CreationError::InvalidSemantics(Bolt12SemanticError::MissingSigningPubkey)); } Ok(()) @@ -9100,7 +9118,7 @@ where /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice pub fn request_refund_payment( &self, refund: &Refund - ) -> Result { + ) -> Result { let expanded_key = &self.inbound_payment_key; let entropy = &*self.entropy_source; let secp_ctx = &self.secp_ctx; @@ -9109,7 +9127,7 @@ where let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32; if refund.chain() != self.chain_hash { - return Err(Bolt12SemanticError::UnsupportedChain); + return Err(Bolt12CreationError::InvalidSemantics(Bolt12SemanticError::UnsupportedChain)); } let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); @@ -9120,7 +9138,7 @@ where let payment_paths = self.create_blinded_payment_paths( amount_msats, payment_secret, payment_context ) - .map_err(|_| Bolt12SemanticError::MissingPaths)?; + .map_err(|_| Bolt12CreationError::BlindedPathCreationFailed)?; #[cfg(feature = "std")] let builder = refund.respond_using_derived_keys( @@ -9137,7 +9155,7 @@ where let builder: InvoiceBuilder = builder.into(); let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?; let reply_paths = self.create_blinded_paths(OffersContext::Unknown {}) - .map_err(|_| Bolt12SemanticError::MissingPaths)?; + .map_err(|_| Bolt12CreationError::BlindedPathCreationFailed)?; let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap(); if refund.paths().is_empty() { @@ -9166,7 +9184,7 @@ where Ok(invoice) }, - Err(()) => Err(Bolt12SemanticError::InvalidAmount), + Err(()) => Err(Bolt12CreationError::InvalidSemantics(Bolt12SemanticError::InvalidAmount)), } } diff --git a/lightning/src/ln/offers_tests.rs b/lightning/src/ln/offers_tests.rs index 940da1f7763..5e575767a9c 100644 --- a/lightning/src/ln/offers_tests.rs +++ b/lightning/src/ln/offers_tests.rs @@ -46,7 +46,7 @@ use core::time::Duration; use crate::blinded_path::{BlindedPath, IntroductionNode}; use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentContext}; use crate::events::{Event, MessageSendEventsProvider, PaymentPurpose}; -use crate::ln::channelmanager::{Bolt12PaymentError, MAX_SHORT_LIVED_RELATIVE_EXPIRY, PaymentId, RecentPaymentDetails, Retry, self}; +use crate::ln::channelmanager::{Bolt12CreationError, Bolt12PaymentError, MAX_SHORT_LIVED_RELATIVE_EXPIRY, PaymentId, RecentPaymentDetails, Retry, self}; use crate::ln::functional_test_utils::*; use crate::ln::msgs::{ChannelMessageHandler, Init, NodeAnnouncement, OnionMessage, OnionMessageHandler, RoutingMessageHandler, SocketAddress, UnsignedGossipMessage, UnsignedNodeAnnouncement}; use crate::ln::outbound_payment::IDEMPOTENCY_TIMEOUT_TICKS; @@ -1600,7 +1600,7 @@ fn fails_creating_or_paying_for_offer_without_connected_peers() { let absolute_expiry = alice.node.duration_since_epoch() + MAX_SHORT_LIVED_RELATIVE_EXPIRY; match alice.node.create_offer_builder(Some(absolute_expiry)) { Ok(_) => panic!("Expected error"), - Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths), + Err(e) => assert_eq!(e, Bolt12CreationError::BlindedPathCreationFailed), } let mut args = ReconnectArgs::new(alice, bob); @@ -1616,7 +1616,7 @@ fn fails_creating_or_paying_for_offer_without_connected_peers() { match david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None) { Ok(_) => panic!("Expected error"), - Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths), + Err(e) => assert_eq!(e, Bolt12CreationError::BlindedPathCreationFailed), } assert!(nodes[0].node.list_recent_payments().is_empty()); @@ -1674,7 +1674,7 @@ fn fails_creating_refund_or_sending_invoice_without_connected_peers() { 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None ) { Ok(_) => panic!("Expected error"), - Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths), + Err(e) => assert_eq!(e, Bolt12CreationError::BlindedPathCreationFailed), } let mut args = ReconnectArgs::new(charlie, david); @@ -1688,7 +1688,7 @@ fn fails_creating_refund_or_sending_invoice_without_connected_peers() { match alice.node.request_refund_payment(&refund) { Ok(_) => panic!("Expected error"), - Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths), + Err(e) => assert_eq!(e, Bolt12CreationError::BlindedPathCreationFailed), } let mut args = ReconnectArgs::new(alice, bob); @@ -1720,7 +1720,7 @@ fn fails_creating_invoice_request_for_unsupported_chain() { let payment_id = PaymentId([1; 32]); match bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None) { Ok(_) => panic!("Expected error"), - Err(e) => assert_eq!(e, Bolt12SemanticError::UnsupportedChain), + Err(e) => assert_eq!(e, Bolt12CreationError::InvalidSemantics(Bolt12SemanticError::UnsupportedChain)), } } @@ -1747,7 +1747,7 @@ fn fails_sending_invoice_with_unsupported_chain_for_refund() { match alice.node.request_refund_payment(&refund) { Ok(_) => panic!("Expected error"), - Err(e) => assert_eq!(e, Bolt12SemanticError::UnsupportedChain), + Err(e) => assert_eq!(e, Bolt12CreationError::InvalidSemantics(Bolt12SemanticError::UnsupportedChain)), } } @@ -1780,7 +1780,7 @@ fn fails_creating_invoice_request_without_blinded_reply_path() { match david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None) { Ok(_) => panic!("Expected error"), - Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths), + Err(e) => assert_eq!(e, Bolt12CreationError::BlindedPathCreationFailed), } assert!(nodes[0].node.list_recent_payments().is_empty()); @@ -1820,7 +1820,7 @@ fn fails_creating_invoice_request_with_duplicate_payment_id() { match david.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None) { Ok(_) => panic!("Expected error"), - Err(e) => assert_eq!(e, Bolt12SemanticError::DuplicatePaymentId), + Err(e) => assert_eq!(e, Bolt12CreationError::DuplicatePaymentId), } expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id); @@ -1848,7 +1848,7 @@ fn fails_creating_refund_with_duplicate_payment_id() { 10_000, absolute_expiry, payment_id, Retry::Attempts(0), None ) { Ok(_) => panic!("Expected error"), - Err(e) => assert_eq!(e, Bolt12SemanticError::DuplicatePaymentId), + Err(e) => assert_eq!(e, Bolt12CreationError::DuplicatePaymentId), } expect_recent_payment!(nodes[0], RecentPaymentDetails::AwaitingInvoice, payment_id); @@ -1970,7 +1970,7 @@ fn fails_sending_invoice_without_blinded_payment_paths_for_refund() { match alice.node.request_refund_payment(&refund) { Ok(_) => panic!("Expected error"), - Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths), + Err(e) => assert_eq!(e, Bolt12CreationError::BlindedPathCreationFailed), } } @@ -2093,7 +2093,7 @@ fn test_insufficient_liquidity_for_bolt12_offer() { match result { Ok(_) => panic!("Expected error with insufficient liquidity."), Err(e) => { - assert_eq!(e, Bolt12SemanticError::InsufficientLiquidity); + assert_eq!(e, Bolt12CreationError::InsufficientLiquidity); } } } diff --git a/lightning/src/offers/parse.rs b/lightning/src/offers/parse.rs index 00181d1ef27..8ed40c3026f 100644 --- a/lightning/src/offers/parse.rs +++ b/lightning/src/offers/parse.rs @@ -177,8 +177,6 @@ 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, /// Blinded paths were provided but were not expected. @@ -193,8 +191,6 @@ pub enum Bolt12SemanticError { UnexpectedPaymentHash, /// A signature was expected but was missing. MissingSignature, - /// There is insufficient liquidity to complete the payment. - InsufficientLiquidity, } impl From for Bolt12ParseError {