Skip to content

Commit 1a3344b

Browse files
committed
Use ldk-node payment pagination
Read payment pages directly from ldk-node so the server does not store duplicate payment records. Keep the server database for forwarded-payment history. AI assistance: OpenAI Codex was used for this change.
1 parent d5c0a29 commit 1a3344b

5 files changed

Lines changed: 26 additions & 90 deletions

File tree

docs/configuration.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,12 @@ Two resolution methods are supported via the `mode` field:
209209
<network>/ # e.g., bitcoin/, regtest/, signet/
210210
api_key # API key
211211
ldk-server.log # Log file
212-
ldk_node_data.sqlite # LDK Node state (channels, on-chain wallet)
213-
ldk_server_data.sqlite # Payment and forwarding history
212+
ldk_node_data.sqlite # LDK Node state (channels, wallet, payments)
213+
ldk_server_data.sqlite # Forwarded-payment history
214214
```
215215

216216
The mnemonic is the node's master secret, required to recover on-chain funds. On first start,
217217
ldk-server generates a fresh 24-word BIP39 mnemonic at `<storage_dir>/keys_mnemonic` if the file
218-
does not already exist. `ldk_node_data.sqlite` holds channel state, both are required to recover
219-
channel funds. See [Operations - Backups](operations.md#backups) for backup guidance.
218+
does not already exist. `ldk_node_data.sqlite` holds channel state and payment history. Both files
219+
are required to recover channel funds. See [Operations - Backups](operations.md#backups) for backup
220+
guidance.

docs/operations.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ the following config to `/etc/logrotate.d/ldk-server` (adjust the log path to ma
5353
| File | Priority | Description |
5454
| -------------------------------------- | ------------ | -------------------------------------------------------------------------- |
5555
| `<storage_dir>/keys_mnemonic` | **Critical** | BIP39 mnemonic. Required to recover on-chain funds. Default for new installs. |
56-
| `<network_dir>/ldk_node_data.sqlite` | **Critical** | Channel state and on-chain wallet data. Required to recover channel funds. |
57-
| `<network_dir>/ldk_server_data.sqlite` | Nice-to-have | Payment and forwarding history |
56+
| `<network_dir>/ldk_node_data.sqlite` | **Critical** | Channel state, on-chain wallet data, and payment history. Required to recover channel funds. |
57+
| `<network_dir>/ldk_server_data.sqlite` | Nice-to-have | Forwarded-payment history |
5858

5959
### What is Reconstructable
6060

ldk-server/src/api/list_payments.rs

Lines changed: 8 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,58 +9,25 @@
99

1010
use std::sync::Arc;
1111

12-
use bytes::Bytes;
12+
use ldk_node::payment::PageToken as NodePageToken;
1313
use ldk_server_grpc::api::{ListPaymentsRequest, ListPaymentsResponse};
14-
use ldk_server_grpc::types::{PageToken, Payment};
15-
use prost::Message;
14+
use ldk_server_grpc::types::PageToken;
1615

1716
use crate::api::error::LdkServerError;
18-
use crate::api::error::LdkServerErrorCode::InternalServerError;
19-
use crate::io::persist::{
20-
PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
21-
};
2217
use crate::service::Context;
18+
use crate::util::proto_adapter::payment_to_proto;
2319

2420
pub(crate) async fn handle_list_payments_request(
2521
context: Arc<Context>, request: ListPaymentsRequest,
2622
) -> Result<ListPaymentsResponse, LdkServerError> {
27-
let page_token = request.page_token.map(|p| (p.token, p.index));
28-
let list_response = context
29-
.paginated_kv_store
30-
.list(
31-
PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
32-
PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
33-
page_token,
34-
)
35-
.map_err(|e| {
36-
LdkServerError::new(InternalServerError, format!("Failed to list payments: {}", e))
37-
})?;
23+
let page_token = request.page_token.map(|p| NodePageToken::new(p.token));
24+
let page = context.node.list_payments(page_token)?;
3825

39-
let mut payments: Vec<Payment> = Vec::with_capacity(list_response.keys.len());
40-
for key in list_response.keys {
41-
let payment_bytes = context
42-
.paginated_kv_store
43-
.read(
44-
PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
45-
PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
46-
&key,
47-
)
48-
.map_err(|e| {
49-
LdkServerError::new(
50-
InternalServerError,
51-
format!("Failed to read payment data: {}", e),
52-
)
53-
})?;
54-
let payment = Payment::decode(Bytes::from(payment_bytes)).map_err(|e| {
55-
LdkServerError::new(InternalServerError, format!("Failed to decode payment: {}", e))
56-
})?;
57-
payments.push(payment);
58-
}
5926
let response = ListPaymentsResponse {
60-
payments,
61-
next_page_token: list_response
27+
payments: page.payments.into_iter().map(payment_to_proto).collect(),
28+
next_page_token: page
6229
.next_page_token
63-
.map(|(token, index)| PageToken { token, index }),
30+
.map(|token| PageToken { token: token.to_string(), index: 0 }),
6431
};
6532
Ok(response)
6633
}

ldk-server/src/io/persist/mod.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,3 @@ pub(crate) mod sqlite_store;
1313
/// The forwarded payments will be persisted under this prefix.
1414
pub(crate) const FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE: &str = "forwarded_payments";
1515
pub(crate) const FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE: &str = "";
16-
17-
/// The payments will be persisted under this prefix.
18-
pub(crate) const PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE: &str = "payments";
19-
pub(crate) const PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE: &str = "";

ldk-server/src/main.rs

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ use crate::io::persist::paginated_kv_store::PaginatedKVStore;
4646
use crate::io::persist::sqlite_store::SqliteStore;
4747
use crate::io::persist::{
4848
FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
49-
FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
50-
PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
49+
FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
5150
};
5251
use crate::service::NodeService;
5352
use crate::util::config::{load_config, ArgsConfig, ChainSource};
@@ -504,7 +503,7 @@ fn main() {
504503
.map(node_to_proto_custom_tlv)
505504
.collect();
506505

507-
send_event_and_upsert_payment(
506+
send_payment_event(
508507
&payment_id,
509508
move |payment_ref| {
510509
event_envelope::Event::PaymentReceived(events::PaymentReceived {
@@ -515,7 +514,6 @@ fn main() {
515514
},
516515
&event_node,
517516
&event_sender,
518-
Arc::clone(&paginated_store),
519517
);
520518

521519
if let Some(metrics) = &metrics {
@@ -527,38 +525,36 @@ fn main() {
527525
let bolt12_invoice = bolt12_invoice.as_ref().and_then(|invoice| {
528526
invoice.bolt12_invoice().map(|i| i.encode().to_lower_hex_string())
529527
});
530-
send_event_and_upsert_payment(&payment_id,
528+
send_payment_event(&payment_id,
531529
|payment_ref| event_envelope::Event::PaymentSuccessful(events::PaymentSuccessful {
532530
payment_id: payment_id.to_string(),
533531
payment: Some(payment_ref.clone()),
534532
payment_preimage,
535533
bolt12_invoice,
536534
}),
537535
&event_node,
538-
&event_sender,
539-
Arc::clone(&paginated_store));
536+
&event_sender);
540537

541538
if let Some(metrics) = &metrics {
542539
metrics.update_payments_count(true);
543540
metrics.update_all_balances(&event_node);
544541
}
545542
},
546543
Event::PaymentFailed {payment_id, ..} => {
547-
send_event_and_upsert_payment(&payment_id,
544+
send_payment_event(&payment_id,
548545
|payment_ref| event_envelope::Event::PaymentFailed(events::PaymentFailed {
549546
payment_id: payment_id.to_string(),
550547
payment: Some(payment_ref.clone()),
551548
}),
552549
&event_node,
553-
&event_sender,
554-
Arc::clone(&paginated_store));
550+
&event_sender);
555551

556552
if let Some(metrics) = &metrics {
557553
metrics.update_payments_count(false);
558554
}
559555
},
560556
Event::PaymentClaimable { payment_id, custom_records, claim_deadline, .. } => {
561-
send_event_and_upsert_payment(
557+
send_payment_event(
562558
&payment_id,
563559
|payment_ref| {
564560
event_envelope::Event::PaymentClaimable(
@@ -572,7 +568,6 @@ fn main() {
572568
},
573569
&event_node,
574570
&event_sender,
575-
Arc::clone(&paginated_store),
576571
);
577572
},
578573
Event::PaymentForwarded {
@@ -706,10 +701,9 @@ fn main() {
706701
log::logger().flush();
707702
}
708703

709-
fn send_event_and_upsert_payment(
704+
fn send_payment_event(
710705
payment_id: &PaymentId, payment_to_event: impl FnOnce(&Payment) -> event_envelope::Event,
711706
event_node: &Node, event_sender: &broadcast::Sender<EventEnvelope>,
712-
paginated_store: Arc<dyn PaginatedKVStore>,
713707
) {
714708
match event_node.payment(payment_id) {
715709
Ok(Some(payment_details)) => {
@@ -720,7 +714,9 @@ fn send_event_and_upsert_payment(
720714
debug!("No event subscribers connected, skipping event: {e}");
721715
}
722716

723-
upsert_payment_details(event_node, Arc::clone(&paginated_store), &payment);
717+
if let Err(e) = event_node.event_handled() {
718+
error!("Failed to mark event as handled: {e}");
719+
}
724720
},
725721
Ok(None) => error!("Unable to find payment with payment ID: {payment_id}"),
726722
Err(e) => error!("Failed to retrieve payment with payment ID {payment_id}: {e}"),
@@ -879,30 +875,6 @@ fn closure_reason_details(
879875
}
880876
}
881877

882-
fn upsert_payment_details(
883-
event_node: &Node, paginated_store: Arc<dyn PaginatedKVStore>, payment: &Payment,
884-
) {
885-
let time =
886-
SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs() as i64;
887-
888-
match paginated_store.write(
889-
PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
890-
PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
891-
&payment.payment_id,
892-
time,
893-
&payment.encode_to_vec(),
894-
) {
895-
Ok(_) => {
896-
if let Err(e) = event_node.event_handled() {
897-
error!("Failed to mark event as handled: {e}");
898-
}
899-
},
900-
Err(e) => {
901-
error!("Failed to write payment to persistence: {e}");
902-
},
903-
}
904-
}
905-
906878
/// Loads the API key from a file, or generates a new one if it doesn't exist.
907879
/// The API key file is stored with 0400 permissions (read-only for owner).
908880
fn load_or_generate_api_key(storage_dir: &Path) -> std::io::Result<String> {

0 commit comments

Comments
 (0)