Skip to content

Commit 1af5168

Browse files
authored
Merge pull request #242 from benthecarman/underpay
Add BOLT11 underpaying send RPC
2 parents 3e9e537 + a57a5e2 commit 1af5168

13 files changed

Lines changed: 309 additions & 50 deletions

File tree

docs/api-guide.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,17 @@ 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` | Send part of the amount for a fixed-amount BOLT11 invoice |
101+
102+
> [!NOTE]
103+
> `Bolt11SendUnderpaying` sends one part of a multi-part payment (MPP) for a fixed-amount
104+
> BOLT11 invoice. Other nodes must send compatible partial payments for the same invoice
105+
> until the combined amount equals the invoice amount. Without those payments, the
106+
> receiver holds the incomplete MPP payment and eventually fails it.
100107
101108
### BOLT11 Hodl Invoices
102109

e2e-tests/tests/e2e.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,58 @@ async fn test_cli_bolt11_send() {
855855
assert!(matches!(&event_b.event, Some(Event::PaymentReceived(_))));
856856
}
857857

858+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
859+
async fn test_cli_bolt11_send_underpaying_split_payment() {
860+
let bitcoind = TestBitcoind::new();
861+
let server_a = LdkServerHandle::start(&bitcoind).await;
862+
let server_b = LdkServerHandle::start(&bitcoind).await;
863+
let server_c = LdkServerHandle::start(&bitcoind).await;
864+
865+
// Subscribe to events on all three nodes before any payment is sent.
866+
let mut events_a = server_a.client().subscribe_events().await.unwrap();
867+
let mut events_b = server_b.client().subscribe_events().await.unwrap();
868+
let mut events_c = server_c.client().subscribe_events().await.unwrap();
869+
870+
// Each payer gets its own direct channel into the receiver. The channels are sized well
871+
// above the 50,000 sat HTLCs because LDK limits a channel's maximum HTLC size to a fraction
872+
// of its capacity.
873+
setup_funded_channel(&bitcoind, &server_a, &server_c, 300_000).await;
874+
setup_funded_channel(&bitcoind, &server_b, &server_c, 300_000).await;
875+
876+
// Create one invoice for the full amount that the two payers will jointly cover.
877+
let invoice_resp = server_c
878+
.client()
879+
.bolt11_receive(Bolt11ReceiveRequest {
880+
amount_msat: Some(100_000_000),
881+
description: Some(Bolt11InvoiceDescription {
882+
kind: Some(bolt11_invoice_description::Kind::Direct(
883+
"split payment test".to_string(),
884+
)),
885+
}),
886+
expiry_secs: 3600,
887+
})
888+
.await
889+
.unwrap();
890+
891+
// Both payers independently send half of the invoice amount.
892+
let output_a =
893+
run_cli(&server_a, &["bolt11-send-underpaying", &invoice_resp.invoice, "50000sat"]);
894+
let output_b =
895+
run_cli(&server_b, &["bolt11-send-underpaying", &invoice_resp.invoice, "50000sat"]);
896+
assert!(!output_a["payment_id"].as_str().unwrap().is_empty());
897+
assert!(!output_b["payment_id"].as_str().unwrap().is_empty());
898+
899+
// The receiver completes the payment only after both partial HTLCs arrive.
900+
let event_c = wait_for_event(&mut events_c, |e| matches!(e, Event::PaymentReceived(_))).await;
901+
assert!(matches!(&event_c.event, Some(Event::PaymentReceived(_))));
902+
903+
// Both payers complete their part of the payment successfully.
904+
let event_a = wait_for_event(&mut events_a, |e| matches!(e, Event::PaymentSuccessful(_))).await;
905+
assert!(matches!(&event_a.event, Some(Event::PaymentSuccessful(_))));
906+
let event_b = wait_for_event(&mut events_b, |e| matches!(e, Event::PaymentSuccessful(_))).await;
907+
assert!(matches!(&event_b.event, Some(Event::PaymentSuccessful(_))));
908+
}
909+
858910
#[tokio::test]
859911
async fn test_cli_pay() {
860912
let bitcoind = TestBitcoind::new();

ldk-server-cli/src/main.rs

Lines changed: 63 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,
@@ -247,6 +248,34 @@ enum Commands {
247248
)]
248249
max_channel_saturation_power_of_half: Option<u32>,
249250
},
251+
#[command(
252+
about = "Send part of a fixed-amount BOLT11 invoice. Other nodes must send partial payments for the same invoice until the combined amount equals the invoice amount"
253+
)]
254+
Bolt11SendUnderpaying {
255+
#[arg(help = "A fixed-amount BOLT11 invoice for a payment within the Lightning Network")]
256+
invoice: String,
257+
#[arg(
258+
help = "Amount from this payer, for example 50sat or 50000msat. Must be less than the invoice amount"
259+
)]
260+
amount: Amount,
261+
#[arg(
262+
long,
263+
help = "Maximum total routing fee, e.g. 50sat or 50000msat. Defaults to 1% of payment + 50 sats"
264+
)]
265+
max_total_routing_fee: Option<Amount>,
266+
#[arg(long, help = "Maximum total CLTV delta we accept for the route (default: 1008)")]
267+
max_total_cltv_expiry_delta: Option<u32>,
268+
#[arg(
269+
long,
270+
help = "Maximum number of paths that may be used by MPP payments (default: 10)"
271+
)]
272+
max_path_count: Option<u32>,
273+
#[arg(
274+
long,
275+
help = "Maximum share of a channel's total capacity to send over a channel, as a power of 1/2 (default: 2)"
276+
)]
277+
max_channel_saturation_power_of_half: Option<u32>,
278+
},
250279
#[command(about = "Return a BOLT12 offer for receiving payments")]
251280
Bolt12Receive {
252281
#[arg(help = "Description to attach along with the offer")]
@@ -771,6 +800,34 @@ async fn main() {
771800
.await,
772801
);
773802
},
803+
Commands::Bolt11SendUnderpaying {
804+
invoice,
805+
amount,
806+
max_total_routing_fee,
807+
max_total_cltv_expiry_delta,
808+
max_path_count,
809+
max_channel_saturation_power_of_half,
810+
} => {
811+
let amount_msat = amount.to_msat();
812+
let max_total_routing_fee_msat = max_total_routing_fee.map(|a| a.to_msat());
813+
let route_parameters = RouteParametersConfig {
814+
max_total_routing_fee_msat,
815+
max_total_cltv_expiry_delta: max_total_cltv_expiry_delta
816+
.unwrap_or(DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA),
817+
max_path_count: max_path_count.unwrap_or(DEFAULT_MAX_PATH_COUNT),
818+
max_channel_saturation_power_of_half: max_channel_saturation_power_of_half
819+
.unwrap_or(DEFAULT_MAX_CHANNEL_SATURATION_POWER_OF_HALF),
820+
};
821+
handle_response_result::<_, Bolt11SendUnderpayingResponse>(
822+
client
823+
.bolt11_send_underpaying(Bolt11SendUnderpayingRequest {
824+
invoice,
825+
amount_msat,
826+
route_parameters: Some(route_parameters),
827+
})
828+
.await,
829+
);
830+
},
774831
Commands::Bolt12Receive { description, amount, expiry_secs, quantity } => {
775832
let amount_msat = amount.map(|a| a.to_msat());
776833
handle_response_result::<_, Bolt12ReceiveResponse>(

ldk-server-client/src/client.rs

Lines changed: 36 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,17 @@ impl LdkServerClient {
242244
self.grpc_unary(&request, BOLT11_SEND_PATH).await
243245
}
244246

247+
/// Send part of the amount for a fixed-amount BOLT11 invoice.
248+
///
249+
/// Other nodes must send partial payments for the same invoice until the combined amount equals
250+
/// the invoice amount. Without those payments, the receiver holds the incomplete MPP payment
251+
/// and eventually fails it.
252+
pub async fn bolt11_send_underpaying(
253+
&self, request: Bolt11SendUnderpayingRequest,
254+
) -> Result<Bolt11SendUnderpayingResponse, LdkServerError> {
255+
self.grpc_unary(&request, BOLT11_SEND_UNDERPAYING_PATH).await
256+
}
257+
245258
/// Retrieve a new BOLT12 offer.
246259
pub async fn bolt12_receive(
247260
&self, request: Bolt12ReceiveRequest,

ldk-server-grpc/src/api.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,37 @@ pub struct Bolt11SendResponse {
374374
#[prost(string, tag = "1")]
375375
pub payment_id: ::prost::alloc::string::String,
376376
}
377+
/// Send part of the amount for a fixed-amount BOLT11 invoice.
378+
/// Other nodes must send partial payments for the same invoice until the combined amount equals the invoice amount.
379+
/// Without those payments, the receiver holds the incomplete MPP payment and eventually fails it.
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+
/// A fixed-amount BOLT11 invoice for a payment within the Lightning Network.
388+
#[prost(string, tag = "1")]
389+
pub invoice: ::prost::alloc::string::String,
390+
/// Amount in millisatoshis from this payer. 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+
}
377408
/// Returns a BOLT12 offer for the given amount, if specified.
378409
///
379410
/// 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: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,30 @@ message Bolt11SendResponse {
294294
string payment_id = 1;
295295
}
296296

297+
// Send part of the amount for a fixed-amount BOLT11 invoice.
298+
// Other nodes must send partial payments for the same invoice until the combined amount equals the invoice amount.
299+
// Without those payments, the receiver holds the incomplete MPP payment and eventually fails it.
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+
// A fixed-amount BOLT11 invoice for a payment within the Lightning Network.
304+
string invoice = 1;
305+
306+
// Amount in millisatoshis from this payer. 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+
297321
// Returns a BOLT12 offer for the given amount, if specified.
298322
//
299323
// See more:
@@ -930,6 +954,9 @@ service LightningNode {
930954
rpc Bolt11ReceiveVariableAmountViaJitChannel(Bolt11ReceiveVariableAmountViaJitChannelRequest) returns (Bolt11ReceiveVariableAmountViaJitChannelResponse);
931955
// Send a payment for a BOLT11 invoice.
932956
rpc Bolt11Send(Bolt11SendRequest) returns (Bolt11SendResponse);
957+
// Send part of the amount for a fixed-amount BOLT11 invoice.
958+
// Other nodes must send partial payments for the same invoice until the combined amount equals the invoice amount.
959+
rpc Bolt11SendUnderpaying(Bolt11SendUnderpayingRequest) returns (Bolt11SendUnderpayingResponse);
933960
// Return a BOLT12 offer.
934961
rpc Bolt12Receive(Bolt12ReceiveRequest) returns (Bolt12ReceiveResponse);
935962
// Send a payment for a BOLT12 offer.

0 commit comments

Comments
 (0)