Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions libs/sdk-itest/src/environment/cln.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ use crate::environment::container::ContainerExt;
use crate::environment::log::LogConsumer;
use crate::environment::{ApiCredentials, Cert, EnvironmentId};

const CA_PEM_FILE: &str = "/data/.lightning/regtest/ca.pem";
const CLIENT_CERT_FILE: &str = "/data/.lightning/regtest/client.pem";
const CLIENT_KEY_FILE: &str = "/data/.lightning/regtest/client-key.pem";
const CA_PEM_FILE: &str = "/root/.lightning/regtest/ca.pem";
const CLIENT_CERT_FILE: &str = "/root/.lightning/regtest/client.pem";
const CLIENT_KEY_FILE: &str = "/root/.lightning/regtest/client-key.pem";
const CLN_HOSTNAME: &str = "cln";
const GRPC_PORT: u16 = 8888;
const IMAGE_NAME: &str = "elementsproject/lightningd";
Expand Down
29 changes: 29 additions & 0 deletions libs/sdk-itest/src/environment/lnd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,18 @@ use crate::environment::{ApiCredentials, EnvironmentId};

const IMAGE_NAME: &str = "lightninglabs/lnd";
const IMAGE_TAG: &str = "v0.19.3-beta";
const SOCAT_IMAGE: &str = "alpine/socat";
const SOCAT_IMAGE_TAG: &str = "latest";
const LND_HOSTNAME: &str = "lnd";
const LIGHTNING_PORT: u16 = 9735;
const RPC_PORT: u16 = 10009;

pub struct Lnd {
pub lightning_api: ApiCredentials,
pub grpc_api: ApiCredentials,
client: Mutex<Client>,
_container: ContainerAsync<GenericImage>,
_socat: ContainerAsync<GenericImage>,
}

