From 734358b640a30d663ac16d4025d3aba5bebeb975 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Tue, 7 Apr 2026 15:35:09 -0500 Subject: [PATCH 1/2] Return error for invalid onchain send fee rate Previously, an invalid fee_rate_sat_per_vb (overflows) was silently collapsed into None via .and_then(), causing the node to substitute its default fee estimation without informing the user. Now the server returns an InvalidRequestError if the fee rate overflows. Co-Authored-By: Claude Opus 4.6 (1M context) --- ldk-server/src/api/onchain_send.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ldk-server/src/api/onchain_send.rs b/ldk-server/src/api/onchain_send.rs index ea616a17..e1e51b8f 100644 --- a/ldk-server/src/api/onchain_send.rs +++ b/ldk-server/src/api/onchain_send.rs @@ -30,7 +30,12 @@ pub(crate) async fn handle_onchain_send_request( ) })?; - let fee_rate = request.fee_rate_sat_per_vb.and_then(FeeRate::from_sat_per_vb); + let fee_rate = match request.fee_rate_sat_per_vb { + Some(rate) => Some(FeeRate::from_sat_per_vb(rate).ok_or_else(|| { + LdkServerError::new(InvalidRequestError, format!("Invalid fee rate: {} sat/vB", rate)) + })?), + None => None, + }; let txid = match (request.amount_sats, request.send_all) { (Some(amount_sats), None) => { context.node.onchain_payment().send_to_address(&address, amount_sats, fee_rate)? From d9b4876cd0fb6d57f8a159173856a0bcbc20eb01 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Tue, 7 Apr 2026 15:36:39 -0500 Subject: [PATCH 2/2] Error when quantity set without amount in bolt12_receive Previously, setting quantity without amount_msat silently produced a variable-amount offer with no quantity constraint. Now both the server and CLI reject this misconfiguration with a clear error message. Co-Authored-By: Claude Opus 4.6 (1M context) --- ldk-server-cli/src/main.rs | 5 +++++ ldk-server/src/api/bolt12_receive.rs | 30 +++++++++++++++++----------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/ldk-server-cli/src/main.rs b/ldk-server-cli/src/main.rs index f001d871..17beb6dd 100644 --- a/ldk-server-cli/src/main.rs +++ b/ldk-server-cli/src/main.rs @@ -779,6 +779,11 @@ async fn main() { ); }, Commands::Bolt12Receive { description, amount, expiry_secs, quantity } => { + if quantity.is_some() && amount.is_none() { + handle_error_msg( + "quantity can only be set for fixed-amount offers (amount must be provided)", + ); + } let amount_msat = amount.map(|a| a.to_msat()); handle_response_result::<_, Bolt12ReceiveResponse>( client diff --git a/ldk-server/src/api/bolt12_receive.rs b/ldk-server/src/api/bolt12_receive.rs index 6558a6d8..60261973 100644 --- a/ldk-server/src/api/bolt12_receive.rs +++ b/ldk-server/src/api/bolt12_receive.rs @@ -13,23 +13,29 @@ use hex::DisplayHex; use ldk_server_grpc::api::{Bolt12ReceiveRequest, Bolt12ReceiveResponse}; use crate::api::error::LdkServerError; +use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; pub(crate) async fn handle_bolt12_receive_request( context: Arc, request: Bolt12ReceiveRequest, ) -> Result { - let offer = match request.amount_msat { - Some(amount_msat) => context.node.bolt12_payment().receive( - amount_msat, - &request.description, - request.expiry_secs, - request.quantity, - )?, - None => context - .node - .bolt12_payment() - .receive_variable_amount(&request.description, request.expiry_secs)?, - }; + let offer = + match (request.amount_msat, request.quantity) { + (Some(amount_msat), quantity) => context.node.bolt12_payment().receive( + amount_msat, + &request.description, + request.expiry_secs, + quantity, + )?, + (None, Some(_)) => return Err(LdkServerError::new( + InvalidRequestError, + "quantity can only be set for fixed-amount offers (amount_msat must be provided)", + )), + (None, None) => context + .node + .bolt12_payment() + .receive_variable_amount(&request.description, request.expiry_secs)?, + }; let offer_id = offer.id().0.to_lower_hex_string(); let response = Bolt12ReceiveResponse { offer: offer.to_string(), offer_id };