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, lnurl_pay_domain: None, lnurl_pay_comment: None, lnurl_metadata: None, 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#[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 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 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 async fn send_pay(&self, bolt11: String, max_hops: u32) -> NodeResult<PaymentResponse>;
214
215 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 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 async fn get_routing_hints(
260 &self,
261 lsp_info: &LspInformation,
262 ) -> NodeResult<(Vec<RouteHint>, bool)>;
263 async fn get_open_peers(&self) -> NodeResult<HashSet<Vec<u8>>>;
265}