Skip to content

Commit

Permalink
write integration test for taker cli app.
Browse files Browse the repository at this point in the history
  • Loading branch information
KnowWhoami committed Sep 12, 2024
1 parent f1f4317 commit c0bcbd7
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 9 deletions.
9 changes: 0 additions & 9 deletions src/bin/taker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,6 @@ fn main() {
// send the tx to network
rpc.send_raw_transaction(&tx).unwrap();

// confirm balance( Generate blocks)
let mining_address = rpc
.get_new_address(None, None)
.unwrap()
.require_network(bitcoind::bitcoincore_rpc::bitcoin::Network::Regtest)
.unwrap();

rpc.generate_to_address(1, &mining_address).unwrap();

println!("Transaction of amount {:?} sent to {:?}", address, amount);
}

Expand Down
3 changes: 3 additions & 0 deletions src/taker/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ impl Taker {
if self.config.connection_type == ConnectionType::TOR && cfg!(feature = "tor") {
crate::tor::kill_tor_handles(handle.unwrap());
}

Ok(())
}

Expand All @@ -299,7 +300,9 @@ impl Taker {
/// If that fails too. Open an issue at [our github](https://github.com/citadel-tech/coinswap/issues)
pub fn send_coinswap(&mut self, swap_params: SwapParams) -> Result<(), TakerError> {
log::info!("Syncing Offerbook");

self.sync_offerbook(swap_params.maker_count)?;

// Generate new random preimage and initiate the first hop.
let mut preimage = [0u8; 32];
OsRng.fill_bytes(&mut preimage);
Expand Down
262 changes: 262 additions & 0 deletions tests/taker_cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
use bitcoin::{address::NetworkChecked, Address, Amount};
use bitcoind::{bitcoincore_rpc::RpcApi, tempfile::tempdir, BitcoinD, Conf};

use std::{
fs,
path::{Path, PathBuf},
process::Command,
str::FromStr,
};

/// The taker-cli command struct
/// Use it to perform all taker-cli operations
struct TakerCli {
/// taker-cli binary path
target: String,
rpc_network: String,
/// Connection type
network: String,
data_dir: PathBuf,
/// Bitcoind instance
bitcoind: BitcoinD,
fee_rate: u64,
/// no of makers in coinswap
makers_count: usize,
/// no of funding tx created in each swap
tx_count: u32,
}

impl TakerCli {
/// Construct a new [`TakerCli`] struct that also include initiating bitcoind.
fn new(
rpc_network: &str,
network: &str,
fee_rate: u64,
makers_count: usize,
tx_count: u32,
) -> TakerCli {
// Initiate the bitcoind backend.

let temp_dir = tempdir().unwrap().into_path();

let mut conf = Conf::default();

conf.args.push("-txindex=1"); //txindex is must, or else wallet sync won't work.
conf.staticdir = Some(temp_dir.join(".bitcoin"));
log::info!("bitcoind configuration: {:?}", conf.args);

let os = std::env::consts::OS;
let arch = std::env::consts::ARCH;
let key = "BITCOIND_EXE";
let curr_dir_path = std::env::current_dir().unwrap();

let bitcoind_path = match (os, arch) {
("macos", "aarch64") => curr_dir_path.join("bin").join("bitcoind_macos"),
_ => curr_dir_path.join("bin").join("bitcoind"),
};
std::env::set_var(key, bitcoind_path);
let exe_path = bitcoind::exe_path().unwrap();

log::info!("Executable path: {:?}", exe_path);

let bitcoind = BitcoinD::with_conf(exe_path, &conf).unwrap();

// Generate initial 101 blocks
let mining_address = bitcoind
.client
.get_new_address(None, None)
.unwrap()
.require_network(bitcoind::bitcoincore_rpc::bitcoin::Network::Regtest)
.unwrap();
bitcoind
.client
.generate_to_address(101, &mining_address)
.unwrap();

// derive data directory
let data_dir = temp_dir.join(".coinswap/taker/");

TakerCli {
target: "./target/debug/taker".to_string(),
rpc_network: rpc_network.to_string(),
network: network.to_string(),
data_dir,
bitcoind,
fee_rate,
makers_count,
tx_count,
}
}

// Build a cli-command
fn execute(&self, cmd: &[&str]) -> String {
let mut args = Vec::new();

// Data directory
args.push("--data-directory");
args.push(&self.data_dir.as_os_str().to_str().unwrap());

// Full node network
args.push("--NETWORK");
args.push(&self.rpc_network);

// Connection network type (clearnet/tor)
args.push("--network");
args.push(&self.network);

// RPC authentication (user:password) from the cookie file
//
// get rpc_auth
// Path to the cookie file
let cookie_file_path = Path::new(&self.bitcoind.params.cookie_file);

// Read the contents of the cookie file
let rpc_auth = fs::read_to_string(cookie_file_path).expect("failed to read from file");

args.push("--USER:PASSWORD");
args.push(&rpc_auth);

// Full node address for RPC connection
let rpc_address = self.bitcoind.params.rpc_socket.to_string();
args.push("--ADDRESS:PORT");
args.push(&rpc_address);

// Wallet name
args.push("--WALLET");
args.push("test_wallet");

// Custom arguments for the taker-cli command

// makers count
let maker_count = self.makers_count.to_string();
args.push(&maker_count);

// tx_count
let tx_count = self.tx_count.to_string();
args.push(&tx_count);

// fee_rate
let fee_rate = self.fee_rate.to_string();
args.push(&fee_rate);

// Final command to execute
for arg in cmd {
args.push(arg);
}

// Execute the command
let output = Command::new(&self.target).args(args).output().unwrap();

let mut value = output.stdout;
let error = output.stderr;

if !error.is_empty() {
panic!("Error: {:?}", String::from_utf8(error).unwrap());
}

value.pop(); // Remove `\n` at the end

// Get the output string from bytes
let mut output_string = std::str::from_utf8(&value).unwrap().to_string();

// `output_string` contains the log statements along with the required output -> removing the logs.
let end_pos = output_string.rfind("\n").expect("must not fail");
output_string.replace_range(0..end_pos + 1, "");

output_string
}
}

#[test]
fn test_taker_cli() {
// create taker_cli instance
let taker_cli = TakerCli::new("regtest", "clearnet", 1000, 3, 3);

// Fund the taker with 3 utxos of 1 BTC each.
for _ in 0..3 {
// derive the address
let taker_address = taker_cli.execute(&["get-new-address"]);

let taker_address: Address<NetworkChecked> =
Address::from_str(&taker_address).unwrap().assume_checked();

// fund 1 BTC to derived address
taker_cli
.bitcoind
.client
.send_to_address(
&taker_address,
Amount::ONE_BTC,
None,
None,
None,
None,
None,
None,
)
.unwrap();
}

// confirm balance( Generate blocks)
let mining_address = taker_cli
.bitcoind
.client
.get_new_address(None, None)
.unwrap()
.require_network(bitcoind::bitcoincore_rpc::bitcoin::Network::Regtest)
.unwrap();

taker_cli
.bitcoind
.client
.generate_to_address(1, &mining_address)
.unwrap();

// Assert that total_balance & seed_balance must be 3 BTC
let seed_balance = taker_cli.execute(&["seed-balance"]);
let total_balance = taker_cli.execute(&["total-balance"]);

assert_eq!("300000000 SAT", seed_balance);
assert_eq!("300000000 SAT", total_balance);

// Assert that total no of seed-utxos are 3.
let seed_utxos = taker_cli.execute(&["seed-utxo"]);

let no_of_seed_utxos = seed_utxos.matches("ListUnspentResultEntry {").count();
assert_eq!(3, no_of_seed_utxos);

// Send 1000 Sats to wallet itself via send-to-address

// get new external address
let new_address = taker_cli.execute(&["get-new-address"]);

// TODO: unexpected string is getting.
taker_cli.execute(&["send-to-address", &new_address, "1000"]);

// Generate initial 101 blocks
let mining_address = taker_cli
.bitcoind
.client
.get_new_address(None, None)
.unwrap()
.require_network(bitcoind::bitcoincore_rpc::bitcoin::Network::Regtest)
.unwrap();
taker_cli
.bitcoind
.client
.generate_to_address(101, &mining_address)
.unwrap();

// Assert the total_amount & seed_amount must be initial (balance -fee)
let seed_balance = taker_cli.execute(&["seed-balance"]);
let total_balance = taker_cli.execute(&["total-balance"]);

assert_eq!("299999000 SAT", seed_balance);
assert_eq!("299999000 SAT", total_balance);

// Assert that no of seed utxos are 2
let seed_utxos = taker_cli.execute(&["seed-utxo"]);

let no_of_seed_utxos = seed_utxos.matches("ListUnspentResultEntry {").count();
assert_eq!(2, no_of_seed_utxos);
}

0 comments on commit c0bcbd7

Please sign in to comment.