Merge pull request #555 from thesimplekid/regtest_muli_mint

Add LND mint to regtest itests
This commit is contained in:
thesimplekid
2025-01-24 15:11:42 +00:00
committed by GitHub
9 changed files with 299 additions and 91 deletions

View File

@@ -12,6 +12,7 @@ use cdk::nuts::{
};
use cdk::util::unix_time;
use paste::paste;
use tracing::instrument;
use uuid::Uuid;
use crate::ws::main_websocket;
@@ -232,6 +233,7 @@ pub async fn post_mint_bolt11(
(status = 500, description = "Server error", body = ErrorResponse, content_type = "application/json")
)
))]
#[instrument(skip_all)]
/// Request a quote for melting tokens
pub async fn post_melt_bolt11_quote(
State(state): State<MintState>,
@@ -261,6 +263,7 @@ pub async fn post_melt_bolt11_quote(
/// Get melt quote by ID
///
/// Get melt quote state.
#[instrument(skip_all)]
pub async fn get_check_melt_bolt11_quote(
State(state): State<MintState>,
Path(quote_id): Path<Uuid>,
@@ -290,6 +293,7 @@ pub async fn get_check_melt_bolt11_quote(
/// Melt tokens for a Bitcoin payment that the mint will make for the user in exchange
///
/// Requests tokens to be destroyed and sent out via Lightning.
#[instrument(skip_all)]
pub async fn post_melt_bolt11(
State(state): State<MintState>,
Json(payload): Json<MeltBolt11Request<Uuid>>,

View File

@@ -21,6 +21,7 @@ bip39 = { version = "2.0", features = ["rand"] }
anyhow = "1"
cdk = { path = "../cdk", features = ["mint", "wallet"] }
cdk-cln = { path = "../cdk-cln" }
cdk-lnd = { path = "../cdk-lnd" }
cdk-axum = { path = "../cdk-axum" }
cdk-sqlite = { path = "../cdk-sqlite" }
cdk-redb = { path = "../cdk-redb" }
@@ -34,7 +35,7 @@ uuid = { version = "1", features = ["v4"] }
serde = "1"
serde_json = "1"
# ln-regtest-rs = { path = "../../../../ln-regtest-rs" }
ln-regtest-rs = { git = "https://github.com/thesimplekid/ln-regtest-rs", rev = "166038b5" }
ln-regtest-rs = { git = "https://github.com/thesimplekid/ln-regtest-rs", rev = "f9e7bbbb" }
lightning-invoice = { version = "0.32.0", features = ["serde", "std"] }
tracing = { version = "0.1", default-features = false, features = [
"attributes",

View File

@@ -3,13 +3,15 @@ use std::env;
use anyhow::Result;
use cdk::cdk_database::mint_memory::MintMemoryDatabase;
use cdk_integration_tests::init_regtest::{
fund_ln, get_bitcoin_dir, get_cln_dir, get_temp_dir, init_bitcoin_client, init_bitcoind,
init_lnd, init_lnd_client, open_channel, start_cln_mint, BITCOIN_RPC_PASS, BITCOIN_RPC_USER,
create_cln_backend, create_lnd_backend, create_mint, fund_ln, generate_block, get_bitcoin_dir,
get_cln_dir, get_lnd_cert_file_path, get_lnd_dir, get_lnd_macaroon_path, get_temp_dir,
init_bitcoin_client, init_bitcoind, init_lnd, open_channel, BITCOIN_RPC_PASS, BITCOIN_RPC_USER,
LND_ADDR, LND_RPC_ADDR, LND_TWO_ADDR, LND_TWO_RPC_ADDR,
};
use cdk_redb::MintRedbDatabase;
use cdk_sqlite::MintSqliteDatabase;
use ln_regtest_rs::cln::Clnd;
use ln_regtest_rs::ln_client::{ClnClient, LightningClient};
use ln_regtest_rs::ln_client::{ClnClient, LightningClient, LndClient};
use tracing_subscriber::EnvFilter;
const CLN_ADDR: &str = "127.0.0.1:19846";
@@ -22,10 +24,11 @@ async fn main() -> Result<()> {
let sqlx_filter = "sqlx=warn";
let hyper_filter = "hyper=warn";
let h2_filter = "h2=warn";
let rustls_filter = "rustls=warn";
let env_filter = EnvFilter::new(format!(
"{},{},{},{}",
default_filter, sqlx_filter, hyper_filter, h2_filter
"{},{},{},{},{}",
default_filter, sqlx_filter, hyper_filter, h2_filter, rustls_filter
));
tracing_subscriber::fmt().with_env_filter(env_filter).init();
@@ -69,45 +72,136 @@ async fn main() -> Result<()> {
let cln_two_client = ClnClient::new(cln_two_dir.clone(), None).await?;
cln_client.wait_chain_sync().await.unwrap();
cln_two_client.wait_chain_sync().await.unwrap();
fund_ln(&bitcoin_client, &cln_two_client).await.unwrap();
let mut lnd = init_lnd().await;
let lnd_dir = get_lnd_dir("one");
println!("{}", lnd_dir.display());
let mut lnd = init_lnd(lnd_dir.clone(), LND_ADDR, LND_RPC_ADDR).await;
lnd.start_lnd().unwrap();
tracing::info!("Started lnd node");
let lnd_client = init_lnd_client().await.unwrap();
let lnd_client = LndClient::new(
format!("https://{}", LND_RPC_ADDR),
get_lnd_cert_file_path(&lnd_dir),
get_lnd_macaroon_path(&lnd_dir),
)
.await?;
lnd_client.wait_chain_sync().await.unwrap();
fund_ln(&bitcoin_client, &lnd_client).await.unwrap();
open_channel(&bitcoin_client, &cln_client, &lnd_client)
.await
.unwrap();
// create second lnd node
let lnd_two_dir = get_lnd_dir("two");
let mut lnd_two = init_lnd(lnd_two_dir.clone(), LND_TWO_ADDR, LND_TWO_RPC_ADDR).await;
lnd_two.start_lnd().unwrap();
tracing::info!("Started second lnd node");
let addr = "127.0.0.1";
let port = 8085;
let lnd_two_client = LndClient::new(
format!("https://{}", LND_TWO_RPC_ADDR),
get_lnd_cert_file_path(&lnd_two_dir),
get_lnd_macaroon_path(&lnd_two_dir),
)
.await?;
lnd_two_client.wait_chain_sync().await.unwrap();
fund_ln(&bitcoin_client, &lnd_two_client).await.unwrap();
// Open channels concurrently
// Open channels
{
open_channel(&cln_client, &lnd_client).await.unwrap();
tracing::info!("Opened channel between cln and lnd one");
generate_block(&bitcoin_client)?;
// open_channel(&bitcoin_client, &cln_client, &cln_two_client)
// .await
// .unwrap();
// tracing::info!("Opened channel between cln and cln two");
open_channel(&lnd_client, &lnd_two_client).await.unwrap();
tracing::info!("Opened channel between lnd and lnd two");
generate_block(&bitcoin_client)?;
// open_channel(&cln_client, &lnd_two_client).await.unwrap();
// tracing::info!("Opened channel between cln and lnd two");
open_channel(&cln_two_client, &lnd_client).await.unwrap();
tracing::info!("Opened channel between cln two and lnd");
generate_block(&bitcoin_client)?;
open_channel(&cln_client, &lnd_two_client).await.unwrap();
tracing::info!("Opened channel between cln and lnd two");
generate_block(&bitcoin_client)?;
cln_client.wait_channels_active().await?;
cln_two_client.wait_channels_active().await?;
lnd_client.wait_channels_active().await?;
lnd_two_client.wait_channels_active().await?;
}
let mint_addr = "127.0.0.1";
let cln_mint_port = 8085;
let mint_db_kind = env::var("MINT_DATABASE")?;
let temp_dir_path = get_temp_dir();
let db_path = get_temp_dir().join("mint");
let cln_path = temp_dir_path.join("one");
let lnd_mint_db_path = get_temp_dir().join("lnd_mint");
let cln_mint_db_path = get_temp_dir().join("cln_mint");
let cln_backend = create_cln_backend(&cln_client).await?;
let lnd_mint_port = 8087;
let lnd_backend = create_lnd_backend(&lnd_two_client).await?;
match mint_db_kind.as_str() {
"MEMORY" => {
start_cln_mint(addr, port, MintMemoryDatabase::default(), cln_path).await?;
tokio::spawn(async move {
create_mint(
mint_addr,
cln_mint_port,
MintMemoryDatabase::default(),
cln_backend,
)
.await
.expect("Could not start cln mint");
});
create_mint(
mint_addr,
lnd_mint_port,
MintMemoryDatabase::default(),
lnd_backend,
)
.await?;
}
"SQLITE" => {
let sqlite_db = MintSqliteDatabase::new(&db_path).await?;
tokio::spawn(async move {
let sqlite_db = MintSqliteDatabase::new(&cln_mint_db_path)
.await
.expect("Could not create mint db");
sqlite_db.migrate().await;
create_mint(mint_addr, cln_mint_port, sqlite_db, cln_backend)
.await
.expect("Could not start cln mint");
});
let sqlite_db = MintSqliteDatabase::new(&lnd_mint_db_path).await?;
sqlite_db.migrate().await;
start_cln_mint(addr, port, sqlite_db, cln_path).await?;
create_mint(mint_addr, lnd_mint_port, sqlite_db, lnd_backend).await?;
}
"REDB" => {
let redb_db = MintRedbDatabase::new(&db_path).unwrap();
start_cln_mint(addr, port, redb_db, cln_path).await?;
tokio::spawn(async move {
let redb_db = MintRedbDatabase::new(&cln_mint_db_path).unwrap();
create_mint(mint_addr, cln_mint_port, redb_db, cln_backend)
.await
.expect("Could not start cln mint");
});
let redb_db = MintRedbDatabase::new(&lnd_mint_db_path).unwrap();
create_mint(mint_addr, lnd_mint_port, redb_db, lnd_backend).await?;
}
_ => panic!("Unknown mint db type: {}", mint_db_kind),
};

View File

@@ -5,7 +5,9 @@ use axum::Router;
use cdk::mint::Mint;
use tokio::sync::Notify;
use tower_http::cors::CorsLayer;
use tracing::instrument;
#[instrument(skip_all)]
pub async fn start_mint(addr: &str, port: u16, mint: Mint) -> Result<()> {
let mint_arc = Arc::new(mint);

View File

@@ -1,17 +1,20 @@
use std::env;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use anyhow::Result;
use bip39::Mnemonic;
use cdk::cdk_database::{self, MintDatabase};
use cdk::cdk_lightning::{self, MintLightning};
use cdk::mint::{FeeReserve, MintBuilder, MintMeltLimits};
use cdk::nuts::{CurrencyUnit, PaymentMethod};
use cdk_cln::Cln as CdkCln;
use cdk_lnd::Lnd as CdkLnd;
use ln_regtest_rs::bitcoin_client::BitcoinClient;
use ln_regtest_rs::bitcoind::Bitcoind;
use ln_regtest_rs::ln_client::{ClnClient, LightningClient, LndClient};
use ln_regtest_rs::lnd::Lnd;
use tracing::instrument;
use crate::init_mint::start_mint;
@@ -20,11 +23,14 @@ pub const ZMQ_RAW_BLOCK: &str = "tcp://127.0.0.1:28332";
pub const ZMQ_RAW_TX: &str = "tcp://127.0.0.1:28333";
pub const BITCOIN_RPC_USER: &str = "testuser";
pub const BITCOIN_RPC_PASS: &str = "testpass";
const LND_ADDR: &str = "0.0.0.0:18449";
const LND_RPC_ADDR: &str = "localhost:10009";
const BITCOIN_DIR: &str = "bitcoin";
const LND_DIR: &str = "lnd";
pub const LND_ADDR: &str = "0.0.0.0:18449";
pub const LND_RPC_ADDR: &str = "localhost:10009";
pub const LND_TWO_ADDR: &str = "0.0.0.0:18410";
pub const LND_TWO_RPC_ADDR: &str = "localhost:10010";
pub fn get_mint_addr() -> String {
env::var("cdk_itests_mint_addr").expect("Temp dir set")
@@ -77,23 +83,31 @@ pub fn init_bitcoin_client() -> Result<BitcoinClient> {
}
pub fn get_cln_dir(name: &str) -> PathBuf {
let dir = get_temp_dir().join(name);
let dir = get_temp_dir().join("cln").join(name);
std::fs::create_dir_all(&dir).unwrap();
dir
}
pub fn get_lnd_dir() -> PathBuf {
let dir = get_temp_dir().join(LND_DIR);
pub fn get_lnd_dir(name: &str) -> PathBuf {
let dir = get_temp_dir().join("lnd").join(name);
std::fs::create_dir_all(&dir).unwrap();
dir
}
pub async fn init_lnd() -> Lnd {
pub fn get_lnd_cert_file_path(lnd_dir: &Path) -> PathBuf {
lnd_dir.join("tls.cert")
}
pub fn get_lnd_macaroon_path(lnd_dir: &Path) -> PathBuf {
lnd_dir.join("data/chain/bitcoin/regtest/admin.macaroon")
}
pub async fn init_lnd(lnd_dir: PathBuf, lnd_addr: &str, lnd_rpc_addr: &str) -> Lnd {
Lnd::new(
get_bitcoin_dir(),
get_lnd_dir(),
LND_ADDR.parse().unwrap(),
LND_RPC_ADDR.to_string(),
lnd_dir,
lnd_addr.parse().unwrap(),
lnd_rpc_addr.to_string(),
BITCOIN_RPC_USER.to_string(),
BITCOIN_RPC_PASS.to_string(),
ZMQ_RAW_BLOCK.to_string(),
@@ -101,16 +115,11 @@ pub async fn init_lnd() -> Lnd {
)
}
pub async fn init_lnd_client() -> Result<LndClient> {
let lnd_dir = get_lnd_dir();
let cert_file = lnd_dir.join("tls.cert");
let macaroon_file = lnd_dir.join("data/chain/bitcoin/regtest/admin.macaroon");
LndClient::new(
format!("https://{}", LND_RPC_ADDR).parse().unwrap(),
cert_file,
macaroon_file,
)
.await
pub fn generate_block(bitcoin_client: &BitcoinClient) -> Result<()> {
let mine_to_address = bitcoin_client.get_new_address()?;
bitcoin_client.generate_blocks(&mine_to_address, 10)?;
Ok(())
}
pub async fn create_cln_backend(cln_client: &ClnClient) -> Result<CdkCln> {
@@ -124,14 +133,27 @@ pub async fn create_cln_backend(cln_client: &ClnClient) -> Result<CdkCln> {
Ok(CdkCln::new(rpc_path, fee_reserve).await?)
}
pub async fn start_cln_mint<D>(addr: &str, port: u16, database: D, dir: PathBuf) -> Result<()>
pub async fn create_lnd_backend(lnd_client: &LndClient) -> Result<CdkLnd> {
let fee_reserve = FeeReserve {
min_fee_reserve: 1.into(),
percent_fee_reserve: 1.0,
};
Ok(CdkLnd::new(
lnd_client.address.clone(),
lnd_client.cert_file.clone(),
lnd_client.macaroon_file.clone(),
fee_reserve,
)
.await?)
}
#[instrument(skip_all)]
pub async fn create_mint<D, L>(addr: &str, port: u16, database: D, lighting: L) -> Result<()>
where
D: MintDatabase<Err = cdk_database::Error> + Send + Sync + 'static,
L: MintLightning<Err = cdk_lightning::Error> + Send + Sync + 'static,
{
let cln_client = ClnClient::new(dir.clone(), None).await?;
let cln_backend = create_cln_backend(&cln_client).await?;
let mut mint_builder = MintBuilder::new();
mint_builder = mint_builder.with_localstore(Arc::new(database));
@@ -140,7 +162,7 @@ where
CurrencyUnit::Sat,
PaymentMethod::Bolt11,
MintMeltLimits::new(1, 5_000),
Arc::new(cln_backend),
Arc::new(lighting),
);
let mnemonic = Mnemonic::generate(12)?;
@@ -165,7 +187,7 @@ where
{
let ln_address = ln_client.get_new_onchain_address().await?;
bitcoin_client.send_to_address(&ln_address, 2_000_000)?;
bitcoin_client.send_to_address(&ln_address, 5_000_000)?;
ln_client.wait_chain_sync().await?;
@@ -177,11 +199,7 @@ where
Ok(())
}
pub async fn open_channel<C1, C2>(
bitcoin_client: &BitcoinClient,
cln_client: &C1,
lnd_client: &C2,
) -> Result<()>
pub async fn open_channel<C1, C2>(cln_client: &C1, lnd_client: &C2) -> Result<()>
where
C1: LightningClient,
C2: LightningClient,
@@ -197,19 +215,13 @@ where
.await
.unwrap();
cln_client.wait_chain_sync().await?;
lnd_client.wait_chain_sync().await?;
lnd_client
.open_channel(1_500_000, &cln_pubkey.to_string(), Some(750_000))
.await
.unwrap();
let mine_to_address = bitcoin_client.get_new_address()?;
bitcoin_client.generate_blocks(&mine_to_address, 10)?;
cln_client.wait_chain_sync().await?;
lnd_client.wait_chain_sync().await?;
cln_client.wait_channels_active().await?;
lnd_client.wait_channels_active().await?;
Ok(())
}

View File

@@ -15,17 +15,32 @@ use cdk::nuts::{
use cdk::wallet::client::{HttpClient, MintConnector};
use cdk::wallet::{Wallet, WalletSubscription};
use cdk_integration_tests::init_regtest::{
get_cln_dir, get_mint_url, get_mint_ws_url, init_lnd_client,
get_cln_dir, get_lnd_cert_file_path, get_lnd_dir, get_lnd_macaroon_path, get_mint_port,
get_mint_url, get_mint_ws_url, LND_RPC_ADDR, LND_TWO_RPC_ADDR,
};
use futures::{SinkExt, StreamExt};
use lightning_invoice::Bolt11Invoice;
use ln_regtest_rs::ln_client::{ClnClient, LightningClient};
use ln_regtest_rs::ln_client::{ClnClient, LightningClient, LndClient};
use ln_regtest_rs::InvoiceStatus;
use serde_json::json;
use tokio::time::timeout;
use tokio_tungstenite::connect_async;
use tokio_tungstenite::tungstenite::protocol::Message;
// This is the ln wallet we use to send/receive ln payements as the wallet
async fn init_lnd_client() -> LndClient {
let lnd_dir = get_lnd_dir("one");
let cert_file = lnd_dir.join("tls.cert");
let macaroon_file = lnd_dir.join("data/chain/bitcoin/regtest/admin.macaroon");
LndClient::new(
format!("https://{}", LND_RPC_ADDR),
cert_file,
macaroon_file,
)
.await
.unwrap()
}
async fn get_notification<T: StreamExt<Item = Result<Message, E>> + Unpin, E: Debug>(
reader: &mut T,
timeout_to_wait: Duration,
@@ -60,7 +75,7 @@ async fn get_notification<T: StreamExt<Item = Result<Message, E>> + Unpin, E: De
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_regtest_mint_melt_round_trip() -> Result<()> {
let lnd_client = init_lnd_client().await.unwrap();
let lnd_client = init_lnd_client().await;
let wallet = Wallet::new(
&get_mint_url(),
@@ -143,7 +158,7 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_regtest_mint_melt() -> Result<()> {
let lnd_client = init_lnd_client().await?;
let lnd_client = init_lnd_client().await;
let wallet = Wallet::new(
&get_mint_url(),
@@ -174,7 +189,7 @@ async fn test_regtest_mint_melt() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_restore() -> Result<()> {
let lnd_client = init_lnd_client().await?;
let lnd_client = init_lnd_client().await;
let seed = Mnemonic::generate(12)?.to_seed_normalized("");
let wallet = Wallet::new(
@@ -231,7 +246,8 @@ async fn test_restore() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_pay_invoice_twice() -> Result<()> {
let lnd_client = init_lnd_client().await?;
let lnd_client = init_lnd_client().await;
let seed = Mnemonic::generate(12)?.to_seed_normalized("");
let wallet = Wallet::new(
&get_mint_url(),
@@ -243,7 +259,10 @@ async fn test_pay_invoice_twice() -> Result<()> {
let mint_quote = wallet.mint_quote(100.into(), None).await?;
lnd_client.pay_invoice(mint_quote.request).await?;
lnd_client
.pay_invoice(mint_quote.request)
.await
.expect("Could not pay invoice");
let proofs = wallet
.mint(&mint_quote.id, SplitTarget::default(), None)
@@ -266,8 +285,8 @@ async fn test_pay_invoice_twice() -> Result<()> {
match melt_two {
Err(err) => match err {
cdk::Error::RequestAlreadyPaid => (),
_ => {
bail!("Wrong invoice already paid");
err => {
bail!("Wrong invoice already paid: {}", err.to_string());
}
},
Ok(_) => {
@@ -284,7 +303,7 @@ async fn test_pay_invoice_twice() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_internal_payment() -> Result<()> {
let lnd_client = init_lnd_client().await?;
let lnd_client = init_lnd_client().await;
let seed = Mnemonic::generate(12)?.to_seed_normalized("");
let wallet = Wallet::new(
@@ -328,13 +347,33 @@ async fn test_internal_payment() -> Result<()> {
.await
.unwrap();
let cln_one_dir = get_cln_dir("one");
let cln_client = ClnClient::new(cln_one_dir.clone(), None).await?;
let check_paid = match get_mint_port() {
8085 => {
let cln_one_dir = get_cln_dir("one");
let cln_client = ClnClient::new(cln_one_dir.clone(), None).await?;
let payment_hash = Bolt11Invoice::from_str(&mint_quote.request)?;
let check_paid = cln_client
.check_incoming_payment_status(&payment_hash.payment_hash().to_string())
.await?;
let payment_hash = Bolt11Invoice::from_str(&mint_quote.request)?;
cln_client
.check_incoming_payment_status(&payment_hash.payment_hash().to_string())
.await
.expect("Could not check invoice")
}
8087 => {
let lnd_two_dir = get_lnd_dir("two");
let lnd_client = LndClient::new(
format!("https://{}", LND_TWO_RPC_ADDR),
get_lnd_cert_file_path(&lnd_two_dir),
get_lnd_macaroon_path(&lnd_two_dir),
)
.await?;
let payment_hash = Bolt11Invoice::from_str(&mint_quote.request)?;
lnd_client
.check_incoming_payment_status(&payment_hash.payment_hash().to_string())
.await
.expect("Could not check invoice")
}
_ => panic!("Unknown mint port"),
};
match check_paid {
InvoiceStatus::Unpaid => (),
@@ -356,7 +395,7 @@ async fn test_internal_payment() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_cached_mint() -> Result<()> {
let lnd_client = init_lnd_client().await.unwrap();
let lnd_client = init_lnd_client().await;
let wallet = Wallet::new(
&get_mint_url(),

View File

@@ -25,10 +25,12 @@ use error::Error;
use fedimint_tonic_lnd::lnrpc::fee_limit::Limit;
use fedimint_tonic_lnd::lnrpc::payment::PaymentStatus;
use fedimint_tonic_lnd::lnrpc::FeeLimit;
use fedimint_tonic_lnd::tonic::Code;
use fedimint_tonic_lnd::Client;
use futures::{Stream, StreamExt};
use tokio::sync::Mutex;
use tokio_util::sync::CancellationToken;
use tracing::instrument;
pub mod error;
@@ -75,6 +77,7 @@ impl Lnd {
impl MintLightning for Lnd {
type Err = cdk_lightning::Error;
#[instrument(skip_all)]
fn get_settings(&self) -> Settings {
Settings {
mpp: false,
@@ -83,14 +86,17 @@ impl MintLightning for Lnd {
}
}
#[instrument(skip_all)]
fn is_wait_invoice_active(&self) -> bool {
self.wait_invoice_is_active.load(Ordering::SeqCst)
}
#[instrument(skip_all)]
fn cancel_wait_invoice(&self) {
self.wait_invoice_cancel_token.cancel()
}
#[instrument(skip_all)]
async fn wait_any_invoice(
&self,
) -> Result<Pin<Box<dyn Stream<Item = String> + Send>>, Self::Err> {
@@ -163,6 +169,7 @@ impl MintLightning for Lnd {
.boxed())
}
#[instrument(skip_all)]
async fn get_payment_quote(
&self,
melt_quote_request: &MeltQuoteBolt11Request,
@@ -189,6 +196,7 @@ impl MintLightning for Lnd {
})
}
#[instrument(skip_all)]
async fn pay_invoice(
&self,
melt_quote: mint::MeltQuote,
@@ -196,6 +204,23 @@ impl MintLightning for Lnd {
max_fee: Option<Amount>,
) -> Result<PayInvoiceResponse, Self::Err> {
let payment_request = melt_quote.request;
let bolt11 = Bolt11Invoice::from_str(&payment_request)?;
let pay_state = self
.check_outgoing_payment(&bolt11.payment_hash().to_string())
.await?;
match pay_state.status {
MeltQuoteState::Unpaid | MeltQuoteState::Unknown | MeltQuoteState::Failed => (),
MeltQuoteState::Paid => {
tracing::debug!("Melt attempted on invoice already paid");
return Err(Self::Err::InvoiceAlreadyPaid);
}
MeltQuoteState::Pending => {
tracing::debug!("Melt attempted on invoice already pending");
return Err(Self::Err::InvoicePaymentPending);
}
}
let amount_msat: u64 = match melt_quote.msat_to_pay {
Some(amount_msat) => amount_msat.into(),
@@ -225,7 +250,10 @@ impl MintLightning for Lnd {
.lightning()
.send_payment_sync(fedimint_tonic_lnd::tonic::Request::new(pay_req))
.await
.map_err(|_| Error::PaymentFailed)?
.map_err(|err| {
tracing::warn!("Lightning payment failed: {}", err);
Error::PaymentFailed
})?
.into_inner();
let total_amount = payment_response
@@ -250,6 +278,7 @@ impl MintLightning for Lnd {
})
}
#[instrument(skip(self, description))]
async fn create_invoice(
&self,
amount: Amount,
@@ -287,6 +316,7 @@ impl MintLightning for Lnd {
})
}
#[instrument(skip(self))]
async fn check_incoming_invoice_status(
&self,
request_lookup_id: &str,
@@ -319,6 +349,7 @@ impl MintLightning for Lnd {
}
}
#[instrument(skip(self))]
async fn check_outgoing_payment(
&self,
payment_hash: &str,
@@ -327,15 +358,32 @@ impl MintLightning for Lnd {
payment_hash: hex::decode(payment_hash).map_err(|_| Error::InvalidHash)?,
no_inflight_updates: true,
};
let mut payment_stream = self
let payment_response = self
.client
.lock()
.await
.router()
.track_payment_v2(track_request)
.await
.unwrap()
.into_inner();
.await;
let mut payment_stream = match payment_response {
Ok(stream) => stream.into_inner(),
Err(err) => {
let err_code = err.code();
if err_code == Code::NotFound {
return Ok(PayInvoiceResponse {
payment_lookup_id: payment_hash.to_string(),
payment_preimage: None,
status: MeltQuoteState::Unknown,
total_spent: Amount::ZERO,
unit: self.get_settings().unit,
});
} else {
return Err(cdk_lightning::Error::UnknownPaymentState);
}
}
};
while let Some(update_result) = payment_stream.next().await {
match update_result {

View File

@@ -21,6 +21,7 @@ use crate::util::unix_time;
use crate::{cdk_lightning, Amount, Error};
impl Mint {
#[instrument(skip_all)]
fn check_melt_request_acceptable(
&self,
amount: Amount,

View File

@@ -12,11 +12,11 @@ cleanup() {
wait $CDK_ITEST_MINT_BIN_PID
echo "Mint binary terminated"
# Kill processes
lncli --lnddir="$cdk_itests/lnd" --network=regtest stop
lightning-cli --regtest --lightning-dir="$cdk_itests/one/" stop
lightning-cli --regtest --lightning-dir="$cdk_itests/two/" stop
lncli --lnddir="$cdk_itests/lnd/one" --network=regtest stop
lncli --lnddir="$cdk_itests/lnd/two" --network=regtest --rpcserver=localhost:10010 stop
lightning-cli --regtest --lightning-dir="$cdk_itests/cln/one/" stop
lightning-cli --regtest --lightning-dir="$cdk_itests/cln/two/" stop
bitcoin-cli --datadir="$cdk_itests/bitcoin" -rpcuser=testuser -rpcpassword=testpass -rpcport=18443 stop
# Remove the temporary directory
@@ -47,7 +47,10 @@ export MINT_DATABASE="$1";
cargo build -p cdk-integration-tests
cargo build --bin regtest_mint
# cargo run --bin regtest_mint > "$cdk_itests/mint.log" 2>&1 &
cargo run --bin regtest_mint &
echo $cdk_itests
# Capture its PID
CDK_ITEST_MINT_BIN_PID=$!
@@ -84,9 +87,13 @@ done
# Run cargo test
cargo test -p cdk-integration-tests --test regtest
# Run cargo test with the http_subscription feature
# # Run cargo test with the http_subscription feature
cargo test -p cdk-integration-tests --test regtest --features http_subscription
# Run tests with lnd mint
export cdk_itests_mint_port=8087;
cargo test -p cdk-integration-tests --test regtest
# Capture the exit status of cargo test
test_status=$?