Skip to content

Commit

Permalink
tests: add lnd to integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
orbitalturtle committed Oct 2, 2023
1 parent ede113f commit 15970c6
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 13 deletions.
6 changes: 4 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ log = "0.4.17"
simple_logger = "4.0.0"
tokio = { version = "1.25.0", features = ["rt", "rt-multi-thread"] }
tonic = "0.8.3"
tonic_lnd = "0.5.1"
tonic_lnd = { git = "https://github.com/Kixunil/tonic_lnd", rev = "fac4a67a8d4951d62fc020d61d38628c0064e6df" }
hex = "0.4.3"
configure_me = "0.4.0"
bytes = "1.4.0"

[dev-dependencies]
bitcoincore-rpc = { package="core-rpc", version = "0.17.0" }
bitcoind = { version = "0.30.0", features = [ "22_0" ] }
chrono = { version = "0.4.26" }
ldk-sample = { git = "https://github.com/lndk-org/ldk-sample" }
mockall = "0.11.3"
tempfile = "3.5.0"
Expand Down
219 changes: 210 additions & 9 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
mod test_utils;

use bitcoin::network::constants::Network;
use bitcoin::secp256k1::PublicKey;
use bitcoincore_rpc::{bitcoin::Network as BitcoindNetwork, json, RpcApi};
use bitcoind::{get_available_port, BitcoinD, Conf};
use bitcoind::{get_available_port, BitcoinD, Conf, ConnectParams};
use chrono::Utc;
use ldk_sample::config::LdkUserInfo;
use ldk_sample::node_api::Node as LdkNode;
use std::net::SocketAddr;
use std::path::PathBuf;
use std::process::{Child, Command, Stdio};
use std::thread;
use std::{env, fs};
use tempfile::{tempdir, TempDir};
use tempfile::{tempdir, Builder, TempDir};
use tokio::time::Duration;
use tonic_lnd::lnrpc::GetInfoRequest;
use tonic_lnd::Client;

const LNDK_TESTS_FOLDER: &str = "lndk-tests";

