Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
run: cargo build --verbose --color always
- name: Check clippy if on msrv
if: matrix.msrv
run: cargo clippy --all-features -- -D warnings
run: cargo clippy --all-features -- -D warnings -A clippy::drop_non_drop
- name: Test on Rust ${{ matrix.toolchain }}
run: cargo test
- name: Cargo check release on Rust ${{ matrix.toolchain }}
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ldk-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2021"

[dependencies]
ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "d1bbf978c8b7abe87ae2e40793556c1fe4e7ea49" }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891" }
serde = { version = "1.0.203", default-features = false, features = ["derive"] }
hyper = { version = "1", default-features = false, features = ["server", "http1"] }
http-body-util = { version = "0.1", default-features = false }
Expand Down
53 changes: 12 additions & 41 deletions ldk-server/src/api/list_forwarded_payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,67 +7,38 @@
// You may not use this file except in accordance with one or both of these
// licenses.

use bytes::Bytes;
use ldk_server_protos::api::{ListForwardedPaymentsRequest, ListForwardedPaymentsResponse};
use ldk_server_protos::types::{ForwardedPayment, PageToken};
use prost::Message;

use crate::api::error::LdkServerError;
use crate::api::error::LdkServerErrorCode::InternalServerError;
use crate::io::persist::{
FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
};
use crate::io::persist;
use crate::service::Context;
use crate::util::proto_adapter::stored_forwarded_payment_to_proto;

pub(crate) fn handle_list_forwarded_payments_request(
context: Context, request: ListForwardedPaymentsRequest,
) -> Result<ListForwardedPaymentsResponse, LdkServerError> {
let page_token = request.page_token.map(|p| (p.token, p.index));
let list_response = context
.paginated_kv_store
.list(
FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
page_token,
)

let list_response = persist::list_forwarded_payments(context.paginated_kv_store, page_token)
.map_err(|e| {
LdkServerError::new(
InternalServerError,
format!("Failed to list forwarded payments: {}", e),
)
})?;

let mut forwarded_payments: Vec<ForwardedPayment> =
Vec::with_capacity(list_response.keys.len());
for key in list_response.keys {
let forwarded_payment_bytes = context
.paginated_kv_store
.read(
FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
&key,
)
.map_err(|e| {
LdkServerError::new(
InternalServerError,
format!("Failed to read forwarded payment data: {}", e),
)
})?;
let forwarded_payment = ForwardedPayment::decode(Bytes::from(forwarded_payment_bytes))
.map_err(|e| {
LdkServerError::new(
InternalServerError,
format!("Failed to decode forwarded payment: {}", e),
)
})?;
forwarded_payments.push(forwarded_payment);
}
let response = ListForwardedPaymentsResponse {
let forwarded_payments: Vec<ForwardedPayment> = list_response
.forwarded_payments
.into_iter()
.map(stored_forwarded_payment_to_proto)
.collect();

Ok(ListForwardedPaymentsResponse {
forwarded_payments,
next_page_token: list_response
.next_page_token
.map(|(token, index)| PageToken { token, index }),
};
Ok(response)
})
}
45 changes: 9 additions & 36 deletions ldk-server/src/api/list_payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,31 @@
// You may not use this file except in accordance with one or both of these
// licenses.

use bytes::Bytes;
use ldk_server_protos::api::{ListPaymentsRequest, ListPaymentsResponse};
use ldk_server_protos::types::{PageToken, Payment};
use prost::Message;

use crate::api::error::LdkServerError;
use crate::api::error::LdkServerErrorCode::InternalServerError;
use crate::io::persist::{
PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
};
use crate::io::persist;
use crate::service::Context;
use crate::util::proto_adapter::payment_to_proto;

