Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions libs/gl-plugin/src/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -193,6 +194,66 @@ impl Node for PluginNodeServer {

let mut rpc = rpc_arc.lock().await;

// Check if we have sufficient incoming capacity to skip JIT channel negotiation.
// We require capacity + 5% buffer to account for fees and routing.
// Only check for specific amounts (not "any" amount invoices).
if req.amount_msat > 0 {
let receivable = self
.get_receivable_capacity(&mut rpc)
.await
.unwrap_or(0);

// Add 5% buffer: capacity >= amount * 1.05
// Equivalent to: capacity * 100 >= amount * 105
let has_sufficient_capacity = req.amount_msat
.saturating_mul(105)
.checked_div(100)
.map(|required| receivable >= required)
.unwrap_or(false);

if has_sufficient_capacity {
log::info!(
"Sufficient incoming capacity ({} msat) for invoice amount ({} msat), creating regular invoice",
receivable,
req.amount_msat
);

// Create a regular invoice without JIT channel negotiation
let invreq = cln_rpc::model::requests::InvoiceRequest {
amount_msat: cln_rpc::primitives::AmountOrAny::Amount(
cln_rpc::primitives::Amount::from_msat(req.amount_msat),
),
description: req.description.clone(),
label: req.label.clone(),
expiry: None,
fallbacks: None,
preimage: None,
cltv: Some(144),
deschashonly: None,
exposeprivatechannels: None,
};

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: res.created_index.unwrap_or(0) as u32,
expires_at: res.expires_at as u32,
payment_hash: <cln_rpc::primitives::Sha256 as Borrow<[u8]>>::borrow(&res.payment_hash).to_vec(),
payment_secret: res.payment_secret.to_vec(),
}));
}

log::info!(
"Insufficient incoming capacity ({} msat) for invoice amount ({} msat), negotiating JIT channel",
receivable,
req.amount_msat
);
}

// Get the CLN version to determine which RPC method to use
let version = rpc
.call_typed(&cln_rpc::model::requests::GetinfoRequest {})
Expand Down Expand Up @@ -783,6 +844,29 @@ impl PluginNodeServer {
Ok(res)
}

/// Get the total receivable capacity across all active channels.
///
/// Returns the sum of `receivable_msat` for all channels in
/// `CHANNELD_NORMAL` state with a connected peer.
async fn get_receivable_capacity(&self, rpc: &mut cln_rpc::ClnRpc) -> Result<u64, Error> {
use cln_rpc::primitives::ChannelState;

let res = rpc
.call_typed(&cln_rpc::model::requests::ListpeerchannelsRequest { id: None })
.await?;

let total: u64 = res
.channels
.into_iter()
.filter(|c| c.peer_connected && c.state == ChannelState::CHANNELD_NORMAL)
.filter_map(|c| c.receivable_msat)
.map(|a| a.msat())
.sum();

log::debug!("Total receivable capacity: {} msat", total);
Ok(total)
}

async fn get_reconnect_peers(
&self,
) -> Result<Vec<cln_rpc::model::requests::ConnectRequest>, Error> {
Expand Down
Loading