From c3f7257fc5e2b5ce4856cb01924ba6212a30f61c Mon Sep 17 00:00:00 2001 From: Leonardo Lima Date: Fri, 21 Mar 2025 12:03:36 -0300 Subject: [PATCH 1/6] deps(bdk_testenv): bump `electrsd` to `0.36.1` - upgrades the `electrsd` to latest `0.36.1`. - updates the `electrsd` features to use `corepc-node_28_2`, it's the previously named `bitcoind`. - introduce `bitcoin` as a dependency, in order to use the `bitcoind/rand-std` feature, which is now toggled by `bdk_testenv/std`. - remove the MSRV pinned `home` dependency, it's not currently required. --- ci/pin-msrv.sh | 4 +- crates/testenv/Cargo.toml | 7 +-- crates/testenv/src/lib.rs | 100 +++++++++++++++++++------------------- 3 files changed, 55 insertions(+), 56 deletions(-) diff --git a/ci/pin-msrv.sh b/ci/pin-msrv.sh index 10e9234bb9..15797f1457 100755 --- a/ci/pin-msrv.sh +++ b/ci/pin-msrv.sh @@ -10,6 +10,4 @@ set -euo pipefail # cargo clean # rustup override set 1.85.0 -cargo update -p home --precise "0.5.11" -cargo update -p time --precise "0.3.45" -cargo update -p time-core --precise "0.1.7" +# e.g cargo update -p home --precise "0.5.11" diff --git a/crates/testenv/Cargo.toml b/crates/testenv/Cargo.toml index 2a707fe32e..d24ae7e5fd 100644 --- a/crates/testenv/Cargo.toml +++ b/crates/testenv/Cargo.toml @@ -17,15 +17,16 @@ workspace = true [dependencies] bdk_chain = { path = "../chain", version = "0.23.1", default-features = false } -electrsd = { version = "0.28.0", features = [ "legacy" ], default-features = false } +electrsd = { version = "0.36.1", features = [ "legacy" ], default-features = false } +bitcoin = { version = "0.32.0", default-features = false } [dev-dependencies] bdk_testenv = { path = "." } [features] default = ["std", "download"] -download = ["electrsd/bitcoind_25_0", "electrsd/esplora_a33e97e1"] -std = ["bdk_chain/std"] +download = ["electrsd/corepc-node_28_2", "electrsd/esplora_a33e97e1"] +std = ["bdk_chain/std", "bitcoin/rand-std"] serde = ["bdk_chain/serde"] [package.metadata.docs.rs] diff --git a/crates/testenv/src/lib.rs b/crates/testenv/src/lib.rs index 7572fbf4b8..0f2950320b 100644 --- a/crates/testenv/src/lib.rs +++ b/crates/testenv/src/lib.rs @@ -5,21 +5,17 @@ pub mod utils; use bdk_chain::{ bitcoin::{ address::NetworkChecked, block::Header, hash_types::TxMerkleNode, hashes::Hash, - secp256k1::rand::random, transaction, Address, Amount, Block, BlockHash, CompactTarget, - ScriptBuf, ScriptHash, Transaction, TxIn, TxOut, Txid, + secp256k1::rand::random, transaction, Address, Amount, Block, BlockHash, ScriptBuf, + ScriptHash, Transaction, TxIn, TxOut, Txid, }, local_chain::CheckPoint, }; -use bitcoincore_rpc::{ - bitcoincore_rpc_json::{GetBlockTemplateModes, GetBlockTemplateRules}, - RpcApi, -}; -use electrsd::bitcoind::anyhow::Context; +use electrsd::corepc_node::{anyhow::Context, TemplateRequest, TemplateRules}; pub use electrsd; -pub use electrsd::bitcoind; -pub use electrsd::bitcoind::anyhow; -pub use electrsd::bitcoind::bitcoincore_rpc; +pub use electrsd::corepc_client; +pub use electrsd::corepc_node; +pub use electrsd::corepc_node::anyhow; pub use electrsd::electrum_client; use electrsd::electrum_client::ElectrumApi; use std::time::Duration; @@ -27,7 +23,7 @@ use std::time::Duration; /// Struct for running a regtest environment with a single `bitcoind` node with an `electrs` /// instance connected to it. pub struct TestEnv { - pub bitcoind: electrsd::bitcoind::BitcoinD, + pub bitcoind: electrsd::corepc_node::Node, pub electrsd: electrsd::ElectrsD, } @@ -35,7 +31,7 @@ pub struct TestEnv { #[derive(Debug)] pub struct Config<'a> { /// [`bitcoind::Conf`] - pub bitcoind: bitcoind::Conf<'a>, + pub bitcoind: corepc_node::Conf<'a>, /// [`electrsd::Conf`] pub electrsd: electrsd::Conf<'a>, } @@ -45,7 +41,7 @@ impl Default for Config<'_> { /// which is required for testing `bdk_esplora`. fn default() -> Self { Self { - bitcoind: bitcoind::Conf::default(), + bitcoind: corepc_node::Conf::default(), electrsd: { let mut conf = electrsd::Conf::default(); conf.http_enabled = true; @@ -65,11 +61,11 @@ impl TestEnv { pub fn new_with_config(config: Config) -> anyhow::Result { let bitcoind_exe = match std::env::var("BITCOIND_EXE") { Ok(path) => path, - Err(_) => bitcoind::downloaded_exe_path().context( + Err(_) => corepc_node::downloaded_exe_path().context( "you need to provide an env var BITCOIND_EXE or specify a bitcoind version feature", )?, }; - let bitcoind = bitcoind::BitcoinD::with_conf(bitcoind_exe, &config.bitcoind)?; + let bitcoind = corepc_node::Node::with_conf(bitcoind_exe, &config.bitcoind)?; let electrs_exe = match std::env::var("ELECTRS_EXE") { Ok(path) => path, @@ -87,7 +83,7 @@ impl TestEnv { } /// Exposes the [`RpcApi`] calls from [`bitcoincore_rpc`]. - pub fn rpc_client(&self) -> &impl RpcApi { + pub fn rpc_client(&self) -> &corepc_node::Client { &self.bitcoind.client } @@ -118,26 +114,27 @@ impl TestEnv { ) -> anyhow::Result> { let coinbase_address = match address { Some(address) => address, - None => self - .bitcoind - .client - .get_new_address(None, None)? - .assume_checked(), + None => self.bitcoind.client.new_address()?, }; let block_hashes = self .bitcoind .client - .generate_to_address(count as _, &coinbase_address)?; + .generate_to_address(count as _, &coinbase_address)? + .into_model()? + .0; Ok(block_hashes) } /// Mine a block that is guaranteed to be empty even with transactions in the mempool. pub fn mine_empty_block(&self) -> anyhow::Result<(usize, BlockHash)> { - let bt = self.bitcoind.client.get_block_template( - GetBlockTemplateModes::Template, - &[GetBlockTemplateRules::SegWit], - &[], - )?; + let request = TemplateRequest { + rules: vec![TemplateRules::Segwit], + }; + let bt = self + .bitcoind + .client + .get_block_template(&request)? + .into_model()?; let txdata = vec![Transaction { version: transaction::Version::ONE, @@ -146,7 +143,7 @@ impl TestEnv { previous_output: bdk_chain::bitcoin::OutPoint::default(), script_sig: ScriptBuf::builder() .push_int(bt.height as _) - // randomn number so that re-mining creates unique block + // random number so that re-mining creates unique block .push_int(random()) .into_script(), sequence: bdk_chain::bitcoin::Sequence::default(), @@ -158,19 +155,16 @@ impl TestEnv { }], }]; - let bits: [u8; 4] = bt - .bits - .clone() - .try_into() - .expect("rpc provided us with invalid bits"); - let mut block = Block { header: Header { - version: bdk_chain::bitcoin::block::Version::default(), + version: bt.version, prev_blockhash: bt.previous_block_hash, merkle_root: TxMerkleNode::all_zeros(), - time: Ord::max(bt.min_time, std::time::UNIX_EPOCH.elapsed()?.as_secs()) as u32, - bits: CompactTarget::from_consensus(u32::from_be_bytes(bits)), + time: Ord::max( + bt.min_time, + std::time::UNIX_EPOCH.elapsed()?.as_secs() as u32, + ), + bits: bt.bits, nonce: 0, }, txdata, @@ -186,6 +180,7 @@ impl TestEnv { } self.bitcoind.client.submit_block(&block)?; + Ok((bt.height as usize, block.block_hash())) } @@ -238,14 +233,15 @@ impl TestEnv { /// Invalidate a number of blocks of a given size `count`. pub fn invalidate_blocks(&self, count: usize) -> anyhow::Result<()> { - let mut hash = self.bitcoind.client.get_best_block_hash()?; + let mut hash = self.bitcoind.client.get_best_block_hash()?.block_hash()?; for _ in 0..count { let prev_hash = self .bitcoind .client - .get_block_info(&hash)? - .previousblockhash; - self.bitcoind.client.invalidate_block(&hash)?; + .get_block_verbose_one(hash)? + .into_model()? + .previous_block_hash; + self.bitcoind.client.invalidate_block(hash)?; match prev_hash { Some(prev_hash) => hash = prev_hash, None => break, @@ -290,27 +286,31 @@ impl TestEnv { let txid = self .bitcoind .client - .send_to_address(address, amount, None, None, None, None, None, None)?; + .send_to_address(address, amount)? + .txid()?; Ok(txid) } /// Create a checkpoint linked list of all the blocks in the chain. pub fn make_checkpoint_tip(&self) -> CheckPoint { CheckPoint::from_blocks((0_u32..).map_while(|height| { - self.bitcoind - .client - .get_block_hash(height as u64) + self.get_block_hash(height as u64) .ok() - .map(|hash| (height, hash)) + .map(|block_hash| (height, block_hash)) })) .expect("must craft tip") } /// Get the genesis hash of the blockchain. pub fn genesis_hash(&self) -> anyhow::Result { - let hash = self.bitcoind.client.get_block_hash(0)?; + let hash = self.bitcoind.client.get_block_hash(0)?.into_model()?.0; Ok(hash) } + + /// Get block hash by `height` from the `bitcoind` client. + pub fn get_block_hash(&self, height: u64) -> anyhow::Result { + Ok(self.bitcoind.client.get_block_hash(height)?.block_hash()?) + } } #[cfg(test)] @@ -318,7 +318,7 @@ impl TestEnv { mod test { use crate::TestEnv; use core::time::Duration; - use electrsd::bitcoind::{anyhow::Result, bitcoincore_rpc::RpcApi}; + use electrsd::corepc_node::anyhow::Result; /// This checks that reorgs initiated by `bitcoind` is detected by our `electrsd` instance. #[test] @@ -328,7 +328,7 @@ mod test { // Mine some blocks. env.mine_blocks(101, None)?; env.wait_until_electrum_sees_block(Duration::from_secs(6))?; - let height = env.bitcoind.client.get_block_count()?; + let height = env.bitcoind.client.get_block_count()?.into_model().0; let blocks = (0..=height) .map(|i| env.bitcoind.client.get_block_hash(i)) .collect::, _>>()?; @@ -336,7 +336,7 @@ mod test { // Perform reorg on six blocks. env.reorg(6)?; env.wait_until_electrum_sees_block(Duration::from_secs(6))?; - let reorged_height = env.bitcoind.client.get_block_count()?; + let reorged_height = env.bitcoind.client.get_block_count()?.into_model().0; let reorged_blocks = (0..=height) .map(|i| env.bitcoind.client.get_block_hash(i)) .collect::, _>>()?; From f3931455da40fa1f4ee9159bfca63931665ef2a7 Mon Sep 17 00:00:00 2001 From: valued mammal Date: Fri, 21 Nov 2025 19:36:58 -0500 Subject: [PATCH 2/6] test(bdk_bitcoind_rpc): update it's tests to use `ClientExt` - introduces `ClientExt` trait to `bdk_bitcoind_rpc` common tests module. - update all `bdk_bitcoind_rpc` tests to build the `Emitter` using the `ClientExt` trait, which allows it to use the old `bitcoincore-rpc::RpcApi` with the corepc-node background. --- crates/bitcoind_rpc/src/lib.rs | 17 ++- crates/bitcoind_rpc/tests/common/mod.rs | 20 +++ crates/bitcoind_rpc/tests/test_emitter.rs | 144 ++++++++++-------- crates/bitcoind_rpc/tests/test_filter_iter.rs | 27 ++-- 4 files changed, 128 insertions(+), 80 deletions(-) create mode 100644 crates/bitcoind_rpc/tests/common/mod.rs diff --git a/crates/bitcoind_rpc/src/lib.rs b/crates/bitcoind_rpc/src/lib.rs index 30fc556bea..06c4fe0aa5 100644 --- a/crates/bitcoind_rpc/src/lib.rs +++ b/crates/bitcoind_rpc/src/lib.rs @@ -399,7 +399,7 @@ impl BitcoindRpcErrorExt for bitcoincore_rpc::Error { #[cfg(test)] #[cfg_attr(coverage_nightly, coverage(off))] mod test { - use crate::{bitcoincore_rpc::RpcApi, Emitter, NO_EXPECTED_MEMPOOL_TXS}; + use crate::{Emitter, NO_EXPECTED_MEMPOOL_TXS}; use bdk_chain::local_chain::LocalChain; use bdk_testenv::{anyhow, TestEnv}; use bitcoin::{hashes::Hash, Address, Amount, ScriptBuf, Txid, WScriptHash}; @@ -408,14 +408,15 @@ mod test { #[test] fn test_expected_mempool_txids_accumulate_and_remove() -> anyhow::Result<()> { let env = TestEnv::new()?; - let chain = LocalChain::from_genesis(env.rpc_client().get_block_hash(0)?).0; + let (chain, _) = LocalChain::from_genesis(env.genesis_hash()?); let chain_tip = chain.tip(); - let mut emitter = Emitter::new( - env.rpc_client(), - chain_tip.clone(), - 1, - NO_EXPECTED_MEMPOOL_TXS, - ); + + let rpc_client = bitcoincore_rpc::Client::new( + &env.bitcoind.rpc_url(), + bitcoincore_rpc::Auth::CookieFile(env.bitcoind.params.cookie_file.clone()), + )?; + + let mut emitter = Emitter::new(&rpc_client, chain_tip.clone(), 1, NO_EXPECTED_MEMPOOL_TXS); env.mine_blocks(100, None)?; while emitter.next_block()?.is_some() {} diff --git a/crates/bitcoind_rpc/tests/common/mod.rs b/crates/bitcoind_rpc/tests/common/mod.rs new file mode 100644 index 0000000000..bbd914cca7 --- /dev/null +++ b/crates/bitcoind_rpc/tests/common/mod.rs @@ -0,0 +1,20 @@ +use bdk_testenv::anyhow; +use bdk_testenv::TestEnv; + +/// This trait is used for testing. It allows creating a new [`bitcoincore_rpc::Client`] connected +/// to the instance of bitcoind running in the test environment. This way the `TestEnv` and the +/// `Emitter` aren't required to share the same client. In the future when we no longer depend on +/// `bitcoincore-rpc`, this can be updated to return the production client that is used by BDK. +pub trait ClientExt { + /// Creates a new [`bitcoincore_rpc::Client`] connected to the current node instance. + fn get_rpc_client(&self) -> anyhow::Result; +} + +impl ClientExt for TestEnv { + fn get_rpc_client(&self) -> anyhow::Result { + Ok(bitcoincore_rpc::Client::new( + &self.bitcoind.rpc_url(), + bitcoincore_rpc::Auth::CookieFile(self.bitcoind.params.cookie_file.clone()), + )?) + } +} diff --git a/crates/bitcoind_rpc/tests/test_emitter.rs b/crates/bitcoind_rpc/tests/test_emitter.rs index 6453037e6f..67cbb329fb 100644 --- a/crates/bitcoind_rpc/tests/test_emitter.rs +++ b/crates/bitcoind_rpc/tests/test_emitter.rs @@ -7,9 +7,16 @@ use bdk_chain::{ spk_txout::SpkTxOutIndex, Balance, BlockId, CanonicalizationParams, IndexedTxGraph, Merge, }; -use bdk_testenv::{anyhow, TestEnv}; -use bitcoin::{hashes::Hash, Block, Network, OutPoint, ScriptBuf, WScriptHash}; -use bitcoincore_rpc::RpcApi; +use bdk_testenv::{ + anyhow, + corepc_node::{Input, Output}, + TestEnv, +}; +use bitcoin::{hashes::Hash, Block, Network, ScriptBuf, WScriptHash}; + +use crate::common::ClientExt; + +mod common; /// Ensure that blocks are emitted in order even after reorg. /// @@ -20,21 +27,18 @@ use bitcoincore_rpc::RpcApi; #[test] pub fn test_sync_local_chain() -> anyhow::Result<()> { let env = TestEnv::new()?; - let network_tip = env.rpc_client().get_block_count()?; - let (mut local_chain, _) = LocalChain::from_genesis(env.rpc_client().get_block_hash(0)?); - let mut emitter = Emitter::new( - env.rpc_client(), - local_chain.tip(), - 0, - NO_EXPECTED_MEMPOOL_TXS, - ); + let network_tip = env.rpc_client().get_block_count()?.into_model().0; + let (mut local_chain, _) = LocalChain::from_genesis(env.genesis_hash()?); + + let client = ClientExt::get_rpc_client(&env)?; + let mut emitter = Emitter::new(&client, local_chain.tip(), 0, NO_EXPECTED_MEMPOOL_TXS); // Mine some blocks and return the actual block hashes. // Because initializing `ElectrsD` already mines some blocks, we must include those too when // returning block hashes. let exp_hashes = { let mut hashes = (0..=network_tip) - .map(|height| env.rpc_client().get_block_hash(height)) + .map(|height| env.get_block_hash(height)) .collect::, _>>()?; hashes.extend(env.mine_blocks(101 - network_tip as usize, None)?); hashes @@ -140,19 +144,24 @@ fn test_into_tx_graph() -> anyhow::Result<()> { let addr_0 = env .rpc_client() .get_new_address(None, None)? + .address()? .assume_checked(); + let addr_1 = env .rpc_client() .get_new_address(None, None)? + .address()? .assume_checked(); + let addr_2 = env .rpc_client() .get_new_address(None, None)? + .address()? .assume_checked(); env.mine_blocks(101, None)?; - let (mut chain, _) = LocalChain::from_genesis(env.rpc_client().get_block_hash(0)?); + let (mut chain, _) = LocalChain::from_genesis(env.genesis_hash()?); let mut indexed_tx_graph = IndexedTxGraph::::new({ let mut index = SpkTxOutIndex::::default(); index.insert_spk(0, addr_0.script_pubkey()); @@ -161,7 +170,8 @@ fn test_into_tx_graph() -> anyhow::Result<()> { index }); - let emitter = &mut Emitter::new(env.rpc_client(), chain.tip(), 0, NO_EXPECTED_MEMPOOL_TXS); + let client = ClientExt::get_rpc_client(&env)?; + let emitter = &mut Emitter::new(&client, chain.tip(), 0, NO_EXPECTED_MEMPOOL_TXS); while let Some(emission) = emitter.next_block()? { let height = emission.block_height(); @@ -174,16 +184,11 @@ fn test_into_tx_graph() -> anyhow::Result<()> { let exp_txids = { let mut txids = BTreeSet::new(); for _ in 0..3 { - txids.insert(env.rpc_client().send_to_address( - &addr_0, - Amount::from_sat(10_000), - None, - None, - None, - None, - None, - None, - )?); + txids.insert( + env.rpc_client() + .send_to_address(&addr_0, Amount::from_sat(10_000))? + .txid()?, + ); } txids }; @@ -210,7 +215,10 @@ 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.rpc_client().get_block_info(&exp_block_hash)?.height as u32; + let exp_block_height = env + .rpc_client() + .get_block_verbose_one(exp_block_hash)? + .height as u32; let exp_anchors = exp_txids .iter() .map({ @@ -250,9 +258,11 @@ fn ensure_block_emitted_after_reorg_is_at_reorg_height() -> anyhow::Result<()> { const CHAIN_TIP_HEIGHT: usize = 110; let env = TestEnv::new()?; + + let client = ClientExt::get_rpc_client(&env)?; let mut emitter = Emitter::new( - env.rpc_client(), - CheckPoint::new(0, env.rpc_client().get_block_hash(0)?), + &client, + CheckPoint::new(0, env.genesis_hash()?), EMITTER_START_HEIGHT as _, NO_EXPECTED_MEMPOOL_TXS, ); @@ -325,9 +335,11 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> { const SEND_AMOUNT: Amount = Amount::from_sat(10_000); let env = TestEnv::new()?; + + let client = ClientExt::get_rpc_client(&env)?; let mut emitter = Emitter::new( - env.rpc_client(), - CheckPoint::new(0, env.rpc_client().get_block_hash(0)?), + &client, + CheckPoint::new(0, env.genesis_hash()?), 0, NO_EXPECTED_MEMPOOL_TXS, ); @@ -336,12 +348,13 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> { let addr_to_mine = env .rpc_client() .get_new_address(None, None)? + .address()? .assume_checked(); let spk_to_track = ScriptBuf::new_p2wsh(&WScriptHash::all_zeros()); let addr_to_track = Address::from_script(&spk_to_track, Network::Regtest)?; // setup receiver - let (mut recv_chain, _) = LocalChain::from_genesis(env.rpc_client().get_block_hash(0)?); + let (mut recv_chain, _) = LocalChain::from_genesis(env.genesis_hash()?); let mut recv_graph = IndexedTxGraph::::new({ let mut recv_index = SpkTxOutIndex::default(); recv_index.insert_spk((), spk_to_track.clone()); @@ -358,14 +371,16 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> { // lock outputs that send to `addr_to_track` let outpoints_to_lock = env .rpc_client() - .get_transaction(&txid, None)? - .transaction()? + .get_transaction(txid)? + .into_model()? + .tx .output .into_iter() .enumerate() .filter(|(_, txo)| txo.script_pubkey == spk_to_track) - .map(|(vout, _)| OutPoint::new(txid, vout as _)) + .map(|(vout, _)| (txid, vout as u32)) .collect::>(); + env.rpc_client().lock_unspent(&outpoints_to_lock)?; let _ = env.mine_blocks(1, None)?; @@ -413,9 +428,11 @@ fn mempool_avoids_re_emission() -> anyhow::Result<()> { const MEMPOOL_TX_COUNT: usize = 2; let env = TestEnv::new()?; + + let client = ClientExt::get_rpc_client(&env)?; let mut emitter = Emitter::new( - env.rpc_client(), - CheckPoint::new(0, env.rpc_client().get_block_hash(0)?), + &client, + CheckPoint::new(0, env.genesis_hash()?), 0, NO_EXPECTED_MEMPOOL_TXS, ); @@ -424,6 +441,7 @@ fn mempool_avoids_re_emission() -> anyhow::Result<()> { let addr = env .rpc_client() .get_new_address(None, None)? + .address()? .assume_checked(); env.mine_blocks(BLOCKS_TO_MINE, Some(addr.clone()))?; while emitter.next_block()?.is_some() {} @@ -482,10 +500,11 @@ fn no_agreement_point() -> anyhow::Result<()> { let env = TestEnv::new()?; + let client = ClientExt::get_rpc_client(&env)?; // start height is 99 let mut emitter = Emitter::new( - env.rpc_client(), - CheckPoint::new(0, env.rpc_client().get_block_hash(0)?), + &client, + CheckPoint::new(0, env.genesis_hash()?), (PREMINE_COUNT - 2) as u32, NO_EXPECTED_MEMPOOL_TXS, ); @@ -507,12 +526,12 @@ 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.rpc_client().get_block_hash(101)?; + let block_hash_101a = env.rpc_client().get_block_hash(101)?.block_hash()?; // invalidate blocks 99a, 100a, 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)?; + 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)?; @@ -542,13 +561,11 @@ fn no_agreement_point() -> anyhow::Result<()> { #[test] fn test_expect_tx_evicted() -> anyhow::Result<()> { use bdk_bitcoind_rpc::bitcoincore_rpc::bitcoin; - use bdk_bitcoind_rpc::bitcoincore_rpc::bitcoincore_rpc_json::CreateRawTransactionInput; use bdk_chain::miniscript; use bdk_chain::spk_txout::SpkTxOutIndex; use bitcoin::constants::genesis_block; use bitcoin::secp256k1::Secp256k1; use bitcoin::Network; - use std::collections::HashMap; let env = TestEnv::new()?; let s = bdk_testenv::utils::DESCRIPTORS[0]; @@ -570,12 +587,10 @@ fn test_expect_tx_evicted() -> anyhow::Result<()> { &Address::from_script(&spk, Network::Regtest)?, Amount::ONE_BTC, )?; - let tx_1 = env - .rpc_client() - .get_transaction(&txid_1, None)? - .transaction()?; + let tx_1 = env.rpc_client().get_transaction(txid_1)?.into_model()?.tx; - let mut emitter = Emitter::new(env.rpc_client(), chain.tip(), 1, core::iter::once(tx_1)); + let client = ClientExt::get_rpc_client(&env)?; + let mut emitter = Emitter::new(&client, chain.tip(), 1, core::iter::once(tx_1)); while let Some(emission) = emitter.next_block()? { let height = emission.block_height(); chain.apply_header(&emission.block.header, height)?; @@ -592,25 +607,28 @@ fn test_expect_tx_evicted() -> anyhow::Result<()> { // Get `prevout` from core. let core = env.rpc_client(); - let tx1 = &core.get_raw_transaction(&txid_1, None)?; + let tx1 = core.get_transaction(txid_1)?.into_model()?.tx; let txin = &tx1.input[0]; let op = txin.previous_output; // Create `tx1b` using the previous output from tx1. - let utxo = CreateRawTransactionInput { + let utxo = Input { txid: op.txid, - vout: op.vout, + vout: op.vout as u64, sequence: None, }; - let addr = core.get_new_address(None, None)?.assume_checked(); - let tx = core.create_raw_transaction( - &[utxo], - &HashMap::from([(addr.to_string(), Amount::from_btc(49.99)?)]), - None, - None, - )?; - let res = core.sign_raw_transaction_with_wallet(&tx, None, None)?; - let tx1b = res.transaction()?; + + let addr = core + .get_new_address(None, None)? + .address()? + .assume_checked(); + + let outputs = [Output::new(addr, Amount::from_btc(49.99)?)]; + let tx = core + .create_raw_transaction(&[utxo], &outputs)? + .into_model()? + .0; + let tx1b = core.sign_raw_transaction_with_wallet(&tx)?.into_model()?.tx; // Send the tx. let _txid_2 = core.send_raw_transaction(&tx1b)?; @@ -651,11 +669,13 @@ fn detect_new_mempool_txs() -> anyhow::Result<()> { let addr = env .rpc_client() .get_new_address(None, None)? + .address()? .require_network(Network::Regtest)?; + let client = ClientExt::get_rpc_client(&env)?; let mut emitter = Emitter::new( - env.rpc_client(), - CheckPoint::new(0, env.rpc_client().get_block_hash(0)?), + &client, + CheckPoint::new(0, env.genesis_hash()?), 0, NO_EXPECTED_MEMPOOL_TXS, ); diff --git a/crates/bitcoind_rpc/tests/test_filter_iter.rs b/crates/bitcoind_rpc/tests/test_filter_iter.rs index eafe8250ba..e45e533110 100644 --- a/crates/bitcoind_rpc/tests/test_filter_iter.rs +++ b/crates/bitcoind_rpc/tests/test_filter_iter.rs @@ -1,11 +1,15 @@ use bdk_bitcoind_rpc::bip158::{Error, FilterIter}; use bdk_core::CheckPoint; -use bdk_testenv::{anyhow, bitcoind, TestEnv}; +use bdk_testenv::{anyhow, corepc_node, TestEnv}; use bitcoin::{Address, Amount, Network, ScriptBuf}; use bitcoincore_rpc::RpcApi; +use crate::common::ClientExt; + +mod common; + fn testenv() -> anyhow::Result { - let mut conf = bitcoind::Conf::default(); + let mut conf = corepc_node::Conf::default(); conf.args.push("-blockfilterindex=1"); conf.args.push("-peerblockfilters=1"); TestEnv::new_with_config(bdk_testenv::Config { @@ -17,13 +21,12 @@ fn testenv() -> anyhow::Result { #[test] fn filter_iter_matches_blocks() -> anyhow::Result<()> { let env = testenv()?; - let addr = env - .rpc_client() + let addr = ClientExt::get_rpc_client(&env)? .get_new_address(None, None)? .assume_checked(); let _ = env.mine_blocks(100, Some(addr.clone()))?; - assert_eq!(env.rpc_client().get_block_count()?, 101); + assert_eq!(ClientExt::get_rpc_client(&env)?.get_block_count()?, 101); // Send tx to external address to confirm at height = 102 let _txid = env.send( @@ -38,7 +41,8 @@ fn filter_iter_matches_blocks() -> anyhow::Result<()> { let genesis_hash = env.genesis_hash()?; let cp = CheckPoint::new(0, genesis_hash); - let iter = FilterIter::new(&env.bitcoind.client, cp, [addr.script_pubkey()]); + let client = ClientExt::get_rpc_client(&env)?; + let iter = FilterIter::new(&client, cp, [addr.script_pubkey()]); for res in iter { let event = res?; @@ -60,7 +64,8 @@ fn filter_iter_error_wrong_network() -> anyhow::Result<()> { // Try to initialize FilterIter with a CP on the wrong network let cp = CheckPoint::new(0, bitcoin::hashes::Hash::hash(b"wrong-hash")); - let mut iter = FilterIter::new(&env.bitcoind.client, cp, [ScriptBuf::new()]); + let client = ClientExt::get_rpc_client(&env)?; + let mut iter = FilterIter::new(&client, cp, [ScriptBuf::new()]); assert!(matches!(iter.next(), Some(Err(Error::ReorgDepthExceeded)))); Ok(()) @@ -72,7 +77,7 @@ fn filter_iter_detects_reorgs() -> anyhow::Result<()> { const MINE_TO: u32 = 16; let env = testenv()?; - let rpc = env.rpc_client(); + let rpc = ClientExt::get_rpc_client(&env)?; while rpc.get_block_count()? < MINE_TO as u64 { let _ = env.mine_blocks(1, None)?; } @@ -81,7 +86,8 @@ fn filter_iter_detects_reorgs() -> anyhow::Result<()> { let cp = CheckPoint::new(0, genesis_hash); let spk = ScriptBuf::from_hex("0014446906a6560d8ad760db3156706e72e171f3a2aa")?; - let mut iter = FilterIter::new(&env.bitcoind.client, cp, [spk]); + let client = ClientExt::get_rpc_client(&env)?; + let mut iter = FilterIter::new(&client, cp, [spk]); // Process events to height (MINE_TO - 1) loop { @@ -131,7 +137,8 @@ fn event_checkpoint_connects_to_local_chain() -> anyhow::Result<()> { .collect(); // Construct iter - let mut iter = FilterIter::new(&env.bitcoind.client, cp, vec![ScriptBuf::new()]); + let client = ClientExt::get_rpc_client(&env)?; + let mut iter = FilterIter::new(&client, cp, vec![ScriptBuf::new()]); // Now reorg 3 blocks (14, 15, 16) let new_hashes: BTreeMap = (14..=16).zip(env.reorg(3)?).collect(); From b78a9dedf8be14aef09d15ba2ad362d268b742c7 Mon Sep 17 00:00:00 2001 From: Leonardo Lima Date: Thu, 27 Nov 2025 17:09:20 -0300 Subject: [PATCH 3/6] test(bdk_chain): update to new `bdk_testenv` APIs - updates tests that relies on `bdk_testenv` to it's new `corepc-node` APIs. --- crates/chain/tests/test_indexed_tx_graph.rs | 75 ++++++++++++--------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/crates/chain/tests/test_indexed_tx_graph.rs b/crates/chain/tests/test_indexed_tx_graph.rs index 7a2f8ea60f..18d1ff1bf4 100644 --- a/crates/chain/tests/test_indexed_tx_graph.rs +++ b/crates/chain/tests/test_indexed_tx_graph.rs @@ -3,10 +3,7 @@ #[macro_use] mod common; -use std::{ - collections::{BTreeSet, HashMap}, - sync::Arc, -}; +use std::{collections::BTreeSet, str::FromStr, sync::Arc}; use bdk_chain::{ indexed_tx_graph::{self, IndexedTxGraph}, @@ -18,8 +15,9 @@ use bdk_chain::{ }; use bdk_testenv::{ anyhow::{self}, - bitcoincore_rpc::{json::CreateRawTransactionInput, RpcApi}, - block_id, hash, + block_id, + corepc_node::{Input, Output}, + hash, utils::{new_tx, DESCRIPTORS}, TestEnv, }; @@ -67,6 +65,7 @@ fn relevant_conflicts() -> anyhow::Result<()> { let sender_addr = client .get_new_address(None, None)? + .address()? .require_network(Network::Regtest)?; let recv_spk = gen_spk(); @@ -79,30 +78,37 @@ fn relevant_conflicts() -> anyhow::Result<()> { env.mine_blocks(101, None)?; let tx_input = client - .list_unspent(None, None, None, None, None)? + .list_unspent()? + .0 .into_iter() .take(1) - .map(|r| CreateRawTransactionInput { - txid: r.txid, - vout: r.vout, + .map(|r| Input { + txid: Txid::from_str(&r.txid).expect("should successfully parse the `Txid`"), + vout: r.vout as u64, sequence: None, }) .collect::>(); let tx_send = { - let outputs = - HashMap::from([(recv_addr.to_string(), Amount::from_btc(49.999_99)?)]); - let tx = client.create_raw_transaction(&tx_input, &outputs, None, Some(true))?; + let outputs = [Output::new(recv_addr, Amount::from_btc(49.999_99)?)]; + let tx = client + .create_raw_transaction(&tx_input, &outputs)? + .into_model()? + .0; client - .sign_raw_transaction_with_wallet(&tx, None, None)? - .transaction()? + .sign_raw_transaction_with_wallet(&tx)? + .into_model()? + .tx }; let tx_cancel = { - let outputs = - HashMap::from([(sender_addr.to_string(), Amount::from_btc(49.999_98)?)]); - let tx = client.create_raw_transaction(&tx_input, &outputs, None, Some(true))?; + let outputs = [Output::new(sender_addr, Amount::from_btc(49.999_98)?)]; + let tx = client + .create_raw_transaction(&tx_input, &outputs)? + .into_model()? + .0; client - .sign_raw_transaction_with_wallet(&tx, None, None)? - .transaction()? + .sign_raw_transaction_with_wallet(&tx)? + .into_model()? + .tx }; Ok(Self { @@ -118,31 +124,36 @@ fn relevant_conflicts() -> anyhow::Result<()> { /// Scans through all transactions in the blockchain + mempool. fn sync(&mut self) -> anyhow::Result<()> { let client = self.env.rpc_client(); - for height in 0..=client.get_block_count()? { - let hash = client.get_block_hash(height)?; - let block = client.get_block(&hash)?; + for height in 0..=client.get_block_count()?.into_model().0 { + let hash = client.get_block_hash(height)?.block_hash()?; + let block = client.get_block(hash)?; let _ = self.graph.apply_block_relevant(&block, height as _); } - let _ = self.graph.batch_insert_relevant_unconfirmed( - client - .get_raw_mempool()? - .into_iter() - .map(|txid| client.get_raw_transaction(&txid, None).map(|tx| (tx, 0))) - .collect::, _>>()?, - ); + + let mempool_txids = client.get_raw_mempool()?.into_model()?.0; + let unconfirmed_txs: Vec<(Transaction, u64)> = mempool_txids + .iter() + .map(|txid| -> anyhow::Result<_> { + let tx = client.get_raw_transaction(*txid)?.transaction()?; + Ok((tx, 0)) + }) + .collect::, _>>()?; + let _ = self + .graph + .batch_insert_relevant_unconfirmed(unconfirmed_txs); Ok(()) } /// Broadcast the original sending transaction. fn broadcast_send(&self) -> anyhow::Result { let client = self.env.rpc_client(); - Ok(client.send_raw_transaction(&self.tx_send)?) + Ok(client.send_raw_transaction(&self.tx_send)?.txid()?) } /// Broadcast the cancellation transaction. fn broadcast_cancel(&self) -> anyhow::Result { let client = self.env.rpc_client(); - Ok(client.send_raw_transaction(&self.tx_cancel)?) + Ok(client.send_raw_transaction(&self.tx_cancel)?.txid()?) } } From cbad92eb69243dbe57d001556ffa31e21d223138 Mon Sep 17 00:00:00 2001 From: Leonardo Lima Date: Fri, 21 Mar 2025 20:46:30 -0300 Subject: [PATCH 4/6] test(bdk_esplora,bdk_electrum): update to new `bdk_testenv` APIs --- crates/electrum/benches/test_sync.rs | 12 +- crates/electrum/src/bdk_electrum_client.rs | 5 +- crates/electrum/tests/test_electrum.rs | 261 ++++++++++----------- crates/esplora/src/async_ext.rs | 16 +- crates/esplora/src/blocking_ext.rs | 22 +- crates/esplora/tests/async_ext.rs | 132 +++++------ crates/esplora/tests/blocking_ext.rs | 138 +++++------ 7 files changed, 292 insertions(+), 294 deletions(-) diff --git a/crates/electrum/benches/test_sync.rs b/crates/electrum/benches/test_sync.rs index 12ecf06aa1..39dd10d9aa 100644 --- a/crates/electrum/benches/test_sync.rs +++ b/crates/electrum/benches/test_sync.rs @@ -10,7 +10,7 @@ use bdk_core::{ CheckPoint, }; use bdk_electrum::BdkElectrumClient; -use bdk_testenv::{anyhow, bitcoincore_rpc::RpcApi, TestEnv}; +use bdk_testenv::{anyhow, TestEnv}; use criterion::{criterion_group, criterion_main, Criterion}; use electrum_client::ElectrumApi; use std::{collections::BTreeSet, time::Duration}; @@ -77,7 +77,15 @@ pub fn test_sync_performance(c: &mut Criterion) { ); // Setup receiver. - let genesis_cp = CheckPoint::new(0, env.bitcoind.client.get_block_hash(0).unwrap()); + let genesis_cp = CheckPoint::new( + 0, + env.bitcoind + .client + .get_block_hash(0) + .unwrap() + .block_hash() + .unwrap(), + ); { let electrum_client = diff --git a/crates/electrum/src/bdk_electrum_client.rs b/crates/electrum/src/bdk_electrum_client.rs index 05b5018710..25da3998ad 100644 --- a/crates/electrum/src/bdk_electrum_client.rs +++ b/crates/electrum/src/bdk_electrum_client.rs @@ -702,7 +702,7 @@ mod test { use bdk_chain::bitcoin::{constants, Network, OutPoint, ScriptBuf, Transaction, TxIn}; use bdk_chain::CheckPoint; use bdk_core::{collections::BTreeMap, spk_client::SyncRequest}; - use bdk_testenv::{anyhow, bitcoincore_rpc::RpcApi, utils::new_tx, TestEnv}; + use bdk_testenv::{anyhow, utils::new_tx, TestEnv}; use core::time::Duration; use electrum_client::Error as ElectrumError; use std::sync::Arc; @@ -786,13 +786,14 @@ mod test { let addr = env .rpc_client() .get_new_address(None, None)? + .address()? .assume_checked(); let txid = env.send(&addr, Amount::from_sat(50_000))?; // Mine block that confirms transaction. env.mine_blocks(1, None)?; env.wait_until_electrum_sees_block(Duration::from_secs(6))?; - let height: u32 = env.rpc_client().get_block_count()? as u32; + let height: u32 = env.rpc_client().get_block_count()?.into_model().0 as u32; // Add the pre-reorg block that the tx is confirmed in to the header cache. let header = electrum_client.inner.block_header(height as usize)?; diff --git a/crates/electrum/tests/test_electrum.rs b/crates/electrum/tests/test_electrum.rs index 07979866e7..318708a194 100644 --- a/crates/electrum/tests/test_electrum.rs +++ b/crates/electrum/tests/test_electrum.rs @@ -8,17 +8,17 @@ use bdk_chain::{ }; use bdk_core::bitcoin::{ key::{Secp256k1, UntweakedPublicKey}, - Network, + Denomination, }; use bdk_electrum::BdkElectrumClient; use bdk_testenv::{ anyhow, - bitcoincore_rpc::{json::CreateRawTransactionInput, RawTx, RpcApi}, + corepc_node::{Input, Output}, TestEnv, }; use core::time::Duration; use electrum_client::ElectrumApi; -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeSet, HashSet}; use std::str::FromStr; // Batch size for `sync_with_electrum`. @@ -89,7 +89,7 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> { let client = BdkElectrumClient::new(electrum_client); let mut graph = IndexedTxGraph::::new(SpkTxOutIndex::<()>::default()); - let (chain, _) = LocalChain::from_genesis(env.bitcoind.client.get_block_hash(0)?); + let (chain, _) = LocalChain::from_genesis(env.genesis_hash()?); // Get receiving address. let receiver_spk = get_test_spk(); @@ -100,48 +100,57 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> { // Select a UTXO to use as an input for constructing our test transactions. let selected_utxo = rpc_client - .list_unspent(None, None, None, Some(false), None)? + .list_unspent()? + .0 .into_iter() // Find a block reward tx. - .find(|utxo| utxo.amount == Amount::from_int_btc(50)) - .expect("Must find a block reward UTXO"); + .find(|utxo| utxo.amount == Amount::from_int_btc(50).to_btc()) + .expect("Must find a block reward UTXO") + .into_model()?; // Derive the sender's address from the selected UTXO. - let sender_spk = selected_utxo.script_pub_key.clone(); + let sender_spk = selected_utxo.script_pubkey.clone(); let sender_addr = Address::from_script(&sender_spk, bdk_chain::bitcoin::Network::Regtest) .expect("Failed to derive address from UTXO"); // Setup the common inputs used by both `send_tx` and `undo_send_tx`. - let inputs = [CreateRawTransactionInput { + let inputs = [Input { txid: selected_utxo.txid, - vout: selected_utxo.vout, + vout: selected_utxo.vout as u64, sequence: None, }]; // Create and sign the `send_tx` that sends funds to the receiver address. - let send_tx_outputs = HashMap::from([( - receiver_addr.to_string(), - selected_utxo.amount - SEND_TX_FEE, - )]); - let send_tx = rpc_client.create_raw_transaction(&inputs, &send_tx_outputs, None, Some(true))?; + let send_tx_outputs = [Output::new( + receiver_addr, + selected_utxo.amount.to_unsigned()? - SEND_TX_FEE, + )]; let send_tx = rpc_client - .sign_raw_transaction_with_wallet(send_tx.raw_hex(), None, None)? - .transaction()?; + .create_raw_transaction(&inputs, &send_tx_outputs)? + .into_model()? + .0; + let send_tx = rpc_client + .sign_raw_transaction_with_wallet(&send_tx)? + .into_model()? + .tx; // Create and sign the `undo_send_tx` transaction. This redirects funds back to the sender // address. - let undo_send_outputs = HashMap::from([( - sender_addr.to_string(), - selected_utxo.amount - UNDO_SEND_TX_FEE, - )]); - let undo_send_tx = - rpc_client.create_raw_transaction(&inputs, &undo_send_outputs, None, Some(true))?; + let undo_send_outputs = [Output::new( + sender_addr, + selected_utxo.amount.to_unsigned()? - UNDO_SEND_TX_FEE, + )]; let undo_send_tx = rpc_client - .sign_raw_transaction_with_wallet(undo_send_tx.raw_hex(), None, None)? - .transaction()?; + .create_raw_transaction(&inputs, &undo_send_outputs)? + .into_model()? + .0; + let undo_send_tx = rpc_client + .sign_raw_transaction_with_wallet(&undo_send_tx)? + .into_model()? + .tx; // Sync after broadcasting the `send_tx`. Ensure that we detect and receive the `send_tx`. - let send_txid = env.rpc_client().send_raw_transaction(send_tx.raw_hex())?; + let send_txid = env.rpc_client().send_raw_transaction(&send_tx)?.txid()?; env.wait_until_electrum_sees_txid(send_txid, Duration::from_secs(6))?; let sync_request = SyncRequest::builder() .chain_tip(chain.tip()) @@ -170,7 +179,8 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> { // mempool. let undo_send_txid = env .rpc_client() - .send_raw_transaction(undo_send_tx.raw_hex())?; + .send_raw_transaction(&undo_send_tx)? + .txid()?; env.wait_until_electrum_sees_txid(undo_send_txid, Duration::from_secs(6))?; let sync_request = SyncRequest::builder() .chain_tip(chain.tip()) @@ -206,43 +216,47 @@ pub fn chained_mempool_tx_sync() -> anyhow::Result<()> { let rpc_client = env.rpc_client(); let electrum_client = electrum_client::Client::new(env.electrsd.electrum_url.as_str())?; - let tracked_addr = rpc_client - .get_new_address(None, None)? - .require_network(Network::Regtest)?; + let tracked_addr = rpc_client.new_address()?; env.mine_blocks(100, None)?; // First unconfirmed tx. let txid1 = env.send(&tracked_addr, Amount::from_btc(1.0)?)?; - // Create second unconfirmed tx that spends the first. - let utxo = rpc_client - .list_unspent(None, Some(0), None, Some(true), None)? - .into_iter() - .find(|utxo| utxo.script_pub_key == tracked_addr.script_pubkey()) - .expect("must find the newly created utxo"); - let tx_that_spends_unconfirmed = rpc_client.create_raw_transaction( - &[CreateRawTransactionInput { - txid: utxo.txid, - vout: utxo.vout, - sequence: None, - }], - &[( - tracked_addr.to_string(), - utxo.amount - Amount::from_sat(1000), - )] - .into(), - None, - None, - )?; - let signed_tx = rpc_client - .sign_raw_transaction_with_wallet(tx_that_spends_unconfirmed.raw_hex(), None, None)? + let raw_tx = rpc_client.get_raw_transaction(txid1)?.transaction()?; + let (vout, utxo) = raw_tx + .output + .iter() + .enumerate() + .find(|(_, utxo)| utxo.script_pubkey == tracked_addr.script_pubkey()) + .expect("must find the newly created UTXO"); + + let tx_that_spends_unconfirmed = rpc_client + .create_raw_transaction( + &[Input { + txid: raw_tx.compute_txid(), + vout: vout as u64, + sequence: None, + }], + &[Output::new( + tracked_addr.clone(), + utxo.value - Amount::from_sat(1000), + )], + )? .transaction()?; - let txid2 = rpc_client.send_raw_transaction(signed_tx.raw_hex())?; + + let signed_tx = rpc_client + .sign_raw_transaction_with_wallet(&tx_that_spends_unconfirmed)? + .into_model()? + .tx; + + let txid2 = rpc_client.send_raw_transaction(&signed_tx)?.txid()?; env.wait_until_electrum_sees_txid(signed_tx.compute_txid(), Duration::from_secs(6))?; - let spk_history = electrum_client.script_get_history(&tracked_addr.script_pubkey())?; + let spk = tracked_addr.clone().script_pubkey(); + let script = spk.as_script(); + let spk_history = electrum_client.script_get_history(script)?; assert!( spk_history.into_iter().any(|tx_res| tx_res.height < 0), "must find tx with negative height" @@ -280,26 +294,16 @@ pub fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> { ]; let _block_hashes = env.mine_blocks(101, None)?; - let txid1 = env.bitcoind.client.send_to_address( - &receive_address1, - Amount::from_sat(10000), - None, - None, - None, - None, - Some(1), - None, - )?; - let txid2 = env.bitcoind.client.send_to_address( - &receive_address0, - Amount::from_sat(20000), - None, - None, - None, - None, - Some(1), - None, - )?; + let txid1 = env + .bitcoind + .client + .send_to_address(&receive_address1, Amount::from_sat(10000))? + .txid()?; + let txid2 = env + .bitcoind + .client + .send_to_address(&receive_address0, Amount::from_sat(20000))? + .txid()?; env.mine_blocks(1, None)?; env.wait_until_electrum_sees_block(Duration::from_secs(6))?; @@ -346,7 +350,8 @@ pub fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> { let tx_fee = env .bitcoind .client - .get_transaction(&tx.compute_txid(), None) + .get_transaction(tx.compute_txid())? + .into_model() .expect("Tx must exist") .fee .expect("Fee must exist") @@ -402,16 +407,11 @@ pub fn test_update_tx_graph_stop_gap() -> anyhow::Result<()> { .collect(); // Then receive coins on the 4th address. - let txid_4th_addr = env.bitcoind.client.send_to_address( - &addresses[3], - Amount::from_sat(10000), - None, - None, - None, - None, - Some(1), - None, - )?; + let txid_4th_addr = env + .bitcoind + .client + .send_to_address(&addresses[3], Amount::from_sat(10000))? + .txid()?; env.mine_blocks(1, None)?; env.wait_until_electrum_sees_block(Duration::from_secs(6))?; @@ -446,16 +446,11 @@ pub fn test_update_tx_graph_stop_gap() -> anyhow::Result<()> { assert_eq!(full_scan_update.last_active_indices[&0], 3); // Now receive a coin on the last address. - let txid_last_addr = env.bitcoind.client.send_to_address( - &addresses[addresses.len() - 1], - Amount::from_sat(10000), - None, - None, - None, - None, - Some(1), - None, - )?; + let txid_last_addr = env + .bitcoind + .client + .send_to_address(&addresses[addresses.len() - 1], Amount::from_sat(10000))? + .txid()?; env.mine_blocks(1, None)?; env.wait_until_electrum_sees_block(Duration::from_secs(6))?; @@ -507,16 +502,12 @@ fn test_sync() -> anyhow::Result<()> { let client = BdkElectrumClient::new(electrum_client); // Setup addresses. - let addr_to_mine = env - .bitcoind - .client - .get_new_address(None, None)? - .assume_checked(); + let addr_to_mine = env.bitcoind.client.new_address()?; let spk_to_track = ScriptBuf::new_p2wsh(&WScriptHash::all_zeros()); let addr_to_track = Address::from_script(&spk_to_track, bdk_chain::bitcoin::Network::Regtest)?; // Setup receiver. - let (mut recv_chain, _) = LocalChain::from_genesis(env.bitcoind.client.get_block_hash(0)?); + let (mut recv_chain, _) = LocalChain::from_genesis(env.genesis_hash()?); let mut recv_graph = IndexedTxGraph::::new({ let mut recv_index = SpkTxOutIndex::default(); recv_index.insert_spk((), spk_to_track.clone()); @@ -619,7 +610,8 @@ fn test_sync() -> anyhow::Result<()> { let tx_fee = env .bitcoind .client - .get_transaction(&tx.txid, None) + .get_transaction(tx.txid)? + .into_model() .expect("Tx must exist") .fee .expect("Fee must exist") @@ -650,16 +642,12 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> { let client = BdkElectrumClient::new(electrum_client); // Setup addresses. - let addr_to_mine = env - .bitcoind - .client - .get_new_address(None, None)? - .assume_checked(); + let addr_to_mine = env.bitcoind.client.new_address()?; let spk_to_track = ScriptBuf::new_p2wsh(&WScriptHash::all_zeros()); let addr_to_track = Address::from_script(&spk_to_track, bdk_chain::bitcoin::Network::Regtest)?; // Setup receiver. - let (mut recv_chain, _) = LocalChain::from_genesis(env.bitcoind.client.get_block_hash(0)?); + let (mut recv_chain, _) = LocalChain::from_genesis(env.genesis_hash()?); let mut recv_graph = IndexedTxGraph::::new({ let mut recv_index = SpkTxOutIndex::default(); recv_index.insert_spk((), spk_to_track.clone()); @@ -745,7 +733,7 @@ fn test_sync_with_coinbase() -> anyhow::Result<()> { let addr_to_track = Address::from_script(&spk_to_track, bdk_chain::bitcoin::Network::Regtest)?; // Setup receiver. - let (mut recv_chain, _) = LocalChain::from_genesis(env.bitcoind.client.get_block_hash(0)?); + let (mut recv_chain, _) = LocalChain::from_genesis(env.genesis_hash()?); let mut recv_graph = IndexedTxGraph::::new({ let mut recv_index = SpkTxOutIndex::default(); recv_index.insert_spk((), spk_to_track.clone()); @@ -780,7 +768,7 @@ fn test_check_fee_calculation() -> anyhow::Result<()> { let addr_to_track = Address::from_script(&spk_to_track, bdk_chain::bitcoin::Network::Regtest)?; // Setup receiver. - let (mut recv_chain, _) = LocalChain::from_genesis(env.bitcoind.client.get_block_hash(0)?); + let (mut recv_chain, _) = LocalChain::from_genesis(env.genesis_hash()?); let mut recv_graph = IndexedTxGraph::::new({ let mut recv_index = SpkTxOutIndex::default(); recv_index.insert_spk((), spk_to_track.clone()); @@ -792,44 +780,42 @@ fn test_check_fee_calculation() -> anyhow::Result<()> { // Send a preliminary tx such that the new utxo in Core's wallet // becomes the input of the next tx - let new_addr = env - .rpc_client() - .get_new_address(None, None)? - .assume_checked(); + let new_addr = env.rpc_client().new_address()?; let prev_amt = SEND_AMOUNT + FEE_AMOUNT; env.send(&new_addr, prev_amt)?; - let prev_block_hash = env.mine_blocks(1, None)?.into_iter().next(); + let _prev_block_hash = env + .mine_blocks(1, None)? + .into_iter() + .next() + .expect("should've successfully mined a block"); let txid = env.send(&addr_to_track, SEND_AMOUNT)?; // Mine a block to confirm sent tx. - let block_hash = env.mine_blocks(1, None)?.into_iter().next(); + let _block_hash = env + .mine_blocks(1, None)? + .into_iter() + .next() + .expect("should've successfully mined a block"); // Look at the tx we just sent, it should have 1 input and 1 output - let tx = env - .rpc_client() - .get_raw_transaction_info(&txid, block_hash.as_ref())?; - assert_eq!(tx.vin.len(), 1); - assert_eq!(tx.vout.len(), 1); - let vin = &tx.vin[0]; - let prev_txid = vin.txid.unwrap(); - let vout = vin.vout.unwrap(); - let outpoint = bdk_chain::bitcoin::OutPoint::new(prev_txid, vout); + let tx = env.rpc_client().get_transaction(txid)?.into_model()?.tx; + assert_eq!(tx.input.len(), 1); + assert_eq!(tx.output.len(), 1); + let outpoint = tx.input[0].previous_output; + let prev_txid = outpoint.txid; // Get the txout of the previous tx let prev_tx = env .rpc_client() - .get_raw_transaction_info(&prev_txid, prev_block_hash.as_ref())?; + .get_transaction(prev_txid)? + .into_model()? + .tx; let txout = prev_tx - .vout + .output .iter() .find(|txout| txout.value == prev_amt) - .unwrap(); - let script_pubkey = ScriptBuf::from_bytes(txout.script_pub_key.hex.to_vec()); - let txout = bdk_chain::bitcoin::TxOut { - value: txout.value, - script_pubkey, - }; + .expect("should've successfully found the existing `TxOut`"); // Sync up to tip. env.wait_until_electrum_sees_block(Duration::from_secs(6))?; @@ -846,7 +832,7 @@ fn test_check_fee_calculation() -> anyhow::Result<()> { .all_txouts() .find(|(_op, txout)| txout.value == prev_amt) .unwrap(); - assert_eq!(graph_txout, (outpoint, &txout)); + assert_eq!(graph_txout, (outpoint, txout)); // Check to see if tx is confirmed. assert_eq!( @@ -872,12 +858,13 @@ fn test_check_fee_calculation() -> anyhow::Result<()> { let tx_fee = env .bitcoind .client - .get_transaction(&tx.txid, None) + .get_transaction(tx.txid) .expect("Tx must exist") .fee + .map(|fee| Amount::from_float_in(fee.abs(), Denomination::BTC)) .expect("Fee must exist") - .abs() - .to_sat() as u64; + .expect("Amount parsing should succeed") + .to_sat(); // Check that the calculated fee matches the fee from the transaction data. assert_eq!(fee, Amount::from_sat(tx_fee)); // 1650sat diff --git a/crates/esplora/src/async_ext.rs b/crates/esplora/src/async_ext.rs index c9cb17c1e1..d504a4723f 100644 --- a/crates/esplora/src/async_ext.rs +++ b/crates/esplora/src/async_ext.rs @@ -560,7 +560,7 @@ mod test { BlockId, }; use bdk_core::{bitcoin, ConfirmationBlockTime}; - use bdk_testenv::{anyhow, bitcoincore_rpc::RpcApi, TestEnv}; + use bdk_testenv::{anyhow, TestEnv}; use esplora_client::Builder; use crate::async_ext::{chain_update, fetch_latest_blocks}; @@ -577,7 +577,7 @@ mod test { let env = TestEnv::new()?; let base_url = format!("http://{}", &env.electrsd.esplora_url.clone().unwrap()); let client = Builder::new(base_url.as_str()).build_async()?; - let initial_height = env.rpc_client().get_block_count()? as u32; + let initial_height = env.rpc_client().get_block_count()?.into_model().0 as u32; let mine_to = 16; let _ = env.mine_blocks((mine_to - initial_height) as usize, None)?; @@ -673,7 +673,11 @@ mod test { ConfirmationBlockTime { block_id: BlockId { height, - hash: env.bitcoind.client.get_block_hash(height as _)?, + hash: env + .bitcoind + .client + .get_block_hash(height as _)? + .block_hash()?, }, confirmation_time: height as _, }, @@ -713,7 +717,11 @@ mod test { ConfirmationBlockTime { block_id: BlockId { height, - hash: env.bitcoind.client.get_block_hash(height as _)?, + hash: env + .bitcoind + .client + .get_block_hash(height as _)? + .block_hash()?, }, confirmation_time: height as _, }, diff --git a/crates/esplora/src/blocking_ext.rs b/crates/esplora/src/blocking_ext.rs index 5f8ab531c9..9413c14afd 100644 --- a/crates/esplora/src/blocking_ext.rs +++ b/crates/esplora/src/blocking_ext.rs @@ -518,7 +518,7 @@ mod test { use bdk_chain::local_chain::LocalChain; use bdk_chain::BlockId; use bdk_core::ConfirmationBlockTime; - use bdk_testenv::{anyhow, bitcoincore_rpc::RpcApi, TestEnv}; + use bdk_testenv::{anyhow, TestEnv}; use esplora_client::{BlockHash, Builder}; use std::collections::{BTreeMap, BTreeSet}; use std::time::Duration; @@ -543,7 +543,7 @@ mod test { let env = TestEnv::new()?; let base_url = format!("http://{}", &env.electrsd.esplora_url.clone().unwrap()); let client = Builder::new(base_url.as_str()).build_blocking(); - let initial_height = env.rpc_client().get_block_count()? as u32; + let initial_height = env.rpc_client().get_block_count()?.into_model().0 as u32; let mine_to = 16; let _ = env.mine_blocks((mine_to - initial_height) as usize, None)?; @@ -639,7 +639,11 @@ mod test { ConfirmationBlockTime { block_id: BlockId { height, - hash: env.bitcoind.client.get_block_hash(height as _)?, + hash: env + .bitcoind + .client + .get_block_hash(height as _)? + .block_hash()?, }, confirmation_time: height as _, }, @@ -678,7 +682,11 @@ mod test { ConfirmationBlockTime { block_id: BlockId { height, - hash: env.bitcoind.client.get_block_hash(height as _)?, + hash: env + .bitcoind + .client + .get_block_hash(height as _)? + .block_hash()?, }, confirmation_time: height as _, }, @@ -741,10 +749,10 @@ mod test { let env = TestEnv::new()?; let blocks = { let bitcoind_client = &env.bitcoind.client; - assert_eq!(bitcoind_client.get_block_count()?, 1); + assert_eq!(bitcoind_client.get_block_count()?.0, 1); [ - (0, bitcoind_client.get_block_hash(0)?), - (1, bitcoind_client.get_block_hash(1)?), + (0, bitcoind_client.get_block_hash(0)?.block_hash()?), + (1, bitcoind_client.get_block_hash(1)?.block_hash()?), ] .into_iter() .chain((2..).zip(env.mine_blocks((TIP_HEIGHT - 1) as usize, None)?)) diff --git a/crates/esplora/tests/async_ext.rs b/crates/esplora/tests/async_ext.rs index 3c628c20df..209e5b7882 100644 --- a/crates/esplora/tests/async_ext.rs +++ b/crates/esplora/tests/async_ext.rs @@ -4,11 +4,10 @@ use bdk_chain::spk_client::{FullScanRequest, SyncRequest}; use bdk_chain::spk_txout::SpkTxOutIndex; use bdk_chain::{ConfirmationBlockTime, IndexedTxGraph, TxGraph}; use bdk_esplora::EsploraAsyncExt; -use bdk_testenv::bitcoincore_rpc::json::CreateRawTransactionInput; -use bdk_testenv::bitcoincore_rpc::RawTx; -use bdk_testenv::{anyhow, bitcoincore_rpc::RpcApi, TestEnv}; +use bdk_testenv::corepc_node::{Input, Output}; +use bdk_testenv::{anyhow, TestEnv}; use esplora_client::{self, Builder}; -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeSet, HashSet}; use std::str::FromStr; use std::thread::sleep; use std::time::Duration; @@ -30,7 +29,7 @@ pub async fn detect_receive_tx_cancel() -> anyhow::Result<()> { let client = Builder::new(base_url.as_str()).build_async()?; let mut graph = IndexedTxGraph::::new(SpkTxOutIndex::<()>::default()); - let (chain, _) = LocalChain::from_genesis(env.bitcoind.client.get_block_hash(0)?); + let (chain, _) = LocalChain::from_genesis(env.genesis_hash()?); // Get receiving address. let receiver_spk = common::get_test_spk(); @@ -41,48 +40,61 @@ pub async fn detect_receive_tx_cancel() -> anyhow::Result<()> { // Select a UTXO to use as an input for constructing our test transactions. let selected_utxo = rpc_client - .list_unspent(None, None, None, Some(false), None)? + .list_unspent()? + .0 .into_iter() // Find a block reward tx. - .find(|utxo| utxo.amount == Amount::from_int_btc(50)) - .expect("Must find a block reward UTXO"); + .find(|utxo| utxo.amount == Amount::from_int_btc(50).to_btc()) + .expect("Must find a block reward UTXO") + .into_model()?; // Derive the sender's address from the selected UTXO. - let sender_spk = selected_utxo.script_pub_key.clone(); + let sender_spk = selected_utxo.script_pubkey.clone(); let sender_addr = Address::from_script(&sender_spk, bdk_chain::bitcoin::Network::Regtest) .expect("Failed to derive address from UTXO"); // Setup the common inputs used by both `send_tx` and `undo_send_tx`. - let inputs = [CreateRawTransactionInput { + let inputs = [Input { txid: selected_utxo.txid, - vout: selected_utxo.vout, + vout: selected_utxo.vout as u64, sequence: None, }]; // Create and sign the `send_tx` that sends funds to the receiver address. - let send_tx_outputs = HashMap::from([( - receiver_addr.to_string(), - selected_utxo.amount - SEND_TX_FEE, - )]); - let send_tx = rpc_client.create_raw_transaction(&inputs, &send_tx_outputs, None, Some(true))?; + let address = receiver_addr; + let value = selected_utxo.amount.to_unsigned()? - SEND_TX_FEE; + let send_tx_outputs = Output::new(address, value); + + let send_tx = rpc_client + .create_raw_transaction(&inputs, &[send_tx_outputs])? + .into_model()? + .0; let send_tx = rpc_client - .sign_raw_transaction_with_wallet(send_tx.raw_hex(), None, None)? - .transaction()?; + .sign_raw_transaction_with_wallet(&send_tx)? + .into_model()? + .tx; // Create and sign the `undo_send_tx` transaction. This redirects funds back to the sender // address. - let undo_send_outputs = HashMap::from([( - sender_addr.to_string(), - selected_utxo.amount - UNDO_SEND_TX_FEE, - )]); - let undo_send_tx = - rpc_client.create_raw_transaction(&inputs, &undo_send_outputs, None, Some(true))?; + let undo_send_outputs = [Output::new( + sender_addr, + selected_utxo.amount.to_unsigned()? - UNDO_SEND_TX_FEE, + )]; let undo_send_tx = rpc_client - .sign_raw_transaction_with_wallet(undo_send_tx.raw_hex(), None, None)? - .transaction()?; + .create_raw_transaction(&inputs, &undo_send_outputs)? + .into_model()? + .0; + let undo_send_tx = rpc_client + .sign_raw_transaction_with_wallet(&undo_send_tx)? + .into_model()? + .tx; // Sync after broadcasting the `send_tx`. Ensure that we detect and receive the `send_tx`. - let send_txid = env.rpc_client().send_raw_transaction(send_tx.raw_hex())?; + let send_txid = env + .rpc_client() + .send_raw_transaction(&send_tx)? + .into_model()? + .0; env.wait_until_electrum_sees_txid(send_txid, Duration::from_secs(6))?; let sync_request = SyncRequest::builder() .chain_tip(chain.tip()) @@ -111,7 +123,8 @@ pub async fn detect_receive_tx_cancel() -> anyhow::Result<()> { // mempool. let undo_send_txid = env .rpc_client() - .send_raw_transaction(undo_send_tx.raw_hex())?; + .send_raw_transaction(&undo_send_tx)? + .txid()?; env.wait_until_electrum_sees_txid(undo_send_txid, Duration::from_secs(6))?; let sync_request = SyncRequest::builder() .chain_tip(chain.tip()) @@ -155,26 +168,16 @@ pub async fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> { ]; let _block_hashes = env.mine_blocks(101, None)?; - let txid1 = env.bitcoind.client.send_to_address( - &receive_address1, - Amount::from_sat(10000), - None, - None, - None, - None, - Some(1), - None, - )?; - let txid2 = env.bitcoind.client.send_to_address( - &receive_address0, - Amount::from_sat(20000), - None, - None, - None, - None, - Some(1), - None, - )?; + let txid1 = env + .bitcoind + .client + .send_to_address(&receive_address1, Amount::from_sat(10000))? + .txid()?; + let txid2 = env + .bitcoind + .client + .send_to_address(&receive_address0, Amount::from_sat(20000))? + .txid()?; let _block_hashes = env.mine_blocks(1, None)?; while client.get_height().await.unwrap() < 102 { sleep(Duration::from_millis(10)) @@ -223,8 +226,9 @@ pub async fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> { let tx_fee = env .bitcoind .client - .get_transaction(&tx.compute_txid(), None) + .get_transaction(tx.compute_txid()) .expect("Tx must exist") + .into_model()? .fee .expect("Fee must exist") .abs() @@ -279,16 +283,11 @@ pub async fn test_async_update_tx_graph_stop_gap() -> anyhow::Result<()> { .collect(); // Then receive coins on the 4th address. - let txid_4th_addr = env.bitcoind.client.send_to_address( - &addresses[3], - Amount::from_sat(10000), - None, - None, - None, - None, - Some(1), - None, - )?; + let txid_4th_addr = env + .bitcoind + .client + .send_to_address(&addresses[3], Amount::from_sat(10000))? + .txid()?; let _block_hashes = env.mine_blocks(1, None)?; while client.get_height().await.unwrap() < 103 { sleep(Duration::from_millis(10)) @@ -325,16 +324,11 @@ pub async fn test_async_update_tx_graph_stop_gap() -> anyhow::Result<()> { assert_eq!(full_scan_update.last_active_indices[&0], 3); // Now receive a coin on the last address. - let txid_last_addr = env.bitcoind.client.send_to_address( - &addresses[addresses.len() - 1], - Amount::from_sat(10000), - None, - None, - None, - None, - Some(1), - None, - )?; + let txid_last_addr = env + .bitcoind + .client + .send_to_address(&addresses[addresses.len() - 1], Amount::from_sat(10000))? + .txid()?; let _block_hashes = env.mine_blocks(1, None)?; while client.get_height().await.unwrap() < 104 { sleep(Duration::from_millis(10)) diff --git a/crates/esplora/tests/blocking_ext.rs b/crates/esplora/tests/blocking_ext.rs index 4d5683e8b2..76ed28fbb5 100644 --- a/crates/esplora/tests/blocking_ext.rs +++ b/crates/esplora/tests/blocking_ext.rs @@ -4,11 +4,10 @@ use bdk_chain::spk_client::{FullScanRequest, SyncRequest}; use bdk_chain::spk_txout::SpkTxOutIndex; use bdk_chain::{ConfirmationBlockTime, IndexedTxGraph, TxGraph}; use bdk_esplora::EsploraExt; -use bdk_testenv::bitcoincore_rpc::json::CreateRawTransactionInput; -use bdk_testenv::bitcoincore_rpc::RawTx; -use bdk_testenv::{anyhow, bitcoincore_rpc::RpcApi, TestEnv}; +use bdk_testenv::corepc_node::{Input, Output}; +use bdk_testenv::{anyhow, TestEnv}; use esplora_client::{self, Builder}; -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeSet, HashSet}; use std::str::FromStr; use std::thread::sleep; use std::time::Duration; @@ -30,7 +29,7 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> { let client = Builder::new(base_url.as_str()).build_blocking(); let mut graph = IndexedTxGraph::::new(SpkTxOutIndex::<()>::default()); - let (chain, _) = LocalChain::from_genesis(env.bitcoind.client.get_block_hash(0)?); + let (chain, _) = LocalChain::from_genesis(env.genesis_hash()?); // Get receiving address. let receiver_spk = common::get_test_spk(); @@ -41,48 +40,58 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> { // Select a UTXO to use as an input for constructing our test transactions. let selected_utxo = rpc_client - .list_unspent(None, None, None, Some(false), None)? + .list_unspent()? + .0 .into_iter() // Find a block reward tx. - .find(|utxo| utxo.amount == Amount::from_int_btc(50)) - .expect("Must find a block reward UTXO"); + .find(|utxo| utxo.amount == Amount::from_int_btc(50).to_btc()) + .expect("Must find a block reward UTXO") + .into_model()?; // Derive the sender's address from the selected UTXO. - let sender_spk = selected_utxo.script_pub_key.clone(); + let sender_spk = selected_utxo.script_pubkey.clone(); let sender_addr = Address::from_script(&sender_spk, bdk_chain::bitcoin::Network::Regtest) .expect("Failed to derive address from UTXO"); // Setup the common inputs used by both `send_tx` and `undo_send_tx`. - let inputs = [CreateRawTransactionInput { + let inputs = [Input { txid: selected_utxo.txid, - vout: selected_utxo.vout, + vout: selected_utxo.vout as u64, sequence: None, }]; // Create and sign the `send_tx` that sends funds to the receiver address. - let send_tx_outputs = HashMap::from([( - receiver_addr.to_string(), - selected_utxo.amount - SEND_TX_FEE, - )]); - let send_tx = rpc_client.create_raw_transaction(&inputs, &send_tx_outputs, None, Some(true))?; + let send_tx_outputs = [Output::new( + receiver_addr, + selected_utxo.amount.to_unsigned()? - SEND_TX_FEE, + )]; + + let send_tx = rpc_client + .create_raw_transaction(&inputs, &send_tx_outputs)? + .into_model()? + .0; let send_tx = rpc_client - .sign_raw_transaction_with_wallet(send_tx.raw_hex(), None, None)? - .transaction()?; + .sign_raw_transaction_with_wallet(&send_tx)? + .into_model()? + .tx; // Create and sign the `undo_send_tx` transaction. This redirects funds back to the sender // address. - let undo_send_outputs = HashMap::from([( - sender_addr.to_string(), - selected_utxo.amount - UNDO_SEND_TX_FEE, - )]); - let undo_send_tx = - rpc_client.create_raw_transaction(&inputs, &undo_send_outputs, None, Some(true))?; + let undo_send_outputs = [Output::new( + sender_addr, + selected_utxo.amount.to_unsigned()? - UNDO_SEND_TX_FEE, + )]; let undo_send_tx = rpc_client - .sign_raw_transaction_with_wallet(undo_send_tx.raw_hex(), None, None)? - .transaction()?; + .create_raw_transaction(&inputs, &undo_send_outputs)? + .into_model()? + .0; + let undo_send_tx = rpc_client + .sign_raw_transaction_with_wallet(&undo_send_tx)? + .into_model()? + .tx; // Sync after broadcasting the `send_tx`. Ensure that we detect and receive the `send_tx`. - let send_txid = env.rpc_client().send_raw_transaction(send_tx.raw_hex())?; + let send_txid = env.rpc_client().send_raw_transaction(&send_tx)?.txid()?; env.wait_until_electrum_sees_txid(send_txid, Duration::from_secs(6))?; let sync_request = SyncRequest::builder() .chain_tip(chain.tip()) @@ -111,7 +120,8 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> { // mempool. let undo_send_txid = env .rpc_client() - .send_raw_transaction(undo_send_tx.raw_hex())?; + .send_raw_transaction(&undo_send_tx)? + .txid()?; env.wait_until_electrum_sees_txid(undo_send_txid, Duration::from_secs(6))?; let sync_request = SyncRequest::builder() .chain_tip(chain.tip()) @@ -156,26 +166,16 @@ pub fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> { ]; let _block_hashes = env.mine_blocks(101, None)?; - let txid1 = env.bitcoind.client.send_to_address( - &receive_address1, - Amount::from_sat(10000), - None, - None, - None, - None, - Some(1), - None, - )?; - let txid2 = env.bitcoind.client.send_to_address( - &receive_address0, - Amount::from_sat(20000), - None, - None, - None, - None, - Some(1), - None, - )?; + let txid1 = env + .bitcoind + .client + .send_to_address(&receive_address1, Amount::from_sat(10000))? + .txid()?; + let txid2 = env + .bitcoind + .client + .send_to_address(&receive_address0, Amount::from_sat(20000))? + .txid()?; let _block_hashes = env.mine_blocks(1, None)?; while client.get_height().unwrap() < 102 { sleep(Duration::from_millis(10)) @@ -224,16 +224,17 @@ pub fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> { let tx_fee = env .bitcoind .client - .get_transaction(&tx.compute_txid(), None) + .get_transaction(tx.compute_txid()) .expect("Tx must exist") .fee .expect("Fee must exist") - .abs() - .to_unsigned() - .expect("valid `Amount`"); + .abs(); // Check that the calculated fee matches the fee from the transaction data. - assert_eq!(fee, tx_fee); + assert_eq!( + fee, + Amount::from_float_in(tx_fee, bdk_core::bitcoin::Denomination::Bitcoin)? + ); } assert_eq!( @@ -280,16 +281,12 @@ pub fn test_update_tx_graph_stop_gap() -> anyhow::Result<()> { .collect(); // Then receive coins on the 4th address. - let txid_4th_addr = env.bitcoind.client.send_to_address( - &addresses[3], - Amount::from_sat(10000), - None, - None, - None, - None, - Some(1), - None, - )?; + let txid_4th_addr = env + .bitcoind + .client + .send_to_address(&addresses[3], Amount::from_sat(10000))? + .into_model()? + .txid; let _block_hashes = env.mine_blocks(1, None)?; while client.get_height().unwrap() < 103 { sleep(Duration::from_millis(10)) @@ -326,16 +323,11 @@ pub fn test_update_tx_graph_stop_gap() -> anyhow::Result<()> { assert_eq!(full_scan_update.last_active_indices[&0], 3); // Now receive a coin on the last address. - let txid_last_addr = env.bitcoind.client.send_to_address( - &addresses[addresses.len() - 1], - Amount::from_sat(10000), - None, - None, - None, - None, - Some(1), - None, - )?; + let txid_last_addr = env + .bitcoind + .client + .send_to_address(&addresses[addresses.len() - 1], Amount::from_sat(10000))? + .txid()?; let _block_hashes = env.mine_blocks(1, None)?; while client.get_height().unwrap() < 104 { sleep(Duration::from_millis(10)) From 939383266b00fb0f676e2a578af8147ca35552e5 Mon Sep 17 00:00:00 2001 From: valued mammal Date: Fri, 23 Jan 2026 14:06:27 -0500 Subject: [PATCH 5/6] build(testenv): `mine_empty_blocks` requires `std` feature Feature-gate TestEnv methods `mine_empty_blocks`, `reorg_empty_blocks` behind `std` feature, as they depend on `rand` which is only available when `std` feature is enabled. --- crates/testenv/src/lib.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/testenv/src/lib.rs b/crates/testenv/src/lib.rs index 0f2950320b..04e552817b 100644 --- a/crates/testenv/src/lib.rs +++ b/crates/testenv/src/lib.rs @@ -2,15 +2,10 @@ pub mod utils; -use bdk_chain::{ - bitcoin::{ - address::NetworkChecked, block::Header, hash_types::TxMerkleNode, hashes::Hash, - secp256k1::rand::random, transaction, Address, Amount, Block, BlockHash, ScriptBuf, - ScriptHash, Transaction, TxIn, TxOut, Txid, - }, - local_chain::CheckPoint, -}; -use electrsd::corepc_node::{anyhow::Context, TemplateRequest, TemplateRules}; +use anyhow::Context; +use bdk_chain::CheckPoint; +use bitcoin::{address::NetworkChecked, Address, Amount, BlockHash, Txid}; +use std::time::Duration; pub use electrsd; pub use electrsd::corepc_client; @@ -18,7 +13,6 @@ pub use electrsd::corepc_node; pub use electrsd::corepc_node::anyhow; pub use electrsd::electrum_client; use electrsd::electrum_client::ElectrumApi; -use std::time::Duration; /// Struct for running a regtest environment with a single `bitcoind` node with an `electrs` /// instance connected to it. @@ -126,7 +120,14 @@ impl TestEnv { } /// Mine a block that is guaranteed to be empty even with transactions in the mempool. + #[cfg(feature = "std")] pub fn mine_empty_block(&self) -> anyhow::Result<(usize, BlockHash)> { + use bitcoin::secp256k1::rand::random; + use bitcoin::{ + block::Header, hashes::Hash, transaction, Block, ScriptBuf, ScriptHash, Transaction, + TxIn, TxMerkleNode, TxOut, + }; + use corepc_node::{TemplateRequest, TemplateRules}; let request = TemplateRequest { rules: vec![TemplateRules::Segwit], }; @@ -266,6 +267,7 @@ impl TestEnv { } /// Reorg with a number of empty blocks of a given size `count`. + #[cfg(feature = "std")] pub fn reorg_empty_blocks(&self, count: usize) -> anyhow::Result> { let start_height = self.bitcoind.client.get_block_count()?; self.invalidate_blocks(count)?; From 303cc7010006dcb546d92d8a4c8c52e43ad14ead Mon Sep 17 00:00:00 2001 From: valued mammal Date: Fri, 23 Jan 2026 14:56:14 -0500 Subject: [PATCH 6/6] docs(testenv): fix rustdocs for `corepc` types --- crates/testenv/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/testenv/src/lib.rs b/crates/testenv/src/lib.rs index 04e552817b..914200e990 100644 --- a/crates/testenv/src/lib.rs +++ b/crates/testenv/src/lib.rs @@ -24,7 +24,7 @@ pub struct TestEnv { /// Configuration parameters. #[derive(Debug)] pub struct Config<'a> { - /// [`bitcoind::Conf`] + /// [`corepc_node::Conf`] pub bitcoind: corepc_node::Conf<'a>, /// [`electrsd::Conf`] pub electrsd: electrsd::Conf<'a>, @@ -76,7 +76,7 @@ impl TestEnv { &self.electrsd.client } - /// Exposes the [`RpcApi`] calls from [`bitcoincore_rpc`]. + /// Exposes the RPC calls from [`corepc_client`]. pub fn rpc_client(&self) -> &corepc_node::Client { &self.bitcoind.client }