Skip to content

Commit a3a7481

Browse files
committed
Add ListForwardedPayments to client and CLI
1 parent b5b4133 commit a3a7481

File tree

3 files changed

+100
-52
lines changed

3 files changed

+100
-52
lines changed

ldk-server/ldk-server-cli/src/main.rs

Lines changed: 73 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,17 @@ use ldk_server_client::ldk_server_protos::api::{
2525
CloseChannelRequest, CloseChannelResponse, ForceCloseChannelRequest, ForceCloseChannelResponse,
2626
GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest, GetNodeInfoResponse,
2727
GetPaymentDetailsRequest, GetPaymentDetailsResponse, ListChannelsRequest, ListChannelsResponse,
28-
ListPaymentsRequest, ListPaymentsResponse, OnchainReceiveRequest, OnchainReceiveResponse,
29-
OnchainSendRequest, OnchainSendResponse, OpenChannelRequest, OpenChannelResponse,
30-
SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse,
28+
ListForwardedPaymentsRequest, ListPaymentsRequest, OnchainReceiveRequest,
29+
OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse, OpenChannelRequest,
30+
OpenChannelResponse, SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse,
3131
UpdateChannelConfigRequest, UpdateChannelConfigResponse,
3232
};
3333
use ldk_server_client::ldk_server_protos::types::{
3434
bolt11_invoice_description, Bolt11InvoiceDescription, ChannelConfig, PageToken,
3535
RouteParametersConfig,
3636
};
3737
use serde::Serialize;
38-
use types::CliListPaymentsResponse;
38+
use types::{CliListForwardedPaymentsResponse, CliListPaymentsResponse, CliPaginatedResponse};
3939

4040
mod config;
4141
mod types;
@@ -294,11 +294,22 @@ enum Commands {
294294
#[arg(help = "Page token to continue from a previous page (format: token:index)")]
295295
page_token: Option<String>,
296296
},
297+
#[command(about = "Get details of a specific payment by its payment ID")]
297298
GetPaymentDetails {
298299
#[arg(short, long, help = "The payment ID in hex-encoded form")]
299300
payment_id: String,
300301
},
301-
#[command(about = "Update the config for a previously opened channel")]
302+
#[command(about = "Retrieves list of all forwarded payments")]
303+
ListForwardedPayments {
304+
#[arg(
305+
short,
306+
long,
307+
help = "Fetch at least this many forwarded payments by iterating through multiple pages. Returns combined results with the last page token. If not provided, returns only a single page."
308+
)]
309+
number_of_payments: Option<u64>,
310+
#[arg(long, help = "Page token to continue from a previous page (format: token:index)")]
311+
page_token: Option<String>,
312+
},
302313
UpdateChannelConfig {
303314
#[arg(short, long, help = "The local user_channel_id of this channel")]
304315
user_channel_id: String,
@@ -589,14 +600,38 @@ async fn main() {
589600
.map(|token_str| parse_page_token(&token_str).unwrap_or_else(|e| handle_error(e)));
590601

591602
handle_response_result::<_, CliListPaymentsResponse>(
592-
handle_list_payments(client, number_of_payments, page_token).await,
603+
fetch_paginated(
604+
number_of_payments,
605+
page_token,
606+
|pt| client.list_payments(ListPaymentsRequest { page_token: pt }),
607+
|r| (r.payments, r.next_page_token),
608+
)
609+
.await,
593610
);
594611
},
595612
Commands::GetPaymentDetails { payment_id } => {
596613
handle_response_result::<_, GetPaymentDetailsResponse>(
597614
client.get_payment_details(GetPaymentDetailsRequest { payment_id }).await,
598615
);
599616
},
617+
Commands::ListForwardedPayments { number_of_payments, page_token } => {
618+
let page_token = page_token
619+
.map(|token_str| parse_page_token(&token_str).unwrap_or_else(|e| handle_error(e)));
620+
621+
handle_response_result::<_, CliListForwardedPaymentsResponse>(
622+
fetch_paginated(
623+
number_of_payments,
624+
page_token,
625+
|pt| {
626+
client.list_forwarded_payments(ListForwardedPaymentsRequest {
627+
page_token: pt,
628+
})
629+
},
630+
|r| (r.forwarded_payments, r.next_page_token),
631+
)
632+
.await,
633+
);
634+
},
600635
Commands::UpdateChannelConfig {
601636
user_channel_id,
602637
counterparty_node_id,
@@ -648,37 +683,40 @@ fn build_open_channel_config(
648683
})
649684
}
650685

