diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58f86b77..c55fe200 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,7 @@ jobs: self-care: name: Flake self-check runs-on: ubuntu-latest + timeout-minutes: 15 steps: - uses: actions/checkout@v4 - name: Check Nix flake inputs @@ -23,6 +24,7 @@ jobs: pre-commit-checks: name: "Cargo fmt, typos" runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: checkout uses: actions/checkout@v4 @@ -45,6 +47,7 @@ jobs: examples: name: "Run examples" runs-on: ubuntu-latest + timeout-minutes: 15 strategy: matrix: build-args: @@ -69,6 +72,7 @@ jobs: clippy: name: "Stable build, clippy and test" runs-on: ubuntu-latest + timeout-minutes: 15 strategy: matrix: build-args: @@ -128,6 +132,7 @@ jobs: itest: name: "Integration regtest tests" runs-on: ubuntu-latest + timeout-minutes: 15 strategy: matrix: build-args: @@ -138,7 +143,6 @@ jobs: [ REDB, SQLITE, - MEMORY, ] steps: - name: checkout @@ -157,6 +161,7 @@ jobs: fake-wallet-itest: name: "Integration fake wallet tests" runs-on: ubuntu-latest + timeout-minutes: 15 strategy: matrix: build-args: @@ -167,7 +172,6 @@ jobs: [ REDB, SQLITE, - MEMORY, ] steps: - name: checkout @@ -186,6 +190,7 @@ jobs: pure-itest: name: "Integration fake wallet tests" runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: checkout uses: actions/checkout@v4 @@ -201,6 +206,7 @@ jobs: msrv-build: name: "MSRV build" runs-on: ubuntu-latest + timeout-minutes: 15 strategy: matrix: build-args: @@ -233,6 +239,7 @@ jobs: db-msrv-build: name: "DB MSRV build" runs-on: ubuntu-latest + timeout-minutes: 15 strategy: matrix: build-args: @@ -255,6 +262,7 @@ jobs: check-wasm: name: Check WASM runs-on: ubuntu-latest + timeout-minutes: 15 strategy: matrix: rust: @@ -283,6 +291,7 @@ jobs: check-wasm-msrv: name: Check WASM runs-on: ubuntu-latest + timeout-minutes: 15 strategy: matrix: rust: diff --git a/.gitignore b/.gitignore index a654112d..fa01a359 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ config.toml .pre-commit-config.yaml result Cargo.lock +.aider* diff --git a/crates/cdk-integration-tests/src/bin/fake_wallet.rs b/crates/cdk-integration-tests/src/bin/fake_wallet.rs deleted file mode 100644 index 9d83e96c..00000000 --- a/crates/cdk-integration-tests/src/bin/fake_wallet.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::env; - -use anyhow::Result; -use cdk::cdk_database::mint_memory::MintMemoryDatabase; -use cdk_integration_tests::init_fake_wallet::start_fake_mint; -use cdk_integration_tests::init_regtest::get_temp_dir; -use cdk_redb::MintRedbDatabase; -use cdk_sqlite::MintSqliteDatabase; - -#[tokio::main] -async fn main() -> Result<()> { - let addr = "127.0.0.1"; - let port = 8086; - - let mint_db_kind = env::var("MINT_DATABASE")?; - - match mint_db_kind.as_str() { - "MEMORY" => { - start_fake_mint(addr, port, MintMemoryDatabase::default()).await?; - } - "SQLITE" => { - let sqlite_db = MintSqliteDatabase::new(&get_temp_dir().join("mint")).await?; - sqlite_db.migrate().await; - start_fake_mint(addr, port, sqlite_db).await?; - } - "REDB" => { - let redb_db = MintRedbDatabase::new(&get_temp_dir().join("mint")).unwrap(); - start_fake_mint(addr, port, redb_db).await?; - } - _ => panic!("Unknown mint db type: {}", mint_db_kind), - }; - Ok(()) -} diff --git a/crates/cdk-integration-tests/src/bin/regtest_mint.rs b/crates/cdk-integration-tests/src/bin/regtest_mint.rs deleted file mode 100644 index 3715c719..00000000 --- a/crates/cdk-integration-tests/src/bin/regtest_mint.rs +++ /dev/null @@ -1,209 +0,0 @@ -use std::env; - -use anyhow::Result; -use cdk::cdk_database::mint_memory::MintMemoryDatabase; -use cdk_integration_tests::init_regtest::{ - 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, LndClient}; -use tracing_subscriber::EnvFilter; - -const CLN_ADDR: &str = "127.0.0.1:19846"; -const CLN_TWO_ADDR: &str = "127.0.0.1:19847"; - -#[tokio::main] -async fn main() -> Result<()> { - let default_filter = "debug"; - - 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, rustls_filter - )); - - tracing_subscriber::fmt().with_env_filter(env_filter).init(); - - let mut bitcoind = init_bitcoind(); - bitcoind.start_bitcoind()?; - - let bitcoin_client = init_bitcoin_client()?; - bitcoin_client.create_wallet().ok(); - bitcoin_client.load_wallet()?; - - let new_add = bitcoin_client.get_new_address()?; - bitcoin_client.generate_blocks(&new_add, 200).unwrap(); - - let cln_one_dir = get_cln_dir("one"); - let mut clnd = Clnd::new( - get_bitcoin_dir(), - cln_one_dir.clone(), - CLN_ADDR.into(), - BITCOIN_RPC_USER.to_string(), - BITCOIN_RPC_PASS.to_string(), - ); - clnd.start_clnd()?; - - let cln_client = ClnClient::new(cln_one_dir.clone(), None).await?; - - cln_client.wait_chain_sync().await.unwrap(); - - fund_ln(&bitcoin_client, &cln_client).await.unwrap(); - - // Create second cln - let cln_two_dir = get_cln_dir("two"); - let mut clnd_two = Clnd::new( - get_bitcoin_dir(), - cln_two_dir.clone(), - CLN_TWO_ADDR.into(), - BITCOIN_RPC_USER.to_string(), - BITCOIN_RPC_PASS.to_string(), - ); - clnd_two.start_clnd()?; - - let cln_two_client = ClnClient::new(cln_two_dir.clone(), None).await?; - - cln_two_client.wait_chain_sync().await.unwrap(); - - fund_ln(&bitcoin_client, &cln_two_client).await.unwrap(); - - 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 = 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(); - - // 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 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 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" => { - 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" => { - tokio::spawn(async move { - let cln_sqlite_db = MintSqliteDatabase::new(&cln_mint_db_path) - .await - .expect("Could not create CLN mint db"); - cln_sqlite_db.migrate().await; - create_mint(mint_addr, cln_mint_port, cln_sqlite_db, cln_backend) - .await - .expect("Could not start cln mint"); - }); - - let lnd_sqlite_db = MintSqliteDatabase::new(&lnd_mint_db_path).await?; - lnd_sqlite_db.migrate().await; - create_mint(mint_addr, lnd_mint_port, lnd_sqlite_db, lnd_backend).await?; - } - "REDB" => { - tokio::spawn(async move { - let cln_redb_db = MintRedbDatabase::new(&cln_mint_db_path).unwrap(); - create_mint(mint_addr, cln_mint_port, cln_redb_db, cln_backend) - .await - .expect("Could not start cln mint"); - }); - - let lnd_redb_db = MintRedbDatabase::new(&lnd_mint_db_path).unwrap(); - create_mint(mint_addr, lnd_mint_port, lnd_redb_db, lnd_backend).await?; - } - _ => panic!("Unknown mint db type: {}", mint_db_kind), - }; - - Ok(()) -} diff --git a/crates/cdk-integration-tests/src/bin/start_regtest.rs b/crates/cdk-integration-tests/src/bin/start_regtest.rs new file mode 100644 index 00000000..5abe65cf --- /dev/null +++ b/crates/cdk-integration-tests/src/bin/start_regtest.rs @@ -0,0 +1,63 @@ +use std::fs::OpenOptions; +use std::io::Write; +use std::sync::Arc; +use std::time::Duration; + +use anyhow::{bail, Result}; +use cdk_integration_tests::init_regtest::{get_temp_dir, start_regtest_end}; +use tokio::signal; +use tokio::sync::{oneshot, Notify}; +use tokio::time::timeout; +use tracing_subscriber::EnvFilter; + +fn signal_progress() { + let temp_dir = get_temp_dir(); + let mut pipe = OpenOptions::new() + .write(true) + .open(temp_dir.join("progress_pipe")) + .expect("Failed to open pipe"); + + pipe.write_all(b"checkpoint1\n") + .expect("Failed to write to pipe"); +} + +#[tokio::main] +async fn main() -> Result<()> { + let default_filter = "debug"; + + 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, rustls_filter + )); + + tracing_subscriber::fmt().with_env_filter(env_filter).init(); + + let shutdown_regtest = Arc::new(Notify::new()); + let shutdown_clone = shutdown_regtest.clone(); + let (tx, rx) = oneshot::channel(); + tokio::spawn(async move { + start_regtest_end(tx, shutdown_clone) + .await + .expect("Error starting regtest"); + }); + + match timeout(Duration::from_secs(300), rx).await { + Ok(_) => { + tracing::info!("Regtest set up"); + signal_progress(); + } + Err(_) => { + tracing::error!("regtest setup timed out after 5 minutes"); + bail!("Could not set up regtest"); + } + } + + signal::ctrl_c().await?; + + Ok(()) +} diff --git a/crates/cdk-integration-tests/src/init_fake_wallet.rs b/crates/cdk-integration-tests/src/init_fake_wallet.rs deleted file mode 100644 index b6f53ac2..00000000 --- a/crates/cdk-integration-tests/src/init_fake_wallet.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::collections::{HashMap, HashSet}; -use std::sync::Arc; - -use anyhow::Result; -use bip39::Mnemonic; -use cdk::cdk_database::{self, MintDatabase}; -use cdk::mint::{FeeReserve, MintBuilder, MintMeltLimits}; -use cdk::nuts::{CurrencyUnit, PaymentMethod}; -use cdk::types::QuoteTTL; -use cdk_fake_wallet::FakeWallet; -use tracing_subscriber::EnvFilter; - -use crate::init_mint::start_mint; - -pub async fn start_fake_mint(addr: &str, port: u16, database: D) -> Result<()> -where - D: MintDatabase + Send + Sync + 'static, -{ - let default_filter = "debug"; - - let sqlx_filter = "sqlx=warn"; - let hyper_filter = "hyper=warn"; - - let env_filter = EnvFilter::new(format!( - "{},{},{}", - default_filter, sqlx_filter, hyper_filter - )); - - // Parse input - tracing_subscriber::fmt().with_env_filter(env_filter).init(); - - let fee_reserve = FeeReserve { - min_fee_reserve: 1.into(), - percent_fee_reserve: 0.0, - }; - - let fake_wallet = FakeWallet::new(fee_reserve, HashMap::default(), HashSet::default(), 0); - - let mut mint_builder = MintBuilder::new(); - - let localstore = Arc::new(database); - mint_builder = mint_builder.with_localstore(localstore.clone()); - - mint_builder = mint_builder.add_ln_backend( - CurrencyUnit::Sat, - PaymentMethod::Bolt11, - MintMeltLimits::new(1, 5_000), - Arc::new(fake_wallet), - ); - - let fee_reserve = FeeReserve { - min_fee_reserve: 1.into(), - percent_fee_reserve: 0.0, - }; - - let fake_wallet = FakeWallet::new(fee_reserve, HashMap::default(), HashSet::default(), 0); - - mint_builder = mint_builder.add_ln_backend( - CurrencyUnit::Usd, - PaymentMethod::Bolt11, - MintMeltLimits::new(1, 5_000), - Arc::new(fake_wallet), - ); - - let mnemonic = Mnemonic::generate(12)?; - - mint_builder = mint_builder - .with_name("fake test mint".to_string()) - .with_description("fake test mint".to_string()) - .with_seed(mnemonic.to_seed_normalized("").to_vec()); - - localstore - .set_mint_info(mint_builder.mint_info.clone()) - .await?; - let quote_ttl = QuoteTTL::new(10000, 10000); - localstore.set_quote_ttl(quote_ttl).await?; - - let mint = mint_builder.build().await?; - - start_mint(addr, port, mint).await?; - - Ok(()) -} diff --git a/crates/cdk-integration-tests/src/init_mint.rs b/crates/cdk-integration-tests/src/init_mint.rs deleted file mode 100644 index 6ff23803..00000000 --- a/crates/cdk-integration-tests/src/init_mint.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::sync::Arc; - -use anyhow::Result; -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); - - let v1_service = cdk_axum::create_mint_router(Arc::clone(&mint_arc)) - .await - .unwrap(); - - let mint_service = Router::new() - .merge(v1_service) - .layer(CorsLayer::permissive()); - - let mint = Arc::clone(&mint_arc); - - let shutdown = Arc::new(Notify::new()); - - tokio::spawn({ - let shutdown = Arc::clone(&shutdown); - async move { mint.wait_for_paid_invoices(shutdown).await } - }); - - tracing::info!("Staring Axum server"); - axum::Server::bind(&format!("{}:{}", addr, port).as_str().parse().unwrap()) - .serve(mint_service.into_make_service()) - .await?; - - Ok(()) -} diff --git a/crates/cdk-integration-tests/src/init_regtest.rs b/crates/cdk-integration-tests/src/init_regtest.rs index 4675b04b..f94f3a31 100644 --- a/crates/cdk-integration-tests/src/init_regtest.rs +++ b/crates/cdk-integration-tests/src/init_regtest.rs @@ -3,21 +3,16 @@ 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::types::QuoteTTL; +use cdk::mint::FeeReserve; 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::cln::Clnd; use ln_regtest_rs::ln_client::{ClnClient, LightningClient, LndClient}; use ln_regtest_rs::lnd::Lnd; -use tracing::instrument; - -use crate::init_mint::start_mint; +use tokio::sync::oneshot::Sender; +use tokio::sync::Notify; pub const BITCOIND_ADDR: &str = "127.0.0.1:18443"; pub const ZMQ_RAW_BLOCK: &str = "tcp://127.0.0.1:28332"; @@ -33,6 +28,9 @@ 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 const CLN_ADDR: &str = "127.0.0.1:19846"; +pub const CLN_TWO_ADDR: &str = "127.0.0.1:19847"; + pub fn get_mint_addr() -> String { env::var("cdk_itests_mint_addr").expect("Temp dir set") } @@ -149,43 +147,6 @@ pub async fn create_lnd_backend(lnd_client: &LndClient) -> Result { .await?) } -#[instrument(skip_all)] -pub async fn create_mint(addr: &str, port: u16, database: D, lighting: L) -> Result<()> -where - D: MintDatabase + Send + Sync + 'static, - L: MintLightning + Send + Sync + 'static, -{ - let mut mint_builder = MintBuilder::new(); - let localstore = Arc::new(database); - mint_builder = mint_builder.with_localstore(localstore.clone()); - - mint_builder = mint_builder.add_ln_backend( - CurrencyUnit::Sat, - PaymentMethod::Bolt11, - MintMeltLimits::new(1, 5_000), - Arc::new(lighting), - ); - - let mnemonic = Mnemonic::generate(12)?; - - mint_builder = mint_builder - .with_name("regtest mint".to_string()) - .with_description("regtest mint".to_string()) - .with_seed(mnemonic.to_seed_normalized("").to_vec()); - - let mint = mint_builder.build().await?; - - localstore - .set_mint_info(mint_builder.mint_info.clone()) - .await?; - let quote_ttl = QuoteTTL::new(10000, 10000); - localstore.set_quote_ttl(quote_ttl).await?; - - start_mint(addr, port, mint).await?; - - Ok(()) -} - pub async fn fund_ln(bitcoin_client: &BitcoinClient, ln_client: &C) -> Result<()> where C: LightningClient, @@ -230,3 +191,123 @@ where Ok(()) } + +pub async fn start_regtest_end(sender: Sender<()>, notify: Arc) -> anyhow::Result<()> { + let mut bitcoind = init_bitcoind(); + bitcoind.start_bitcoind()?; + + let bitcoin_client = init_bitcoin_client()?; + bitcoin_client.create_wallet().ok(); + bitcoin_client.load_wallet()?; + + let new_add = bitcoin_client.get_new_address()?; + bitcoin_client.generate_blocks(&new_add, 200).unwrap(); + + let cln_one_dir = get_cln_dir("one"); + let mut clnd = Clnd::new( + get_bitcoin_dir(), + cln_one_dir.clone(), + CLN_ADDR.into(), + BITCOIN_RPC_USER.to_string(), + BITCOIN_RPC_PASS.to_string(), + ); + clnd.start_clnd()?; + + let cln_client = ClnClient::new(cln_one_dir.clone(), None).await?; + + cln_client.wait_chain_sync().await.unwrap(); + + fund_ln(&bitcoin_client, &cln_client).await.unwrap(); + + // Create second cln + let cln_two_dir = get_cln_dir("two"); + let mut clnd_two = Clnd::new( + get_bitcoin_dir(), + cln_two_dir.clone(), + CLN_TWO_ADDR.into(), + BITCOIN_RPC_USER.to_string(), + BITCOIN_RPC_PASS.to_string(), + ); + clnd_two.start_clnd()?; + + let cln_two_client = ClnClient::new(cln_two_dir.clone(), None).await?; + + cln_two_client.wait_chain_sync().await.unwrap(); + + fund_ln(&bitcoin_client, &cln_two_client).await.unwrap(); + + 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 = 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(); + + // 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 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?; + } + + // Send notification that regtest set up is complete + sender.send(()).expect("Could not send oneshot"); + + // Wait until we are told to shutdown + // If we return the bitcoind, lnd, and cln will be dropped and shutdown + notify.notified().await; + + Ok(()) +} diff --git a/crates/cdk-integration-tests/src/lib.rs b/crates/cdk-integration-tests/src/lib.rs index 8625c7bf..70b47187 100644 --- a/crates/cdk-integration-tests/src/lib.rs +++ b/crates/cdk-integration-tests/src/lib.rs @@ -1,24 +1,13 @@ -use std::str::FromStr; use std::sync::Arc; use anyhow::{bail, Result}; use cdk::amount::{Amount, SplitTarget}; -use cdk::dhke::construct_proofs; -use cdk::mint_url::MintUrl; use cdk::nuts::nut00::ProofsMethods; -use cdk::nuts::nut17::Params; -use cdk::nuts::{ - CurrencyUnit, Id, KeySet, MintBolt11Request, MintQuoteBolt11Request, MintQuoteState, - NotificationPayload, PreMintSecrets, Proofs, State, -}; -use cdk::wallet::client::{HttpClient, MintConnector}; -use cdk::wallet::subscription::SubscriptionManager; +use cdk::nuts::{MintQuoteState, NotificationPayload, State}; use cdk::wallet::WalletSubscription; use cdk::Wallet; -use tokio::time::{timeout, Duration}; +use tokio::time::{sleep, timeout, Duration}; -pub mod init_fake_wallet; -pub mod init_mint; pub mod init_pure_tests; pub mod init_regtest; @@ -30,19 +19,7 @@ pub async fn wallet_mint( ) -> Result<()> { let quote = wallet.mint_quote(amount, description).await?; - let mut subscription = wallet - .subscribe(WalletSubscription::Bolt11MintQuoteState(vec![quote - .id - .clone()])) - .await; - - while let Some(msg) = subscription.recv().await { - if let NotificationPayload::MintQuoteBolt11Response(response) = msg { - if response.state == MintQuoteState::Paid { - break; - } - } - } + wait_for_mint_to_be_paid(&wallet, "e.id, 60).await?; let proofs = wallet.mint("e.id, split_target, None).await?; @@ -53,70 +30,6 @@ pub async fn wallet_mint( Ok(()) } -pub async fn mint_proofs( - mint_url: &str, - amount: Amount, - keyset_id: Id, - mint_keys: &KeySet, - description: Option, -) -> anyhow::Result { - println!("Minting for ecash"); - println!(); - - let wallet_client = HttpClient::new(MintUrl::from_str(mint_url)?); - - let request = MintQuoteBolt11Request { - amount, - unit: CurrencyUnit::Sat, - description, - pubkey: None, - }; - - let mint_quote = wallet_client.post_mint_quote(request).await?; - - println!("Please pay: {}", mint_quote.request); - - let subscription_client = SubscriptionManager::new(Arc::new(wallet_client.clone())); - - let mut subscription = subscription_client - .subscribe( - mint_url.parse()?, - Params { - filters: vec![mint_quote.quote.clone()], - kind: cdk::nuts::nut17::Kind::Bolt11MintQuote, - id: "sub".into(), - }, - ) - .await; - - while let Some(msg) = subscription.recv().await { - if let NotificationPayload::MintQuoteBolt11Response(response) = msg { - if response.state == MintQuoteState::Paid { - break; - } - } - } - - let premint_secrets = PreMintSecrets::random(keyset_id, amount, &SplitTarget::default())?; - - let request = MintBolt11Request { - quote: mint_quote.quote, - outputs: premint_secrets.blinded_messages(), - signature: None, - }; - - let mint_response = wallet_client.post_mint(request).await?; - - let pre_swap_proofs = construct_proofs( - mint_response.signatures, - premint_secrets.rs(), - premint_secrets.secrets(), - &mint_keys.clone().keys, - )?; - - Ok(pre_swap_proofs) -} - // Get all pending from wallet and attempt to swap // Will panic if there are no pending // Will return Ok if swap fails as expected @@ -154,6 +67,7 @@ pub async fn attempt_to_swap_pending(wallet: &Wallet) -> Result<()> { Ok(()) } +#[allow(clippy::incompatible_msrv)] pub async fn wait_for_mint_to_be_paid( wallet: &Wallet, mint_quote_id: &str, @@ -164,7 +78,6 @@ pub async fn wait_for_mint_to_be_paid( mint_quote_id.to_owned(), ])) .await; - // Create the timeout future let wait_future = async { while let Some(msg) = subscription.recv().await { @@ -177,9 +90,36 @@ pub async fn wait_for_mint_to_be_paid( Ok(()) }; - // Wait for either the payment to complete or timeout - match timeout(Duration::from_secs(timeout_secs), wait_future).await { - Ok(result) => result, - Err(_) => Err(anyhow::anyhow!("Timeout waiting for mint quote to be paid")), + let timeout_future = timeout(Duration::from_secs(timeout_secs), wait_future); + + let check_interval = Duration::from_secs(5); + + let periodic_task = async { + loop { + match wallet.mint_quote_state(mint_quote_id).await { + Ok(result) => { + if result.state == MintQuoteState::Paid { + tracing::info!("mint quote paid via poll"); + return Ok(()); + } + } + Err(e) => { + tracing::error!("Could not check mint quote status: {:?}", e); + } + } + sleep(check_interval).await; + } + }; + + tokio::select! { + result = timeout_future => { + match result { + Ok(payment_result) => payment_result, + Err(_) => Err(anyhow::anyhow!("Timeout waiting for mint quote to be paid")), + } + } + result = periodic_task => { + result // Now propagates the result from periodic checks + } } } diff --git a/crates/cdk-integration-tests/tests/fake_wallet.rs b/crates/cdk-integration-tests/tests/fake_wallet.rs index 59370a16..99a782f8 100644 --- a/crates/cdk-integration-tests/tests/fake_wallet.rs +++ b/crates/cdk-integration-tests/tests/fake_wallet.rs @@ -377,6 +377,41 @@ async fn test_fake_melt_change_in_quote() -> Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_database_type() -> Result<()> { + // Get the database type and work dir from environment + let db_type = std::env::var("MINT_DATABASE").expect("MINT_DATABASE env var should be set"); + let work_dir = + std::env::var("CDK_MINTD_WORK_DIR").expect("CDK_MINTD_WORK_DIR env var should be set"); + + // Check that the correct database file exists + match db_type.as_str() { + "REDB" => { + let db_path = std::path::Path::new(&work_dir).join("cdk-mintd.redb"); + assert!( + db_path.exists(), + "Expected redb database file to exist at {:?}", + db_path + ); + } + "SQLITE" => { + let db_path = std::path::Path::new(&work_dir).join("cdk-mintd.sqlite"); + assert!( + db_path.exists(), + "Expected sqlite database file to exist at {:?}", + db_path + ); + } + "MEMORY" => { + // Memory database has no file to check + println!("Memory database in use - no file to check"); + } + _ => bail!("Unknown database type: {}", db_type), + } + + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_fake_mint_with_witness() -> Result<()> { let wallet = Wallet::new( @@ -803,6 +838,7 @@ async fn test_fake_mint_multiple_unit_melt() -> Result<()> { }; let http_client = HttpClient::new(MINT_URL.parse()?); + let response = http_client.post_melt(melt_request.clone()).await; match response { diff --git a/crates/cdk-integration-tests/tests/regtest.rs b/crates/cdk-integration-tests/tests/regtest.rs index 5f81466a..499a735f 100644 --- a/crates/cdk-integration-tests/tests/regtest.rs +++ b/crates/cdk-integration-tests/tests/regtest.rs @@ -14,7 +14,8 @@ use cdk::nuts::{ PreMintSecrets, State, }; use cdk::wallet::client::{HttpClient, MintConnector}; -use cdk::wallet::{Wallet, WalletSubscription}; +use cdk::wallet::Wallet; +use cdk::WalletSubscription; use cdk_integration_tests::init_regtest::{ 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, @@ -140,7 +141,8 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> { NotificationPayload::MeltQuoteBolt11Response(melt) => melt, _ => panic!("Wrong payload"), }; - assert_eq!(payload.amount + payload.fee_reserve, 100.into()); + + assert_eq!(payload.amount + payload.fee_reserve, 50.into()); assert_eq!(payload.quote.to_string(), melt.id); assert_eq!(payload.state, MeltQuoteState::Unpaid); @@ -151,7 +153,7 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> { NotificationPayload::MeltQuoteBolt11Response(melt) => melt, _ => panic!("Wrong payload"), }; - assert_eq!(payload.amount + payload.fee_reserve, 100.into()); + assert_eq!(payload.amount + payload.fee_reserve, 50.into()); assert_eq!(payload.quote.to_string(), melt.id); assert_eq!(payload.state, MeltQuoteState::Paid); @@ -422,19 +424,7 @@ async fn test_cached_mint() -> Result<()> { let quote = wallet.mint_quote(mint_amount, None).await?; lnd_client.pay_invoice(quote.request).await?; - let mut subscription = wallet - .subscribe(WalletSubscription::Bolt11MintQuoteState(vec![quote - .id - .clone()])) - .await; - - while let Some(msg) = subscription.recv().await { - if let NotificationPayload::MintQuoteBolt11Response(response) = msg { - if response.state == MintQuoteState::Paid { - break; - } - } - } + wait_for_mint_to_be_paid(&wallet, "e.id, 60).await?; let active_keyset_id = wallet.get_active_mint_keyset().await?.id; let http_client = HttpClient::new(get_mint_url("0").as_str().parse()?); @@ -458,6 +448,59 @@ async fn test_cached_mint() -> Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_websocket_connection() -> Result<()> { + let wallet = Wallet::new( + &get_mint_url("0"), + CurrencyUnit::Sat, + Arc::new(WalletMemoryDatabase::default()), + &Mnemonic::generate(12)?.to_seed_normalized(""), + None, + )?; + + // Create a small mint quote to test notifications + let mint_quote = wallet.mint_quote(10.into(), None).await?; + + // Subscribe to notifications for this quote + let mut subscription = wallet + .subscribe(WalletSubscription::Bolt11MintQuoteState(vec![mint_quote + .id + .clone()])) + .await; + + // First check we get the unpaid state + let msg = timeout(Duration::from_secs(10), subscription.recv()) + .await + .expect("timeout waiting for unpaid notification") + .ok_or_else(|| anyhow::anyhow!("No unpaid notification received"))?; + + match msg { + NotificationPayload::MintQuoteBolt11Response(response) => { + assert_eq!(response.quote.to_string(), mint_quote.id); + assert_eq!(response.state, MintQuoteState::Unpaid); + } + _ => bail!("Unexpected notification type"), + } + + let lnd_client = init_lnd_client().await; + lnd_client.pay_invoice(mint_quote.request).await?; + + // Wait for paid notification with 10 second timeout + let msg = timeout(Duration::from_secs(10), subscription.recv()) + .await + .expect("timeout waiting for paid notification") + .ok_or_else(|| anyhow::anyhow!("No paid notification received"))?; + + match msg { + NotificationPayload::MintQuoteBolt11Response(response) => { + assert_eq!(response.quote.to_string(), mint_quote.id); + assert_eq!(response.state, MintQuoteState::Paid); + Ok(()) + } + _ => bail!("Unexpected notification type"), + } +} + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_multimint_melt() -> Result<()> { let lnd_client = init_lnd_client().await; @@ -482,26 +525,14 @@ async fn test_multimint_melt() -> Result<()> { // Fund the wallets let quote = wallet1.mint_quote(mint_amount, None).await?; lnd_client.pay_invoice(quote.request.clone()).await?; - loop { - let quote_status = wallet1.mint_quote_state("e.id).await?; - if quote_status.state == MintQuoteState::Paid { - break; - } - tracing::debug!("Quote not yet paid"); - } + wait_for_mint_to_be_paid(&wallet1, "e.id, 60).await?; wallet1 .mint("e.id, SplitTarget::default(), None) .await?; let quote = wallet2.mint_quote(mint_amount, None).await?; lnd_client.pay_invoice(quote.request.clone()).await?; - loop { - let quote_status = wallet2.mint_quote_state("e.id).await?; - if quote_status.state == MintQuoteState::Paid { - break; - } - tracing::debug!("Quote not yet paid"); - } + wait_for_mint_to_be_paid(&wallet2, "e.id, 60).await?; wallet2 .mint("e.id, SplitTarget::default(), None) .await?; @@ -538,3 +569,38 @@ async fn test_multimint_melt() -> Result<()> { assert!(result1.state == MeltQuoteState::Paid); Ok(()) } + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_database_type() -> Result<()> { + // Get the database type and work dir from environment + let db_type = std::env::var("MINT_DATABASE").expect("MINT_DATABASE env var should be set"); + let work_dir = + std::env::var("CDK_MINTD_WORK_DIR").expect("CDK_MINTD_WORK_DIR env var should be set"); + + // Check that the correct database file exists + match db_type.as_str() { + "REDB" => { + let db_path = std::path::Path::new(&work_dir).join("cdk-mintd.redb"); + assert!( + db_path.exists(), + "Expected redb database file to exist at {:?}", + db_path + ); + } + "SQLITE" => { + let db_path = std::path::Path::new(&work_dir).join("cdk-mintd.sqlite"); + assert!( + db_path.exists(), + "Expected sqlite database file to exist at {:?}", + db_path + ); + } + "MEMORY" => { + // Memory database has no file to check + println!("Memory database in use - no file to check"); + } + _ => bail!("Unknown database type: {}", db_type), + } + + Ok(()) +} diff --git a/crates/cdk-lnd/src/error.rs b/crates/cdk-lnd/src/error.rs index 197705c9..ffff7e61 100644 --- a/crates/cdk-lnd/src/error.rs +++ b/crates/cdk-lnd/src/error.rs @@ -33,6 +33,9 @@ pub enum Error { /// Errors coming from the backend #[error("LND error: `{0}`")] LndError(Status), + /// Errors invalid config + #[error("LND invalid config: `{0}`")] + InvalidConfig(String), } impl From for cdk::cdk_lightning::Error { diff --git a/crates/cdk-lnd/src/lib.rs b/crates/cdk-lnd/src/lib.rs index 9b1770d9..f107817c 100644 --- a/crates/cdk-lnd/src/lib.rs +++ b/crates/cdk-lnd/src/lib.rs @@ -55,6 +55,32 @@ impl Lnd { macaroon_file: PathBuf, fee_reserve: FeeReserve, ) -> Result { + // Validate address is not empty + if address.is_empty() { + return Err(Error::InvalidConfig("LND address cannot be empty".into())); + } + + // Validate cert_file exists and is not empty + if !cert_file.exists() || cert_file.metadata().map(|m| m.len() == 0).unwrap_or(true) { + return Err(Error::InvalidConfig(format!( + "LND certificate file not found or empty: {:?}", + cert_file + ))); + } + + // Validate macaroon_file exists and is not empty + if !macaroon_file.exists() + || macaroon_file + .metadata() + .map(|m| m.len() == 0) + .unwrap_or(true) + { + return Err(Error::InvalidConfig(format!( + "LND macaroon file not found or empty: {:?}", + macaroon_file + ))); + } + let client = fedimint_tonic_lnd::connect(address.to_string(), &cert_file, &macaroon_file) .await .map_err(|err| { diff --git a/crates/cdk-mintd/Cargo.toml b/crates/cdk-mintd/Cargo.toml index cdef1276..8a847c65 100644 --- a/crates/cdk-mintd/Cargo.toml +++ b/crates/cdk-mintd/Cargo.toml @@ -42,7 +42,7 @@ bitcoin = { version = "0.32.2", features = [ "rand", "rand-std", ] } -tokio = { version = "1", default-features = false } +tokio = { version = "1", default-features = false, features = ["signal"] } tracing = { version = "0.1", default-features = false, features = [ "attributes", "log", diff --git a/crates/cdk-mintd/src/main.rs b/crates/cdk-mintd/src/main.rs index 65620e8e..d5d606d2 100644 --- a/crates/cdk-mintd/src/main.rs +++ b/crates/cdk-mintd/src/main.rs @@ -412,7 +412,18 @@ async fn main() -> anyhow::Result<()> { .parse()?, ) .serve(mint_service.into_make_service()) - .await; + .with_graceful_shutdown(shutdown_signal()); + + match axum_result.await { + Ok(_) => { + tracing::info!("Axum server stopped with okay status"); + } + Err(err) => { + tracing::warn!("Axum server stopped with error"); + tracing::error!("{}", err); + bail!("Axum exited with error") + } + } shutdown.notify_waiters(); @@ -423,18 +434,6 @@ async fn main() -> anyhow::Result<()> { } } - match axum_result { - Ok(_) => { - tracing::info!("Axum server stopped with okay status"); - } - Err(err) => { - tracing::warn!("Axum server stopped with error"); - tracing::error!("{}", err); - - bail!("Axum exited with error") - } - } - Ok(()) } @@ -469,3 +468,10 @@ fn work_dir() -> Result { Ok(dir) } + +async fn shutdown_signal() { + tokio::signal::ctrl_c() + .await + .expect("failed to install CTRL+C handler"); + tracing::info!("Shutdown signal received"); +} diff --git a/misc/fake_itests.sh b/misc/fake_itests.sh index deba74fd..633b0149 100755 --- a/misc/fake_itests.sh +++ b/misc/fake_itests.sh @@ -4,12 +4,9 @@ cleanup() { echo "Cleaning up..." - # Kill the Rust binary process - echo "Killing the Rust binary with PID $RUST_BIN_PID" - kill $CDK_ITEST_MINT_BIN_PID - - # Wait for the Rust binary to terminate - wait $CDK_ITEST_MINT_BIN_PID + echo "Killing the cdk mintd" + kill -2 $cdk_mintd_pid + wait $cdk_mintd_pid echo "Mint binary terminated" @@ -29,7 +26,6 @@ export cdk_itests=$(mktemp -d) export cdk_itests_mint_addr="127.0.0.1"; export cdk_itests_mint_port=8086; -URL="http://$cdk_itests_mint_addr:$cdk_itests_mint_port/v1/info" # Check if the temporary directory was created successfully if [[ ! -d "$cdk_itests" ]]; then echo "Failed to create temp directory" @@ -40,11 +36,25 @@ echo "Temp directory created: $cdk_itests" export MINT_DATABASE="$1"; cargo build -p cdk-integration-tests -cargo build --bin fake_wallet -cargo run --bin fake_wallet & -# Capture its PID -CDK_ITEST_MINT_BIN_PID=$! + +export CDK_MINTD_URL="http://$cdk_itests_mint_addr:$cdk_itests_mint_port"; +export CDK_MINTD_WORK_DIR="$cdk_itests"; +export CDK_MINTD_LISTEN_HOST=$cdk_itests_mint_addr; +export CDK_MINTD_LISTEN_PORT=$cdk_itests_mint_port; +export CDK_MINTD_LN_BACKEND="fakewallet"; +export CDK_MINTD_FAKE_WALLET_SUPPORTED_UNITS="sat,usd"; +export CDK_MINTD_MNEMONIC="eye survey guilt napkin crystal cup whisper salt luggage manage unveil loyal"; +export CDK_MINTD_FAKE_WALLET_FEE_PERCENT="0"; +export CDK_MINTD_FAKE_WALLET_RESERVE_FEE_MIN="1"; +export CDK_MINTD_DATABASE=$MINT_DATABASE; + + +echo "Starting fake mintd"; +cargo run --bin cdk-mintd & +cdk_mintd_pid=$! + +URL="http://$cdk_itests_mint_addr:$cdk_itests_mint_port/v1/info" TIMEOUT=100 START_TIME=$(date +%s) # Loop until the endpoint returns a 200 OK status or timeout is reached diff --git a/misc/itests.sh b/misc/itests.sh index 0c6854bb..dbfb689b 100755 --- a/misc/itests.sh +++ b/misc/itests.sh @@ -4,20 +4,21 @@ cleanup() { echo "Cleaning up..." - # Kill the Rust binary process - echo "Killing the Rust binary with PID $RUST_BIN_PID" - kill $CDK_ITEST_MINT_BIN_PID + echo "Killing the cdk mintd" + kill -2 $cdk_mintd_pid + wait $cdk_mintd_pid + + + echo "Killing the cdk lnd mintd" + kill -2 $cdk_mintd_lnd_pid + wait $cdk_mintd_lnd_pid + + echo "Killing the cdk regtest" + kill -2 $cdk_regtest_pid + wait $cdk_regtest_pid - # Wait for the Rust binary to terminate - wait $CDK_ITEST_MINT_BIN_PID echo "Mint binary terminated" - # Kill processes - 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 rm -rf "$cdk_itests" @@ -36,7 +37,6 @@ export cdk_itests_mint_addr="127.0.0.1"; export cdk_itests_mint_port_0=8085; export cdk_itests_mint_port_1=8087; -URL="http://$cdk_itests_mint_addr:$cdk_itests_mint_port_0/v1/info" # Check if the temporary directory was created successfully if [[ ! -d "$cdk_itests" ]]; then echo "Failed to create temp directory" @@ -47,13 +47,102 @@ echo "Temp directory created: $cdk_itests" 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 & + +cargo run --bin start_regtest & + +cdk_regtest_pid=$! +mkfifo "$cdk_itests/progress_pipe" +rm -f "$cdk_itests/signal_received" # Ensure clean state +# Start reading from pipe in background +(while read line; do + case "$line" in + "checkpoint1") + echo "Reached first checkpoint" + touch "$cdk_itests/signal_received" + exit 0 + ;; + esac +done < "$cdk_itests/progress_pipe") & +# Wait for up to 120 seconds +for ((i=0; i<120; i++)); do + if [ -f "$cdk_itests/signal_received" ]; then + echo "break signal received" + break + fi + sleep 1 +done +echo "Regtest set up continuing" + +echo "Starting regtest mint" +# cargo run --bin regtest_mint & + +export CDK_MINTD_CLN_RPC_PATH="$cdk_itests/cln/one/regtest/lightning-rpc"; + + +export CDK_MINTD_URL="http://$cdk_itests_mint_addr:$cdk_itests_mint_port_0"; +export CDK_MINTD_WORK_DIR="$cdk_itests"; +export CDK_MINTD_LISTEN_HOST=$cdk_itests_mint_addr; +export CDK_MINTD_LISTEN_PORT=$cdk_itests_mint_port_0; +export CDK_MINTD_LN_BACKEND="cln"; +export CDK_MINTD_MNEMONIC="eye survey guilt napkin crystal cup whisper salt luggage manage unveil loyal"; +export CDK_MINTD_DATABASE=$MINT_DATABASE; + +echo "Starting cln mintd"; +cargo run --bin cdk-mintd & +cdk_mintd_pid=$! + echo $cdk_itests -# Capture its PID -CDK_ITEST_MINT_BIN_PID=$! + +URL="http://$cdk_itests_mint_addr:$cdk_itests_mint_port_0/v1/info" + +TIMEOUT=100 +START_TIME=$(date +%s) +# Loop until the endpoint returns a 200 OK status or timeout is reached +while true; do + # Get the current time + CURRENT_TIME=$(date +%s) + + # Calculate the elapsed time + ELAPSED_TIME=$((CURRENT_TIME - START_TIME)) + + # Check if the elapsed time exceeds the timeout + if [ $ELAPSED_TIME -ge $TIMEOUT ]; then + echo "Timeout of $TIMEOUT seconds reached. Exiting..." + exit 1 + fi + + # Make a request to the endpoint and capture the HTTP status code + HTTP_STATUS=$(curl -o /dev/null -s -w "%{http_code}" $URL) + + # Check if the HTTP status is 200 OK + if [ "$HTTP_STATUS" -eq 200 ]; then + echo "Received 200 OK from $URL" + break + else + echo "Waiting for 200 OK response, current status: $HTTP_STATUS" + sleep 2 # Wait for 2 seconds before retrying + fi +done + + +export CDK_MINTD_LND_ADDRESS="https://localhost:10010"; +export CDK_MINTD_LND_CERT_FILE="$cdk_itests/lnd/two/tls.cert"; +export CDK_MINTD_LND_MACAROON_FILE="$cdk_itests/lnd/two/data/chain/bitcoin/regtest/admin.macaroon"; + +export CDK_MINTD_URL="http://$cdk_itests_mint_addr:$cdk_itests_mint_port_1"; +mkdir -p "$cdk_itests/lnd_mint" +export CDK_MINTD_WORK_DIR="$cdk_itests/lnd_mint"; +export CDK_MINTD_LISTEN_HOST=$cdk_itests_mint_addr; +export CDK_MINTD_LISTEN_PORT=$cdk_itests_mint_port_1; +export CDK_MINTD_LN_BACKEND="lnd"; +export CDK_MINTD_MNEMONIC="eye survey guilt napkin crystal cup whisper salt luggage manage unveil loyal"; + +echo "Starting lnd mintd"; +cargo run --bin cdk-mintd & +cdk_mintd_lnd_pid=$! + +URL="http://$cdk_itests_mint_addr:$cdk_itests_mint_port_1/v1/info" TIMEOUT=100 START_TIME=$(date +%s) @@ -88,7 +177,7 @@ 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 # Switch Mints: Run tests with LND mint