forked from citadel-tech/coinswap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
taker_cli.rs
234 lines (179 loc) · 7.18 KB
/
taker_cli.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
use bitcoin::{address::NetworkChecked, Address, Amount, Transaction};
use bitcoind::{bitcoincore_rpc::RpcApi, tempfile::env::temp_dir, BitcoinD, Conf};
use std::{
fs,
path::{Path, PathBuf},
process::Command,
str::FromStr,
};
/// The taker-cli command struct
struct TakerCli {
data_dir: PathBuf,
bitcoind: BitcoinD,
}
impl TakerCli {
/// Construct a new [`TakerCli`] struct that also include initiating bitcoind.
fn new() -> TakerCli {
// Initiate the bitcoind backend.
let temp_dir = temp_dir().join(".coinswap");
// Remove if previously existing
if temp_dir.exists() {
fs::remove_dir_all::<PathBuf>(temp_dir.clone()).unwrap();
}
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();
let data_dir = temp_dir.join("taker");
TakerCli { data_dir, bitcoind }
}
// Execute a cli-command
fn execute(&self, cmd: &[&str]) -> String {
let mut args = vec![
"--data-directory",
self.data_dir.as_os_str().to_str().unwrap(),
"--bitcoin-network",
"regtest",
"--connection-type",
"clearnet",
];
// RPC authentication (user:password) from the cookie file
let cookie_file_path = Path::new(&self.bitcoind.params.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);
args.push("--WALLET");
args.push("test_wallet");
// makers count
args.push("3");
// tx_count
args.push("3");
// fee_rate
args.push("1000");
for arg in cmd {
args.push(arg);
}
let output = Command::new("./target/debug/taker")
.args(args)
.output()
.unwrap();
// Capture the standard output and error from the command execution
let mut value = output.stdout;
let error = output.stderr;
// Panic if there is any error output
if !error.is_empty() {
panic!("Error: {:?}", String::from_utf8(error).unwrap());
}
// Remove the `\n` at the end of the output
value.pop();
// Convert the output bytes to a UTF-8 string
let output_string = std::str::from_utf8(&value).unwrap().to_string();
output_string
}
/// Generate Blocks in regtest node.
pub fn generate_blocks(&self, n: u64) {
let mining_address = self
.bitcoind
.client
.get_new_address(None, None)
.unwrap()
.require_network(bitcoind::bitcoincore_rpc::bitcoin::Network::Regtest)
.unwrap();
self.bitcoind
.client
.generate_to_address(n, &mining_address)
.unwrap();
}
}
#[test]
fn test_taker_cli() {
let taker_cli = TakerCli::new();
// Fund the taker with 3 utxos of 1 BTC each.
for _ in 0..3 {
let taker_address = taker_cli.execute(&["get-new-address"]);
let taker_address: Address<NetworkChecked> =
Address::from_str(&taker_address).unwrap().assume_checked();
taker_cli
.bitcoind
.client
.send_to_address(
&taker_address,
Amount::ONE_BTC,
None,
None,
None,
None,
None,
None,
)
.unwrap();
}
// confirm balance
taker_cli.generate_blocks(10);
// 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 100,000 sats to a new address within the wallet, with a fee of 1,000 sats.
// 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("transaction_hex").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(" sat").unwrap();
let _fee_rate = &response[fee_rate_start..fee_rate_end];
// TODO: Determine if asserts are needed for the calculated fee rate.
let tx: Transaction = bitcoin::consensus::encode::deserialize_hex(tx_hex).unwrap();
// broadcast signed transaction
taker_cli.bitcoind.client.send_raw_transaction(&tx).unwrap();
taker_cli.generate_blocks(10);
// 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"]);
// Since the amount is sent back to our wallet, the transaction fee is deducted from the 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!(4, no_of_seed_utxos);
taker_cli.bitcoind.client.stop().unwrap();
// Wait for some time for successfull shutdown of bitcoind.
std::thread::sleep(std::time::Duration::from_secs(3));
}