mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-20 06:05:09 +01:00
feat: cln lightning
This commit is contained in:
13
Cargo.toml
13
Cargo.toml
@@ -22,24 +22,23 @@ keywords = ["bitcoin", "e-cash", "cashu"]
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
async-trait = "0.1.74"
|
async-trait = "0.1.74"
|
||||||
|
anyhow = "1"
|
||||||
|
bitcoin = { version = "0.30", default-features = false } # lightning-invoice uses v0.30
|
||||||
bip39 = "2.0"
|
bip39 = "2.0"
|
||||||
cdk = { version = "0.1", path = "./crates/cdk", default-features = false }
|
cdk = { version = "0.1", path = "./crates/cdk", default-features = false }
|
||||||
cdk-rexie = { version = "0.1", path = "./crates/cdk-rexie", default-features = false }
|
cdk-rexie = { version = "0.1", path = "./crates/cdk-rexie", default-features = false }
|
||||||
cdk-sqlite = { version = "0.1", path = "./crates/cdk-sqlite", default-features = false }
|
cdk-sqlite = { version = "0.1", path = "./crates/cdk-sqlite", default-features = false }
|
||||||
cdk-redb = { version = "0.1", path = "./crates/cdk-redb", default-features = false }
|
cdk-redb = { version = "0.1", path = "./crates/cdk-redb", default-features = false }
|
||||||
|
cdk-cln = { version = "0.1", path = "./crates/cdk-cln", default-features = false }
|
||||||
tokio = { version = "1", default-features = false }
|
tokio = { version = "1", default-features = false }
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] }
|
tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] }
|
||||||
serde = { version = "1", default-features = false, features = ["derive"] }
|
serde = { version = "1", default-features = false, features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
serde-wasm-bindgen = { version = "0.6.5", default-features = false }
|
serde-wasm-bindgen = "0.6.5"
|
||||||
|
futures = { version = "0.3.28", default-feature = false }
|
||||||
web-sys = { version = "0.3.69", default-features = false, features = ["console"] }
|
web-sys = { version = "0.3.69", default-features = false, features = ["console"] }
|
||||||
bitcoin = { version = "0.30", features = [
|
uuid = { version = "1", features = ["v4"] }
|
||||||
"serde",
|
|
||||||
"rand",
|
|
||||||
"rand-std",
|
|
||||||
] } # lightning-invoice uses v0.30
|
|
||||||
anyhow = "1"
|
|
||||||
|
|
||||||
[profile]
|
[profile]
|
||||||
|
|
||||||
|
|||||||
20
crates/cdk-cln/Cargo.toml
Normal file
20
crates/cdk-cln/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "cdk-cln"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["CDK Developers"]
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
rust-version.workspace = true # MSRV
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
async-trait.workspace = true
|
||||||
|
bitcoin.workspace = true
|
||||||
|
cdk = { workspace = true, default-features = false, features = ["mint"] }
|
||||||
|
cln-rpc = "0.1.9"
|
||||||
|
futures.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
|
tracing.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
|
uuid.workspace = true
|
||||||
25
crates/cdk-cln/src/error.rs
Normal file
25
crates/cdk-cln/src/error.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Wrong CLN response
|
||||||
|
#[error("Wrong cln response")]
|
||||||
|
WrongClnResponse,
|
||||||
|
/// Unknown invoice
|
||||||
|
#[error("Unknown invoice")]
|
||||||
|
UnknownInvoice,
|
||||||
|
/// Cln Error
|
||||||
|
#[error(transparent)]
|
||||||
|
Cln(#[from] cln_rpc::Error),
|
||||||
|
/// Cln Rpc Error
|
||||||
|
#[error(transparent)]
|
||||||
|
ClnRpc(#[from] cln_rpc::RpcError),
|
||||||
|
#[error("`{0}`")]
|
||||||
|
Custom(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for cdk::cdk_lightning::Error {
|
||||||
|
fn from(e: Error) -> Self {
|
||||||
|
Self::Lightning(Box::new(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
256
crates/cdk-cln/src/lib.rs
Normal file
256
crates/cdk-cln/src/lib.rs
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
//! CDK lightning backend for CLN
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use cdk::cdk_lightning::{self, MintLightning, PayInvoiceResponse};
|
||||||
|
use cdk::nuts::{MeltQuoteState, MintQuoteState};
|
||||||
|
use cdk::util::{hex, unix_time};
|
||||||
|
use cdk::Bolt11Invoice;
|
||||||
|
use cln_rpc::model::requests::{
|
||||||
|
InvoiceRequest, ListinvoicesRequest, PayRequest, WaitanyinvoiceRequest,
|
||||||
|
};
|
||||||
|
use cln_rpc::model::responses::{ListinvoicesInvoicesStatus, PayStatus, WaitanyinvoiceResponse};
|
||||||
|
use cln_rpc::model::Request;
|
||||||
|
use cln_rpc::primitives::{Amount as CLN_Amount, AmountOrAny};
|
||||||
|
use error::Error;
|
||||||
|
use futures::{Stream, StreamExt};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Cln {
|
||||||
|
rpc_socket: PathBuf,
|
||||||
|
cln_client: Arc<Mutex<cln_rpc::ClnRpc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cln {
|
||||||
|
pub async fn new(rpc_socket: PathBuf) -> Result<Self, Error> {
|
||||||
|
let cln_client = cln_rpc::ClnRpc::new(&rpc_socket).await?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
rpc_socket,
|
||||||
|
cln_client: Arc::new(Mutex::new(cln_client)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl MintLightning for Cln {
|
||||||
|
type Err = cdk_lightning::Error;
|
||||||
|
|
||||||
|
async fn wait_any_invoice(
|
||||||
|
&self,
|
||||||
|
) -> Result<Pin<Box<dyn Stream<Item = Bolt11Invoice> + Send>>, Self::Err> {
|
||||||
|
let last_pay_index = self.get_last_pay_index().await?;
|
||||||
|
let cln_client = cln_rpc::ClnRpc::new(&self.rpc_socket).await?;
|
||||||
|
|
||||||
|
Ok(futures::stream::unfold(
|
||||||
|
(cln_client, last_pay_index),
|
||||||
|
|(mut cln_client, mut last_pay_idx)| async move {
|
||||||
|
loop {
|
||||||
|
let invoice_res = cln_client
|
||||||
|
.call(cln_rpc::Request::WaitAnyInvoice(WaitanyinvoiceRequest {
|
||||||
|
timeout: None,
|
||||||
|
lastpay_index: last_pay_idx,
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let invoice: WaitanyinvoiceResponse = match invoice_res {
|
||||||
|
Ok(invoice) => invoice,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!("Error fetching invoice: {e}");
|
||||||
|
// Let's not spam CLN with requests on failure
|
||||||
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
|
// Retry same request
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.try_into()
|
||||||
|
.expect("Wrong response from CLN");
|
||||||
|
|
||||||
|
last_pay_idx = invoice.pay_index;
|
||||||
|
|
||||||
|
if let Some(bolt11) = invoice.bolt11 {
|
||||||
|
if let Ok(invoice) = Bolt11Invoice::from_str(&bolt11) {
|
||||||
|
break Some((invoice, (cln_client, last_pay_idx)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.boxed())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn pay_invoice(
|
||||||
|
&self,
|
||||||
|
bolt11: Bolt11Invoice,
|
||||||
|
partial_msats: Option<u64>,
|
||||||
|
max_fee_msats: Option<u64>,
|
||||||
|
) -> Result<PayInvoiceResponse, Self::Err> {
|
||||||
|
let mut cln_client = self.cln_client.lock().await;
|
||||||
|
let cln_response = cln_client
|
||||||
|
.call(Request::Pay(PayRequest {
|
||||||
|
bolt11: bolt11.to_string(),
|
||||||
|
amount_msat: None,
|
||||||
|
label: None,
|
||||||
|
riskfactor: None,
|
||||||
|
maxfeepercent: None,
|
||||||
|
retry_for: None,
|
||||||
|
maxdelay: None,
|
||||||
|
exemptfee: None,
|
||||||
|
localinvreqid: None,
|
||||||
|
exclude: None,
|
||||||
|
maxfee: max_fee_msats.map(CLN_Amount::from_msat),
|
||||||
|
description: None,
|
||||||
|
partial_msat: partial_msats.map(CLN_Amount::from_msat),
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
.map_err(Error::from)?;
|
||||||
|
|
||||||
|
let response = match cln_response {
|
||||||
|
cln_rpc::Response::Pay(pay_response) => {
|
||||||
|
let status = match pay_response.status {
|
||||||
|
PayStatus::COMPLETE => MeltQuoteState::Paid,
|
||||||
|
PayStatus::PENDING => MeltQuoteState::Pending,
|
||||||
|
PayStatus::FAILED => MeltQuoteState::Unpaid,
|
||||||
|
};
|
||||||
|
PayInvoiceResponse {
|
||||||
|
payment_preimage: Some(hex::encode(pay_response.payment_preimage.to_vec())),
|
||||||
|
payment_hash: pay_response.payment_hash.to_string(),
|
||||||
|
status,
|
||||||
|
total_spent_msats: pay_response.amount_sent_msat.msat(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
tracing::warn!("CLN returned wrong response kind");
|
||||||
|
return Err(cdk_lightning::Error::from(Error::WrongClnResponse));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_invoice(
|
||||||
|
&self,
|
||||||
|
amount_msats: u64,
|
||||||
|
description: String,
|
||||||
|
unix_expiry: u64,
|
||||||
|
) -> Result<Bolt11Invoice, Self::Err> {
|
||||||
|
let time_now = unix_time();
|
||||||
|
assert!(unix_expiry > time_now);
|
||||||
|
|
||||||
|
let mut cln_client = self.cln_client.lock().await;
|
||||||
|
|
||||||
|
let amount_msat = AmountOrAny::Amount(CLN_Amount::from_msat(amount_msats));
|
||||||
|
let cln_response = cln_client
|
||||||
|
.call(cln_rpc::Request::Invoice(InvoiceRequest {
|
||||||
|
amount_msat,
|
||||||
|
description,
|
||||||
|
label: Uuid::new_v4().to_string(),
|
||||||
|
expiry: Some(unix_expiry - time_now),
|
||||||
|
fallbacks: None,
|
||||||
|
preimage: None,
|
||||||
|
cltv: None,
|
||||||
|
deschashonly: None,
|
||||||
|
exposeprivatechannels: None,
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
.map_err(Error::from)?;
|
||||||
|
|
||||||
|
let invoice = match cln_response {
|
||||||
|
cln_rpc::Response::Invoice(invoice_res) => {
|
||||||
|
Bolt11Invoice::from_str(&invoice_res.bolt11)?
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
tracing::warn!("CLN returned wrong response kind");
|
||||||
|
return Err(Error::WrongClnResponse.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(invoice)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_invoice_status(&self, payment_hash: &str) -> Result<MintQuoteState, Self::Err> {
|
||||||
|
let mut cln_client = self.cln_client.lock().await;
|
||||||
|
|
||||||
|
let cln_response = cln_client
|
||||||
|
.call(Request::ListInvoices(ListinvoicesRequest {
|
||||||
|
payment_hash: Some(payment_hash.to_string()),
|
||||||
|
label: None,
|
||||||
|
invstring: None,
|
||||||
|
offer_id: None,
|
||||||
|
index: None,
|
||||||
|
limit: None,
|
||||||
|
start: None,
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
.map_err(Error::from)?;
|
||||||
|
|
||||||
|
let status = match cln_response {
|
||||||
|
cln_rpc::Response::ListInvoices(invoice_response) => {
|
||||||
|
match invoice_response.invoices.first() {
|
||||||
|
Some(invoice_response) => {
|
||||||
|
cln_invoice_status_to_mint_state(invoice_response.status)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
tracing::info!(
|
||||||
|
"Check invoice called on unknown payment_hash: {}",
|
||||||
|
payment_hash
|
||||||
|
);
|
||||||
|
return Err(Error::WrongClnResponse.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
tracing::warn!("CLN returned wrong response kind");
|
||||||
|
return Err(Error::Custom("CLN returned wrong response kind".to_string()).into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cln {
|
||||||
|
async fn get_last_pay_index(&self) -> Result<Option<u64>, Error> {
|
||||||
|
let mut cln_client = self.cln_client.lock().await;
|
||||||
|
let cln_response = cln_client
|
||||||
|
.call(cln_rpc::Request::ListInvoices(ListinvoicesRequest {
|
||||||
|
index: None,
|
||||||
|
invstring: None,
|
||||||
|
label: None,
|
||||||
|
limit: None,
|
||||||
|
offer_id: None,
|
||||||
|
payment_hash: None,
|
||||||
|
start: None,
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
.map_err(Error::from)?;
|
||||||
|
|
||||||
|
match cln_response {
|
||||||
|
cln_rpc::Response::ListInvoices(invoice_res) => match invoice_res.invoices.last() {
|
||||||
|
Some(last_invoice) => Ok(last_invoice.pay_index),
|
||||||
|
None => Ok(None),
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
tracing::warn!("CLN returned wrong response kind");
|
||||||
|
Err(Error::WrongClnResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cln_invoice_status_to_mint_state(status: ListinvoicesInvoicesStatus) -> MintQuoteState {
|
||||||
|
match status {
|
||||||
|
ListinvoicesInvoicesStatus::UNPAID => MintQuoteState::Unpaid,
|
||||||
|
ListinvoicesInvoicesStatus::PAID => MintQuoteState::Paid,
|
||||||
|
ListinvoicesInvoicesStatus::EXPIRED => MintQuoteState::Unpaid,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,12 +12,13 @@ license.workspace = true
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["mint", "wallet"]
|
default = ["mint", "wallet"]
|
||||||
mint = []
|
mint = ["dep:futures"]
|
||||||
wallet = ["dep:reqwest"]
|
wallet = ["dep:reqwest"]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
|
anyhow.workspace = true
|
||||||
base64 = "0.22" # bitcoin uses v0.13 (optional dep)
|
base64 = "0.22" # bitcoin uses v0.13 (optional dep)
|
||||||
bitcoin = { workspace = true, features = [
|
bitcoin = { workspace = true, features = [
|
||||||
"serde",
|
"serde",
|
||||||
@@ -38,8 +39,9 @@ serde_json.workspace = true
|
|||||||
serde_with = "3.4"
|
serde_with = "3.4"
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
futures = { workspace = true, optional = true }
|
||||||
url = "2.3"
|
url = "2.3"
|
||||||
uuid = { version = "1", features = ["v4"] }
|
uuid.workspace = true
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
tokio = { workspace = true, features = [
|
tokio = { workspace = true, features = [
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
//! CDK Amount
|
//! CDK Amount
|
||||||
//!
|
//!
|
||||||
//! Is any and will be treated as the unit of the wallet
|
//! Is any unit and will be treated as the unit of the wallet
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Number of satoshis
|
/// Amount can be any unit
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Amount(u64);
|
pub struct Amount(u64);
|
||||||
@@ -68,18 +68,6 @@ impl Amount {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Kinds of targeting that are supported
|
|
||||||
#[derive(
|
|
||||||
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize,
|
|
||||||
)]
|
|
||||||
pub enum SplitTarget {
|
|
||||||
/// Default target; least amount of proofs
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
/// Target amount for wallet to have most proofs that add up to value
|
|
||||||
Value(Amount),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Amount {
|
impl Default for Amount {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Amount::ZERO
|
Amount::ZERO
|
||||||
@@ -173,6 +161,18 @@ impl core::iter::Sum for Amount {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Kinds of targeting that are supported
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
|
pub enum SplitTarget {
|
||||||
|
/// Default target; least amount of proofs
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
/// Target amount for wallet to have most proofs that add up to value
|
||||||
|
Value(Amount),
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
72
crates/cdk/src/cdk_lightning/mod.rs
Normal file
72
crates/cdk/src/cdk_lightning/mod.rs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
//! CDK Mint Lightning
|
||||||
|
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use futures::Stream;
|
||||||
|
use lightning_invoice::{Bolt11Invoice, ParseOrSemanticError};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::nuts::{MeltQuoteState, MintQuoteState};
|
||||||
|
|
||||||
|
/// CDK Lightning Error
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Lightning Error
|
||||||
|
#[error(transparent)]
|
||||||
|
Lightning(Box<dyn std::error::Error + Send + Sync>),
|
||||||
|
/// Serde Error
|
||||||
|
#[error(transparent)]
|
||||||
|
Serde(#[from] serde_json::Error),
|
||||||
|
/// AnyHow Error
|
||||||
|
#[error(transparent)]
|
||||||
|
Anyhow(#[from] anyhow::Error),
|
||||||
|
/// Parse Error
|
||||||
|
#[error(transparent)]
|
||||||
|
Parse(#[from] ParseOrSemanticError),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MintLighting Trait
|
||||||
|
#[async_trait]
|
||||||
|
pub trait MintLightning {
|
||||||
|
/// Mint Lightning Error
|
||||||
|
type Err: Into<Error> + From<Error>;
|
||||||
|
|
||||||
|
/// Create a new invoice
|
||||||
|
async fn create_invoice(
|
||||||
|
&self,
|
||||||
|
msats: u64,
|
||||||
|
description: String,
|
||||||
|
unix_expiry: u64,
|
||||||
|
) -> Result<Bolt11Invoice, Self::Err>;
|
||||||
|
|
||||||
|
/// Pay bolt11 invoice
|
||||||
|
async fn pay_invoice(
|
||||||
|
&self,
|
||||||
|
bolt11: Bolt11Invoice,
|
||||||
|
partial_msats: Option<u64>,
|
||||||
|
max_fee_msats: Option<u64>,
|
||||||
|
) -> Result<PayInvoiceResponse, Self::Err>;
|
||||||
|
|
||||||
|
/// Listen for invoices to be paid to the mint
|
||||||
|
async fn wait_any_invoice(
|
||||||
|
&self,
|
||||||
|
) -> Result<Pin<Box<dyn Stream<Item = Bolt11Invoice> + Send>>, Self::Err>;
|
||||||
|
|
||||||
|
/// Check the status of an incoming payment
|
||||||
|
async fn check_invoice_status(&self, payment_hash: &str) -> Result<MintQuoteState, Self::Err>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pay invoice response
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PayInvoiceResponse {
|
||||||
|
/// Payment hash
|
||||||
|
pub payment_hash: String,
|
||||||
|
/// Payment Preimage
|
||||||
|
pub payment_preimage: Option<String>,
|
||||||
|
/// Status
|
||||||
|
pub status: MeltQuoteState,
|
||||||
|
/// Totoal Amount Spent in msats
|
||||||
|
pub total_spent_msats: u64,
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
pub mod amount;
|
pub mod amount;
|
||||||
pub mod cdk_database;
|
pub mod cdk_database;
|
||||||
|
#[cfg(feature = "mint")]
|
||||||
|
pub mod cdk_lightning;
|
||||||
pub mod dhke;
|
pub mod dhke;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ buildargs=(
|
|||||||
"-p cdk-redb --no-default-features --features mint"
|
"-p cdk-redb --no-default-features --features mint"
|
||||||
"-p cdk-sqlite --no-default-features --features mint"
|
"-p cdk-sqlite --no-default-features --features mint"
|
||||||
"-p cdk-sqlite --no-default-features --features wallet"
|
"-p cdk-sqlite --no-default-features --features wallet"
|
||||||
|
"-p cdk-cln"
|
||||||
"--bin cdk-cli"
|
"--bin cdk-cli"
|
||||||
"--examples"
|
"--examples"
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user