Files
cdk/crates/cdk-integration-tests/src/bin/start_regtest_mints.rs
thesimplekid 3a3cd88ee9 Mintd lib (#914)
* feat(cdk-integration-tests): refactor regtest setup and mintd integration

- Replace shell-based regtest setup with Rust binary (start_regtest_mints)
- Add cdk-mintd crate to workspace and integration tests
- Improve environment variable handling for test configurations
- Update integration tests to use proper temp directory management
- Remove deprecated start_regtest.rs binary
- Enhance CLN client connection with retry logic
- Simplify regtest shell script (itests.sh) to use new binary
- Fix tracing filters and improve error handling in setup
- Update dependencies and configurations for integration tests

fix: killing

chore: comment tests for ci debugging

chore: compile

Revert "chore: comment tests for ci debugging"

This reverts commit bfc594c11cf37caeaa6445cb854ae5567d2da6bd.

* chore: sql cipher

* fix: removal of sqlite cipher

* fix: auth password

* refactor(cdk-mintd): improve database password handling and function signatures

- Pass database password as parameter instead of parsing CLI args in setup_database
- Update function signatures for run_mintd and run_mintd_with_shutdown to accept db_password
- Remove direct CLI parsing from database setup logic
- Fix auth database initialization to use correct type when sqlcipher feature enabled
2025-07-31 00:43:43 -04:00

306 lines
9.6 KiB
Rust

//! Binary for starting regtest mints
//!
//! This binary provides a programmatic way to start regtest mints for testing purposes:
//! 1. Sets up a regtest environment with CLN and LND nodes
//! 2. Starts CLN and LND mint instances using the cdk-mintd library
//! 3. Configures the mints to connect to the respective Lightning Network backends
//! 4. Waits for both mints to be ready and responsive
//! 5. Keeps them running until interrupted (Ctrl+C)
//! 6. Gracefully shuts down all services on receiving shutdown signal
//!
//! This approach offers better control and integration compared to external scripts,
//! making it easier to run integration tests with consistent configuration.
use std::fs;
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
use anyhow::Result;
use cdk_integration_tests::cli::CommonArgs;
use cdk_integration_tests::init_regtest::start_regtest_end;
use cdk_integration_tests::shared;
use clap::Parser;
use tokio::signal::unix::SignalKind;
use tokio::signal::{self};
use tokio::sync::{oneshot, Notify};
use tokio::time::timeout;
#[derive(Parser)]
#[command(name = "start-regtest-mints")]
#[command(about = "Start regtest mints", long_about = None)]
struct Args {
#[command(flatten)]
common: CommonArgs,
/// Database type (sqlite)
database_type: String,
/// Working directory path
work_dir: String,
/// Mint address (default: 127.0.0.1)
#[arg(default_value = "127.0.0.1")]
mint_addr: String,
/// CLN port (default: 8085)
#[arg(default_value_t = 8085)]
cln_port: u16,
/// LND port (default: 8087)
#[arg(default_value_t = 8087)]
lnd_port: u16,
}
/// Start regtest CLN mint using the library
async fn start_cln_mint(
temp_dir: &Path,
port: u16,
shutdown: Arc<Notify>,
) -> Result<tokio::task::JoinHandle<()>> {
let cln_rpc_path = temp_dir
.join("cln")
.join("one")
.join("regtest")
.join("lightning-rpc");
let cln_config = cdk_mintd::config::Cln {
rpc_path: cln_rpc_path,
bolt12: false,
fee_percent: 0.0,
reserve_fee_min: 0.into(),
};
// Create settings struct for CLN mint using shared function
let settings = shared::create_cln_settings(
port,
temp_dir
.join("cln")
.join("one")
.join("regtest")
.join("lightning-rpc"),
"eye survey guilt napkin crystal cup whisper salt luggage manage unveil loyal".to_string(),
cln_config,
);
println!("Starting CLN mintd on port {port}");
let temp_dir = temp_dir.to_path_buf();
let shutdown_clone = shutdown.clone();
// Run the mint in a separate task
let handle = tokio::spawn(async move {
// Create a future that resolves when the shutdown signal is received
let shutdown_future = async move {
shutdown_clone.notified().await;
println!("CLN mint shutdown signal received");
};
match cdk_mintd::run_mintd_with_shutdown(&temp_dir, &settings, shutdown_future, None).await
{
Ok(_) => println!("CLN mint exited normally"),
Err(e) => eprintln!("CLN mint exited with error: {e}"),
}
});
Ok(handle)
}
/// Start regtest LND mint using the library
async fn start_lnd_mint(
temp_dir: &Path,
port: u16,
shutdown: Arc<Notify>,
) -> Result<tokio::task::JoinHandle<()>> {
let lnd_cert_file = temp_dir.join("lnd").join("two").join("tls.cert");
let lnd_macaroon_file = temp_dir
.join("lnd")
.join("two")
.join("data")
.join("chain")
.join("bitcoin")
.join("regtest")
.join("admin.macaroon");
let lnd_work_dir = temp_dir.join("lnd_mint");
// Create work directory for LND mint
fs::create_dir_all(&lnd_work_dir)?;
let lnd_config = cdk_mintd::config::Lnd {
address: "https://localhost:10010".to_string(),
cert_file: lnd_cert_file,
macaroon_file: lnd_macaroon_file,
fee_percent: 0.0,
reserve_fee_min: 0.into(),
};
// Create settings struct for LND mint using shared function
let settings = shared::create_lnd_settings(
port,
lnd_config,
"cattle gold bind busy sound reduce tone addict baby spend february strategy".to_string(),
);
println!("Starting LND mintd on port {port}");
let lnd_work_dir = lnd_work_dir.clone();
let shutdown_clone = shutdown.clone();
// Run the mint in a separate task
let handle = tokio::spawn(async move {
// Create a future that resolves when the shutdown signal is received
let shutdown_future = async move {
shutdown_clone.notified().await;
println!("LND mint shutdown signal received");
};
match cdk_mintd::run_mintd_with_shutdown(&lnd_work_dir, &settings, shutdown_future, None)
.await
{
Ok(_) => println!("LND mint exited normally"),
Err(e) => eprintln!("LND mint exited with error: {e}"),
}
});
Ok(handle)
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Args::parse();
// Initialize logging based on CLI arguments
shared::setup_logging(&args.common);
let temp_dir = shared::init_working_directory(&args.work_dir)?;
// Write environment variables to a .env file in the temp_dir
let mint_url_1 = format!("http://{}:{}", args.mint_addr, args.cln_port);
let mint_url_2 = format!("http://{}:{}", args.mint_addr, args.lnd_port);
let env_vars: Vec<(&str, &str)> = vec![
("CDK_TEST_MINT_URL", &mint_url_1),
("CDK_TEST_MINT_URL_2", &mint_url_2),
];
shared::write_env_file(&temp_dir, &env_vars)?;
// Start regtest
println!("Starting regtest...");
let shutdown_regtest = shared::create_shutdown_handler();
let shutdown_clone = shutdown_regtest.clone();
let (tx, rx) = oneshot::channel();
let shutdown_clone_one = Arc::clone(&shutdown_clone);
let temp_dir_clone = temp_dir.clone();
tokio::spawn(async move {
start_regtest_end(&temp_dir_clone, tx, shutdown_clone_one)
.await
.expect("Error starting regtest");
});
match timeout(Duration::from_secs(300), rx).await {
Ok(_) => {
tracing::info!("Regtest set up");
}
Err(_) => {
tracing::error!("regtest setup timed out after 5 minutes");
anyhow::bail!("Could not set up regtest");
}
}
// Start CLN mint
let cln_handle = start_cln_mint(&temp_dir, args.cln_port, shutdown_clone.clone()).await?;
// Wait for CLN mint to be ready
if let Err(e) = shared::wait_for_mint_ready(args.cln_port, 100).await {
eprintln!("Error waiting for CLN mint: {e}");
return Err(e);
}
// Start LND mint
let lnd_handle = start_lnd_mint(&temp_dir, args.lnd_port, shutdown_clone.clone()).await?;
// Wait for LND mint to be ready
if let Err(e) = shared::wait_for_mint_ready(args.lnd_port, 100).await {
eprintln!("Error waiting for LND mint: {e}");
return Err(e);
}
println!("All regtest mints started successfully!");
println!("CLN mint: http://{}:{}", args.mint_addr, args.cln_port);
println!("LND mint: http://{}:{}", args.mint_addr, args.lnd_port);
shared::display_mint_info(args.cln_port, &temp_dir, &args.database_type); // Using CLN port for display
println!();
println!("Environment variables set:");
println!(
" CDK_TEST_MINT_URL=http://{}:{}",
args.mint_addr, args.cln_port
);
println!(
" CDK_TEST_MINT_URL_2=http://{}:{}",
args.mint_addr, args.lnd_port
);
println!(" CDK_ITESTS_DIR={}", temp_dir.display());
println!();
println!("You can now run integration tests with:");
println!(" cargo test -p cdk-integration-tests --test regtest");
println!(" cargo test -p cdk-integration-tests --test happy_path_mint_wallet");
println!(" etc.");
println!();
println!("Press Ctrl+C to stop the mints...");
// Create a future to wait for either Ctrl+C signal or unexpected mint termination
let shutdown_future = async {
// Wait for either SIGINT (Ctrl+C) or SIGTERM
let mut sigterm = signal::unix::signal(SignalKind::terminate())
.expect("Failed to create SIGTERM signal handler");
tokio::select! {
_ = signal::ctrl_c() => {
tracing::info!("Received SIGINT (Ctrl+C), shutting down mints...");
}
_ = sigterm.recv() => {
tracing::info!("Received SIGTERM, shutting down mints...");
}
}
println!("\nShutdown signal received, shutting down mints...");
shutdown_clone.notify_waiters();
};
// Monitor mint handles for unexpected termination
let monitor_mints = async {
loop {
if cln_handle.is_finished() {
println!("CLN mint finished unexpectedly");
return;
}
if lnd_handle.is_finished() {
println!("LND mint finished unexpectedly");
return;
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
};
// Wait for either shutdown signal or mint termination
tokio::select! {
_ = shutdown_future => {
println!("Shutdown signal received, waiting for mints to stop...");
}
_ = monitor_mints => {
println!("One or more mints terminated unexpectedly");
}
}
// Wait for mints to finish gracefully
if let Err(e) = tokio::try_join!(cln_handle, lnd_handle) {
eprintln!("Error waiting for mints to shut down: {e}");
}
println!("All services shut down successfully");
Ok(())
}