impl Lnd {
Expand All @@ -40,6 +45,7 @@ impl Lnd {
.with_exposed_port(RPC_PORT.into())
.with_wait_for(WaitFor::message_on_stdout("Server listening on"))
.with_network(environment_id.network_name())
.with_hostname(LND_HOSTNAME)
.with_log_consumer(LogConsumer::new("lnd"))
.with_cmd([
"--bitcoin.regtest",
Expand Down Expand Up @@ -72,22 +78,45 @@ impl Lnd {
container
.copy_file("/root/.lnd/tls.cert", &cert_path)
.await?;
let tls_cert = container.read_file("/root/.lnd/tls.cert").await?;
let macaroon_path = working_dir.join("admin.macaroon");
container
.copy_file(
"/root/.lnd/data/chain/bitcoin/regtest/admin.macaroon",
&macaroon_path,
)
.await?;
let macaroon = container
.read_file("/root/.lnd/data/chain/bitcoin/regtest/admin.macaroon")
.await?;

let lightning_api = ApiCredentials::from_container(&container, LIGHTNING_PORT).await?;
let port = container.get_host_port_ipv4(RPC_PORT).await?;
let endpoint = format!("https://localhost:{port}");
let client = tonic_lnd::connect(endpoint, &cert_path, &macaroon_path).await?;

let socat = GenericImage::new(SOCAT_IMAGE, SOCAT_IMAGE_TAG)
.with_exposed_port(RPC_PORT.into())
.with_wait_for(WaitFor::message_on_stderr("listening on"))
.with_network(environment_id.network_name())
.with_log_consumer(LogConsumer::new("lnd-socat"))
.with_copy_to("/root/.lnd/tls.cert", tls_cert.clone())
.with_cmd([
"-dd",
format!("TCP-LISTEN:{RPC_PORT},fork").as_str(),
format!("OPENSSL:{LND_HOSTNAME}:{RPC_PORT},cafile=/root/.lnd/tls.cert").as_str(),
])
.start()
.await?;

let mut grpc_api = ApiCredentials::from_container(&socat, RPC_PORT).await?;
grpc_api.macaroon = macaroon;

Ok(Self {
lightning_api,
grpc_api,
client: Mutex::new(client),
_socat: socat,
_container: container,
})
}
Expand Down
21 changes: 20 additions & 1 deletion libs/sdk-itest/src/environment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod log;
mod lsp;
mod mempool;
mod rgs;
mod swapd;
mod vss;

use std::path::PathBuf;
Expand All @@ -22,6 +23,7 @@ use lsp::Lsp;
use mempool::Mempool;
use rand::Rng;
use rgs::Rgs;
use swapd::Swapd;
use testcontainers::{ContainerAsync, Image};
use testdir::testdir;
use tokio::sync::OnceCell;
Expand Down Expand Up @@ -83,6 +85,7 @@ pub struct ApiCredentials {
pub username: String,
pub password: String,
pub cert: Cert,
pub macaroon: Vec<u8>,
}

impl ApiCredentials {
Expand Down Expand Up @@ -128,6 +131,7 @@ pub struct Environment {
lsp: OnceCell<Lsp>,
lnd: OnceCell<Lnd>,
cln: OnceCell<Cln>,
swapd: OnceCell<Swapd>,
channel: OnceCell<()>,
cln_channel: OnceCell<()>,
rgs: OnceCell<Rgs>,
Expand Down Expand Up @@ -229,6 +233,21 @@ impl Environment {
self.cln().await
}

#[instrument(skip(self))]
pub async fn swapd(&self) -> Result<&ApiCredentials> {
let swapd = self
.swapd
.get_or_try_init(|| async {
info!("Initializing swapd");
let bitcoind_api = self.bitcoind_api();
let lnd_grpc_api = async { Ok(&self.lnd().await?.grpc_api) };
let result = Swapd::new(&self.environmnet_id, bitcoind_api, lnd_grpc_api).await;
log_result(result, "swapd")
})
.await?;
Ok(&swapd.api)
}

#[instrument(skip(self))]
pub async fn rgs(&self) -> Result<&ApiCredentials> {
let rgs = self
Expand Down Expand Up @@ -339,7 +358,7 @@ impl Environment {
}

#[instrument(skip(self))]
async fn bitcoind(&self) -> Result<&Bitcoind> {
pub async fn bitcoind(&self) -> Result<&Bitcoind> {
let bitcoind = self
.bitcoind
.get_or_try_init(|| async {
Expand Down
73 changes: 73 additions & 0 deletions libs/sdk-itest/src/environment/swapd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use anyhow::{Error, Result};
use futures::TryFutureExt;
use testcontainers::core::WaitFor;
use testcontainers::core::wait::LogWaitStrategy;
use testcontainers::runners::AsyncRunner;
use testcontainers::{ContainerAsync, GenericImage, ImageExt};
use testcontainers_modules::postgres::Postgres;
use tokio::try_join;

use crate::environment::log::LogConsumer;
use crate::environment::{ApiCredentials, EnvironmentId};

const IMAGE_NAME: &str = "swapd";
const IMAGE_TAG: &str = "latest";
const RPC_PORT: u16 = 8011;

pub struct Swapd {
pub api: ApiCredentials,
_container: ContainerAsync<GenericImage>,
_postgres: ContainerAsync<Postgres>,
}

impl Swapd {
pub async fn new(
environment_id: &EnvironmentId,
bitcoind_api: impl Future<Output = Result<&ApiCredentials>>,
lnd_grpc_api: impl Future<Output = Result<&ApiCredentials>>,
) -> Result<Self> {
let postgres = Postgres::default()
.with_tag("16")
.with_network(environment_id.network_name())
.with_log_consumer(LogConsumer::new("swapd-postgres"))
.start()
.map_err(Error::msg);
let (postgres, bitcoind_api, lnd_grpc_api) =
try_join!(postgres, bitcoind_api, lnd_grpc_api)?;
let postgres_host = postgres.get_bridge_ip_address().await?.to_string();

let container = GenericImage::new(IMAGE_NAME, IMAGE_TAG)
.with_exposed_port(RPC_PORT.into())
.with_wait_for(WaitFor::Log(LogWaitStrategy::stdout("swapd started")))
.with_network(environment_id.network_name())
.with_log_consumer(LogConsumer::new("swapd"))
.with_copy_to(
"/data/lnd/admin.macaroon",
hex::encode(&lnd_grpc_api.macaroon).into_bytes(),
)
.with_env_var("NO_COLOR", "1")
.with_cmd([
"--auto-migrate",
"--chain-poll-interval-seconds=5",
format!("--lnd-grpc-address={}", lnd_grpc_api.endpoint()).as_str(),
"--lnd-grpc-macaroon=/data/lnd/admin.macaroon",
"--log-level=swapd=debug,info",
"--network=regtest",
format!("--address=0.0.0.0:{RPC_PORT}").as_str(),
format!("--db-url=postgresql://postgres:postgres@{postgres_host}/postgres?sslmode=disable").as_str(),
format!("--bitcoind-rpc-address={}", bitcoind_api.endpoint()).as_str(),
format!("--bitcoind-rpc-user={}", bitcoind_api.username).as_str(),
format!("--bitcoind-rpc-password={}", bitcoind_api.password).as_str(),
])
.start()
.await?;

let api = ApiCredentials::from_container(&container, RPC_PORT).await?;

Ok(Self {
api,
_container: container,
_postgres: postgres,
})
}
}
64 changes: 63 additions & 1 deletion libs/sdk-itest/tests/node.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
mod event_listener;

use std::str::FromStr;
use std::time::Duration;

use bitcoin::Amount;
use bitcoin::{Address, Amount};
use breez_sdk_core::error::{ConnectError, SendPaymentError};
use breez_sdk_core::{
BreezEvent, BreezServices, Config, ConnectRequest, ListPaymentsRequest, LnPaymentDetails,
Expand Down Expand Up @@ -32,6 +33,67 @@ async fn test_environment() {
Environment::default().cln_with_channel().await.unwrap();
}

#[rstest]
#[tokio::test(flavor = "multi_thread")]
#[test_log::test]
async fn test_node_swap() {
let env = Environment::default();
let (bitcoind, esplora, vss, lsp, _lnd, swapd) = try_join!(
env.bitcoind(),
env.esplora_api(),
env.vss_api(),
env.lsp_external_address(),
env.lnd_with_channel(),
env.swapd(),
)
.unwrap();
info!("Esplora is running: {}", esplora.external_endpoint());
info!(" VSS is running: {}", vss.external_endpoint());
info!(" LND is running");

let seed = rand::rng().random::<[u8; 64]>().to_vec();

let mut config = Config::regtest(String::new());
config.working_dir = testdir!().to_string_lossy().to_string();
config.mempoolspace_url = None;
config.esplora_url = esplora.external_endpoint();
config.vss_url = vss.external_endpoint();
config.rgs_url = "localhost:9".to_string();
config.lsps2_address = lsp;
config.breezserver = swapd.endpoint();

info!("Starting a fresh node with restore_only=None");
let req = ConnectRequest {
config: config.clone(),
seed: seed.clone(),
restore_only: None,
};

let (tx, mut events) = mpsc::channel(100);
let services = BreezServices::connect(req, Box::new(EventListenerImpl::new(tx)))
.await
.unwrap();

let res = services.receive_onchain(Default::default()).await.unwrap();
assert!(res.bitcoin_address.starts_with("bcrt1"));
let address = Address::from_str(&res.bitcoin_address)
.unwrap()
.assume_checked();
let amount = Amount::from_sat(50_000);
bitcoind.fund_address(&address, amount).await.unwrap();
bitcoind.generate_blocks(6).await.unwrap();

info!("Waiting for BreezEvent::InvoicePaid...");
wait_for!(matches!(
events.recv().await,
Some(BreezEvent::InvoicePaid { .. })
));

services.disconnect().await.unwrap();
drop(services);
assert!(events.is_closed());
}

#[rstest]
#[tokio::test(flavor = "multi_thread")]
#[test_log::test]
Expand Down