mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-19 13:44:55 +01:00
Refactor mintd used in itest (#616)
This commit is contained in:
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
@@ -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:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ config.toml
|
||||
.pre-commit-config.yaml
|
||||
result
|
||||
Cargo.lock
|
||||
.aider*
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
63
crates/cdk-integration-tests/src/bin/start_regtest.rs
Normal file
63
crates/cdk-integration-tests/src/bin/start_regtest.rs
Normal file
@@ -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(())
|
||||
}
|
||||
@@ -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<D>(addr: &str, port: u16, database: D) -> Result<()>
|
||||
where
|
||||
D: MintDatabase<Err = cdk_database::Error> + 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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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<CdkLnd> {
|
||||
.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 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<C>(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<Notify>) -> 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(())
|
||||
}
|
||||
|
||||
@@ -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<String>,
|
||||
) -> anyhow::Result<Proofs> {
|
||||
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,
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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<Error> for cdk::cdk_lightning::Error {
|
||||
|
||||
@@ -55,6 +55,32 @@ impl Lnd {
|
||||
macaroon_file: PathBuf,
|
||||
fee_reserve: FeeReserve,
|
||||
) -> Result<Self, Error> {
|
||||
// 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| {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<PathBuf> {
|
||||
|
||||
Ok(dir)
|
||||
}
|
||||
|
||||
async fn shutdown_signal() {
|
||||
tokio::signal::ctrl_c()
|
||||
.await
|
||||
.expect("failed to install CTRL+C handler");
|
||||
tracing::info!("Shutdown signal received");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
125
misc/itests.sh
125
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
|
||||
|
||||
Reference in New Issue
Block a user