diff --git a/server/src/api/get_payment_details.rs b/server/src/api/get_payment_details.rs new file mode 100644 index 0000000..52008a3 --- /dev/null +++ b/server/src/api/get_payment_details.rs @@ -0,0 +1,23 @@ +use crate::util::proto_adapter::payment_to_proto; +use hex::FromHex; +use ldk_node::lightning::ln::channelmanager::PaymentId; +use ldk_node::Node; +use protos::{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, +) -> 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 response = GetPaymentDetailsResponse { + payment: payment_details.map(|payment| payment_to_proto(payment)), + }; + + Ok(response) +} diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index 6355e9d..0ce3e71 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -4,6 +4,7 @@ pub(crate) mod bolt12_receive; pub(crate) mod bolt12_send; pub(crate) mod close_channel; pub(crate) mod get_node_info; +pub(crate) mod get_payment_details; pub(crate) mod list_channels; pub(crate) mod onchain_receive; pub(crate) mod onchain_send; diff --git a/server/src/service.rs b/server/src/service.rs index 15eaa79..2c66809 100644 --- a/server/src/service.rs +++ b/server/src/service.rs @@ -17,6 +17,9 @@ use crate::api::bolt12_receive::{handle_bolt12_receive_request, BOLT12_RECEIVE_P use crate::api::bolt12_send::{handle_bolt12_send_request, BOLT12_SEND_PATH}; use crate::api::close_channel::{handle_close_channel_request, CLOSE_CHANNEL_PATH}; use crate::api::get_node_info::{handle_get_node_info_request, GET_NODE_INFO}; +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::onchain_receive::{handle_onchain_receive_request, ONCHAIN_RECEIVE_PATH}; use crate::api::onchain_send::{handle_onchain_send_request, ONCHAIN_SEND_PATH}; @@ -58,6 +61,9 @@ impl Service> for NodeService { 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)), + GET_PAYMENT_DETAILS_PATH => { + Box::pin(handle_request(node, req, handle_get_payment_details_request)) + }, path => { let error = format!("Unknown request: {}", path).into_bytes(); Box::pin(async { diff --git a/server/src/util/proto_adapter.rs b/server/src/util/proto_adapter.rs index 55d6437..9f9efe7 100644 --- a/server/src/util/proto_adapter.rs +++ b/server/src/util/proto_adapter.rs @@ -1,7 +1,12 @@ +use bytes::Bytes; use hex::prelude::*; use ldk_node::config::{ChannelConfig, MaxDustHTLCExposure}; +use ldk_node::payment::{PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus}; use ldk_node::ChannelDetails; -use protos::{Channel, OutPoint}; +use protos::payment_kind::Kind::{ + Bolt11, Bolt11Jit, Bolt12Offer, Bolt12Refund, Onchain, Spontaneous, +}; +use protos::{Channel, LspFeeLimits, OutPoint, Payment}; pub(crate) fn channel_to_proto(channel: ChannelDetails) -> Channel { Channel { @@ -61,3 +66,75 @@ pub(crate) fn channel_config_to_proto(channel_config: ChannelConfig) -> protos:: }, } } + +pub(crate) fn payment_to_proto(payment: PaymentDetails) -> Payment { + Payment { + id: payment.id.0.to_lower_hex_string(), + kind: Some(payment_kind_to_proto(payment.kind)), + amount_msat: payment.amount_msat, + direction: match payment.direction { + PaymentDirection::Inbound => protos::PaymentDirection::Inbound.into(), + PaymentDirection::Outbound => protos::PaymentDirection::Outbound.into(), + }, + status: match payment.status { + PaymentStatus::Pending => protos::PaymentStatus::Pending.into(), + PaymentStatus::Succeeded => protos::PaymentStatus::Succeeded.into(), + PaymentStatus::Failed => protos::PaymentStatus::Failed.into(), + }, + latest_update_timestamp: payment.latest_update_timestamp, + } +} + +pub(crate) fn payment_kind_to_proto(payment_kind: PaymentKind) -> protos::PaymentKind { + match payment_kind { + PaymentKind::Onchain => protos::PaymentKind { kind: Some(Onchain(protos::Onchain {})) }, + PaymentKind::Bolt11 { hash, preimage, secret } => protos::PaymentKind { + kind: Some(Bolt11(protos::Bolt11 { + hash: hash.to_string(), + preimage: preimage.map(|p| p.to_string()), + secret: secret.map(|s| Bytes::copy_from_slice(&s.0)), + })), + }, + PaymentKind::Bolt11Jit { hash, preimage, secret, lsp_fee_limits } => protos::PaymentKind { + kind: Some(Bolt11Jit(protos::Bolt11Jit { + hash: hash.to_string(), + preimage: preimage.map(|p| p.to_string()), + secret: secret.map(|s| Bytes::copy_from_slice(&s.0)), + lsp_fee_limits: Some(LspFeeLimits { + max_total_opening_fee_msat: lsp_fee_limits.max_total_opening_fee_msat, + max_proportional_opening_fee_ppm_msat: lsp_fee_limits + .max_proportional_opening_fee_ppm_msat, + }), + })), + }, + PaymentKind::Bolt12Offer { hash, preimage, secret, offer_id, payer_note, quantity } => { + protos::PaymentKind { + kind: Some(Bolt12Offer(protos::Bolt12Offer { + hash: hash.map(|h| h.to_string()), + preimage: preimage.map(|p| p.to_string()), + secret: secret.map(|s| Bytes::copy_from_slice(&s.0)), + offer_id: offer_id.0.to_lower_hex_string(), + payer_note: payer_note.map(|s| s.to_string()), + quantity, + })), + } + }, + PaymentKind::Bolt12Refund { hash, preimage, secret, payer_note, quantity } => { + protos::PaymentKind { + kind: Some(Bolt12Refund(protos::Bolt12Refund { + hash: hash.map(|h| h.to_string()), + preimage: preimage.map(|p| p.to_string()), + secret: secret.map(|s| Bytes::copy_from_slice(&s.0)), + payer_note: payer_note.map(|s| s.to_string()), + quantity, + })), + } + }, + PaymentKind::Spontaneous { hash, preimage } => protos::PaymentKind { + kind: Some(Spontaneous(protos::Spontaneous { + hash: hash.to_string(), + preimage: preimage.map(|p| p.to_string()), + })), + }, + } +}