breez_sdk_core/
node_api.rs

1use std::collections::HashSet;
2use std::pin::Pin;
3use std::time::{SystemTime, UNIX_EPOCH};
4
5use anyhow::Result;
6use serde_json::Value;
7use tokio::sync::{mpsc, watch};
8use tokio_stream::Stream;
9
10use sdk_common::prelude::*;
11
12use crate::{
13    bitcoin::bip32::{ChildNumber, ExtendedPrivKey},
14    lightning_invoice::RawBolt11Invoice,
15    persist::error::PersistError,
16    CustomMessage, LnUrlAuthError, LspInformation, MaxChannelAmount, NodeCredentials, Payment, PaymentType,PaymentDetails,LnPaymentDetails,PaymentStatus,
17    PaymentResponse, PrepareRedeemOnchainFundsRequest, PrepareRedeemOnchainFundsResponse,
18    RouteHint, RouteHintHop, SyncResponse, TlvEntry,
19};
20
21pub type NodeResult<T, E = NodeError> = Result<T, E>;
22
23#[derive(Debug, thiserror::Error)]
24pub enum NodeError {
25    #[error("{0}")]
26    Credentials(String),
27
28    #[error("{0}")]
29    Generic(String),
30
31    #[error(transparent)]
32    InvalidInvoice(#[from] InvoiceError),
33
34    #[error("{0}")]
35    InvoiceExpired(String),
36
37    #[error("{0}")]
38    InvoiceNoDescription(String),
39
40    #[error("{0}")]
41    InvoicePreimageAlreadyExists(String),
42
43    #[error("{0}")]
44    PaymentFailed(String),
45
46    #[error("{0}")]
47    PaymentTimeout(String),
48
49    #[error(transparent)]
50    Persistance(#[from] PersistError),
51
52    #[error("{0}")]
53    RestoreOnly(String),
54
55    #[error("{0}")]
56    RouteTooExpensive(String),
57
58    #[error("{0}")]
59    RouteNotFound(String),
60
61    #[error("{0}")]
62    ServiceConnectivity(String),
63
64    #[error("{0}")]
65    InsufficientFunds(String),
66
67    #[error("invoice already paid")]
68    InvoiceAlreadyPaid,
69}
70
71impl NodeError {
72    pub(crate) fn credentials(err: &str) -> Self {
73        Self::Credentials(err.to_string())
74    }
75
76    pub(crate) fn generic(err: &str) -> Self {
77        Self::Generic(err.to_string())
78    }
79}
80
81impl From<crate::bitcoin::bip32::Error> for NodeError {
82    fn from(err: crate::bitcoin::bip32::Error) -> Self {
83        Self::Generic(err.to_string())
84    }
85}
86
87impl From<NodeError> for sdk_common::prelude::LnUrlError {
88    fn from(value: NodeError) -> Self {
89        match value {
90            NodeError::InvalidInvoice(err) => Self::InvalidInvoice(format!("{err}")),
91            NodeError::ServiceConnectivity(err) => Self::ServiceConnectivity(err),
92            _ => Self::Generic(value.to_string()),
93        }
94    }
95}
96
97impl From<NodeError> for LnUrlAuthError {
98    fn from(value: NodeError) -> Self {
99        match value {
100            NodeError::ServiceConnectivity(err) => Self::ServiceConnectivity { err },
101            _ => Self::Generic {
102                err: value.to_string(),
103            },
104        }
105    }
106}
107
108pub struct CreateInvoiceRequest {
109    pub amount_msat: u64,
110    pub description: String,
111    pub payer_amount_msat: Option<u64>,
112    pub preimage: Option<Vec<u8>>,
113    pub use_description_hash: Option<bool>,
114    pub expiry: Option<u32>,
115    pub cltv: Option<u32>,
116}
117
118pub struct FetchBolt11Result {
119    pub bolt11: String,
120    pub payer_amount_msat: Option<u64>,
121}
122
123#[derive(Debug, Clone)]
124pub struct IncomingPayment {
125    pub label: String,
126    pub payment_hash: Vec<u8>,
127    pub preimage: Vec<u8>,
128    pub amount_msat: u64,
129    pub bolt11: String,
130}
131
132impl TryFrom<IncomingPayment> for Payment {
133    type Error = NodeError;
134
135    fn try_from(p: IncomingPayment) -> std::result::Result<Self, Self::Error> {
136		let payment_time = SystemTime::now().duration_since(UNIX_EPOCH).map_err(|e| NodeError::Generic(format!("{e}")))?.as_secs() as i64;
137        let ln_invoice = parse_invoice(&p.bolt11)?;
138        Ok(Payment {
139            id: hex::encode(p.payment_hash.clone()),
140            payment_type: PaymentType::Received,
141            payment_time,
142            amount_msat: p.amount_msat,
143            fee_msat: 0,
144            status: PaymentStatus::Complete,
145            error: None,
146            description: ln_invoice.description,
147            details: PaymentDetails::Ln {
148                data: LnPaymentDetails {
149                    payment_hash: hex::encode(p.payment_hash),
150                    label: p.label,
151                    destination_pubkey: ln_invoice.payee_pubkey,
152                    payment_preimage: hex::encode(p.preimage),
153                    keysend: false,
154                    bolt11: p.bolt11,
155                    lnurl_success_action: None, // For received payments, this is None
156                    lnurl_pay_domain: None,     // For received payments, this is None
157                    lnurl_pay_comment: None,    // For received payments, this is None
158                    lnurl_metadata: None,       // For received payments, this is None
159                    ln_address: None,
160                    lnurl_withdraw_endpoint: None,
161                    swap_info: None,
162                    reverse_swap_info: None,
163                    pending_expiration_block: None,
164                    open_channel_bolt11: None,
165                },
166            },
167            metadata: None,
168        })
169    }
170}
171
172/// Trait covering functions affecting the LN node
173#[cfg_attr(test, mockall::automock)]
174#[tonic::async_trait]
175pub trait NodeAPI: Send + Sync {
176    async fn node_credentials(&self) -> NodeResult<Option<NodeCredentials>>;
177    async fn configure_node(&self, close_to_address: Option<String>) -> NodeResult<()>;
178    async fn create_invoice(&self, request: CreateInvoiceRequest) -> NodeResult<String>;
179    async fn delete_invoice(&self, bolt11: String) -> NodeResult<()>;
180    /// Fetches an existing BOLT11 invoice from the node
181    async fn fetch_bolt11(&self, payment_hash: Vec<u8>) -> NodeResult<Option<FetchBolt11Result>>;
182    async fn pull_changed(
183        &self,
184        sync_state: Option<Value>,
185        match_local_balance: bool,
186    ) -> NodeResult<SyncResponse>;
187    /// As per the `pb::PayRequest` docs, `amount_msat` is only needed when the invoice doesn't specify an amount
188    async fn send_payment(
189        &self,
190        bolt11: String,
191        amount_msat: Option<u64>,
192        label: Option<String>,
193    ) -> NodeResult<Payment>;
194    async fn send_spontaneous_payment(
195        &self,
196        node_id: String,
197        amount_msat: u64,
198        extra_tlvs: Option<Vec<TlvEntry>>,
199        label: Option<String>,
200    ) -> NodeResult<Payment>;
201    async fn send_trampoline_payment(
202        &self,
203        bolt11: String,
204        amount_msat: u64,
205        label: Option<String>,
206        trampoline_node_id: Vec<u8>,
207    ) -> NodeResult<Payment>;
208    async fn node_id(&self) -> NodeResult<String>;
209
210    /// Attempts to find a payment path "manually" and send the htlcs in a way that will drain
211    /// Large channels first.
212    /// This is useful function to send the largest amount possible to a node.
213    async fn send_pay(&self, bolt11: String, max_hops: u32) -> NodeResult<PaymentResponse>;
214
215    /// Calculates the maximum amount that can be sent to a node.
216    async fn max_sendable_amount<'a>(
217        &self,
218        payee_node_id: Option<Vec<u8>>,
219        max_hops: u32,
220        last_hop: Option<&'a RouteHintHop>,
221    ) -> NodeResult<Vec<MaxChannelAmount>>;
222    async fn redeem_onchain_funds(
223        &self,
224        to_address: String,
225        sat_per_vbyte: u32,
226    ) -> NodeResult<Vec<u8>>;
227    async fn prepare_redeem_onchain_funds(
228        &self,
229        req: PrepareRedeemOnchainFundsRequest,
230    ) -> NodeResult<PrepareRedeemOnchainFundsResponse>;
231    async fn start(&self, shutdown: mpsc::Receiver<()>);
232    async fn start_keep_alive(&self, shutdown: watch::Receiver<()>);
233    async fn connect_peer(&self, node_id: String, addr: String) -> NodeResult<()>;
234    async fn sign_invoice(&self, invoice: RawBolt11Invoice) -> NodeResult<String>;
235    async fn close_peer_channels(&self, node_id: String) -> NodeResult<Vec<String>>;
236    async fn stream_incoming_payments(
237        &self,
238    ) -> NodeResult<Pin<Box<dyn Stream<Item = IncomingPayment> + Send>>>;
239    async fn stream_log_messages(
240        &self,
241    ) -> NodeResult<Pin<Box<dyn Stream<Item = String> + Send>>>;
242    async fn static_backup(&self) -> NodeResult<Vec<String>>;
243    async fn execute_command(&self, command: String) -> NodeResult<Value>;
244    async fn generate_diagnostic_data(&self) -> NodeResult<Value>;
245    async fn sign_message(&self, message: &str) -> NodeResult<String>;
246    async fn check_message(&self, message: &str, pubkey: &str, signature: &str)
247        -> NodeResult<bool>;
248    async fn send_custom_message(&self, message: CustomMessage) -> NodeResult<()>;
249    async fn stream_custom_messages(
250        &self,
251    ) -> NodeResult<Pin<Box<dyn Stream<Item = Result<CustomMessage>> + Send>>>;
252
253    /// Gets the private key at the path specified
254    async fn derive_bip32_key(&self, path: Vec<ChildNumber>) -> NodeResult<ExtendedPrivKey>;
255    async fn legacy_derive_bip32_key(&self, path: Vec<ChildNumber>) -> NodeResult<ExtendedPrivKey>;
256
257    /// Gets the routing hints related to all private channels that the node has.
258    /// Also returns a boolean indicating if the node has a public channel or not.
259    async fn get_routing_hints(
260        &self,
261        lsp_info: &LspInformation,
262    ) -> NodeResult<(Vec<RouteHint>, bool)>;
263    /// Get peers with whom we have an open channel
264    async fn get_open_peers(&self) -> NodeResult<HashSet<Vec<u8>>>;
265}