pub async fn setup_test_infrastructure(test_name: &str) -> (BitcoindNode, LdkNode, LdkNode) {
pub async fn setup_test_infrastructure(
test_name: &str,
) -> (BitcoindNode, LndNode, LdkNode, LdkNode) {
let bitcoind = setup_bitcoind().await;
let (ldk_test_dir, _lnd_test_dir) = setup_test_dirs();
let (ldk_test_dir, lnd_test_dir) = setup_test_dirs();
let mut lnd = LndNode::new(
bitcoind.node.params.clone(),
bitcoind.zmq_block_port,
bitcoind.zmq_tx_port,
lnd_test_dir,
test_name.clone(),
);
lnd.setup_client().await;

let connect_params = bitcoind.node.params.get_cookie_values().unwrap();

Expand Down Expand Up @@ -42,7 +62,7 @@ pub async fn setup_test_infrastructure(test_name: &str) -> (BitcoindNode, LdkNod
let ldk1 = ldk_sample::start_ldk(ldk1_config, test_name).await;
let ldk2 = ldk_sample::start_ldk(ldk2_config, test_name).await;

(bitcoind, ldk1, ldk2)
(bitcoind, lnd, ldk1, ldk2)
}

// Sets up /tmp/lndk-tests folder where we'll store the bins, data directories, and logs needed
Expand Down Expand Up @@ -76,8 +96,8 @@ fn setup_test_dirs() -> (PathBuf, PathBuf) {
pub struct BitcoindNode {
node: BitcoinD,
_data_dir: TempDir,
_zmq_block_port: u16,
_zmq_tx_port: u16,
zmq_block_port: u16,
zmq_tx_port: u16,
}

pub async fn setup_bitcoind() -> BitcoindNode {
Expand All @@ -104,7 +124,188 @@ pub async fn setup_bitcoind() -> BitcoindNode {
BitcoindNode {
node: bitcoind,
_data_dir: data_dir,
_zmq_block_port: zmq_block_port,
_zmq_tx_port: zmq_tx_port,
zmq_block_port,
zmq_tx_port,
}
}

// LndNode holds the tools we need to interact with a Lightning node.
pub struct LndNode {
address: String,
_lnd_dir_tmp: TempDir,
cert_path: String,
macaroon_path: String,
_handle: Child,
client: Option<Client>,
}

impl LndNode {
fn new(
bitcoind_connect_params: ConnectParams,
zmq_block_port: u16,
zmq_tx_port: u16,
lnd_data_dir: PathBuf,
test_name: &str,
) -> LndNode {
let lnd_exe_dir = env::temp_dir().join("lndk-tests").join("bin");
env::set_current_dir(lnd_exe_dir).expect("couldn't set current directory");

let lnd_dir_binding = Builder::new()
.prefix("lnd-data-")
.tempdir_in(lnd_data_dir.clone())
.unwrap();
let lnd_dir = lnd_dir_binding.path();

let macaroon_path = lnd_dir
.join("data/chain/bitcoin/regtest/admin.macaroon")
.to_str()
.unwrap()
.to_string();

let connect_params = bitcoind_connect_params.get_cookie_values().unwrap();
let now_timestamp = Utc::now();
let timestamp = now_timestamp.format("%d-%m-%Y-%H%M");
let log_dir_path_buf = lnd_data_dir.join(format!("lnd-logs-{test_name}-{timestamp}"));
let log_dir = log_dir_path_buf.as_path();
let data_dir = lnd_dir.join("data").to_str().unwrap().to_string();
let cert_path = lnd_dir.to_str().unwrap().to_string() + "/tls.cert";
let key_path = lnd_dir.to_str().unwrap().to_string() + "/tls.key";

// Have node run on a randomly assigned grpc port. That way, if we run more than one lnd node, they won't
// clash.
let port = bitcoind::get_available_port().unwrap();
let rpc_addr = format!("localhost:{}", port);
let args = [
format!("--rpclisten={}", rpc_addr),
format!("--norest"),
// With this flag, we don't have to unlock the wallet on startup.
format!("--noseedbackup"),
format!("--bitcoin.active"),
format!("--bitcoin.node=bitcoind"),
format!("--bitcoin.regtest"),
format!("--datadir={}", data_dir),
format!("--tlscertpath={}", cert_path),
format!("--tlskeypath={}", key_path),
format!("--logdir={}", log_dir.display()),
format!("--bitcoind.rpcuser={}", connect_params.0.unwrap()),
format!("--bitcoind.rpcpass={}", connect_params.1.unwrap()),
format!(
"--bitcoind.zmqpubrawblock=tcp://127.0.0.1:{}",
zmq_block_port
),
format!("--bitcoind.zmqpubrawtx=tcp://127.0.0.1:{}", zmq_tx_port),
format!(
"--bitcoind.rpchost={:?}",
bitcoind_connect_params.rpc_socket
),
];

// TODO: For Windows we might need to add ".exe" at the end.
let cmd = Command::new("./lnd-itest")
.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to execute lnd command");

LndNode {
address: format!("https://{}", rpc_addr),
_lnd_dir_tmp: lnd_dir_binding,
cert_path: cert_path,
macaroon_path: macaroon_path,
_handle: cmd,
client: None,
}
}

// Setup the client we need to interact with the LND node.
async fn setup_client(&mut self) {
// We need to give lnd some time to start up before we'll be able to interact with it via the client.
let mut retry = false;
let mut retry_num = 0;
while retry_num == 0 || retry {
thread::sleep(Duration::from_secs(2));

let client_result = tonic_lnd::connect(
self.address.clone(),
self.cert_path.clone(),
self.macaroon_path.clone(),
)
.await;

match client_result {
Ok(client) => {
self.client = Some(client);

retry = false;
retry_num += 1;
}
Err(err) => {
println!(
"getting client error {err}, retrying call {} time",
retry_num
);
if retry_num == 3 {
panic!("could not set up client: {err}")
}
retry = true;
retry_num += 1;
}
}
}
}

#[allow(dead_code)]
pub async fn get_info(&mut self) -> tonic_lnd::lnrpc::GetInfoResponse {
let resp = if let Some(client) = self.client.clone() {
let get_info_req = GetInfoRequest {};
let make_request = || async {
client
.clone()
.lightning()
.get_info(get_info_req.clone())
.await
};
let resp = test_utils::retry_async(make_request, String::from("get_info"));
resp.await.unwrap()
} else {
panic!("No client")
};

resp
}

// connect_to_peer connects to the specified peer.
pub async fn connect_to_peer(
&mut self,
node_id: PublicKey,
addr: SocketAddr,
) -> tonic_lnd::lnrpc::ConnectPeerResponse {
let ln_addr = tonic_lnd::lnrpc::LightningAddress {
pubkey: node_id.to_string(),
host: addr.to_string(),
};

let connect_req = tonic_lnd::lnrpc::ConnectPeerRequest {
addr: Some(ln_addr),
timeout: 20,
..Default::default()
};

let resp = if let Some(client) = self.client.clone() {
let make_request = || async {
client
.clone()
.lightning()
.connect_peer(connect_req.clone())
.await
};
let resp = test_utils::retry_async(make_request, String::from("connect_peer"));
resp.await.unwrap()
} else {
panic!("No client")
};

resp
}
}
10 changes: 9 additions & 1 deletion tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@ mod common;
#[tokio::test(flavor = "multi_thread")]
async fn test_ldk_send_onion_message() {
let test_name = "send_onion_message";
let (_bitcoind, ldk1, ldk2) = common::setup_test_infrastructure(test_name).await;
let (_bitcoind, _lnd, ldk1, ldk2) = common::setup_test_infrastructure(test_name).await;
let (node_id_2, node_addr_2) = ldk2.get_node_info();
ldk1.connect_to_peer(node_id_2, node_addr_2).await.unwrap();

let data: Vec<u8> = vec![72, 101, 108, 108, 111];
let res = ldk1.send_onion_message(vec![node_id_2], 65, data).await;
assert!(res.is_ok());
}

#[tokio::test(flavor = "multi_thread")]
async fn test_ldk_lnd_connect() {
let test_name = "ldk_lnd_connect";
let (_bitcoind, mut lnd, ldk1, _ldk2) = common::setup_test_infrastructure(test_name).await;
let (pubkey, addr) = ldk1.get_node_info();
lnd.connect_to_peer(pubkey, addr).await;
}

0 comments on commit 15970c6

Please sign in to comment.