Skip to content

Commit 32619ed

Browse files
committed
Add BOLT11 underpaying send RPC
Expose Bolt11SendUnderpaying through the proto API, server handler, client, CLI, MCP tool registry, tests, and docs. AI-assisted-by: OpenAI Codex
1 parent 9f960bc commit 32619ed

12 files changed

Lines changed: 240 additions & 50 deletions

File tree

docs/api-guide.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,11 @@ All RPCs are unary (single request, single response) unless noted otherwise.
9393

9494
### BOLT11 Payments
9595

96-
| RPC | Description |
97-
|-----------------|-------------------------------------------------------------------|
98-
| `Bolt11Receive` | Create an invoice (fixed or variable amount) with automatic claim |
99-
| `Bolt11Send` | Pay a BOLT11 invoice (with optional routing config) |
96+
| RPC | Description |
97+
|---------------------------|------------------------------------------------------------------------------------|
98+
| `Bolt11Receive` | Create an invoice (fixed or variable amount) with automatic claim |
99+
| `Bolt11Send` | Pay a BOLT11 invoice (with optional routing config) |
100+
| `Bolt11SendUnderpaying` | Pay less than the invoice amount when the receiver accepts underpaying HTLCs |
100101

101102
### BOLT11 Hodl Invoices
102103

