From 1e1bb6ee3b9747c5017a641ec965a9a728f84f65 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 10 Feb 2026 11:32:06 +0100 Subject: [PATCH 1/2] gl-plugin: Skip JIT channel negotiation when sufficient capacity exists Before creating an LSPS2 invoice that requires JIT channel negotiation, check if the node already has sufficient incoming capacity to receive the payment directly. When the receivable capacity (with a 5% buffer for fees) exceeds the invoice amount, create a regular invoice instead of negotiating with the LSP. This avoids unnecessary LSP fees and channel opening costs when the node can already receive the payment. The capacity check: - Sums receivable_msat across all CHANNELD_NORMAL channels with connected peers - Applies a 5% buffer to account for routing fees - Falls back to JIT negotiation if capacity is insufficient or for 'any amount' invoices From c6d2dff0141865b6946517aabad0343fb19d7a48 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 11 Feb 2026 13:27:05 +0100 Subject: [PATCH 2/2] gl-plugin: Use call_typed for regular invoice creation Replace call_raw with call_typed when creating a regular invoice in the LSP invoice handler. This provides better type safety by using the generated cln_rpc::model::requests::InvoiceRequest and cln_rpc::model::responses::InvoiceResponse types. Benefits: - Compile-time type checking for request/response fields - No manual hex decoding needed for payment_hash/payment_secret - Access to created_index field from the response - Cleaner code without custom request/response structs --- libs/gl-plugin/src/node/mod.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/libs/gl-plugin/src/node/mod.rs b/libs/gl-plugin/src/node/mod.rs index c865fb70c..f3bbd3cfc 100644 --- a/libs/gl-plugin/src/node/mod.rs +++ b/libs/gl-plugin/src/node/mod.rs @@ -25,6 +25,7 @@ use tokio_stream::wrappers::ReceiverStream; use tonic::{transport::ServerTlsConfig, Code, Request, Response, Status}; mod wrapper; use gl_client::bitcoin; +use std::borrow::Borrow; use std::str::FromStr; pub use wrapper::WrappedNodeServer; @@ -218,7 +219,7 @@ impl Node for PluginNodeServer { ); // Create a regular invoice without JIT channel negotiation - let invreq = crate::requests::Invoice { + let invreq = cln_rpc::model::requests::InvoiceRequest { amount_msat: cln_rpc::primitives::AmountOrAny::Amount( cln_rpc::primitives::Amount::from_msat(req.amount_msat), ), @@ -230,24 +231,19 @@ impl Node for PluginNodeServer { cltv: Some(144), deschashonly: None, exposeprivatechannels: None, - dev_routes: None, }; - let res: crate::responses::Invoice = rpc - .call_raw("invoice", &invreq) + let res = rpc + .call_typed(&invreq) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; return Ok(Response::new(pb::LspInvoiceResponse { bolt11: res.bolt11, - created_index: 0, // Not available in our Invoice response - expires_at: res.expiry_time, - payment_hash: hex::decode(&res.payment_hash) - .map_err(|e| Status::new(Code::Internal, format!("Invalid payment_hash: {}", e)))?, - payment_secret: res - .payment_secret - .map(|s| hex::decode(&s).unwrap_or_default()) - .unwrap_or_default(), + created_index: res.created_index.unwrap_or(0) as u32, + expires_at: res.expires_at as u32, + payment_hash: >::borrow(&res.payment_hash).to_vec(), + payment_secret: res.payment_secret.to_vec(), })); }