Skip to content

Commit e390c20

Browse files
committed
Test feature parity of API methods
1 parent 0b85f92 commit e390c20

File tree

2 files changed

+349
-0
lines changed

2 files changed

+349
-0
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ reqwest = { version = "0.11", optional = true, default-features = false, feature
2424

2525
[dev-dependencies]
2626
serde_json = "1.0"
27+
tokio = { version = "1.20.1", features = ["full"] }
28+
electrsd = { version = "0.19.1", features = ["legacy", "esplora_a33e97e1", "bitcoind_22_0"] }
29+
electrum-client = "0.10"
30+
lazy_static = "1.4.0"
2731

2832
[features]
2933
default = ["blocking", "async", "async-https"]

src/lib.rs

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,98 @@ impl_error!(bitcoin::hashes::hex::Error, Hex, Error);
198198
#[cfg(test)]
199199
mod test {
200200
use super::*;
201+
use bitcoin::Amount;
202+
use electrsd::{
203+
bitcoind, bitcoind::bitcoincore_rpc::bitcoincore_rpc_json::AddressType,
204+
bitcoind::bitcoincore_rpc::RpcApi, bitcoind::BitcoinD, ElectrsD,
205+
};
206+
use electrum_client::ElectrumApi;
207+
use lazy_static::lazy_static;
208+
use std::sync::{Mutex, Once};
209+
use std::time::Duration;
210+
211+
lazy_static! {
212+
static ref BITCOIND: BitcoinD = {
213+
let bitcoind_exe =
214+
bitcoind::downloaded_exe_path().expect("bitcoind version feature must be enabled");
215+
let conf = bitcoind::Conf::default();
216+
BitcoinD::with_conf(bitcoind_exe, &conf).unwrap()
217+
};
218+
static ref ELECTRSD: ElectrsD = {
219+
let electrs_exe =
220+
electrsd::downloaded_exe_path().expect("electrs version feature must be enabled");
221+
let mut conf = electrsd::Conf::default();
222+
conf.http_enabled = true;
223+
ElectrsD::with_conf(electrs_exe, &BITCOIND, &conf).unwrap()
224+
};
225+
static ref MINER: Mutex<()> = Mutex::new(());
226+
}
227+
228+
static PREMINE: Once = Once::new();
229+
230+
fn setup_clients() -> (BlockingClient, AsyncClient) {
231+
PREMINE.call_once(|| {
232+
let _miner = MINER.lock().unwrap();
233+
generate_blocks_and_wait(101);
234+
});
235+
236+
let esplora_url = ELECTRSD.esplora_url.as_ref().unwrap();
237+
238+
let builder = Builder::new(&format!("http://{}", esplora_url));
239+
let blocking_client = builder.build_blocking().unwrap();
240+
241+
let builder_async = Builder::new(&format!("http://{}", esplora_url));
242+
let async_client = builder_async.build_async().unwrap();
243+
244+
(blocking_client, async_client)
245+
}
246+
247+
fn generate_blocks_and_wait(num: usize) {
248+
let cur_height = BITCOIND.client.get_block_count().unwrap();
249+
generate_blocks(num);
250+
wait_for_block(cur_height as usize + num);
251+
}
252+
253+
fn generate_blocks(num: usize) {
254+
let address = BITCOIND
255+
.client
256+
.get_new_address(Some("test"), Some(AddressType::Legacy))
257+
.unwrap();
258+
let _block_hashes = BITCOIND
259+
.client
260+
.generate_to_address(num as u64, &address)
261+
.unwrap();
262+
}
263+
264+
fn wait_for_block(min_height: usize) {
265+
let mut header = ELECTRSD.client.block_headers_subscribe().unwrap();
266+
loop {
267+
if header.height >= min_height {
268+
break;
269+
}
270+
header = exponential_backoff_poll(|| {
271+
ELECTRSD.trigger().unwrap();
272+
ELECTRSD.client.ping().unwrap();
273+
ELECTRSD.client.block_headers_pop().unwrap()
274+
});
275+
}
276+
}
277+
278+
fn exponential_backoff_poll<T, F>(mut poll: F) -> T
279+
where
280+
F: FnMut() -> Option<T>,
281+
{
282+
let mut delay = Duration::from_millis(64);
283+
loop {
284+
match poll() {
285+
Some(data) => break data,
286+
None if delay.as_millis() < 512 => delay = delay.mul_f32(2.0),
287+
None => {}
288+
}
289+
290+
std::thread::sleep(delay);
291+
}
292+
}
201293

202294
#[test]
203295
fn feerate_parsing() {
@@ -242,4 +334,257 @@ mod test {
242334
"should inherit from value for 25"
243335
);
244336
}
337+
338+
#[tokio::test]
339+
async fn test_get_tx() {
340+
let (blocking_client, async_client) = setup_clients();
341+
342+
let address = BITCOIND
343+
.client
344+
.get_new_address(Some("test"), Some(AddressType::Legacy))
345+
.unwrap();
346+
let txid = BITCOIND
347+
.client
348+
.send_to_address(
349+
&address,
350+
Amount::from_sat(1000),
351+
None,
352+
None,
353+
None,
354+
None,
355+
None,
356+
None,
357+
)
358+
.unwrap();
359+
let _miner = MINER.lock().unwrap();
360+
generate_blocks_and_wait(1);
361+
362+
let tx = blocking_client.get_tx(&txid).unwrap();
363+
let tx_async = async_client.get_tx(&txid).await.unwrap();
364+
assert_eq!(tx, tx_async);
365+
}
366+
367+
#[tokio::test]
368+
async fn test_get_tx_no_opt() {
369+
let (blocking_client, async_client) = setup_clients();
370+
371+
let address = BITCOIND
372+
.client
373+
.get_new_address(Some("test"), Some(AddressType::Legacy))
374+
.unwrap();
375+
let txid = BITCOIND
376+
.client
377+
.send_to_address(
378+
&address,
379+
Amount::from_sat(1000),
380+
None,
381+
None,
382+
None,
383+
None,
384+
None,
385+
None,
386+
)
387+
.unwrap();
388+
let _miner = MINER.lock().unwrap();
389+
generate_blocks_and_wait(1);
390+
391+
let tx_no_opt = blocking_client.get_tx_no_opt(&txid).unwrap();
392+
let tx_no_opt_async = async_client.get_tx_no_opt(&txid).await.unwrap();
393+
assert_eq!(tx_no_opt, tx_no_opt_async);
394+
}
395+
396+
#[tokio::test]
397+
async fn test_get_tx_status() {
398+
let (blocking_client, async_client) = setup_clients();
399+
400+
let address = BITCOIND
401+
.client
402+
.get_new_address(Some("test"), Some(AddressType::Legacy))
403+
.unwrap();
404+
let txid = BITCOIND
405+
.client
406+
.send_to_address(
407+
&address,
408+
Amount::from_sat(1000),
409+
None,
410+
None,
411+
None,
412+
None,
413+
None,
414+
None,
415+
)
416+
.unwrap();
417+
let _miner = MINER.lock().unwrap();
418+
generate_blocks_and_wait(1);
419+
420+
let tx_status = blocking_client.get_tx_status(&txid).unwrap().unwrap();
421+
let tx_status_async = async_client.get_tx_status(&txid).await.unwrap().unwrap();
422+
assert_eq!(tx_status, tx_status_async);
423+
assert_eq!(tx_status.confirmed, true);
424+
}
425+
426+
#[tokio::test]
427+
async fn test_get_header() {
428+
let (blocking_client, async_client) = setup_clients();
429+
let block_header = blocking_client.get_header(53).unwrap();
430+
let block_header_async = async_client.get_header(53).await.unwrap();
431+
assert_eq!(block_header, block_header_async);
432+
}
433+
434+
#[tokio::test]
435+
async fn test_get_merkle_proof() {
436+
let (blocking_client, async_client) = setup_clients();
437+
438+
let address = BITCOIND
439+
.client
440+
.get_new_address(Some("test"), Some(AddressType::Legacy))
441+
.unwrap();
442+
let txid = BITCOIND
443+
.client
444+
.send_to_address(
445+
&address,
446+
Amount::from_sat(1000),
447+
None,
448+
None,
449+
None,
450+
None,
451+
None,
452+
None,
453+
)
454+
.unwrap();
455+
let _miner = MINER.lock().unwrap();
456+
generate_blocks_and_wait(1);
457+
458+
let merkle_proof = blocking_client.get_merkle_proof(&txid).unwrap().unwrap();
459+
let merkle_proof_async = async_client.get_merkle_proof(&txid).await.unwrap().unwrap();
460+
assert_eq!(merkle_proof, merkle_proof_async);
461+
assert!(merkle_proof.pos > 0);
462+
}
463+
464+
#[tokio::test]
465+
async fn test_get_output_status() {
466+
let (blocking_client, async_client) = setup_clients();
467+
468+
let address = BITCOIND
469+
.client
470+
.get_new_address(Some("test"), Some(AddressType::Legacy))
471+
.unwrap();
472+
let txid = BITCOIND
473+
.client
474+
.send_to_address(
475+
&address,
476+
Amount::from_sat(1000),
477+
None,
478+
None,
479+
None,
480+
None,
481+
None,
482+
None,
483+
)
484+
.unwrap();
485+
let _miner = MINER.lock().unwrap();
486+
generate_blocks_and_wait(1);
487+
488+
let output_status = blocking_client
489+
.get_output_status(&txid, 1)
490+
.unwrap()
491+
.unwrap();
492+
let output_status_async = async_client
493+
.get_output_status(&txid, 1)
494+
.await
495+
.unwrap()
496+
.unwrap();
497+
498+
assert_eq!(output_status, output_status_async);
499+
}
500+
501+
#[tokio::test]
502+
async fn test_get_height() {
503+
let (blocking_client, async_client) = setup_clients();
504+
let block_height = blocking_client.get_height().unwrap();
505+
let block_height_async = async_client.get_height().await.unwrap();
506+
assert!(block_height > 0);
507+
assert_eq!(block_height, block_height_async);
508+
}
509+
510+
#[tokio::test]
511+
async fn test_get_tip_hash() {
512+
let (blocking_client, async_client) = setup_clients();
513+
let tip_hash = blocking_client.get_tip_hash().unwrap();
514+
let tip_hash_async = async_client.get_tip_hash().await.unwrap();
515+
assert_eq!(tip_hash, tip_hash_async);
516+
}
517+
518+
#[tokio::test]
519+
async fn test_get_txid_at_block_index() {
520+
let (blocking_client, async_client) = setup_clients();
521+
522+
let block_hash = BITCOIND.client.get_block_hash(23).unwrap();
523+
524+
let txid_at_block_index = blocking_client
525+
.get_txid_at_block_index(&block_hash, 0)
526+
.unwrap()
527+
.unwrap();
528+
let txid_at_block_index_async = async_client
529+
.get_txid_at_block_index(&block_hash, 0)
530+
.await
531+
.unwrap()
532+
.unwrap();
533+
assert_eq!(txid_at_block_index, txid_at_block_index_async);
534+
}
535+
536+
#[tokio::test]
537+
async fn test_get_fee_estimates() {
538+
let (blocking_client, async_client) = setup_clients();
539+
let fee_estimates = blocking_client.get_fee_estimates().unwrap();
540+
let fee_estimates_async = async_client.get_fee_estimates().await.unwrap();
541+
assert_eq!(fee_estimates.len(), fee_estimates_async.len());
542+
}
543+
544+
#[tokio::test]
545+
async fn test_scripthash_txs() {
546+
let (blocking_client, async_client) = setup_clients();
547+
548+
let address = BITCOIND
549+
.client
550+
.get_new_address(Some("test"), Some(AddressType::Legacy))
551+
.unwrap();
552+
let txid = BITCOIND
553+
.client
554+
.send_to_address(
555+
&address,
556+
Amount::from_sat(1000),
557+
None,
558+
None,
559+
None,
560+
None,
561+
None,
562+
None,
563+
)
564+
.unwrap();
565+
let _miner = MINER.lock().unwrap();
566+
generate_blocks_and_wait(1);
567+
568+
let expected_tx = BITCOIND
569+
.client
570+
.get_transaction(&txid, None)
571+
.unwrap()
572+
.transaction()
573+
.unwrap();
574+
let script = &expected_tx.output[0].script_pubkey;
575+
let scripthash_txs_txids: Vec<Txid> = blocking_client
576+
.scripthash_txs(script, None)
577+
.unwrap()
578+
.iter()
579+
.map(|tx| tx.txid)
580+
.collect();
581+
let scripthash_txs_txids_async: Vec<Txid> = async_client
582+
.scripthash_txs(script, None)
583+
.await
584+
.unwrap()
585+
.iter()
586+
.map(|tx| tx.txid)
587+
.collect();
588+
assert_eq!(scripthash_txs_txids, scripthash_txs_txids_async);
589+
}
245590
}

0 commit comments

Comments
 (0)