651-
async fn handle_list_payments(
652-
client: LdkServerClient, number_of_payments: Option<u64>, initial_page_token: Option<PageToken>,
653-
) -> Result<ListPaymentsResponse, LdkServerError> {
654-
if let Some(count) = number_of_payments {
655-
list_n_payments(client, count, initial_page_token).await
656-
} else {
657-
// Fetch single page
658-
client.list_payments(ListPaymentsRequest { page_token: initial_page_token }).await
659-
}
660-
}
661-
662-
async fn list_n_payments(
663-
client: LdkServerClient, target_count: u64, initial_page_token: Option<PageToken>,
664-
) -> Result<ListPaymentsResponse, LdkServerError> {
665-
let mut payments = Vec::with_capacity(target_count as usize);
666-
let mut page_token = initial_page_token;
667-
let mut next_page_token;
668-
669-
loop {
670-
let response = client.list_payments(ListPaymentsRequest { page_token }).await?;
671-
672-
payments.extend(response.payments);
673-
next_page_token = response.next_page_token;
686+
async fn fetch_paginated<T, R, Fut>(
687+
target_count: Option<u64>, initial_page_token: Option<PageToken>,
688+
fetch_page: impl Fn(Option<PageToken>) -> Fut,
689+
extract: impl Fn(R) -> (Vec<T>, Option<PageToken>),
690+
) -> Result<CliPaginatedResponse<T>, LdkServerError>
691+
where
692+
Fut: std::future::Future<Output = Result<R, LdkServerError>>,
693+
{
694+
match target_count {
695+
Some(count) => {
696+
let mut items = Vec::with_capacity(count as usize);
697+
let mut page_token = initial_page_token;
698+
let mut next_page_token;
699+
700+
loop {
701+
let response = fetch_page(page_token).await?;
702+
let (new_items, new_next_page_token) = extract(response);
703+
items.extend(new_items);
704+
next_page_token = new_next_page_token;
705+
706+
if items.len() >= count as usize || next_page_token.is_none() {
707+
break;
708+
}
709+
page_token = next_page_token;
710+
}
674711

675-
if payments.len() >= target_count as usize || next_page_token.is_none() {
676-
break;
677-
}
678-
page_token = next_page_token;
712+
Ok(CliPaginatedResponse::new(items, next_page_token))
713+
},
714+
None => {
715+
let response = fetch_page(initial_page_token).await?;
716+
let (items, next_page_token) = extract(response);
717+
Ok(CliPaginatedResponse::new(items, next_page_token))
718+
},
679719
}
680-
681-
Ok(ListPaymentsResponse { payments, next_page_token })
682720
}
683721

684722
fn handle_response_result<Rs, Js>(response: Result<Rs, LdkServerError>)

ldk-server/ldk-server-cli/src/types.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,29 @@
1313
//! of API responses for CLI output. These wrappers ensure that the CLI's output
1414
//! format matches what users expect and what the CLI can parse back as input.
1515
16-
use ldk_server_client::ldk_server_protos::api::ListPaymentsResponse;
17-
use ldk_server_client::ldk_server_protos::types::{PageToken, Payment};
16+
use ldk_server_client::ldk_server_protos::types::{ForwardedPayment, PageToken, Payment};
1817
use serde::Serialize;
1918

20-
/// CLI-specific wrapper for ListPaymentsResponse that formats the page token
19+
/// CLI-specific wrapper for paginated responses that formats the page token
2120
/// as "token:idx" instead of a JSON object.
2221
#[derive(Debug, Clone, Serialize)]
23-
pub struct CliListPaymentsResponse {
24-
/// List of payments.
25-
pub payments: Vec<Payment>,
22+
pub struct CliPaginatedResponse<T> {
23+
/// List of items.
24+
pub list: Vec<T>,
2625
/// Next page token formatted as "token:idx", or None if no more pages.
2726
#[serde(skip_serializing_if = "Option::is_none")]
2827
pub next_page_token: Option<String>,
2928
}
3029

31-
impl From<ListPaymentsResponse> for CliListPaymentsResponse {
32-
fn from(response: ListPaymentsResponse) -> Self {
33-
let next_page_token = response.next_page_token.map(format_page_token);
34-
35-
CliListPaymentsResponse { payments: response.payments, next_page_token }
30+
impl<T> CliPaginatedResponse<T> {
31+
pub fn new(list: Vec<T>, next_page_token: Option<PageToken>) -> Self {
32+
Self { list, next_page_token: next_page_token.map(format_page_token) }
3633
}
3734
}
3835

36+
pub type CliListPaymentsResponse = CliPaginatedResponse<Payment>;
37+
pub type CliListForwardedPaymentsResponse = CliPaginatedResponse<ForwardedPayment>;
38+
3939
fn format_page_token(token: PageToken) -> String {
4040
format!("{}:{}", token.token, token.index)
4141
}

ldk-server/ldk-server-client/src/client.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@ use ldk_server_protos::api::{
1717
CloseChannelRequest, CloseChannelResponse, ForceCloseChannelRequest, ForceCloseChannelResponse,
1818
GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest, GetNodeInfoResponse,
1919
GetPaymentDetailsRequest, GetPaymentDetailsResponse, ListChannelsRequest, ListChannelsResponse,
20-
ListPaymentsRequest, ListPaymentsResponse, OnchainReceiveRequest, OnchainReceiveResponse,
21-
OnchainSendRequest, OnchainSendResponse, OpenChannelRequest, OpenChannelResponse,
22-
SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse,
23-
UpdateChannelConfigRequest, UpdateChannelConfigResponse,
20+
ListForwardedPaymentsRequest, ListForwardedPaymentsResponse, ListPaymentsRequest,
21+
ListPaymentsResponse, OnchainReceiveRequest, OnchainReceiveResponse, OnchainSendRequest,
22+
OnchainSendResponse, OpenChannelRequest, OpenChannelResponse, SpliceInRequest,
23+
SpliceInResponse, SpliceOutRequest, SpliceOutResponse, UpdateChannelConfigRequest,
24+
UpdateChannelConfigResponse,
2425
};
2526
use ldk_server_protos::endpoints::{
2627
BOLT11_RECEIVE_PATH, BOLT11_SEND_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH,
2728
CLOSE_CHANNEL_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_NODE_INFO_PATH,
28-
GET_PAYMENT_DETAILS_PATH, LIST_CHANNELS_PATH, LIST_PAYMENTS_PATH, ONCHAIN_RECEIVE_PATH,
29-
ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SPLICE_IN_PATH, SPLICE_OUT_PATH,
29+
GET_PAYMENT_DETAILS_PATH, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH,
30+
ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SPLICE_IN_PATH, SPLICE_OUT_PATH,
3031
UPDATE_CHANNEL_CONFIG_PATH,
3132
};
3233
use ldk_server_protos::error::{ErrorCode, ErrorResponse};
@@ -242,6 +243,15 @@ impl LdkServerClient {
242243
self.post_request(&request, &url).await
243244
}
244245

246+
/// Retrieves list of all forwarded payments.
247+
/// For API contract/usage, refer to docs for [`ListForwardedPaymentsRequest`] and [`ListForwardedPaymentsResponse`].
248+
pub async fn list_forwarded_payments(
249+
&self, request: ListForwardedPaymentsRequest,
250+
) -> Result<ListForwardedPaymentsResponse, LdkServerError> {
251+
let url = format!("https://{}/{LIST_FORWARDED_PAYMENTS_PATH}", self.base_url);
252+
self.post_request(&request, &url).await
253+
}
254+
245255
async fn post_request<Rq: Message, Rs: Message + Default>(
246256
&self, request: &Rq, url: &str,
247257
) -> Result<Rs, LdkServerError> {

0 commit comments

Comments
 (0)