pub(crate) fn handle_list_payments_request(
context: Context, request: ListPaymentsRequest,
) -> Result<ListPaymentsResponse, LdkServerError> {
let page_token = request.page_token.map(|p| (p.token, p.index));
let list_response = context
.paginated_kv_store
.list(
PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
page_token,
)
.map_err(|e| {

let list_response =
persist::list_payments(context.paginated_kv_store, page_token).map_err(|e| {
LdkServerError::new(InternalServerError, format!("Failed to list payments: {}", e))
})?;

let mut payments: Vec<Payment> = Vec::with_capacity(list_response.keys.len());
for key in list_response.keys {
let payment_bytes = context
.paginated_kv_store
.read(
PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
&key,
)
.map_err(|e| {
LdkServerError::new(
InternalServerError,
format!("Failed to read payment data: {}", e),
)
})?;
let payment = Payment::decode(Bytes::from(payment_bytes)).map_err(|e| {
LdkServerError::new(InternalServerError, format!("Failed to decode payment: {}", e))
})?;
payments.push(payment);
}
let response = ListPaymentsResponse {
let payments: Vec<Payment> = list_response.payments.into_iter().map(payment_to_proto).collect();

Ok(ListPaymentsResponse {
payments,
next_page_token: list_response
.next_page_token
.map(|(token, index)| PageToken { token, index }),
};
Ok(response)
})
}
84 changes: 84 additions & 0 deletions ldk-server/src/io/persist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@

pub(crate) mod paginated_kv_store;
pub(crate) mod sqlite_store;
pub(crate) mod types;

use std::io;
use std::sync::Arc;

use ldk_node::lightning::util::ser::Readable;
use ldk_node::payment::PaymentDetails;

use paginated_kv_store::PaginatedKVStore;
use types::StoredForwardedPayment;

/// The forwarded payments will be persisted under this prefix.
pub(crate) const FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE: &str = "forwarded_payments";
Expand All @@ -17,3 +27,77 @@ pub(crate) const FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE: &str = "";
/// The payments will be persisted under this prefix.
pub(crate) const PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE: &str = "payments";
pub(crate) const PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE: &str = "";

/// Response from listing payments.
pub(crate) struct ListPaymentsResponse {
pub payments: Vec<PaymentDetails>,
pub next_page_token: Option<(String, i64)>,
}

/// Response from listing forwarded payments.
pub(crate) struct ListForwardedPaymentsResponse {
pub forwarded_payments: Vec<StoredForwardedPayment>,
pub next_page_token: Option<(String, i64)>,
}

/// List and deserialize payments from the store.
pub(crate) fn list_payments(
store: Arc<dyn PaginatedKVStore>, page_token: Option<(String, i64)>,
) -> Result<ListPaymentsResponse, io::Error> {
let list_response = store.list(
PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
page_token,
)?;

let mut payments = Vec::with_capacity(list_response.keys.len());
for key in list_response.keys {
let payment_bytes = store.read(
PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
&key,
)?;

let mut cursor = io::Cursor::new(&payment_bytes);
let payment = PaymentDetails::read(&mut cursor).map_err(|e| {
io::Error::new(io::ErrorKind::InvalidData, format!("Failed to decode payment: {}", e))
})?;
payments.push(payment);
}

Ok(ListPaymentsResponse { payments, next_page_token: list_response.next_page_token })
}

/// List and deserialize forwarded payments from the store.
pub(crate) fn list_forwarded_payments(
store: Arc<dyn PaginatedKVStore>, page_token: Option<(String, i64)>,
) -> Result<ListForwardedPaymentsResponse, io::Error> {
let list_response = store.list(
FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
page_token,
)?;

let mut forwarded_payments = Vec::with_capacity(list_response.keys.len());
for key in list_response.keys {
let payment_bytes = store.read(
FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,
FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
&key,
)?;

let mut cursor = io::Cursor::new(&payment_bytes);
let payment = StoredForwardedPayment::read(&mut cursor).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("Failed to decode forwarded payment: {}", e),
)
})?;
forwarded_payments.push(payment);
}

Ok(ListForwardedPaymentsResponse {
forwarded_payments,
next_page_token: list_response.next_page_token,
})
}
58 changes: 58 additions & 0 deletions ldk-server/src/io/persist/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// This file is Copyright its original authors, visible in version control
// history.
//
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// You may not use this file except in accordance with one or both of these
// licenses.

//! Storage types for persisting payment data.
//!
//! These types are separate from the proto definitions to decouple the storage format
//! from the API format. This allows the storage schema to evolve independently and
//! provides better control over backwards compatibility.

use ldk_node::lightning::impl_writeable_tlv_based;
use ldk_node::lightning::routing::gossip::NodeId;

/// A forwarded payment stored in the database.
///
/// This type is needed because ldk-node doesn't persist forwarded payment events -
/// it only emits them. We need our own storage type to track forwarding history.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StoredForwardedPayment {
/// The channel id of the incoming channel.
pub prev_channel_id: [u8; 32],
/// The channel id of the outgoing channel.
pub next_channel_id: [u8; 32],
/// The user_channel_id of the incoming channel.
pub prev_user_channel_id: u128,
/// The user_channel_id of the outgoing channel.
pub next_user_channel_id: Option<u128>,
/// The node id of the previous node.
pub prev_node_id: NodeId,
/// The node id of the next node.
pub next_node_id: NodeId,
/// The total fee earned in millisatoshis.
pub total_fee_earned_msat: Option<u64>,
/// The skimmed fee in millisatoshis.
pub skimmed_fee_msat: Option<u64>,
/// Whether the payment was claimed from an on-chain transaction.
pub claim_from_onchain_tx: bool,
/// The outbound amount forwarded in millisatoshis.
pub outbound_amount_forwarded_msat: Option<u64>,
}

impl_writeable_tlv_based!(StoredForwardedPayment, {
(0, prev_channel_id, required),
(2, next_channel_id, required),
(4, prev_user_channel_id, required),
(6, next_user_channel_id, option),
(8, prev_node_id, required),
(10, next_node_id, required),
(12, total_fee_earned_msat, option),
(14, skimmed_fee_msat, option),
(16, claim_from_onchain_tx, required),
(18, outbound_amount_forwarded_msat, option),
});
Loading