Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
KnowWhoami committed Sep 17, 2024
1 parent a73ef73 commit 462ba1e
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 8 deletions.
50 changes: 44 additions & 6 deletions src/bin/taker.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use std::path::PathBuf;

use bitcoin::Amount;
use bitcoind::bitcoincore_rpc::{json::ListUnspentResultEntry, Auth};
use bitcoin::{Address, Amount};
use bitcoind::bitcoincore_rpc::{json::ListUnspentResultEntry, Auth, Client, RpcApi};
use clap::Parser;
use coinswap::{
taker::{SwapParams, Taker, TakerBehavior},
utill::{
parse_proxy_auth, read_bitcoin_network_string, read_connection_network_string, setup_logger,
},
wallet::RPCConfig,
wallet::{Destination, RPCConfig, SendAmount},
};

use serde_json::Value;
use std::{convert::TryFrom, path::PathBuf, str::FromStr};
/// taker-cli is a command line app to use taker client API's.
#[derive(Parser, Debug)]
#[clap(version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"),
Expand Down Expand Up @@ -82,6 +81,19 @@ enum Commands {
TotalBalance,
/// Returns a new address
GetNewAddress,
/// Send to an external wallet address.
SendToAddress {
/// Recipient address
#[clap(name = "address")]
address: String,
/// Amount to be sent (in sats)
#[clap(name = "amount")]
amount: u64,
/// Fee of a Tx(in sats)
#[clap(name = "fee")]
fee: u64,
},

/// Sync the offer book
SyncOfferBook,
/// Initiate the coinswap process
Expand Down Expand Up @@ -175,6 +187,32 @@ fn main() {
let address = taker.get_wallet_mut().get_next_external_address().unwrap();
println!("{:?}", address);
}
Commands::SendToAddress {
address,
amount,
fee,
} => {
let fee = Amount::from_sat(fee);
let send_amount = SendAmount::Amount(Amount::from_sat(amount));
let destination =
Destination::Address(Address::from_str(&address).unwrap().assume_checked());

let coins_to_spend = taker.get_wallet().list_all_utxo_spend_info(None).unwrap();
// create a signed tx
let tx = taker
.get_wallet_mut()
.spend_from_wallet(fee, send_amount, destination, &coins_to_spend)
.unwrap();

let calculated_fee_rate = fee / (tx.weight()); //TODO : multiply with 1000 or not?

println!(
"transaction_hex : {:?}",
bitcoin::consensus::encode::serialize_hex(&tx)
);
println!("Calculated FeeRate : {:?}", calculated_fee_rate);
}

Commands::SyncOfferBook => {
taker.sync_offerbook(args.maker_count).unwrap();
}
Expand Down
7 changes: 6 additions & 1 deletion src/utill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,12 @@ pub fn seed_phrase_to_unique_id(seed: &str) -> String {
/// Setup function that will only run once, even if called multiple times.
/// Takes an optional log level to set the desired logging verbosity.
pub fn setup_logger(log_level: Option<LevelFilter>) {
let log_level = log_level.unwrap_or_else(|| LevelFilter::Info);
let log_level = if let Some(level) = log_level {
level
} else {
LevelFilter::Info
};

Once::new().call_once(|| {
env::set_var("RUST_LOG", "coinswap=info");
let taker_log_dir = get_taker_dir().join("debug.log");
Expand Down
302 changes: 302 additions & 0 deletions tests/taker_cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
use bitcoin::{address::NetworkChecked, Address, Amount, Transaction};
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 directory = dirs::home_dir().unwrap().join(".coinswap");

let mut conf = Conf::default();

conf.args.push("-txindex=1"); //txindex is must, or else wallet sync won't work.
let a = conf.staticdir.clone();
conf.staticdir = Some(PathBuf::from("/home/divyanshgupta/.coinswap/.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 = directory.join(".coinswap/taker/");

TakerCli {
target: "/home/divyanshgupta/coinswap/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 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"]);

let response = taker_cli.execute(&["send-to-address", &new_address, "100000", "1000"]);

// Extract Transaction hex string
let tx_hex_start = response.find("").unwrap() + "transaction_hex : \"".len();
let tx_hex_end = response.find("\"\n").unwrap();

let tx_hex = &response[tx_hex_start..tx_hex_end];

// Extract FeeRate
let fee_rate_start = response.find("FeeRate(").unwrap() + "FeeRate(".len();
let fee_rate_end = response.find(")").unwrap();

let fee_rate = &response[fee_rate_start..fee_rate_end];

let tx: Transaction = bitcoin::consensus::encode::deserialize_hex(tx_hex).unwrap();

// broadcast signed transaction
taker_cli.bitcoind.client.send_raw_transaction(&tx).unwrap();

// confirm balances
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);

let a = taker_cli.bitcoind.client.get_wallet_info().unwrap();

let a = taker_cli
.bitcoind
.client
.list_transactions(None, Some(0), None, None);

// confirm balances
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();
// stopping Bitcoind
taker_cli.bitcoind.client.stop().unwrap();
}
Loading

0 comments on commit 462ba1e

Please sign in to comment.