From debf857a03685153c37043f23d872448c19fb301 Mon Sep 17 00:00:00 2001 From: Ian Slane Date: Fri, 12 Jul 2024 16:25:40 -0600 Subject: [PATCH] Abort if insufficient liquidity in `pay_for_offer` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces a check to abort early if there's insufficient liquidity when fulfilling the payment in `pay_for_offer` The logic gets the available liquidity by summing up the  `next_outbound_htlc_limit_msat` from all usable channels. Then, compares the total liquidity amount with the total payment amount. If the payment amount is > the available liquidity, the function logs an error and returns a  Bolt12SemanticError::InsufficientLiquidity error. --- lightning/src/ln/channelmanager.rs | 22 +++++++++++++++++++++- lightning/src/offers/parse.rs | 2 ++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 89f967c4f70..f87daf8ad85 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -64,7 +64,7 @@ use crate::ln::wire::Encode; use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice}; use crate::offers::invoice_error::InvoiceError; use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequestBuilder}; -use crate::offers::offer::{Offer, OfferBuilder}; +use crate::offers::offer::{Offer, OfferBuilder, Amount}; use crate::offers::parse::Bolt12SemanticError; use crate::offers::refund::{Refund, RefundBuilder}; use crate::onion_message::async_payments::{AsyncPaymentsMessage, HeldHtlcAvailable, ReleaseHeldHtlc, AsyncPaymentsMessageHandler}; @@ -8816,6 +8816,26 @@ where None => builder, Some(payer_note) => builder.payer_note(payer_note), }; + + let total_liquidity: u64 = self.list_usable_channels().iter().map(|channel| channel.next_outbound_htlc_limit_msat).sum(); + let total_amount_msats = match amount_msats { + Some(amount_msats) => Some(amount_msats), + None => match offer.amount() { + Some(Amount::Bitcoin {amount_msats}) => Some(amount_msats), + _ => None, + }, + }; + + if let Some(amount) = total_amount_msats { + return if amount > total_liquidity { + log_error!(self.logger, "Insufficient liquidity for payment with payment id: {}", payment_id); + Err(Bolt12SemanticError::InsufficientLiquidity) + } else { + log_error!(self.logger, "Offer amount is missing for payment with id: {}", payment_id); + Err(Bolt12SemanticError::InvalidAmount) + } + } + let invoice_request = builder.build_and_sign()?; let context = OffersContext::OutboundPayment { payment_id }; diff --git a/lightning/src/offers/parse.rs b/lightning/src/offers/parse.rs index c48d745a9ff..00181d1ef27 100644 --- a/lightning/src/offers/parse.rs +++ b/lightning/src/offers/parse.rs @@ -193,6 +193,8 @@ 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 {