ldk-server-cli/src/main.rs

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@ use ldk_server_client::ldk_server_grpc::api::{
2828
Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11ReceiveVariableAmountViaJitChannelRequest,
2929
Bolt11ReceiveVariableAmountViaJitChannelResponse, Bolt11ReceiveViaJitChannelRequest,
3030
Bolt11ReceiveViaJitChannelResponse, Bolt11SendRequest, Bolt11SendResponse,
31-
Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse,
32-
CloseChannelRequest, CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse,
33-
DecodeInvoiceRequest, DecodeInvoiceResponse, DecodeOfferRequest, DecodeOfferResponse,
34-
DisconnectPeerRequest, DisconnectPeerResponse, ExportPathfindingScoresRequest,
35-
ForceCloseChannelRequest, ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse,
36-
GetNodeInfoRequest, GetNodeInfoResponse, GetPaymentDetailsRequest, GetPaymentDetailsResponse,
31+
Bolt11SendUnderpayingRequest, Bolt11SendUnderpayingResponse, Bolt12ReceiveRequest,
32+
Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse, CloseChannelRequest,
33+
CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse, DecodeInvoiceRequest,
34+
DecodeInvoiceResponse, DecodeOfferRequest, DecodeOfferResponse, DisconnectPeerRequest,
35+
DisconnectPeerResponse, ExportPathfindingScoresRequest, ForceCloseChannelRequest,
36+
ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest,
37+
GetNodeInfoResponse, GetPaymentDetailsRequest, GetPaymentDetailsResponse,
3738
GraphGetChannelRequest, GraphGetChannelResponse, GraphGetNodeRequest, GraphGetNodeResponse,
3839
GraphListChannelsRequest, GraphListChannelsResponse, GraphListNodesRequest,
3940
GraphListNodesResponse, ListChannelsRequest, ListChannelsResponse,
@@ -246,6 +247,32 @@ enum Commands {
246247
)]
247248
max_channel_saturation_power_of_half: Option<u32>,
248249
},
250+
#[command(about = "Underpay a BOLT11 invoice")]
251+
Bolt11SendUnderpaying {
252+
#[arg(help = "A BOLT11 invoice for a payment within the Lightning Network")]
253+
invoice: String,
254+
#[arg(
255+
help = "Amount to send, e.g. 50sat or 50000msat. Must be less than the invoice amount"
256+
)]
257+
amount: Amount,
258+
#[arg(
259+
long,
260+
help = "Maximum total routing fee, e.g. 50sat or 50000msat. Defaults to 1% of payment + 50 sats"
261+
)]
262+
max_total_routing_fee: Option<Amount>,
263+
#[arg(long, help = "Maximum total CLTV delta we accept for the route (default: 1008)")]
264+
max_total_cltv_expiry_delta: Option<u32>,
265+
#[arg(
266+
long,
267+
help = "Maximum number of paths that may be used by MPP payments (default: 10)"
268+
)]
269+
max_path_count: Option<u32>,
270+
#[arg(
271+
long,
272+
help = "Maximum share of a channel's total capacity to send over a channel, as a power of 1/2 (default: 2)"
273+
)]
274+
max_channel_saturation_power_of_half: Option<u32>,
275+
},
249276
#[command(about = "Return a BOLT12 offer for receiving payments")]
250277
Bolt12Receive {
251278
#[arg(help = "Description to attach along with the offer")]
@@ -765,6 +792,34 @@ async fn main() {
765792
.await,
766793
);
767794
},
795+
Commands::Bolt11SendUnderpaying {
796+
invoice,
797+
amount,
798+
max_total_routing_fee,
799+
max_total_cltv_expiry_delta,
800+
max_path_count,
801+
max_channel_saturation_power_of_half,
802+
} => {
803+
let amount_msat = amount.to_msat();
804+
let max_total_routing_fee_msat = max_total_routing_fee.map(|a| a.to_msat());
805+
let route_parameters = RouteParametersConfig {
806+
max_total_routing_fee_msat,
807+
max_total_cltv_expiry_delta: max_total_cltv_expiry_delta
808+
.unwrap_or(DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA),
809+
max_path_count: max_path_count.unwrap_or(DEFAULT_MAX_PATH_COUNT),
810+
max_channel_saturation_power_of_half: max_channel_saturation_power_of_half
811+
.unwrap_or(DEFAULT_MAX_CHANNEL_SATURATION_POWER_OF_HALF),
812+
};
813+
handle_response_result::<_, Bolt11SendUnderpayingResponse>(
814+
client
815+
.bolt11_send_underpaying(Bolt11SendUnderpayingRequest {
816+
invoice,
817+
amount_msat,
818+
route_parameters: Some(route_parameters),
819+
})
820+
.await,
821+
);
822+
},
768823
Commands::Bolt12Receive { description, amount, expiry_secs, quantity } => {
769824
let amount_msat = amount.map(|a| a.to_msat());
770825
handle_response_result::<_, Bolt12ReceiveResponse>(

ldk-server-client/src/client.rs

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,36 +21,38 @@ use ldk_server_grpc::api::{
2121
Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11ReceiveVariableAmountViaJitChannelRequest,
2222
Bolt11ReceiveVariableAmountViaJitChannelResponse, Bolt11ReceiveViaJitChannelRequest,
2323
Bolt11ReceiveViaJitChannelResponse, Bolt11SendRequest, Bolt11SendResponse,
24-
Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse,
25-
CloseChannelRequest, CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse,
26-
DecodeInvoiceRequest, DecodeInvoiceResponse, DecodeOfferRequest, DecodeOfferResponse,
27-
DisconnectPeerRequest, DisconnectPeerResponse, ExportPathfindingScoresRequest,
28-
ExportPathfindingScoresResponse, ForceCloseChannelRequest, ForceCloseChannelResponse,
29-
GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest, GetNodeInfoResponse,
30-
GetPaymentDetailsRequest, GetPaymentDetailsResponse, GraphGetChannelRequest,
31-
GraphGetChannelResponse, GraphGetNodeRequest, GraphGetNodeResponse, GraphListChannelsRequest,
32-
GraphListChannelsResponse, GraphListNodesRequest, GraphListNodesResponse, ListChannelsRequest,
33-
ListChannelsResponse, ListForwardedPaymentsRequest, ListForwardedPaymentsResponse,
34-
ListPaymentsRequest, ListPaymentsResponse, ListPeersRequest, ListPeersResponse,
35-
OnchainReceiveRequest, OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse,
36-
OpenChannelRequest, OpenChannelResponse, SignMessageRequest, SignMessageResponse,
37-
SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse, SpontaneousSendRequest,
24+
Bolt11SendUnderpayingRequest, Bolt11SendUnderpayingResponse, Bolt12ReceiveRequest,
25+
Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse, CloseChannelRequest,
26+
CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse, DecodeInvoiceRequest,
27+
DecodeInvoiceResponse, DecodeOfferRequest, DecodeOfferResponse, DisconnectPeerRequest,
28+
DisconnectPeerResponse, ExportPathfindingScoresRequest, ExportPathfindingScoresResponse,
29+
ForceCloseChannelRequest, ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse,
30+
GetNodeInfoRequest, GetNodeInfoResponse, GetPaymentDetailsRequest, GetPaymentDetailsResponse,
31+
GraphGetChannelRequest, GraphGetChannelResponse, GraphGetNodeRequest, GraphGetNodeResponse,
32+
GraphListChannelsRequest, GraphListChannelsResponse, GraphListNodesRequest,
33+
GraphListNodesResponse, ListChannelsRequest, ListChannelsResponse,
34+
ListForwardedPaymentsRequest, ListForwardedPaymentsResponse, ListPaymentsRequest,
35+
ListPaymentsResponse, ListPeersRequest, ListPeersResponse, OnchainReceiveRequest,
36+
OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse, OpenChannelRequest,
37+
OpenChannelResponse, SignMessageRequest, SignMessageResponse, SpliceInRequest,
38+
SpliceInResponse, SpliceOutRequest, SpliceOutResponse, SpontaneousSendRequest,
3839
SpontaneousSendResponse, SubscribeEventsRequest, UnifiedSendRequest, UnifiedSendResponse,
3940
UpdateChannelConfigRequest, UpdateChannelConfigResponse, VerifySignatureRequest,
4041
VerifySignatureResponse,
4142
};
4243
use ldk_server_grpc::endpoints::{
4344
BOLT11_CLAIM_FOR_HASH_PATH, BOLT11_FAIL_FOR_HASH_PATH, BOLT11_RECEIVE_FOR_HASH_PATH,
4445
BOLT11_RECEIVE_PATH, BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH,
45-
BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH, BOLT11_SEND_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH,
46-
CLOSE_CHANNEL_PATH, CONNECT_PEER_PATH, DECODE_INVOICE_PATH, DECODE_OFFER_PATH,
47-
DISCONNECT_PEER_PATH, EXPORT_PATHFINDING_SCORES_PATH, FORCE_CLOSE_CHANNEL_PATH,
48-
GET_BALANCES_PATH, GET_METRICS_PATH, GET_NODE_INFO_PATH, GET_PAYMENT_DETAILS_PATH,
49-
GRAPH_GET_CHANNEL_PATH, GRAPH_GET_NODE_PATH, GRAPH_LIST_CHANNELS_PATH, GRAPH_LIST_NODES_PATH,
50-
GRPC_SERVICE_PREFIX, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH,
51-
LIST_PEERS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SIGN_MESSAGE_PATH,
52-
SPLICE_IN_PATH, SPLICE_OUT_PATH, SPONTANEOUS_SEND_PATH, SUBSCRIBE_EVENTS_PATH,
53-
UNIFIED_SEND_PATH, UPDATE_CHANNEL_CONFIG_PATH, VERIFY_SIGNATURE_PATH,
46+
BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH, BOLT11_SEND_PATH, BOLT11_SEND_UNDERPAYING_PATH,
47+
BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH, CLOSE_CHANNEL_PATH, CONNECT_PEER_PATH,
48+
DECODE_INVOICE_PATH, DECODE_OFFER_PATH, DISCONNECT_PEER_PATH, EXPORT_PATHFINDING_SCORES_PATH,
49+
FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_METRICS_PATH, GET_NODE_INFO_PATH,
50+
GET_PAYMENT_DETAILS_PATH, GRAPH_GET_CHANNEL_PATH, GRAPH_GET_NODE_PATH,
51+
GRAPH_LIST_CHANNELS_PATH, GRAPH_LIST_NODES_PATH, GRPC_SERVICE_PREFIX, LIST_CHANNELS_PATH,
52+
LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH, LIST_PEERS_PATH, ONCHAIN_RECEIVE_PATH,
53+
ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SIGN_MESSAGE_PATH, SPLICE_IN_PATH, SPLICE_OUT_PATH,
54+
SPONTANEOUS_SEND_PATH, SUBSCRIBE_EVENTS_PATH, UNIFIED_SEND_PATH, UPDATE_CHANNEL_CONFIG_PATH,
55+
VERIFY_SIGNATURE_PATH,
5456
};
5557
use ldk_server_grpc::events::EventEnvelope;
5658
use ldk_server_grpc::grpc::{
@@ -242,6 +244,13 @@ impl LdkServerClient {
242244
self.grpc_unary(&request, BOLT11_SEND_PATH).await
243245
}
244246

247+
/// Send an underpaying payment for a BOLT11 invoice.
248+
pub async fn bolt11_send_underpaying(
249+
&self, request: Bolt11SendUnderpayingRequest,
250+
) -> Result<Bolt11SendUnderpayingResponse, LdkServerError> {
251+
self.grpc_unary(&request, BOLT11_SEND_UNDERPAYING_PATH).await
252+
}
253+
245254
/// Retrieve a new BOLT12 offer.
246255
pub async fn bolt12_receive(
247256
&self, request: Bolt12ReceiveRequest,

ldk-server-grpc/src/api.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,35 @@ pub struct Bolt11SendResponse {
376376
#[prost(string, tag = "1")]
377377
pub payment_id: ::prost::alloc::string::String,
378378
}
379+
/// Send an underpaying payment for a BOLT11 invoice.
380+
/// See more: <https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.send_using_amount_underpaying>
381+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
382+
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
383+
#[cfg_attr(feature = "serde", serde(default))]
384+
#[allow(clippy::derive_partial_eq_without_eq)]
385+
#[derive(Clone, PartialEq, ::prost::Message)]
386+
pub struct Bolt11SendUnderpayingRequest {
387+
/// An invoice for a payment within the Lightning Network.
388+
#[prost(string, tag = "1")]
389+
pub invoice: ::prost::alloc::string::String,
390+
/// Amount in millisatoshis to send. Must be less than the amount required by the invoice.
391+
#[prost(uint64, tag = "2")]
392+
pub amount_msat: u64,
393+
/// Configuration options for payment routing and pathfinding.
394+
#[prost(message, optional, tag = "3")]
395+
pub route_parameters: ::core::option::Option<super::types::RouteParametersConfig>,
396+
}
397+
/// The response for the `Bolt11SendUnderpaying` RPC. On failure, a gRPC error status is returned.
398+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
399+
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
400+
#[cfg_attr(feature = "serde", serde(default))]
401+
#[allow(clippy::derive_partial_eq_without_eq)]
402+
#[derive(Clone, PartialEq, ::prost::Message)]
403+
pub struct Bolt11SendUnderpayingResponse {
404+
/// An identifier used to uniquely identify a payment in hex-encoded form.
405+
#[prost(string, tag = "1")]
406+
pub payment_id: ::prost::alloc::string::String,
407+
}
379408
/// Returns a BOLT12 offer for the given amount, if specified.
380409
///
381410
/// See more:

ldk-server-grpc/src/endpoints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub const BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH: &str = "Bolt11ReceiveViaJitChanne
2222
pub const BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH: &str =
2323
"Bolt11ReceiveVariableAmountViaJitChannel";
2424
pub const BOLT11_SEND_PATH: &str = "Bolt11Send";
25+
pub const BOLT11_SEND_UNDERPAYING_PATH: &str = "Bolt11SendUnderpaying";
2526
pub const BOLT12_RECEIVE_PATH: &str = "Bolt12Receive";
2627
pub const BOLT12_SEND_PATH: &str = "Bolt12Send";
2728
pub const OPEN_CHANNEL_PATH: &str = "OpenChannel";

ldk-server-grpc/src/proto/api.proto

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,28 @@ message Bolt11SendResponse {
296296
string payment_id = 1;
297297
}
298298

299+
// Send an underpaying payment for a BOLT11 invoice.
300+
// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.send_using_amount_underpaying
301+
message Bolt11SendUnderpayingRequest {
302+
303+
// An invoice for a payment within the Lightning Network.
304+
string invoice = 1;
305+
306+
// Amount in millisatoshis to send. Must be less than the amount required by the invoice.
307+
uint64 amount_msat = 2;
308+
309+
// Configuration options for payment routing and pathfinding.
310+
optional types.RouteParametersConfig route_parameters = 3;
311+
312+
}
313+
314+
// The response for the `Bolt11SendUnderpaying` RPC. On failure, a gRPC error status is returned.
315+
message Bolt11SendUnderpayingResponse {
316+
317+
// An identifier used to uniquely identify a payment in hex-encoded form.
318+
string payment_id = 1;
319+
}
320+
299321
// Returns a BOLT12 offer for the given amount, if specified.
300322
//
301323
// See more:
@@ -928,6 +950,8 @@ service LightningNode {
928950
rpc Bolt11ReceiveVariableAmountViaJitChannel(Bolt11ReceiveVariableAmountViaJitChannelRequest) returns (Bolt11ReceiveVariableAmountViaJitChannelResponse);
929951
// Send a payment for a BOLT11 invoice.
930952
rpc Bolt11Send(Bolt11SendRequest) returns (Bolt11SendResponse);
953+
// Send an underpaying payment for a BOLT11 invoice.
954+
rpc Bolt11SendUnderpaying(Bolt11SendUnderpayingRequest) returns (Bolt11SendUnderpayingResponse);
931955
// Return a BOLT12 offer.
932956
rpc Bolt12Receive(Bolt12ReceiveRequest) returns (Bolt12ReceiveResponse);
933957
// Send a payment for a BOLT12 offer.

ldk-server-mcp/src/tools/handlers.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ use ldk_server_client::client::LdkServerClient;
1212
use ldk_server_client::ldk_server_grpc::api::{
1313
Bolt11ClaimForHashRequest, Bolt11FailForHashRequest, Bolt11ReceiveForHashRequest,
1414
Bolt11ReceiveRequest, Bolt11ReceiveVariableAmountViaJitChannelRequest,
15-
Bolt11ReceiveViaJitChannelRequest, Bolt11SendRequest, Bolt12ReceiveRequest, Bolt12SendRequest,
16-
CloseChannelRequest, ConnectPeerRequest, DecodeInvoiceRequest, DecodeOfferRequest,
17-
DisconnectPeerRequest, ExportPathfindingScoresRequest, ForceCloseChannelRequest,
18-
GetBalancesRequest, GetNodeInfoRequest, GetPaymentDetailsRequest, GraphGetChannelRequest,
19-
GraphGetNodeRequest, GraphListChannelsRequest, GraphListNodesRequest, ListChannelsRequest,
15+
Bolt11ReceiveViaJitChannelRequest, Bolt11SendRequest, Bolt11SendUnderpayingRequest,
16+
Bolt12ReceiveRequest, Bolt12SendRequest, CloseChannelRequest, ConnectPeerRequest,
17+
DecodeInvoiceRequest, DecodeOfferRequest, DisconnectPeerRequest,
18+
ExportPathfindingScoresRequest, ForceCloseChannelRequest, GetBalancesRequest,
19+
GetNodeInfoRequest, GetPaymentDetailsRequest, GraphGetChannelRequest, GraphGetNodeRequest,
20+
GraphListChannelsRequest, GraphListNodesRequest, ListChannelsRequest,
2021
ListForwardedPaymentsRequest, ListPaymentsRequest, ListPeersRequest, OnchainReceiveRequest,
2122
OnchainSendRequest, OpenChannelRequest, SignMessageRequest, SpliceInRequest, SpliceOutRequest,
2223
SpontaneousSendRequest, UnifiedSendRequest, UpdateChannelConfigRequest, VerifySignatureRequest,
@@ -191,6 +192,17 @@ pub async fn handle_bolt11_send(client: &LdkServerClient, args: Value) -> Result
191192
serialize_response(response)
192193
}
193194

195+
pub async fn handle_bolt11_send_underpaying(
196+
client: &LdkServerClient, args: Value,
197+
) -> Result<Value, McpError> {
198+
let request: Bolt11SendUnderpayingRequest =
199+
parse_request_with_route_parameters(args, |request: &mut Bolt11SendUnderpayingRequest| {
200+
&mut request.route_parameters
201+
})?;
202+
let response = client.bolt11_send_underpaying(request).await.map_err(McpError::from)?;
203+
serialize_response(response)
204+
}
205+
194206
pub async fn handle_bolt12_receive(
195207
client: &LdkServerClient, args: Value,
196208
) -> Result<Value, McpError> {

ldk-server-mcp/src/tools/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@ pub fn build_tool_registry() -> ToolRegistry {
139139
schema::bolt11_send_schema,
140140
|client, args| Box::pin(handlers::handle_bolt11_send(client, args)),
141141
),
142+
tool_spec(
143+
"bolt11_send_underpaying",
144+
"Underpay a BOLT11 Lightning invoice",
145+
schema::bolt11_send_underpaying_schema,
146+
|client, args| Box::pin(handlers::handle_bolt11_send_underpaying(client, args)),
147+
),
142148
tool_spec(
143149
"bolt12_receive",
144150
"Create a BOLT12 offer for receiving Lightning payments",

ldk-server-mcp/src/tools/schema.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,24 @@ pub fn bolt11_send_schema() -> Value {
311311
})
312312
}
313313

314+
pub fn bolt11_send_underpaying_schema() -> Value {
315+
json!({
316+
"type": "object",
317+
"properties": {
318+
"invoice": {
319+
"type": "string",
320+
"description": "A BOLT11 invoice string to underpay"
321+
},
322+
"amount_msat": {
323+
"type": "integer",
324+
"description": "Amount in millisatoshis to send. Must be less than the invoice amount"
325+
},
326+
"route_parameters": route_parameters_config_schema()
327+
},
328+
"required": ["invoice", "amount_msat"]
329+
})
330+
}
331+
314332
pub fn bolt12_receive_schema() -> Value {
315333
json!({
316334
"type": "object",

ldk-server-mcp/tests/integration.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::io::{BufRead, BufReader, Write};
1111

1212
use serde_json::{json, Value};
1313

14-
const NUM_TOOLS: usize = 37;
14+
const NUM_TOOLS: usize = 38;
1515
const EXPECTED_TOOLS: [&str; NUM_TOOLS] = [
1616
"bolt11_claim_for_hash",
1717
"bolt11_fail_for_hash",
@@ -20,6 +20,7 @@ const EXPECTED_TOOLS: [&str; NUM_TOOLS] = [
2020
"bolt11_receive_variable_amount_via_jit_channel",
2121
"bolt11_receive_via_jit_channel",
2222
"bolt11_send",
23+
"bolt11_send_underpaying",
2324
"bolt12_receive",
2425
"bolt12_send",
2526
"close_channel",
@@ -323,6 +324,14 @@ fn test_decode_invoice_unreachable() {
323324
assert_unreachable_tool("decode_invoice", json!({ "invoice": "lnbc1example" }));
324325
}
325326

327+
#[test]
328+
fn test_bolt11_send_underpaying_unreachable() {
329+
assert_unreachable_tool(
330+
"bolt11_send_underpaying",
331+
json!({ "invoice": "lnbc1example", "amount_msat": 1000 }),
332+
);
333+
}
334+
326335
#[test]
327336
fn test_decode_offer_unreachable() {
328337
assert_unreachable_tool("decode_offer", json!({ "offer": "lno1example" }));

0 commit comments

Comments
 (0)