1#[macro_use]
13extern crate serde_json;
14
15pub use serial_test::serial;
16
17use std::collections::HashMap;
18use std::env;
19use std::ops::Deref;
20use std::path::PathBuf;
21use std::str::FromStr;
22use std::time::Duration;
23
24#[allow(unused_imports)]
25use log::{debug, error, info, trace};
26
27use bitcoin::consensus::encode::{deserialize, serialize};
28use bitcoin::hashes::hex::{FromHex, ToHex};
29use bitcoin::hashes::sha256d;
30use bitcoin::secp256k1::{Secp256k1, Verification};
31use bitcoin::{Address, Amount, PublicKey, Script, Transaction, Txid};
32
33use miniscript::descriptor::DescriptorPublicKey;
34use miniscript::{Descriptor, MiniscriptKey, TranslatePk};
35
36pub use bitcoincore_rpc::bitcoincore_rpc_json::AddressType;
37pub use bitcoincore_rpc::{Auth, Client as RpcClient, RpcApi};
38
39pub use electrum_client::{Client as ElectrumClient, ElectrumApi};
40
41fn get_auth() -> Auth {
43 match env::var("BDK_RPC_AUTH").as_ref().map(String::as_ref) {
44 Ok("USER_PASS") => Auth::UserPass(
45 env::var("BDK_RPC_USER").unwrap(),
46 env::var("BDK_RPC_PASS").unwrap(),
47 ),
48 _ => Auth::CookieFile(PathBuf::from(
49 env::var("BDK_RPC_COOKIEFILE")
50 .unwrap_or_else(|_| "/home/user/.bitcoin/regtest/.cookie".to_string()),
51 )),
52 }
53}
54
55pub fn get_electrum_url() -> String {
56 env::var("BDK_ELECTRUM_URL").unwrap_or_else(|_| "tcp://127.0.0.1:50001".to_string())
57}
58
59pub struct TestClient {
60 client: RpcClient,
61 electrum: ElectrumClient,
62}
63
64#[derive(Clone, Debug)]
65pub struct TestIncomingOutput {
66 pub value: u64,
67 pub to_address: String,
68}
69
70impl TestIncomingOutput {
71 pub fn new(value: u64, to_address: Address) -> Self {
72 Self {
73 value,
74 to_address: to_address.to_string(),
75 }
76 }
77}
78
79#[derive(Clone, Debug)]
80pub struct TestIncomingTx {
81 pub output: Vec<TestIncomingOutput>,
82 pub min_confirmations: Option<u64>,
83 pub locktime: Option<i64>,
84 pub replaceable: Option<bool>,
85}
86
87impl TestIncomingTx {
88 pub fn new(
89 output: Vec<TestIncomingOutput>,
90 min_confirmations: Option<u64>,
91 locktime: Option<i64>,
92 replaceable: Option<bool>,
93 ) -> Self {
94 Self {
95 output,
96 min_confirmations,
97 locktime,
98 replaceable,
99 }
100 }
101
102 pub fn add_output(&mut self, output: TestIncomingOutput) {
103 self.output.push(output);
104 }
105}
106
107#[doc(hidden)]
108pub trait TranslateDescriptor {
109 fn derive_translated<C: Verification>(
111 &self,
112 secp: &Secp256k1<C>,
113 index: u32,
114 ) -> Descriptor<PublicKey>;
115}
116
117impl TranslateDescriptor for Descriptor<DescriptorPublicKey> {
118 fn derive_translated<C: Verification>(
119 &self,
120 secp: &Secp256k1<C>,
121 index: u32,
122 ) -> Descriptor<PublicKey> {
123 let translate = |key: &DescriptorPublicKey| -> PublicKey {
124 match key {
125 DescriptorPublicKey::XPub(xpub) => {
126 xpub.xkey
127 .derive_pub(secp, &xpub.derivation_path)
128 .expect("hardened derivation steps")
129 .public_key
130 }
131 DescriptorPublicKey::SinglePub(key) => key.key,
132 }
133 };
134
135 self.derive(index)
136 .translate_pk_infallible(|pk| translate(pk), |pkh| translate(pkh).to_pubkeyhash())
137 }
138}
139
140#[macro_export]
141macro_rules! testutils {
142 ( @external $descriptors:expr, $child:expr ) => ({
143 use bitcoin::secp256k1::Secp256k1;
144 use miniscript::descriptor::{Descriptor, DescriptorPublicKey, DescriptorTrait};
145
146 use $crate::TranslateDescriptor;
147
148 let secp = Secp256k1::new();
149
150 let parsed = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &$descriptors.0).expect("Failed to parse descriptor in `testutils!(@external)`").0;
151 parsed.derive_translated(&secp, $child).address(bitcoin::Network::Regtest).expect("No address form")
152 });
153 ( @internal $descriptors:expr, $child:expr ) => ({
154 use bitcoin::secp256k1::Secp256k1;
155 use miniscript::descriptor::{Descriptor, DescriptorPublicKey, DescriptorTrait};
156
157 use $crate::TranslateDescriptor;
158
159 let secp = Secp256k1::new();
160
161 let parsed = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &$descriptors.1.expect("Missing internal descriptor")).expect("Failed to parse descriptor in `testutils!(@internal)`").0;
162 parsed.derive_translated(&secp, $child).address(bitcoin::Network::Regtest).expect("No address form")
163 });
164 ( @e $descriptors:expr, $child:expr ) => ({ testutils!(@external $descriptors, $child) });
165 ( @i $descriptors:expr, $child:expr ) => ({ testutils!(@internal $descriptors, $child) });
166
167 ( @tx ( $( ( $( $addr:tt )* ) => $amount:expr ),+ ) $( ( @locktime $locktime:expr ) )* $( ( @confirmations $confirmations:expr ) )* $( ( @replaceable $replaceable:expr ) )* ) => ({
168 let mut outs = Vec::new();
169 $( outs.push(testutils::TestIncomingOutput::new($amount, testutils!( $($addr)* ))); )+
170
171 let mut locktime = None::<i64>;
172 $( locktime = Some($locktime); )*
173
174 let mut min_confirmations = None::<u64>;
175 $( min_confirmations = Some($confirmations); )*
176
177 let mut replaceable = None::<bool>;
178 $( replaceable = Some($replaceable); )*
179
180 testutils::TestIncomingTx::new(outs, min_confirmations, locktime, replaceable)
181 });
182
183 ( @literal $key:expr ) => ({
184 let key = $key.to_string();
185 (key, None::<String>, None::<String>)
186 });
187 ( @generate_xprv $( $external_path:expr )* $( ,$internal_path:expr )* ) => ({
188 use rand::Rng;
189
190 let mut seed = [0u8; 32];
191 rand::thread_rng().fill(&mut seed[..]);
192
193 let key = bitcoin::util::bip32::ExtendedPrivKey::new_master(
194 bitcoin::Network::Testnet,
195 &seed,
196 );
197
198 let mut external_path = None::<String>;
199 $( external_path = Some($external_path.to_string()); )*
200
201 let mut internal_path = None::<String>;
202 $( internal_path = Some($internal_path.to_string()); )*
203
204 (key.unwrap().to_string(), external_path, internal_path)
205 });
206 ( @generate_wif ) => ({
207 use rand::Rng;
208
209 let mut key = [0u8; bitcoin::secp256k1::constants::SECRET_KEY_SIZE];
210 rand::thread_rng().fill(&mut key[..]);
211
212 (bitcoin::PrivateKey {
213 compressed: true,
214 network: bitcoin::Network::Testnet,
215 key: bitcoin::secp256k1::SecretKey::from_slice(&key).unwrap(),
216 }.to_string(), None::<String>, None::<String>)
217 });
218
219 ( @keys ( $( $alias:expr => ( $( $key_type:tt )* ) ),+ ) ) => ({
220 let mut map = std::collections::HashMap::new();
221 $(
222 let alias: &str = $alias;
223 map.insert(alias, testutils!( $($key_type)* ));
224 )+
225
226 map
227 });
228
229 ( @descriptors ( $external_descriptor:expr ) $( ( $internal_descriptor:expr ) )* $( ( @keys $( $keys:tt )* ) )* ) => ({
230 use std::str::FromStr;
231 use std::collections::HashMap;
232 use std::convert::TryInto;
233
234 use miniscript::descriptor::{Descriptor, DescriptorPublicKey};
235 use miniscript::TranslatePk;
236
237 let mut keys: HashMap<&'static str, (String, Option<String>, Option<String>)> = HashMap::new();
238 $(
239 keys = testutils!{ @keys $( $keys )* };
240 )*
241
242 let external: Descriptor<String> = FromStr::from_str($external_descriptor).unwrap();
243 let external: Descriptor<String> = external.translate_pk_infallible::<_, _>(|k| {
244 if let Some((key, ext_path, _)) = keys.get(&k.as_str()) {
245 format!("{}{}", key, ext_path.as_ref().unwrap_or(&"".into()))
246 } else {
247 k.clone()
248 }
249 }, |kh| {
250 if let Some((key, ext_path, _)) = keys.get(&kh.as_str()) {
251 format!("{}{}", key, ext_path.as_ref().unwrap_or(&"".into()))
252 } else {
253 kh.clone()
254 }
255
256 });
257 let external = external.to_string();
258
259 let mut internal = None::<String>;
260 $(
261 let string_internal: Descriptor<String> = FromStr::from_str($internal_descriptor).unwrap();
262
263 let string_internal: Descriptor<String> = string_internal.translate_pk_infallible::<_, _>(|k| {
264 if let Some((key, _, int_path)) = keys.get(&k.as_str()) {
265 format!("{}{}", key, int_path.as_ref().unwrap_or(&"".into()))
266 } else {
267 k.clone()
268 }
269 }, |kh| {
270 if let Some((key, _, int_path)) = keys.get(&kh.as_str()) {
271 format!("{}{}", key, int_path.as_ref().unwrap_or(&"".into()))
272 } else {
273 kh.clone()
274 }
275 });
276 internal = Some(string_internal.to_string());
277 )*
278
279 (external, internal)
280 })
281}
282
283fn exponential_backoff_poll<T, F>(mut poll: F) -> T
284where
285 F: FnMut() -> Option<T>,
286{
287 let mut delay = Duration::from_millis(64);
288 loop {
289 match poll() {
290 Some(data) => break data,
291 None if delay.as_millis() < 512 => delay = delay.mul_f32(2.0),
292 None => {}
293 }
294
295 std::thread::sleep(delay);
296 }
297}
298
299impl TestClient {
300 pub fn new() -> Self {
301 let url = env::var("BDK_RPC_URL").unwrap_or_else(|_| "127.0.0.1:18443".to_string());
302 let wallet = env::var("BDK_RPC_WALLET").unwrap_or_else(|_| "bdk-test".to_string());
303 let client =
304 RpcClient::new(format!("http://{}/wallet/{}", url, wallet), get_auth()).unwrap();
305 let electrum = ElectrumClient::new(&get_electrum_url()).unwrap();
306
307 TestClient { client, electrum }
308 }
309
310 fn wait_for_tx(&mut self, txid: Txid, monitor_script: &Script) {
311 exponential_backoff_poll(|| {
313 trace!("wait_for_tx {}", txid);
314
315 self.electrum
316 .script_get_history(monitor_script)
317 .unwrap()
318 .iter()
319 .position(|entry| entry.tx_hash == txid)
320 });
321 }
322
323 fn wait_for_block(&mut self, min_height: usize) {
324 self.electrum.block_headers_subscribe().unwrap();
325
326 loop {
327 let header = exponential_backoff_poll(|| {
328 self.electrum.ping().unwrap();
329 self.electrum.block_headers_pop().unwrap()
330 });
331 if header.height >= min_height {
332 break;
333 }
334 }
335 }
336
337 pub fn receive(&mut self, meta_tx: TestIncomingTx) -> Txid {
338 assert!(
339 !meta_tx.output.is_empty(),
340 "can't create a transaction with no outputs"
341 );
342
343 let mut map = HashMap::new();
344
345 let mut required_balance = 0;
346 for out in &meta_tx.output {
347 required_balance += out.value;
348 map.insert(out.to_address.clone(), Amount::from_sat(out.value));
349 }
350
351 if self.get_balance(None, None).unwrap() < Amount::from_sat(required_balance) {
352 panic!("Insufficient funds in bitcoind. Please generate a few blocks with: `bitcoin-cli generatetoaddress 10 {}`", self.get_new_address(None, None).unwrap());
353 }
354
355 let tx = self
357 .create_raw_transaction_hex(&[], &map, meta_tx.locktime, meta_tx.replaceable)
358 .unwrap();
359 let tx = self.fund_raw_transaction(tx, None, None).unwrap();
360 let mut tx: Transaction = deserialize(&tx.hex).unwrap();
361
362 if let Some(true) = meta_tx.replaceable {
363 for input in &mut tx.input {
365 input.sequence = 0xFFFFFFFD;
366 }
367 }
368
369 let tx = self
370 .sign_raw_transaction_with_wallet(&serialize(&tx), None, None)
371 .unwrap();
372
373 let txid = self
375 .electrum
376 .transaction_broadcast(&deserialize(&tx.hex).unwrap())
377 .unwrap();
378
379 if let Some(num) = meta_tx.min_confirmations {
380 self.generate(num, None);
381 }
382
383 let monitor_script = Address::from_str(&meta_tx.output[0].to_address)
384 .unwrap()
385 .script_pubkey();
386 self.wait_for_tx(txid, &monitor_script);
387
388 debug!("Sent tx: {}", txid);
389
390 txid
391 }
392
393 pub fn bump_fee(&mut self, txid: &Txid) -> Txid {
394 let tx = self.get_raw_transaction_info(txid, None).unwrap();
395 assert!(
396 tx.confirmations.is_none(),
397 "Can't bump tx {} because it's already confirmed",
398 txid
399 );
400
401 let bumped: serde_json::Value = self.call("bumpfee", &[txid.to_string().into()]).unwrap();
402 let new_txid = Txid::from_str(&bumped["txid"].as_str().unwrap().to_string()).unwrap();
403
404 let monitor_script =
405 tx.vout[0].script_pub_key.addresses.as_ref().unwrap()[0].script_pubkey();
406 self.wait_for_tx(new_txid, &monitor_script);
407
408 debug!("Bumped {}, new txid {}", txid, new_txid);
409
410 new_txid
411 }
412
413 pub fn generate_manually(&mut self, txs: Vec<Transaction>) -> String {
414 use bitcoin::blockdata::block::{Block, BlockHeader};
415 use bitcoin::blockdata::script::Builder;
416 use bitcoin::blockdata::transaction::{OutPoint, TxIn, TxOut};
417 use bitcoin::hash_types::{BlockHash, TxMerkleNode};
418
419 let block_template: serde_json::Value = self
420 .call("getblocktemplate", &[json!({"rules": ["segwit"]})])
421 .unwrap();
422 trace!("getblocktemplate: {:#?}", block_template);
423
424 let header = BlockHeader {
425 version: block_template["version"].as_i64().unwrap() as i32,
426 prev_blockhash: BlockHash::from_hex(
427 block_template["previousblockhash"].as_str().unwrap(),
428 )
429 .unwrap(),
430 merkle_root: TxMerkleNode::default(),
431 time: block_template["curtime"].as_u64().unwrap() as u32,
432 bits: u32::from_str_radix(block_template["bits"].as_str().unwrap(), 16).unwrap(),
433 nonce: 0,
434 };
435 debug!("header: {:#?}", header);
436
437 let height = block_template["height"].as_u64().unwrap() as i64;
438 let witness_reserved_value: Vec<u8> = sha256d::Hash::default().as_ref().into();
439 let mut coinbase_tx = Transaction {
441 version: 1,
442 lock_time: 0,
443 input: vec![TxIn {
444 previous_output: OutPoint::null(),
445 script_sig: Builder::new().push_int(height).into_script(),
446 sequence: 0xFFFFFFFF,
447 witness: vec![witness_reserved_value],
448 }],
449 output: vec![],
450 };
451
452 let mut txdata = vec![coinbase_tx.clone()];
453 txdata.extend_from_slice(&txs);
454
455 let mut block = Block { header, txdata };
456
457 let witness_root = block.witness_root();
458 let witness_commitment =
459 Block::compute_witness_commitment(&witness_root, &coinbase_tx.input[0].witness[0]);
460
461 let mut coinbase_witness_commitment_script = vec![0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed];
463 coinbase_witness_commitment_script.extend_from_slice(&witness_commitment);
464
465 coinbase_tx.output.push(TxOut {
466 value: 0,
467 script_pubkey: coinbase_witness_commitment_script.into(),
468 });
469 block.txdata[0] = coinbase_tx;
470
471 let merkle_root = block.merkle_root();
473 block.header.merkle_root = merkle_root;
474
475 assert!(block.check_merkle_root());
476 assert!(block.check_witness_commitment());
477
478 let target = block.header.target();
480 while block.header.validate_pow(&target).is_err() {
481 block.header.nonce = block.header.nonce.checked_add(1).unwrap(); }
483
484 let block_hex: String = serialize(&block).to_hex();
485 debug!("generated block hex: {}", block_hex);
486
487 self.electrum.block_headers_subscribe().unwrap();
488
489 let submit_result: serde_json::Value =
490 self.call("submitblock", &[block_hex.into()]).unwrap();
491 debug!("submitblock: {:?}", submit_result);
492 assert!(
493 submit_result.is_null(),
494 "submitblock error: {:?}",
495 submit_result.as_str()
496 );
497
498 self.wait_for_block(height as usize);
499
500 block.header.block_hash().to_hex()
501 }
502
503 pub fn generate(&mut self, num_blocks: u64, address: Option<Address>) {
504 let address = address.unwrap_or_else(|| self.get_new_address(None, None).unwrap());
505 let hashes = self.generate_to_address(num_blocks, &address).unwrap();
506 let best_hash = hashes.last().unwrap();
507 let height = self.get_block_info(best_hash).unwrap().height;
508
509 self.wait_for_block(height);
510
511 debug!("Generated blocks to new height {}", height);
512 }
513
514 pub fn invalidate(&mut self, num_blocks: u64) {
515 self.electrum.block_headers_subscribe().unwrap();
516
517 let best_hash = self.get_best_block_hash().unwrap();
518 let initial_height = self.get_block_info(&best_hash).unwrap().height;
519
520 let mut to_invalidate = best_hash;
521 for i in 1..=num_blocks {
522 trace!(
523 "Invalidating block {}/{} ({})",
524 i,
525 num_blocks,
526 to_invalidate
527 );
528
529 self.invalidate_block(&to_invalidate).unwrap();
530 to_invalidate = self.get_best_block_hash().unwrap();
531 }
532
533 self.wait_for_block(initial_height - num_blocks as usize);
534
535 debug!(
536 "Invalidated {} blocks to new height of {}",
537 num_blocks,
538 initial_height - num_blocks as usize
539 );
540 }
541
542 pub fn reorg(&mut self, num_blocks: u64) {
543 self.invalidate(num_blocks);
544 self.generate(num_blocks, None);
545 }
546
547 pub fn get_node_address(&self, address_type: Option<AddressType>) -> Address {
548 Address::from_str(
549 &self
550 .get_new_address(None, address_type)
551 .unwrap()
552 .to_string(),
553 )
554 .unwrap()
555 }
556}
557
558impl Deref for TestClient {
559 type Target = RpcClient;
560
561 fn deref(&self) -> &Self::Target {
562 &self.client
563 }
564}