diff --git a/crates/bitcoind_rpc/tests/test_emitter.rs b/crates/bitcoind_rpc/tests/test_emitter.rs index 3264b96d79..201f81c32b 100644 --- a/crates/bitcoind_rpc/tests/test_emitter.rs +++ b/crates/bitcoind_rpc/tests/test_emitter.rs @@ -43,15 +43,23 @@ fn block_to_chain_update(block: &bitcoin::Block, height: u32) -> local_chain::Up pub fn test_sync_local_chain() -> anyhow::Result<()> { let env = TestEnv::new()?; let mut local_chain = LocalChain::default(); - let mut emitter = Emitter::from_height(&env.client, 0); + let tip = env.rpc_client().get_block_count()?; + let mut emitter = Emitter::from_height(env.rpc_client(), tip as u32); // mine some blocks and returned the actual block hashes let exp_hashes = { - let mut hashes = vec![env.client.get_block_hash(0)?]; // include genesis block - hashes.extend(env.mine_blocks(101, None)?); + let mut hashes = (0..=tip) + .map(|height| env.rpc_client().get_block_hash(height)) + .collect::, _>>()?; // includes genesis block + hashes.extend(env.mine_blocks(101 - tip as usize, None, &env.bitcoind)?); hashes }; + (0..tip).for_each(|height| { + let changeset = BTreeMap::from([(height as u32, Some(exp_hashes[height as usize]))]); + local_chain.apply_changeset(&changeset); + }); + // see if the emitter outputs the right blocks println!("first sync:"); while let Some((height, block)) = emitter.next_block()? { @@ -141,13 +149,22 @@ fn test_into_tx_graph() -> anyhow::Result<()> { let env = TestEnv::new()?; println!("getting new addresses!"); - let addr_0 = env.client.get_new_address(None, None)?.assume_checked(); - let addr_1 = env.client.get_new_address(None, None)?.assume_checked(); - let addr_2 = env.client.get_new_address(None, None)?.assume_checked(); + let addr_0 = env + .rpc_client() + .get_new_address(None, None)? + .assume_checked(); + let addr_1 = env + .rpc_client() + .get_new_address(None, None)? + .assume_checked(); + let addr_2 = env + .rpc_client() + .get_new_address(None, None)? + .assume_checked(); println!("got new addresses!"); println!("mining block!"); - env.mine_blocks(101, None)?; + env.mine_blocks(101, None, &env.bitcoind)?; println!("mined blocks!"); let mut chain = LocalChain::default(); @@ -159,7 +176,7 @@ fn test_into_tx_graph() -> anyhow::Result<()> { index }); - let emitter = &mut Emitter::from_height(&env.client, 0); + let emitter = &mut Emitter::from_height(env.rpc_client(), 0); while let Some((height, block)) = emitter.next_block()? { let _ = chain.apply_update(block_to_chain_update(&block, height))?; @@ -171,7 +188,7 @@ fn test_into_tx_graph() -> anyhow::Result<()> { let exp_txids = { let mut txids = BTreeSet::new(); for _ in 0..3 { - txids.insert(env.client.send_to_address( + txids.insert(env.rpc_client().send_to_address( &addr_0, Amount::from_sat(10_000), None, @@ -206,8 +223,8 @@ fn test_into_tx_graph() -> anyhow::Result<()> { } // mine a block that confirms the 3 txs - let exp_block_hash = env.mine_blocks(1, None)?[0]; - let exp_block_height = env.client.get_block_info(&exp_block_hash)?.height as u32; + let exp_block_hash = env.mine_blocks(1, None, &env.bitcoind)?[0]; + let exp_block_height = env.rpc_client().get_block_info(&exp_block_hash)?.height as u32; let exp_anchors = exp_txids .iter() .map({ @@ -247,9 +264,9 @@ fn ensure_block_emitted_after_reorg_is_at_reorg_height() -> anyhow::Result<()> { const CHAIN_TIP_HEIGHT: usize = 110; let env = TestEnv::new()?; - let mut emitter = Emitter::from_height(&env.client, EMITTER_START_HEIGHT as _); + let mut emitter = Emitter::from_height(env.rpc_client(), EMITTER_START_HEIGHT as _); - env.mine_blocks(CHAIN_TIP_HEIGHT, None)?; + env.mine_blocks(CHAIN_TIP_HEIGHT, None, &env.bitcoind)?; while emitter.next_header()?.is_some() {} for reorg_count in 1..=10 { @@ -315,10 +332,13 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> { const SEND_AMOUNT: Amount = Amount::from_sat(10_000); let env = TestEnv::new()?; - let mut emitter = Emitter::from_height(&env.client, 0); + let mut emitter = Emitter::from_height(env.rpc_client(), 0); // setup addresses - let addr_to_mine = env.client.get_new_address(None, None)?.assume_checked(); + let addr_to_mine = env + .rpc_client() + .get_new_address(None, None)? + .assume_checked(); let spk_to_track = ScriptBuf::new_v0_p2wsh(&WScriptHash::all_zeros()); let addr_to_track = Address::from_script(&spk_to_track, bitcoin::Network::Regtest)?; @@ -331,7 +351,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> { }); // mine and sync receiver up to tip - env.mine_blocks(PREMINE_COUNT, Some(addr_to_mine))?; + env.mine_blocks(PREMINE_COUNT, Some(addr_to_mine), &env.bitcoind)?; // create transactions that are tracked by our receiver for _ in 0..ADDITIONAL_COUNT { @@ -339,7 +359,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> { // lock outputs that send to `addr_to_track` let outpoints_to_lock = env - .client + .rpc_client() .get_transaction(&txid, None)? .transaction()? .output @@ -348,9 +368,9 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> { .filter(|(_, txo)| txo.script_pubkey == spk_to_track) .map(|(vout, _)| OutPoint::new(txid, vout as _)) .collect::>(); - env.client.lock_unspent(&outpoints_to_lock)?; + env.rpc_client().lock_unspent(&outpoints_to_lock)?; - let _ = env.mine_blocks(1, None)?; + let _ = env.mine_blocks(1, None, &env.bitcoind)?; } // get emitter up to tip @@ -396,11 +416,14 @@ fn mempool_avoids_re_emission() -> anyhow::Result<()> { const MEMPOOL_TX_COUNT: usize = 2; let env = TestEnv::new()?; - let mut emitter = Emitter::from_height(&env.client, 0); + let mut emitter = Emitter::from_height(env.rpc_client(), 0); // mine blocks and sync up emitter - let addr = env.client.get_new_address(None, None)?.assume_checked(); - env.mine_blocks(BLOCKS_TO_MINE, Some(addr.clone()))?; + let addr = env + .rpc_client() + .get_new_address(None, None)? + .assume_checked(); + env.mine_blocks(BLOCKS_TO_MINE, Some(addr.clone()), &env.bitcoind)?; while emitter.next_header()?.is_some() {} // have some random txs in mempool @@ -451,11 +474,14 @@ fn mempool_re_emits_if_tx_introduction_height_not_reached() -> anyhow::Result<() const MEMPOOL_TX_COUNT: usize = 21; let env = TestEnv::new()?; - let mut emitter = Emitter::from_height(&env.client, 0); + let mut emitter = Emitter::from_height(env.rpc_client(), 0); // mine blocks to get initial balance, sync emitter up to tip - let addr = env.client.get_new_address(None, None)?.assume_checked(); - env.mine_blocks(PREMINE_COUNT, Some(addr.clone()))?; + let addr = env + .rpc_client() + .get_new_address(None, None)? + .assume_checked(); + env.mine_blocks(PREMINE_COUNT, Some(addr.clone()), &env.bitcoind)?; while emitter.next_header()?.is_some() {} // mine blocks to introduce txs to mempool at different heights @@ -528,11 +554,14 @@ fn mempool_during_reorg() -> anyhow::Result<()> { const PREMINE_COUNT: usize = 101; let env = TestEnv::new()?; - let mut emitter = Emitter::from_height(&env.client, 0); + let mut emitter = Emitter::from_height(env.rpc_client(), 0); // mine blocks to get initial balance - let addr = env.client.get_new_address(None, None)?.assume_checked(); - env.mine_blocks(PREMINE_COUNT, Some(addr.clone()))?; + let addr = env + .rpc_client() + .get_new_address(None, None)? + .assume_checked(); + env.mine_blocks(PREMINE_COUNT, Some(addr.clone()), &env.bitcoind)?; // introduce mempool tx at each block extension for _ in 0..TIP_DIFF { @@ -549,7 +578,7 @@ fn mempool_during_reorg() -> anyhow::Result<()> { .into_iter() .map(|(tx, _)| tx.txid()) .collect::>(), - env.client + env.rpc_client() .get_raw_mempool()? .into_iter() .collect::>(), @@ -568,7 +597,7 @@ fn mempool_during_reorg() -> anyhow::Result<()> { // emission. // TODO: How can have have reorg logic in `TestEnv` NOT blacklast old blocks first? let tx_introductions = dbg!(env - .client + .rpc_client() .get_raw_mempool_verbose()? .into_iter() .map(|(txid, entry)| (txid, entry.height as usize)) @@ -643,10 +672,10 @@ fn no_agreement_point() -> anyhow::Result<()> { let env = TestEnv::new()?; // start height is 99 - let mut emitter = Emitter::from_height(&env.client, (PREMINE_COUNT - 2) as u32); + let mut emitter = Emitter::from_height(env.rpc_client(), (PREMINE_COUNT - 2) as u32); // mine 101 blocks - env.mine_blocks(PREMINE_COUNT, None)?; + env.mine_blocks(PREMINE_COUNT, None, &env.bitcoind)?; // emit block 99a let (_, block_header_99a) = emitter.next_header()?.expect("block 99a header"); @@ -658,15 +687,15 @@ fn no_agreement_point() -> anyhow::Result<()> { let block_hash_100a = block_header_100a.block_hash(); // get hash for block 101a - let block_hash_101a = env.client.get_block_hash(101)?; + let block_hash_101a = env.rpc_client().get_block_hash(101)?; // invalidate blocks 99a, 100a, 101a - env.client.invalidate_block(&block_hash_99a)?; - env.client.invalidate_block(&block_hash_100a)?; - env.client.invalidate_block(&block_hash_101a)?; + env.rpc_client().invalidate_block(&block_hash_99a)?; + env.rpc_client().invalidate_block(&block_hash_100a)?; + env.rpc_client().invalidate_block(&block_hash_101a)?; // mine new blocks 99b, 100b, 101b - env.mine_blocks(3, None)?; + env.mine_blocks(3, None, &env.bitcoind)?; // emit block header 99b let (_, block_header_99b) = emitter.next_header()?.expect("block 99b header"); diff --git a/crates/testenv/Cargo.toml b/crates/testenv/Cargo.toml index 0d591b2b72..87df119f80 100644 --- a/crates/testenv/Cargo.toml +++ b/crates/testenv/Cargo.toml @@ -10,8 +10,7 @@ edition = "2021" bitcoin = { version = "0.30", default-features = false } bitcoincore-rpc = { version = "0.17" } bdk_chain = { path = "../chain", version = "0.6", default-features = false } -bdk_bitcoind_rpc = { path = "../bitcoind_rpc", version = "0.1.0", default-features = false } -bitcoind = { version = "0.33", features = ["25_0"] } +electrsd = { version= "0.25.0", features = ["bitcoind_25_0", "esplora_a33e97e1", "legacy"] } anyhow = { version = "1" } [features] diff --git a/crates/testenv/src/lib.rs b/crates/testenv/src/lib.rs index 7859ad9679..536ec87447 100644 --- a/crates/testenv/src/lib.rs +++ b/crates/testenv/src/lib.rs @@ -7,43 +7,64 @@ use bitcoincore_rpc::{ bitcoincore_rpc_json::{GetBlockTemplateModes, GetBlockTemplateRules}, RpcApi, }; +use electrsd::electrum_client::ElectrumApi; pub struct TestEnv { #[allow(dead_code)] - pub daemon: bitcoind::BitcoinD, - pub client: bitcoincore_rpc::Client, + pub bitcoind: electrsd::bitcoind::BitcoinD, + pub electrsd: electrsd::ElectrsD, } impl TestEnv { pub fn new() -> anyhow::Result { - let daemon = match std::env::var_os("TEST_BITCOIND") { - Some(bitcoind_path) => bitcoind::BitcoinD::new(bitcoind_path), - None => bitcoind::BitcoinD::from_downloaded(), + let bitcoind = match std::env::var_os("TEST_BITCOIND") { + Some(bitcoind_path) => electrsd::bitcoind::BitcoinD::new(bitcoind_path), + None => electrsd::bitcoind::BitcoinD::from_downloaded(), }?; - let client = bitcoincore_rpc::Client::new( - &daemon.rpc_url(), - bitcoincore_rpc::Auth::CookieFile(daemon.params.cookie_file.clone()), - )?; - Ok(Self { daemon, client }) + + let electrsd = match std::env::var_os("ELECTRS_EXE") { + Some(env_electrs_exe) => electrsd::ElectrsD::new(env_electrs_exe, &bitcoind), + None => { + let mut electrs_conf = electrsd::Conf::default(); + electrs_conf.http_enabled = true; + let electrs_exe = electrsd::downloaded_exe_path() + .expect("electrs version feature must be enabled"); + electrsd::ElectrsD::with_conf(electrs_exe, &bitcoind, &electrs_conf) + } + }?; + + Ok(Self { bitcoind, electrsd }) + } + + pub fn electrum_client(&self) -> &impl ElectrumApi { + &self.electrsd.client + } + + pub fn rpc_client(&self) -> &impl RpcApi { + &self.bitcoind.client } pub fn mine_blocks( &self, count: usize, address: Option
, + bitcoind: &electrsd::bitcoind::BitcoinD, ) -> anyhow::Result> { let coinbase_address = match address { Some(address) => address, - None => self.client.get_new_address(None, None)?.assume_checked(), + None => bitcoind + .client + .get_new_address(None, None)? + .assume_checked(), }; - let block_hashes = self + let block_hashes = bitcoind .client .generate_to_address(count as _, &coinbase_address)?; Ok(block_hashes) } pub fn mine_empty_block(&self) -> anyhow::Result<(usize, BlockHash)> { - let bt = self.client.get_block_template( + let bt = self.bitcoind.client.get_block_template( GetBlockTemplateModes::Template, &[GetBlockTemplateRules::SegWit], &[], @@ -95,15 +116,19 @@ impl TestEnv { } } - self.client.submit_block(&block)?; + self.bitcoind.client.submit_block(&block)?; Ok((bt.height as usize, block.block_hash())) } - pub fn invalidate_blocks(&self, count: usize) -> anyhow::Result<()> { - let mut hash = self.client.get_best_block_hash()?; + pub fn invalidate_blocks( + &self, + count: usize, + bitcoind: &electrsd::bitcoind::BitcoinD, + ) -> anyhow::Result<()> { + let mut hash = bitcoind.client.get_best_block_hash()?; for _ in 0..count { - let prev_hash = self.client.get_block_info(&hash)?.previousblockhash; - self.client.invalidate_block(&hash)?; + let prev_hash = bitcoind.client.get_block_info(&hash)?.previousblockhash; + bitcoind.client.invalidate_block(&hash)?; match prev_hash { Some(prev_hash) => hash = prev_hash, None => break, @@ -113,12 +138,12 @@ impl TestEnv { } pub fn reorg(&self, count: usize) -> anyhow::Result> { - let start_height = self.client.get_block_count()?; - self.invalidate_blocks(count)?; + let start_height = self.bitcoind.client.get_block_count()?; + self.invalidate_blocks(count, &self.bitcoind)?; - let res = self.mine_blocks(count, None); + let res = self.mine_blocks(count, None, &self.bitcoind); assert_eq!( - self.client.get_block_count()?, + self.bitcoind.client.get_block_count()?, start_height, "reorg should not result in height change" ); @@ -126,14 +151,14 @@ impl TestEnv { } pub fn reorg_empty_blocks(&self, count: usize) -> anyhow::Result> { - let start_height = self.client.get_block_count()?; - self.invalidate_blocks(count)?; + let start_height = self.bitcoind.client.get_block_count()?; + self.invalidate_blocks(count, &self.bitcoind)?; let res = (0..count) .map(|_| self.mine_empty_block()) .collect::, _>>()?; assert_eq!( - self.client.get_block_count()?, + self.bitcoind.client.get_block_count()?, start_height, "reorg should not result in height change" ); @@ -142,6 +167,7 @@ impl TestEnv { pub fn send(&self, address: &Address, amount: Amount) -> anyhow::Result { let txid = self + .bitcoind .client .send_to_address(address, amount, None, None, None, None, None, None)?; Ok(txid)