Files
cdk/crates/cdk-integration-tests/src/init_regtest.rs
thesimplekid f9bb5eb913 feat: itests
fix: melt change promises amount
2024-09-19 11:43:43 +02:00

327 lines
8.7 KiB
Rust

use std::{collections::HashMap, env, path::PathBuf, sync::Arc};
use anyhow::Result;
use axum::Router;
use bip39::Mnemonic;
use cdk::{
cdk_database::mint_memory::MintMemoryDatabase,
cdk_lightning::MintLightning,
mint::{FeeReserve, Mint},
nuts::{CurrencyUnit, MeltMethodSettings, MintInfo, MintMethodSettings},
};
use cdk_axum::LnKey;
use cdk_cln::Cln as CdkCln;
use futures::StreamExt;
use ln_regtest_rs::{
bitcoin_client::BitcoinClient, bitcoind::Bitcoind, cln::Clnd, cln_client::ClnClient, lnd::Lnd,
lnd_client::LndClient,
};
use tower_http::cors::CorsLayer;
use tracing_subscriber::EnvFilter;
const BITCOIND_ADDR: &str = "127.0.0.1:18443";
const ZMQ_RAW_BLOCK: &str = "tcp://127.0.0.1:28332";
const ZMQ_RAW_TX: &str = "tcp://127.0.0.1:28333";
const BITCOIN_RPC_USER: &str = "testuser";
const BITCOIN_RPC_PASS: &str = "testpass";
const CLN_ADDR: &str = "127.0.0.1:19846";
const LND_ADDR: &str = "0.0.0.0:18444";
const LND_RPC_ADDR: &str = "https://127.0.0.1:10009";
const BITCOIN_DIR: &str = "bitcoin";
const CLN_DIR: &str = "cln";
const LND_DIR: &str = "lnd";
pub fn get_mint_addr() -> String {
env::var("cdk_itests_mint_addr").expect("Temp dir set")
}
pub fn get_mint_port() -> u16 {
let dir = env::var("cdk_itests_mint_port").expect("Temp dir set");
dir.parse().unwrap()
}
pub fn get_mint_url() -> String {
format!("http://{}:{}", get_mint_addr(), get_mint_port())
}
pub fn get_temp_dir() -> PathBuf {
let dir = env::var("cdk_itests").expect("Temp dir set");
std::fs::create_dir_all(&dir).unwrap();
dir.parse().expect("Valid path buf")
}
pub fn get_bitcoin_dir() -> PathBuf {
let dir = get_temp_dir().join(BITCOIN_DIR);
std::fs::create_dir_all(&dir).unwrap();
dir
}
pub fn init_bitcoind() -> Bitcoind {
Bitcoind::new(
get_bitcoin_dir(),
BITCOIND_ADDR.parse().unwrap(),
BITCOIN_RPC_USER.to_string(),
BITCOIN_RPC_PASS.to_string(),
ZMQ_RAW_BLOCK.to_string(),
ZMQ_RAW_TX.to_string(),
)
}
pub fn init_bitcoin_client() -> Result<BitcoinClient> {
BitcoinClient::new(
"wallet".to_string(),
BITCOIND_ADDR.into(),
None,
Some(BITCOIN_RPC_USER.to_string()),
Some(BITCOIN_RPC_PASS.to_string()),
)
}
pub fn get_cln_dir() -> PathBuf {
let dir = get_temp_dir().join(CLN_DIR);
std::fs::create_dir_all(&dir).unwrap();
dir
}
pub fn init_cln() -> Clnd {
Clnd::new(
get_bitcoin_dir(),
get_cln_dir(),
CLN_ADDR.to_string().parse().unwrap(),
BITCOIN_RPC_USER.to_string(),
BITCOIN_RPC_PASS.to_string(),
)
}
pub async fn init_cln_client() -> Result<ClnClient> {
ClnClient::new(get_cln_dir(), None).await
}
pub fn get_lnd_dir() -> PathBuf {
let dir = get_temp_dir().join(LND_DIR);
std::fs::create_dir_all(&dir).unwrap();
dir
}
pub async fn init_lnd() -> Lnd {
Lnd::new(
get_bitcoin_dir(),
get_lnd_dir(),
LND_ADDR.parse().unwrap(),
BITCOIN_RPC_USER.to_string(),
BITCOIN_RPC_PASS.to_string(),
ZMQ_RAW_BLOCK.to_string(),
ZMQ_RAW_TX.to_string(),
)
}
pub async fn init_lnd_client() -> Result<LndClient> {
let lnd_dir = get_lnd_dir();
let cert_file = lnd_dir.join("tls.cert");
let macaroon_file = lnd_dir.join("data/chain/bitcoin/regtest/admin.macaroon");
LndClient::new(LND_RPC_ADDR.parse().unwrap(), cert_file, macaroon_file).await
}
pub async fn create_cln_backend(cln_client: &ClnClient) -> Result<CdkCln> {
let rpc_path = cln_client.rpc_path.clone();
let fee_reserve = FeeReserve {
min_fee_reserve: 1.into(),
percent_fee_reserve: 1.0,
};
Ok(CdkCln::new(
rpc_path,
fee_reserve,
MintMethodSettings::default(),
MeltMethodSettings::default(),
)
.await?)
}
pub async fn create_mint() -> Result<Mint> {
let nuts = cdk::nuts::Nuts::new()
.nut07(true)
.nut08(true)
.nut09(true)
.nut10(true)
.nut11(true)
.nut12(true)
.nut14(true);
let mint_info = MintInfo::new().nuts(nuts);
let mnemonic = Mnemonic::generate(12)?;
let mut supported_units: HashMap<CurrencyUnit, (u64, u8)> = HashMap::new();
supported_units.insert(CurrencyUnit::Sat, (0, 32));
let mint = Mint::new(
&get_mint_url(),
&mnemonic.to_seed_normalized(""),
mint_info,
Arc::new(MintMemoryDatabase::default()),
supported_units,
)
.await?;
Ok(mint)
}
pub async fn start_cln_mint() -> Result<()> {
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 mint = create_mint().await?;
let cln_client = init_cln_client().await?;
let cln_backend = create_cln_backend(&cln_client).await?;
let mut ln_backends: HashMap<
LnKey,
Arc<dyn MintLightning<Err = cdk::cdk_lightning::Error> + Sync + Send>,
> = HashMap::new();
ln_backends.insert(
LnKey::new(CurrencyUnit::Sat, cdk::nuts::PaymentMethod::Bolt11),
Arc::new(cln_backend),
);
let quote_ttl = 100000;
let mint_arc = Arc::new(mint);
let v1_service = cdk_axum::create_mint_router(
&get_mint_url(),
Arc::clone(&mint_arc),
ln_backends.clone(),
quote_ttl,
)
.await
.unwrap();
let mint_service = Router::new()
.merge(v1_service)
.layer(CorsLayer::permissive());
let mint = Arc::clone(&mint_arc);
for wallet in ln_backends.values() {
let wallet_clone = Arc::clone(wallet);
let mint = Arc::clone(&mint);
tokio::spawn(async move {
match wallet_clone.wait_any_invoice().await {
Ok(mut stream) => {
while let Some(request_lookup_id) = stream.next().await {
if let Err(err) =
handle_paid_invoice(Arc::clone(&mint), &request_lookup_id).await
{
// nosemgrep: direct-panic
panic!("{:?}", err);
}
}
}
Err(err) => {
// nosemgrep: direct-panic
panic!("Could not get invoice stream: {}", err);
}
}
});
}
println!("Staring Axum server");
axum::Server::bind(
&format!("{}:{}", "127.0.0.1", 8085)
.as_str()
.parse()
.unwrap(),
)
.serve(mint_service.into_make_service())
.await?;
Ok(())
}
/// Update mint quote when called for a paid invoice
async fn handle_paid_invoice(mint: Arc<Mint>, request_lookup_id: &str) -> Result<()> {
println!("Invoice with lookup id paid: {}", request_lookup_id);
if let Ok(Some(mint_quote)) = mint
.localstore
.get_mint_quote_by_request_lookup_id(request_lookup_id)
.await
{
println!(
"Quote {} paid by lookup id {}",
mint_quote.id, request_lookup_id
);
mint.localstore
.update_mint_quote_state(&mint_quote.id, cdk::nuts::MintQuoteState::Paid)
.await?;
}
Ok(())
}
pub async fn fund_ln(
bitcoin_client: &BitcoinClient,
cln_client: &ClnClient,
lnd_client: &LndClient,
) -> Result<()> {
let lnd_address = lnd_client.get_new_address().await?;
bitcoin_client.send_to_address(&lnd_address, 2_000_000)?;
let cln_address = cln_client.get_new_address().await?;
bitcoin_client.send_to_address(&cln_address, 2_000_000)?;
let mining_address = bitcoin_client.get_new_address()?;
bitcoin_client.generate_blocks(&mining_address, 200)?;
cln_client.wait_chain_sync().await?;
lnd_client.wait_chain_sync().await?;
Ok(())
}
pub async fn open_channel(
bitcoin_client: &BitcoinClient,
cln_client: &ClnClient,
lnd_client: &LndClient,
) -> Result<()> {
let cln_info = cln_client.get_info().await?;
let cln_pubkey = cln_info.id;
let cln_address = "127.0.0.1";
let cln_port = 19846;
lnd_client
.connect(cln_pubkey.to_string(), cln_address.to_string(), cln_port)
.await
.unwrap();
lnd_client
.open_channel(1_500_000, &cln_pubkey.to_string(), Some(750_000))
.await
.unwrap();
let mine_to_address = bitcoin_client.get_new_address()?;
bitcoin_client.generate_blocks(&mine_to_address, 10)?;
cln_client.wait_chain_sync().await?;
lnd_client.wait_chain_sync().await?;
cln_client.wait_channels_active().await?;
lnd_client.wait_channels_active().await?;
Ok(())
}