diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3c39bdb..44ba9db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,6 @@ jobs: - name: Pin packages to allow for MSRV if: matrix.msrv run: | - cargo update -p hashlink --precise "0.8.2" --verbose # hashlink 0.8.3 requires hashbrown 0.14, requiring 1.64.0 cargo update -p regex --precise "1.9.6" --verbose # regex 1.10.0 requires rustc 1.65.0 cargo update -p home --precise "0.5.5" --verbose # home v0.5.9 requires rustc 1.70 or newer cargo update -p tokio --precise "1.38.1" --verbose # tokio v1.39.0 requires rustc 1.70 or newer diff --git a/Cargo.lock b/Cargo.lock index 507668c..71226d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,12 +253,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" -[[package]] -name = "bitcoin-private" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" - [[package]] name = "bitcoin-units" version = "0.1.2" @@ -275,15 +269,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" -[[package]] -name = "bitcoin_hashes" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" -dependencies = [ - "bitcoin-private", -] - [[package]] name = "bitcoin_hashes" version = "0.14.0" @@ -447,9 +432,9 @@ dependencies = [ [[package]] name = "fallible-iterator" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fallible-streaming-iterator" @@ -621,26 +606,20 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.13.2" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash 0.8.11", ] -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - [[package]] name = "hashlink" -version = "0.8.2" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0761a1b9491c4f2e3d66aa0f62d0fba0af9a0e2852e4d48ea506632a4b56e6aa" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "hashbrown 0.13.2", + "hashbrown 0.14.5", ] [[package]] @@ -900,9 +879,8 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "ldk-node" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f50eff8ed03ff8847930eba51dc975fc12b10e96619cf9aabd9ab6f50db93ada" +version = "0.4.2" +source = "git+https://github.com/lightningdevkit/ldk-node.git?rev=2095d878be10923845bcdd1dd039ab0e670e723d#2095d878be10923845bcdd1dd039ab0e670e723d" dependencies = [ "base64 0.22.1", "bdk_chain", @@ -989,9 +967,9 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libsqlite3-sys" -version = "0.25.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" dependencies = [ "cc", "pkg-config", @@ -1488,11 +1466,11 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.28.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -1572,7 +1550,7 @@ version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "bitcoin_hashes 0.12.0", + "bitcoin_hashes 0.14.0", "rand", "secp256k1-sys", "serde", diff --git a/ldk-server-protos/src/api.rs b/ldk-server-protos/src/api.rs index 673734d..43172d9 100644 --- a/ldk-server-protos/src/api.rs +++ b/ldk-server-protos/src/api.rs @@ -337,6 +337,44 @@ pub struct ListPaymentsResponse { #[prost(message, repeated, tag = "1")] pub payments: ::prost::alloc::vec::Vec, } +/// Retrieves list of all forwarded payments. +/// See more: +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ListForwardedPaymentsRequest { + /// `page_token` is a pagination token. + /// + /// To query for the first page, `page_token` must not be specified. + /// + /// For subsequent pages, use the value that was returned as `next_page_token` in the previous + /// page's response. + #[prost(message, optional, tag = "1")] + pub page_token: ::core::option::Option, +} +/// The response `content` for the `ListForwardedPayments` API, when HttpStatusCode is OK (200). +/// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ListForwardedPaymentsResponse { + /// List of forwarded payments. + #[prost(message, repeated, tag = "1")] + pub forwarded_payments: ::prost::alloc::vec::Vec, + /// `next_page_token` is a pagination token, used to retrieve the next page of results. + /// Use this value to query for next-page of paginated operation, by specifying + /// this value as the `page_token` in the next request. + /// + /// If `next_page_token` is `None`, then the "last page" of results has been processed and + /// there is no more data to be retrieved. + /// + /// If `next_page_token` is not `None`, it does not necessarily mean that there is more data in the + /// result set. The only way to know when you have reached the end of the result set is when + /// `next_page_token` is `None`. + /// + /// **Caution**: Clients must not assume a specific number of records to be present in a page for + /// paginated response. + #[prost(message, optional, tag = "2")] + pub next_page_token: ::core::option::Option, +} /// Retrieves an overview of all known balances. /// See more: #[allow(clippy::derive_partial_eq_without_eq)] diff --git a/ldk-server-protos/src/proto/api.proto b/ldk-server-protos/src/proto/api.proto index e9a26ba..69348a2 100644 --- a/ldk-server-protos/src/proto/api.proto +++ b/ldk-server-protos/src/proto/api.proto @@ -319,6 +319,40 @@ message ListPaymentsResponse { repeated types.Payment payments = 1; } +// Retrieves list of all forwarded payments. +// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.Event.html#variant.PaymentForwarded +message ListForwardedPaymentsRequest { + // `page_token` is a pagination token. + // + // To query for the first page, `page_token` must not be specified. + // + // For subsequent pages, use the value that was returned as `next_page_token` in the previous + // page's response. + optional types.PageToken page_token = 1; +} + +// The response `content` for the `ListForwardedPayments` API, when HttpStatusCode is OK (200). +// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. +message ListForwardedPaymentsResponse { + // List of forwarded payments. + repeated types.ForwardedPayment forwarded_payments = 1; + + // `next_page_token` is a pagination token, used to retrieve the next page of results. + // Use this value to query for next-page of paginated operation, by specifying + // this value as the `page_token` in the next request. + // + // If `next_page_token` is `None`, then the "last page" of results has been processed and + // there is no more data to be retrieved. + // + // If `next_page_token` is not `None`, it does not necessarily mean that there is more data in the + // result set. The only way to know when you have reached the end of the result set is when + // `next_page_token` is `None`. + // + // **Caution**: Clients must not assume a specific number of records to be present in a page for + // paginated response. + optional types.PageToken next_page_token = 2; +} + // Retrieves an overview of all known balances. // See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.list_balances message GetBalancesRequest {} diff --git a/ldk-server-protos/src/proto/types.proto b/ldk-server-protos/src/proto/types.proto index cc7433b..b2ccd57 100644 --- a/ldk-server-protos/src/proto/types.proto +++ b/ldk-server-protos/src/proto/types.proto @@ -161,6 +161,51 @@ enum PaymentStatus { FAILED = 2; } +// A forwarded payment through our node. +// See more: https://docs.rs/ldk-node/latest/ldk_node/enum.Event.html#variant.PaymentForwarded +message ForwardedPayment{ + // The channel id of the incoming channel between the previous node and us. + string prev_channel_id = 1; + + // The channel id of the outgoing channel between the next node and us. + string next_channel_id = 2; + + // The `user_channel_id` of the incoming channel between the previous node and us. + string prev_user_channel_id = 3; + + // The `user_channel_id` of the outgoing channel between the next node and us. + // This will be `None` if the payment was settled via an on-chain transaction. + // See the caveat described for the `total_fee_earned_msat` field. + optional string next_user_channel_id = 4; + + // The total fee, in milli-satoshis, which was earned as a result of the payment. + // + // Note that if we force-closed the channel over which we forwarded an HTLC while the HTLC was pending, the amount the + // next hop claimed will have been rounded down to the nearest whole satoshi. Thus, the fee calculated here may be + // higher than expected as we still claimed the full value in millisatoshis from the source. + // In this case, `claim_from_onchain_tx` will be set. + // + // If the channel which sent us the payment has been force-closed, we will claim the funds via an on-chain transaction. + // In that case we do not yet know the on-chain transaction fees which we will spend and will instead set this to `None`. + optional uint64 total_fee_earned_msat = 5; + + // The share of the total fee, in milli-satoshis, which was withheld in addition to the forwarding fee. + // This will only be set if we forwarded an intercepted HTLC with less than the expected amount. This means our + // counterparty accepted to receive less than the invoice amount. + // + // The caveat described above the `total_fee_earned_msat` field applies here as well. + optional uint64 skimmed_fee_msat = 6; + + // If this is true, the forwarded HTLC was claimed by our counterparty via an on-chain transaction. + bool claim_from_onchain_tx = 7; + + // The final amount forwarded, in milli-satoshis, after the fee is deducted. + // + // The caveat described above the `total_fee_earned_msat` field applies here as well. + optional uint64 outbound_amount_forwarded_msat = 8; + +} + message Channel { // The channel ID (prior to funding transaction generation, this is a random 32-byte // identifier, afterwards this is the transaction ID of the funding transaction XOR the @@ -579,3 +624,9 @@ message AwaitingThresholdConfirmations { // The amount, in satoshis, of the output being swept. uint64 amount_satoshis = 5; } + +// Token used to determine start of next page in paginated APIs. +message PageToken { + string token = 1; + int64 index = 2; +} diff --git a/ldk-server-protos/src/types.rs b/ldk-server-protos/src/types.rs index 82b7168..d7f8874 100644 --- a/ldk-server-protos/src/types.rs +++ b/ldk-server-protos/src/types.rs @@ -165,6 +165,52 @@ pub struct LspFeeLimits { #[prost(uint64, optional, tag = "2")] pub max_proportional_opening_fee_ppm_msat: ::core::option::Option, } +/// A forwarded payment through our node. +/// See more: +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ForwardedPayment { + /// The channel id of the incoming channel between the previous node and us. + #[prost(string, tag = "1")] + pub prev_channel_id: ::prost::alloc::string::String, + /// The channel id of the outgoing channel between the next node and us. + #[prost(string, tag = "2")] + pub next_channel_id: ::prost::alloc::string::String, + /// The `user_channel_id` of the incoming channel between the previous node and us. + #[prost(string, tag = "3")] + pub prev_user_channel_id: ::prost::alloc::string::String, + /// The `user_channel_id` of the outgoing channel between the next node and us. + /// This will be `None` if the payment was settled via an on-chain transaction. + /// See the caveat described for the `total_fee_earned_msat` field. + #[prost(string, optional, tag = "4")] + pub next_user_channel_id: ::core::option::Option<::prost::alloc::string::String>, + /// The total fee, in milli-satoshis, which was earned as a result of the payment. + /// + /// Note that if we force-closed the channel over which we forwarded an HTLC while the HTLC was pending, the amount the + /// next hop claimed will have been rounded down to the nearest whole satoshi. Thus, the fee calculated here may be + /// higher than expected as we still claimed the full value in millisatoshis from the source. + /// In this case, `claim_from_onchain_tx` will be set. + /// + /// If the channel which sent us the payment has been force-closed, we will claim the funds via an on-chain transaction. + /// In that case we do not yet know the on-chain transaction fees which we will spend and will instead set this to `None`. + #[prost(uint64, optional, tag = "5")] + pub total_fee_earned_msat: ::core::option::Option, + /// The share of the total fee, in milli-satoshis, which was withheld in addition to the forwarding fee. + /// This will only be set if we forwarded an intercepted HTLC with less than the expected amount. This means our + /// counterparty accepted to receive less than the invoice amount. + /// + /// The caveat described above the `total_fee_earned_msat` field applies here as well. + #[prost(uint64, optional, tag = "6")] + pub skimmed_fee_msat: ::core::option::Option, + /// If this is true, the forwarded HTLC was claimed by our counterparty via an on-chain transaction. + #[prost(bool, tag = "7")] + pub claim_from_onchain_tx: bool, + /// The final amount forwarded, in milli-satoshis, after the fee is deducted. + /// + /// The caveat described above the `total_fee_earned_msat` field applies here as well. + #[prost(uint64, optional, tag = "8")] + pub outbound_amount_forwarded_msat: ::core::option::Option, +} #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Channel { @@ -647,6 +693,15 @@ pub struct AwaitingThresholdConfirmations { #[prost(uint64, tag = "5")] pub amount_satoshis: u64, } +/// Token used to determine start of next page in paginated APIs. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PageToken { + #[prost(string, tag = "1")] + pub token: ::prost::alloc::string::String, + #[prost(int64, tag = "2")] + pub index: i64, +} /// Represents the direction of a payment. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] diff --git a/ldk-server/Cargo.toml b/ldk-server/Cargo.toml index ceebf1c..f7fd18e 100644 --- a/ldk-server/Cargo.toml +++ b/ldk-server/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -ldk-node = { version = "0.4.0", default-features = false } +ldk-node = { git = "https://github.com/lightningdevkit/ldk-node.git", rev = "2095d878be10923845bcdd1dd039ab0e670e723d" } serde = { version = "1.0.203", default-features = false, features = ["derive"] } serde_json = { version = "1.0.118", default-features = false } hyper = { version = "1", default-features = false, features = ["server", "http1"] } @@ -15,7 +15,5 @@ prost = { version = "0.11.6", default-features = false, features = ["std"] } ldk-server-protos = { path = "../ldk-server-protos" } bytes = "1.4.0" hex = { package = "hex-conservative", version = "0.2.1", default-features = false } -rusqlite = { version = "0.28.0", features = ["bundled"] } - -[dev-dependencies] +rusqlite = { version = "0.31.0", features = ["bundled"] } rand = "0.8.5" diff --git a/ldk-server/src/api/bolt11_receive.rs b/ldk-server/src/api/bolt11_receive.rs index ff2c03f..0f11cd2 100644 --- a/ldk-server/src/api/bolt11_receive.rs +++ b/ldk-server/src/api/bolt11_receive.rs @@ -1,17 +1,19 @@ -use ldk_node::Node; +use crate::service::Context; use ldk_server_protos::api::{Bolt11ReceiveRequest, Bolt11ReceiveResponse}; -use std::sync::Arc; pub(crate) const BOLT11_RECEIVE_PATH: &str = "Bolt11Receive"; pub(crate) fn handle_bolt11_receive_request( - node: Arc, request: Bolt11ReceiveRequest, + context: Context, request: Bolt11ReceiveRequest, ) -> Result { let invoice = match request.amount_msat { - Some(amount_msat) => { - node.bolt11_payment().receive(amount_msat, &request.description, request.expiry_secs)? - }, - None => node + Some(amount_msat) => context.node.bolt11_payment().receive( + amount_msat, + &request.description, + request.expiry_secs, + )?, + None => context + .node .bolt11_payment() .receive_variable_amount(&request.description, request.expiry_secs)?, }; diff --git a/ldk-server/src/api/bolt11_send.rs b/ldk-server/src/api/bolt11_send.rs index 8e12096..f73f0db 100644 --- a/ldk-server/src/api/bolt11_send.rs +++ b/ldk-server/src/api/bolt11_send.rs @@ -1,21 +1,22 @@ +use crate::service::Context; use bytes::Bytes; use ldk_node::lightning_invoice::Bolt11Invoice; -use ldk_node::Node; use ldk_server_protos::api::{Bolt11SendRequest, Bolt11SendResponse}; use std::str::FromStr; -use std::sync::Arc; pub(crate) const BOLT11_SEND_PATH: &str = "Bolt11Send"; pub(crate) fn handle_bolt11_send_request( - node: Arc, request: Bolt11SendRequest, + context: Context, request: Bolt11SendRequest, ) -> Result { let invoice = Bolt11Invoice::from_str(&request.invoice.as_str()) .map_err(|_| ldk_node::NodeError::InvalidInvoice)?; let payment_id = match request.amount_msat { - None => node.bolt11_payment().send(&invoice, None), - Some(amount_msat) => node.bolt11_payment().send_using_amount(&invoice, amount_msat, None), + None => context.node.bolt11_payment().send(&invoice, None), + Some(amount_msat) => { + context.node.bolt11_payment().send_using_amount(&invoice, amount_msat, None) + }, }?; let response = Bolt11SendResponse { payment_id: Bytes::from(payment_id.0.to_vec()) }; diff --git a/ldk-server/src/api/bolt12_receive.rs b/ldk-server/src/api/bolt12_receive.rs index 8cac1b7..2503987 100644 --- a/ldk-server/src/api/bolt12_receive.rs +++ b/ldk-server/src/api/bolt12_receive.rs @@ -1,20 +1,20 @@ -use ldk_node::Node; +use crate::service::Context; use ldk_server_protos::api::{Bolt12ReceiveRequest, Bolt12ReceiveResponse}; -use std::sync::Arc; pub(crate) const BOLT12_RECEIVE_PATH: &str = "Bolt12Receive"; pub(crate) fn handle_bolt12_receive_request( - node: Arc, request: Bolt12ReceiveRequest, + context: Context, request: Bolt12ReceiveRequest, ) -> Result { let offer = match request.amount_msat { - Some(amount_msat) => node.bolt12_payment().receive( + Some(amount_msat) => context.node.bolt12_payment().receive( amount_msat, &request.description, request.expiry_secs, request.quantity, )?, - None => node + None => context + .node .bolt12_payment() .receive_variable_amount(&request.description, request.expiry_secs)?, }; diff --git a/ldk-server/src/api/bolt12_send.rs b/ldk-server/src/api/bolt12_send.rs index ea0f85c..95f3a48 100644 --- a/ldk-server/src/api/bolt12_send.rs +++ b/ldk-server/src/api/bolt12_send.rs @@ -1,21 +1,20 @@ +use crate::service::Context; use bytes::Bytes; use ldk_node::lightning::offers::offer::Offer; -use ldk_node::Node; use ldk_server_protos::api::{Bolt12SendRequest, Bolt12SendResponse}; use std::str::FromStr; -use std::sync::Arc; pub(crate) const BOLT12_SEND_PATH: &str = "Bolt12Send"; pub(crate) fn handle_bolt12_send_request( - node: Arc, request: Bolt12SendRequest, + context: Context, request: Bolt12SendRequest, ) -> Result { let offer = Offer::from_str(&request.offer.as_str()).map_err(|_| ldk_node::NodeError::InvalidOffer)?; let payment_id = match request.amount_msat { - None => node.bolt12_payment().send(&offer, request.quantity, request.payer_note), - Some(amount_msat) => node.bolt12_payment().send_using_amount( + None => context.node.bolt12_payment().send(&offer, request.quantity, request.payer_note), + Some(amount_msat) => context.node.bolt12_payment().send_using_amount( &offer, amount_msat, request.quantity, diff --git a/ldk-server/src/api/close_channel.rs b/ldk-server/src/api/close_channel.rs index d373535..b1892d7 100644 --- a/ldk-server/src/api/close_channel.rs +++ b/ldk-server/src/api/close_channel.rs @@ -1,13 +1,13 @@ +use crate::service::Context; use ldk_node::bitcoin::secp256k1::PublicKey; -use ldk_node::{Node, UserChannelId}; +use ldk_node::UserChannelId; use ldk_server_protos::api::{CloseChannelRequest, CloseChannelResponse}; use std::str::FromStr; -use std::sync::Arc; pub(crate) const CLOSE_CHANNEL_PATH: &str = "CloseChannel"; pub(crate) fn handle_close_channel_request( - node: Arc, request: CloseChannelRequest, + context: Context, request: CloseChannelRequest, ) -> Result { //TODO: Should this be string? let mut user_channel_id_bytes = [0u8; 16]; @@ -17,12 +17,12 @@ pub(crate) fn handle_close_channel_request( .map_err(|_| ldk_node::NodeError::InvalidPublicKey)?; match request.force_close { - Some(true) => node.force_close_channel( + Some(true) => context.node.force_close_channel( &user_channel_id, counterparty_node_id, request.force_close_reason, )?, - _ => node.close_channel(&user_channel_id, counterparty_node_id)?, + _ => context.node.close_channel(&user_channel_id, counterparty_node_id)?, }; let response = CloseChannelResponse {}; diff --git a/ldk-server/src/api/get_balances.rs b/ldk-server/src/api/get_balances.rs index 535aa5f..e236976 100644 --- a/ldk-server/src/api/get_balances.rs +++ b/ldk-server/src/api/get_balances.rs @@ -1,14 +1,13 @@ +use crate::service::Context; use crate::util::proto_adapter::{lightning_balance_to_proto, pending_sweep_balance_to_proto}; -use ldk_node::Node; use ldk_server_protos::api::{GetBalancesRequest, GetBalancesResponse}; -use std::sync::Arc; pub(crate) const GET_BALANCES: &str = "GetBalances"; pub(crate) fn handle_get_balances_request( - node: Arc, _request: GetBalancesRequest, + context: Context, _request: GetBalancesRequest, ) -> Result { - let balance_details = node.list_balances(); + let balance_details = context.node.list_balances(); let response = GetBalancesResponse { total_onchain_balance_sats: balance_details.total_onchain_balance_sats, diff --git a/ldk-server/src/api/get_node_info.rs b/ldk-server/src/api/get_node_info.rs index ed612f8..59ace45 100644 --- a/ldk-server/src/api/get_node_info.rs +++ b/ldk-server/src/api/get_node_info.rs @@ -1,14 +1,13 @@ -use ldk_node::Node; +use crate::service::Context; use ldk_server_protos::api::{GetNodeInfoRequest, GetNodeInfoResponse}; use ldk_server_protos::types::BestBlock; -use std::sync::Arc; pub(crate) const GET_NODE_INFO: &str = "GetNodeInfo"; pub(crate) fn handle_get_node_info_request( - node: Arc, _request: GetNodeInfoRequest, + context: Context, _request: GetNodeInfoRequest, ) -> Result { - let node_status = node.status(); + let node_status = context.node.status(); let best_block = BestBlock { block_hash: node_status.current_best_block.block_hash.to_string(), @@ -16,7 +15,7 @@ pub(crate) fn handle_get_node_info_request( }; let response = GetNodeInfoResponse { - node_id: node.node_id().to_string(), + node_id: context.node.node_id().to_string(), current_best_block: Some(best_block), latest_lightning_wallet_sync_timestamp: node_status.latest_lightning_wallet_sync_timestamp, latest_onchain_wallet_sync_timestamp: node_status.latest_onchain_wallet_sync_timestamp, diff --git a/ldk-server/src/api/get_payment_details.rs b/ldk-server/src/api/get_payment_details.rs index 6a43df2..620e7d0 100644 --- a/ldk-server/src/api/get_payment_details.rs +++ b/ldk-server/src/api/get_payment_details.rs @@ -1,19 +1,18 @@ +use crate::service::Context; use crate::util::proto_adapter::payment_to_proto; use hex::FromHex; use ldk_node::lightning::ln::channelmanager::PaymentId; -use ldk_node::Node; use ldk_server_protos::api::{GetPaymentDetailsRequest, GetPaymentDetailsResponse}; -use std::sync::Arc; pub(crate) const GET_PAYMENT_DETAILS_PATH: &str = "GetPaymentDetails"; pub(crate) fn handle_get_payment_details_request( - node: Arc, request: GetPaymentDetailsRequest, + context: Context, request: GetPaymentDetailsRequest, ) -> Result { let payment_id_bytes = <[u8; PaymentId::LENGTH]>::from_hex(&request.payment_id) .map_err(|_| ldk_node::NodeError::InvalidPaymentId)?; - let payment_details = node.payment(&PaymentId(payment_id_bytes)); + let payment_details = context.node.payment(&PaymentId(payment_id_bytes)); let response = GetPaymentDetailsResponse { payment: payment_details.map(|payment| payment_to_proto(payment)), diff --git a/ldk-server/src/api/list_channels.rs b/ldk-server/src/api/list_channels.rs index 318eb38..d55f26a 100644 --- a/ldk-server/src/api/list_channels.rs +++ b/ldk-server/src/api/list_channels.rs @@ -1,14 +1,13 @@ +use crate::service::Context; use crate::util::proto_adapter::channel_to_proto; -use ldk_node::Node; use ldk_server_protos::api::{ListChannelsRequest, ListChannelsResponse}; -use std::sync::Arc; pub(crate) const LIST_CHANNELS_PATH: &str = "ListChannels"; pub(crate) fn handle_list_channels_request( - node: Arc, _request: ListChannelsRequest, + context: Context, _request: ListChannelsRequest, ) -> Result { - let channels = node.list_channels().into_iter().map(|c| channel_to_proto(c)).collect(); + let channels = context.node.list_channels().into_iter().map(|c| channel_to_proto(c)).collect(); let response = ListChannelsResponse { channels }; Ok(response) diff --git a/ldk-server/src/api/list_forwarded_payments.rs b/ldk-server/src/api/list_forwarded_payments.rs new file mode 100644 index 0000000..3903f7c --- /dev/null +++ b/ldk-server/src/api/list_forwarded_payments.rs @@ -0,0 +1,47 @@ +use crate::io::{ + FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE, + FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, +}; +use crate::service::Context; +use bytes::Bytes; +use ldk_server_protos::api::{ListForwardedPaymentsRequest, ListForwardedPaymentsResponse}; +use ldk_server_protos::types::{ForwardedPayment, PageToken}; +use prost::Message; + +pub(crate) const LIST_FORWARDED_PAYMENTS_PATH: &str = "ListForwardedPayments"; + +pub(crate) fn handle_list_forwarded_payments_request( + context: Context, request: ListForwardedPaymentsRequest, +) -> Result { + 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, + ) + .map_err(|_| ldk_node::NodeError::ConnectionFailed)?; + + let mut forwarded_payments: Vec = vec![]; + 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(|_| ldk_node::NodeError::ConnectionFailed)?; + let forwarded_payment = ForwardedPayment::decode(Bytes::from(forwarded_payment_bytes)) + .map_err(|_| ldk_node::NodeError::ConnectionFailed)?; + forwarded_payments.push(forwarded_payment); + } + let response = ListForwardedPaymentsResponse { + forwarded_payments, + next_page_token: list_response + .next_page_token + .map(|(token, index)| PageToken { token, index }), + }; + Ok(response) +} diff --git a/ldk-server/src/api/list_payments.rs b/ldk-server/src/api/list_payments.rs index 89da42d..04ef699 100644 --- a/ldk-server/src/api/list_payments.rs +++ b/ldk-server/src/api/list_payments.rs @@ -1,14 +1,13 @@ +use crate::service::Context; use crate::util::proto_adapter::payment_to_proto; -use ldk_node::Node; use ldk_server_protos::api::{ListPaymentsRequest, ListPaymentsResponse}; -use std::sync::Arc; pub(crate) const LIST_PAYMENTS_PATH: &str = "ListPayments"; pub(crate) fn handle_list_payments_request( - node: Arc, _request: ListPaymentsRequest, + context: Context, _request: ListPaymentsRequest, ) -> Result { - let payments = node.list_payments().into_iter().map(|p| payment_to_proto(p)).collect(); + let payments = context.node.list_payments().into_iter().map(|p| payment_to_proto(p)).collect(); let response = ListPaymentsResponse { payments }; Ok(response) diff --git a/ldk-server/src/api/mod.rs b/ldk-server/src/api/mod.rs index 7d0770d..c1b0fa2 100644 --- a/ldk-server/src/api/mod.rs +++ b/ldk-server/src/api/mod.rs @@ -8,6 +8,7 @@ pub(crate) mod get_balances; pub(crate) mod get_node_info; pub(crate) mod get_payment_details; pub(crate) mod list_channels; +pub(crate) mod list_forwarded_payments; pub(crate) mod list_payments; pub(crate) mod onchain_receive; pub(crate) mod onchain_send; diff --git a/ldk-server/src/api/onchain_receive.rs b/ldk-server/src/api/onchain_receive.rs index b7d7676..bfc9af6 100644 --- a/ldk-server/src/api/onchain_receive.rs +++ b/ldk-server/src/api/onchain_receive.rs @@ -1,12 +1,12 @@ -use ldk_node::Node; +use crate::service::Context; use ldk_server_protos::api::{OnchainReceiveRequest, OnchainReceiveResponse}; -use std::sync::Arc; pub(crate) const ONCHAIN_RECEIVE_PATH: &str = "OnchainReceive"; pub(crate) fn handle_onchain_receive_request( - node: Arc, _request: OnchainReceiveRequest, + context: Context, _request: OnchainReceiveRequest, ) -> Result { - let response = - OnchainReceiveResponse { address: node.onchain_payment().new_address()?.to_string() }; + let response = OnchainReceiveResponse { + address: context.node.onchain_payment().new_address()?.to_string(), + }; Ok(response) } diff --git a/ldk-server/src/api/onchain_send.rs b/ldk-server/src/api/onchain_send.rs index fd1ef5f..35cd848 100644 --- a/ldk-server/src/api/onchain_send.rs +++ b/ldk-server/src/api/onchain_send.rs @@ -1,23 +1,25 @@ +use crate::service::Context; use ldk_node::bitcoin::Address; -use ldk_node::Node; use ldk_server_protos::api::{OnchainSendRequest, OnchainSendResponse}; use std::str::FromStr; -use std::sync::Arc; pub(crate) const ONCHAIN_SEND_PATH: &str = "OnchainSend"; pub(crate) fn handle_onchain_send_request( - node: Arc, request: OnchainSendRequest, + context: Context, request: OnchainSendRequest, ) -> Result { let address = Address::from_str(&request.address) .map_err(|_| ldk_node::NodeError::InvalidAddress)? - .require_network(node.config().network) + .require_network(context.node.config().network) .map_err(|_| ldk_node::NodeError::InvalidAddress)?; let txid = match (request.amount_sats, request.send_all) { (Some(amount_sats), None) => { - node.onchain_payment().send_to_address(&address, amount_sats)? + context.node.onchain_payment().send_to_address(&address, amount_sats)? + }, + // Retain existing api behaviour to not retain reserves on `send_all_to_address`. + (None, Some(true)) => { + context.node.onchain_payment().send_all_to_address(&address, false)? }, - (None, Some(true)) => node.onchain_payment().send_all_to_address(&address)?, _ => return Err(ldk_node::NodeError::InvalidAmount), }; let response = OnchainSendResponse { txid: txid.to_string() }; diff --git a/ldk-server/src/api/open_channel.rs b/ldk-server/src/api/open_channel.rs index 927ea0b..d7a002d 100644 --- a/ldk-server/src/api/open_channel.rs +++ b/ldk-server/src/api/open_channel.rs @@ -1,15 +1,14 @@ +use crate::service::Context; use bytes::Bytes; use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::lightning::ln::msgs::SocketAddress; -use ldk_node::Node; use ldk_server_protos::api::{OpenChannelRequest, OpenChannelResponse}; use std::str::FromStr; -use std::sync::Arc; pub(crate) const OPEN_CHANNEL_PATH: &str = "OpenChannel"; pub(crate) fn handle_open_channel( - node: Arc, request: OpenChannelRequest, + context: Context, request: OpenChannelRequest, ) -> Result { let node_id = PublicKey::from_str(&request.node_pubkey) .map_err(|_| ldk_node::NodeError::InvalidPublicKey)?; @@ -17,7 +16,7 @@ pub(crate) fn handle_open_channel( .map_err(|_| ldk_node::NodeError::InvalidSocketAddress)?; let user_channel_id = if request.announce_channel { - node.open_announced_channel( + context.node.open_announced_channel( node_id, address, request.channel_amount_sats, @@ -26,7 +25,7 @@ pub(crate) fn handle_open_channel( None, )? } else { - node.open_channel( + context.node.open_channel( node_id, address, request.channel_amount_sats, diff --git a/ldk-server/src/api/update_channel_config.rs b/ldk-server/src/api/update_channel_config.rs index 0d6c510..c183ebb 100644 --- a/ldk-server/src/api/update_channel_config.rs +++ b/ldk-server/src/api/update_channel_config.rs @@ -1,21 +1,22 @@ +use crate::service::Context; use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::config::{ChannelConfig, MaxDustHTLCExposure}; -use ldk_node::{Node, UserChannelId}; +use ldk_node::UserChannelId; use ldk_server_protos::api::{UpdateChannelConfigRequest, UpdateChannelConfigResponse}; use ldk_server_protos::types::channel_config::MaxDustHtlcExposure; use std::str::FromStr; -use std::sync::Arc; pub(crate) const UPDATE_CHANNEL_CONFIG_PATH: &str = "UpdateChannelConfig"; pub(crate) fn handle_update_channel_config_request( - node: Arc, request: UpdateChannelConfigRequest, + context: Context, request: UpdateChannelConfigRequest, ) -> Result { let user_channel_id: u128 = request.user_channel_id.parse().map_err(|_| ldk_node::NodeError::InvalidChannelId)?; //FIXME: Use ldk/ldk-node's partial config update api. - let current_config = node + let current_config = context + .node .list_channels() .into_iter() .find(|c| c.user_channel_id.0 == user_channel_id) @@ -27,12 +28,15 @@ pub(crate) fn handle_update_channel_config_request( let counterparty_node_id = PublicKey::from_str(&request.counterparty_node_id) .map_err(|_| ldk_node::NodeError::InvalidPublicKey)?; - node.update_channel_config( - &UserChannelId(user_channel_id), - counterparty_node_id, - updated_channel_config, - ) - .map_err(ldk_node::NodeError::from)?; + + context + .node + .update_channel_config( + &UserChannelId(user_channel_id), + counterparty_node_id, + updated_channel_config, + ) + .map_err(ldk_node::NodeError::from)?; Ok(UpdateChannelConfigResponse {}) } diff --git a/ldk-server/src/io/mod.rs b/ldk-server/src/io/mod.rs index 6cda7ce..52975ac 100644 --- a/ldk-server/src/io/mod.rs +++ b/ldk-server/src/io/mod.rs @@ -1,3 +1,7 @@ pub(crate) mod paginated_kv_store; pub(crate) mod sqlite_store; pub(crate) mod utils; + +/// The forwarded payments will be persisted under this prefix. +pub(crate) const FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE: &str = "forwarded_payments"; +pub(crate) const FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE: &str = ""; diff --git a/ldk-server/src/main.rs b/ldk-server/src/main.rs index c4d0e8d..4cdd219 100644 --- a/ldk-server/src/main.rs +++ b/ldk-server/src/main.rs @@ -13,11 +13,22 @@ use tokio::signal::unix::SignalKind; use hyper::server::conn::http1; use hyper_util::rt::TokioIo; +use crate::io::paginated_kv_store::PaginatedKVStore; +use crate::io::sqlite_store::SqliteStore; +use crate::io::{ + FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE, + FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, +}; use crate::util::config::load_config; +use crate::util::proto_adapter::forwarded_payment_to_proto; +use hex::DisplayHex; use ldk_node::config::Config; +use prost::Message; +use rand::Rng; use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::Arc; +use std::time::{SystemTime, UNIX_EPOCH}; const USAGE_GUIDE: &str = "Usage: ldk-server "; @@ -44,7 +55,7 @@ fn main() { let config_file = load_config(Path::new(arg)).expect("Invalid configuration file."); ldk_node_config.log_level = LogLevel::Trace; - ldk_node_config.storage_dir_path = config_file.storage_dir_path; + ldk_node_config.storage_dir_path = config_file.storage_dir_path.clone(); ldk_node_config.listening_addresses = Some(vec![config_file.listening_addr]); ldk_node_config.network = config_file.network; @@ -75,6 +86,15 @@ fn main() { }, }; + let paginated_store = + Arc::new(match SqliteStore::new(PathBuf::from(config_file.storage_dir_path), None, None) { + Ok(store) => store, + Err(e) => { + eprintln!("Failed to create SqliteStore: {:?}", e); + std::process::exit(-1); + }, + }); + println!("Starting up..."); match node.start_with_runtime(Arc::clone(&runtime)) { Ok(()) => {}, @@ -111,28 +131,80 @@ fn main() { "CHANNEL_PENDING: {} from counterparty {}", channel_id, counterparty_node_id ); + event_node.event_handled(); }, Event::ChannelReady { channel_id, counterparty_node_id, .. } => { println!( "CHANNEL_READY: {} from counterparty {:?}", channel_id, counterparty_node_id ); + event_node.event_handled(); }, Event::PaymentReceived { payment_id, payment_hash, amount_msat } => { println!( "PAYMENT_RECEIVED: with id {:?}, hash {}, amount_msat {}", payment_id, payment_hash, amount_msat ); + event_node.event_handled(); + }, + Event::PaymentForwarded { + prev_channel_id, + next_channel_id, + prev_user_channel_id, + next_user_channel_id, + total_fee_earned_msat, + skimmed_fee_msat, + claim_from_onchain_tx, + outbound_amount_forwarded_msat + } => { + + println!("PAYMENT_FORWARDED: with outbound_amount_forwarded_msat {}, total_fee_earned_msat: {}, inbound channel: {}, outbound channel: {}", + outbound_amount_forwarded_msat.unwrap_or(0), total_fee_earned_msat.unwrap_or(0), prev_channel_id, next_channel_id + ); + + let forwarded_payment = forwarded_payment_to_proto( + prev_channel_id, + next_channel_id, + prev_user_channel_id, + next_user_channel_id, + total_fee_earned_msat, + skimmed_fee_msat, + claim_from_onchain_tx, + outbound_amount_forwarded_msat + ); + + // We don't expose this payment-id to the user, it is a temporary measure to generate + // some unique identifiers until we have forwarded-payment-id available in ldk. + // Currently, this is the expected user handling behaviour for forwarded payments. + let mut forwarded_payment_id = [0u8;32]; + rand::thread_rng().fill(&mut forwarded_payment_id); + + let forwarded_payment_creation_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs() as i64; + + match paginated_store.write(FORWARDED_PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE,FORWARDED_PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, + &forwarded_payment_id.to_lower_hex_string(), + forwarded_payment_creation_time, + &forwarded_payment.encode_to_vec(), + ) { + Ok(_) => { + event_node.event_handled(); + } + Err(e) => { + println!("Failed to write forwarded payment to persistence: {}", e); + } + } + }, + _ => { + event_node.event_handled(); }, - _ => {}, } - event_node.event_handled(); + }, res = rest_svc_listener.accept() => { match res { Ok((stream, _)) => { let io_stream = TokioIo::new(stream); - let node_service = NodeService::new(Arc::clone(&node)); + let node_service = NodeService::new(Arc::clone(&node), Arc::clone(&paginated_store) as Arc); runtime.spawn(async move { if let Err(err) = http1::Builder::new().serve_connection(io_stream, node_service).await { eprintln!("Failed to serve connection: {}", err); diff --git a/ldk-server/src/service.rs b/ldk-server/src/service.rs index 1062cbc..16ec58d 100644 --- a/ldk-server/src/service.rs +++ b/ldk-server/src/service.rs @@ -7,10 +7,6 @@ use hyper::{Request, Response, StatusCode}; use prost::Message; -use std::future::Future; -use std::pin::Pin; -use std::sync::Arc; - use crate::api::bolt11_receive::{handle_bolt11_receive_request, BOLT11_RECEIVE_PATH}; use crate::api::bolt11_send::{handle_bolt11_send_request, BOLT11_SEND_PATH}; use crate::api::bolt12_receive::{handle_bolt12_receive_request, BOLT12_RECEIVE_PATH}; @@ -22,6 +18,9 @@ use crate::api::get_payment_details::{ handle_get_payment_details_request, GET_PAYMENT_DETAILS_PATH, }; use crate::api::list_channels::{handle_list_channels_request, LIST_CHANNELS_PATH}; +use crate::api::list_forwarded_payments::{ + handle_list_forwarded_payments_request, LIST_FORWARDED_PAYMENTS_PATH, +}; use crate::api::list_payments::{handle_list_payments_request, LIST_PAYMENTS_PATH}; use crate::api::onchain_receive::{handle_onchain_receive_request, ONCHAIN_RECEIVE_PATH}; use crate::api::onchain_send::{handle_onchain_send_request, ONCHAIN_SEND_PATH}; @@ -29,51 +28,77 @@ use crate::api::open_channel::{handle_open_channel, OPEN_CHANNEL_PATH}; use crate::api::update_channel_config::{ handle_update_channel_config_request, UPDATE_CHANNEL_CONFIG_PATH, }; +use crate::io::paginated_kv_store::PaginatedKVStore; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; #[derive(Clone)] pub struct NodeService { node: Arc, + paginated_kv_store: Arc, } impl NodeService { - pub(crate) fn new(node: Arc) -> Self { - Self { node } + pub(crate) fn new( + node: Arc, paginated_kv_store: Arc, + ) -> Self { + Self { node, paginated_kv_store } } } +pub(crate) struct Context { + pub(crate) node: Arc, + pub(crate) paginated_kv_store: Arc, +} + impl Service> for NodeService { type Response = Response>; type Error = hyper::Error; type Future = Pin> + Send>>; fn call(&self, req: Request) -> Self::Future { - let node = Arc::clone(&self.node); + let context = Context { + node: Arc::clone(&self.node), + paginated_kv_store: Arc::clone(&self.paginated_kv_store), + }; // Exclude '/' from path pattern matching. match &req.uri().path()[1..] { - GET_NODE_INFO => Box::pin(handle_request(node, req, handle_get_node_info_request)), - GET_BALANCES => Box::pin(handle_request(node, req, handle_get_balances_request)), + GET_NODE_INFO => Box::pin(handle_request(context, req, handle_get_node_info_request)), + GET_BALANCES => Box::pin(handle_request(context, req, handle_get_balances_request)), ONCHAIN_RECEIVE_PATH => { - Box::pin(handle_request(node, req, handle_onchain_receive_request)) + Box::pin(handle_request(context, req, handle_onchain_receive_request)) + }, + ONCHAIN_SEND_PATH => { + Box::pin(handle_request(context, req, handle_onchain_send_request)) }, - ONCHAIN_SEND_PATH => Box::pin(handle_request(node, req, handle_onchain_send_request)), BOLT11_RECEIVE_PATH => { - Box::pin(handle_request(node, req, handle_bolt11_receive_request)) + Box::pin(handle_request(context, req, handle_bolt11_receive_request)) }, - BOLT11_SEND_PATH => Box::pin(handle_request(node, req, handle_bolt11_send_request)), + BOLT11_SEND_PATH => Box::pin(handle_request(context, req, handle_bolt11_send_request)), BOLT12_RECEIVE_PATH => { - Box::pin(handle_request(node, req, handle_bolt12_receive_request)) + Box::pin(handle_request(context, req, handle_bolt12_receive_request)) + }, + BOLT12_SEND_PATH => Box::pin(handle_request(context, req, handle_bolt12_send_request)), + OPEN_CHANNEL_PATH => Box::pin(handle_request(context, req, handle_open_channel)), + CLOSE_CHANNEL_PATH => { + Box::pin(handle_request(context, req, handle_close_channel_request)) + }, + LIST_CHANNELS_PATH => { + Box::pin(handle_request(context, req, handle_list_channels_request)) }, - BOLT12_SEND_PATH => Box::pin(handle_request(node, req, handle_bolt12_send_request)), - OPEN_CHANNEL_PATH => Box::pin(handle_request(node, req, handle_open_channel)), - CLOSE_CHANNEL_PATH => Box::pin(handle_request(node, req, handle_close_channel_request)), - LIST_CHANNELS_PATH => Box::pin(handle_request(node, req, handle_list_channels_request)), UPDATE_CHANNEL_CONFIG_PATH => { - Box::pin(handle_request(node, req, handle_update_channel_config_request)) + Box::pin(handle_request(context, req, handle_update_channel_config_request)) }, GET_PAYMENT_DETAILS_PATH => { - Box::pin(handle_request(node, req, handle_get_payment_details_request)) + Box::pin(handle_request(context, req, handle_get_payment_details_request)) + }, + LIST_PAYMENTS_PATH => { + Box::pin(handle_request(context, req, handle_list_payments_request)) + }, + LIST_FORWARDED_PAYMENTS_PATH => { + Box::pin(handle_request(context, req, handle_list_forwarded_payments_request)) }, - LIST_PAYMENTS_PATH => Box::pin(handle_request(node, req, handle_list_payments_request)), path => { let error = format!("Unknown request: {}", path).into_bytes(); Box::pin(async { @@ -91,14 +116,14 @@ impl Service> for NodeService { async fn handle_request< T: Message + Default, R: Message, - F: Fn(Arc, T) -> Result, + F: Fn(Context, T) -> Result, >( - node: Arc, request: Request, handler: F, + context: Context, request: Request, handler: F, ) -> Result<>>::Response, hyper::Error> { // TODO: we should bound the amount of data we read to avoid allocating too much memory. let bytes = request.into_body().collect().await?.to_bytes(); match T::decode(bytes) { - Ok(request) => match handler(node, request) { + Ok(request) => match handler(context, request) { Ok(response) => Ok(Response::builder() .body(Full::new(Bytes::from(response.encode_to_vec()))) // unwrap safety: body only errors when previous chained calls failed. diff --git a/ldk-server/src/util/proto_adapter.rs b/ldk-server/src/util/proto_adapter.rs index 5d5b7d5..143a4d9 100644 --- a/ldk-server/src/util/proto_adapter.rs +++ b/ldk-server/src/util/proto_adapter.rs @@ -1,8 +1,9 @@ use bytes::Bytes; use hex::prelude::*; use ldk_node::config::{ChannelConfig, MaxDustHTLCExposure}; +use ldk_node::lightning::ln::types::ChannelId; use ldk_node::payment::{PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus}; -use ldk_node::{ChannelDetails, LightningBalance, PendingSweepBalance}; +use ldk_node::{ChannelDetails, Event, LightningBalance, PendingSweepBalance, UserChannelId}; use ldk_server_protos::types::lightning_balance::BalanceType::{ ClaimableAwaitingConfirmations, ClaimableOnChannelClose, ContentiousClaimable, CounterpartyRevokedOutputClaimable, MaybePreimageClaimableHtlc, MaybeTimeoutClaimableHtlc, @@ -13,7 +14,7 @@ use ldk_server_protos::types::payment_kind::Kind::{ use ldk_server_protos::types::pending_sweep_balance::BalanceType::{ AwaitingThresholdConfirmations, BroadcastAwaitingConfirmation, PendingBroadcast, }; -use ldk_server_protos::types::{Channel, LspFeeLimits, OutPoint, Payment}; +use ldk_server_protos::types::{Channel, ForwardedPayment, LspFeeLimits, OutPoint, Payment}; pub(crate) fn channel_to_proto(channel: ChannelDetails) -> Channel { Channel { @@ -320,3 +321,21 @@ pub(crate) fn pending_sweep_balance_to_proto( }, } } + +pub(crate) fn forwarded_payment_to_proto( + prev_channel_id: ChannelId, next_channel_id: ChannelId, + prev_user_channel_id: Option, next_user_channel_id: Option, + total_fee_earned_msat: Option, skimmed_fee_msat: Option, claim_from_onchain_tx: bool, + outbound_amount_forwarded_msat: Option, +) -> ForwardedPayment { + ForwardedPayment { + prev_channel_id: prev_channel_id.to_string(), + next_channel_id: next_channel_id.to_string(), + prev_user_channel_id: prev_user_channel_id.expect("").0.to_string(), + next_user_channel_id: next_user_channel_id.map(|u| u.0.to_string()), + total_fee_earned_msat, + skimmed_fee_msat, + claim_from_onchain_tx, + outbound_amount_forwarded_msat, + } +}