Skip to content

Commit ec6d62a

Browse files
committed
Add ListForwardedPayments to client and CLI
1 parent 64ddf6f commit ec6d62a

File tree

3 files changed

+96
-49
lines changed

3 files changed

+96
-49
lines changed

ldk-server-cli/src/main.rs

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@ use ldk_server_client::ldk_server_protos::api::{
1919
CloseChannelRequest, CloseChannelResponse, ForceCloseChannelRequest, ForceCloseChannelResponse,
2020
GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest, GetNodeInfoResponse,
2121
GetPaymentDetailsRequest, GetPaymentDetailsResponse, ListChannelsRequest, ListChannelsResponse,
22-
ListPaymentsRequest, ListPaymentsResponse, OnchainReceiveRequest, OnchainReceiveResponse,
23-
OnchainSendRequest, OnchainSendResponse, OpenChannelRequest, OpenChannelResponse,
24-
SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse,
22+
ListForwardedPaymentsRequest, ListPaymentsRequest, OnchainReceiveRequest,
23+
OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse, OpenChannelRequest,
24+
OpenChannelResponse, SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse,
2525
UpdateChannelConfigRequest, UpdateChannelConfigResponse,
2626
};
2727
use ldk_server_client::ldk_server_protos::types::{
2828
bolt11_invoice_description, Bolt11InvoiceDescription, ChannelConfig, PageToken,
2929
RouteParametersConfig,
3030
};
3131
use serde::Serialize;
32-
use types::CliListPaymentsResponse;
32+
use types::{CliListForwardedPaymentsResponse, CliListPaymentsResponse, CliPaginatedResponse};
3333

3434
mod types;
3535

@@ -204,6 +204,16 @@ enum Commands {
204204
#[arg(short, long, help = "The payment ID in hex-encoded form")]
205205
payment_id: String,
206206
},
207+
ListForwardedPayments {
208+
#[arg(short, long)]
209+
#[arg(
210+
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."
211+
)]
212+
number_of_payments: Option<u64>,
213+
#[arg(long)]
214+
#[arg(help = "Page token to continue from a previous page (format: token:index)")]
215+
page_token: Option<String>,
216+
},
207217
UpdateChannelConfig {
208218
#[arg(short, long)]
209219
user_channel_id: String,
@@ -454,14 +464,38 @@ async fn main() {
454464
.map(|token_str| parse_page_token(&token_str).unwrap_or_else(|e| handle_error(e)));
455465

456466
handle_response_result::<_, CliListPaymentsResponse>(
457-
handle_list_payments(client, number_of_payments, page_token).await,
467+
fetch_paginated(
468+
number_of_payments,
469+
page_token,
470+
|pt| client.list_payments(ListPaymentsRequest { page_token: pt }),
471+
|r| (r.payments, r.next_page_token),
472+
)
473+
.await,
458474
);
459475
},
460476
Commands::GetPaymentDetails { payment_id } => {
461477
handle_response_result::<_, GetPaymentDetailsResponse>(
462478
client.get_payment_details(GetPaymentDetailsRequest { payment_id }).await,
463479
);
464480
},
481+
Commands::ListForwardedPayments { number_of_payments, page_token } => {
482+
let page_token = page_token
483+
.map(|token_str| parse_page_token(&token_str).unwrap_or_else(|e| handle_error(e)));
484+
485+
handle_response_result::<_, CliListForwardedPaymentsResponse>(
486+
fetch_paginated(
487+
number_of_payments,
488+
page_token,
489+
|pt| {
490+
client.list_forwarded_payments(ListForwardedPaymentsRequest {
491+
page_token: pt,
492+
})
493+
},
494+
|r| (r.forwarded_payments, r.next_page_token),
495+
)
496+
.await,
497+
);
498+
},
465499
Commands::UpdateChannelConfig {
466500
user_channel_id,
467501
counterparty_node_id,
@@ -513,37 +547,40 @@ fn build_open_channel_config(
513547
})
514548
}
515549

516-
async fn handle_list_payments(
517-
client: LdkServerClient, number_of_payments: Option<u64>, initial_page_token: Option<PageToken>,
518-
) -> Result<ListPaymentsResponse, LdkServerError> {
519-
if let Some(count) = number_of_payments {
520-
list_n_payments(client, count, initial_page_token).await
521-
} else {
522-
// Fetch single page
523-
client.list_payments(ListPaymentsRequest { page_token: initial_page_token }).await
524-
}
525-
}
526-
527-
async fn list_n_payments(
528-
client: LdkServerClient, target_count: u64, initial_page_token: Option<PageToken>,
529-
) -> Result<ListPaymentsResponse, LdkServerError> {
530-
let mut payments = Vec::with_capacity(target_count as usize);
531-
let mut page_token = initial_page_token;
532-
let mut next_page_token;
550+
async fn fetch_paginated<T, R, Fut>(
551+
target_count: Option<u64>, initial_page_token: Option<PageToken>,
552+
fetch_page: impl Fn(Option<PageToken>) -> Fut,
553+
extract: impl Fn(R) -> (Vec<T>, Option<PageToken>),
554+
) -> Result<CliPaginatedResponse<T>, LdkServerError>
555+
where
556+
Fut: std::future::Future<Output = Result<R, LdkServerError>>,
557+
{
558+
match target_count {
559+
Some(count) => {
560+
let mut items = Vec::with_capacity(count as usize);
561+
let mut page_token = initial_page_token;
562+
let mut next_page_token;
533563

534-
loop {
535-
let response = client.list_payments(ListPaymentsRequest { page_token }).await?;
564+
loop {
565+
let response = fetch_page(page_token).await?;
566+
let (new_items, new_next_page_token) = extract(response);
567+
items.extend(new_items);
568+
next_page_token = new_next_page_token;
536569

537-
payments.extend(response.payments);
538-
next_page_token = response.next_page_token;
570+
if items.len() >= count as usize || next_page_token.is_none() {
571+
break;
572+
}
573+
page_token = next_page_token;
574+
}
539575

540-
if payments.len() >= target_count as usize || next_page_token.is_none() {
541-
break;
542-
}
543-
page_token = next_page_token;
576+
Ok(CliPaginatedResponse::new(items, next_page_token))
577+
},
578+
None => {
579+
let response = fetch_page(initial_page_token).await?;
580+
let (items, next_page_token) = extract(response);
581+
Ok(CliPaginatedResponse::new(items, next_page_token))
582+
},
544583
}
545-
546-
Ok(ListPaymentsResponse { payments, next_page_token })
547584
}
548585

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

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-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)