Skip to content

Commit

Permalink
feat: added electrum and esplora support to TestEnv
Browse files Browse the repository at this point in the history
`TestEnv` now supports both `electrum` and `esplora`. `esplora`
tests have also been updated to use `TestEnv`.
  • Loading branch information
LagginTimes committed Nov 12, 2023
1 parent 79412aa commit 6bb1a40
Show file tree
Hide file tree
Showing 9 changed files with 417 additions and 168 deletions.
1 change: 0 additions & 1 deletion crates/bitcoind_rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ bdk_chain = { path = "../chain", version = "0.6", default-features = false }

[dev-dependencies]
testenv = { path = "../testenv", version = "0.1.0", default_features = false }
bitcoind = { version = "0.33", features = ["25_0"] }
anyhow = { version = "1" }

[features]
Expand Down
91 changes: 61 additions & 30 deletions crates/bitcoind_rpc/tests/test_emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,25 @@ 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)?);
// add the hashes of the genesis block and block generated from initializing electrsd
let mut hashes = (0..=tip)
.map(|height| env.rpc_client().get_block_hash(height))
.collect::<Result<Vec<_>, _>>()?;
hashes.extend(env.mine_blocks(101 - tip as usize, None)?);
hashes
};

// update `local_chain` with block generated from initializing electrsd
for (height, hash) in exp_hashes.iter().enumerate().take(tip as usize) {
let changeset = BTreeMap::from([(height as u32, Some(*hash))]);
local_chain.apply_changeset(&changeset);
}

// see if the emitter outputs the right blocks
println!("first sync:");
while let Some((height, block)) = emitter.next_block()? {
Expand Down Expand Up @@ -141,9 +151,18 @@ 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!");
Expand All @@ -159,7 +178,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))?;
Expand All @@ -171,7 +190,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,
Expand Down Expand Up @@ -207,7 +226,7 @@ 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_height = env.rpc_client().get_block_info(&exp_block_hash)?.height as u32;
let exp_anchors = exp_txids
.iter()
.map({
Expand Down Expand Up @@ -247,13 +266,13 @@ 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)?;
while emitter.next_header()?.is_some() {}

for reorg_count in 1..=10 {
let replaced_blocks = env.reorg_empty_blocks(reorg_count)?;
let replaced_blocks = env.reorg_empty_blocks(reorg_count, &env.bitcoind)?;
let (height, next_header) = emitter.next_header()?.expect("must emit block after reorg");
assert_eq!(
(height as usize, next_header.block_hash()),
Expand Down Expand Up @@ -315,10 +334,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)?;

Expand All @@ -339,7 +361,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
Expand All @@ -348,7 +370,7 @@ 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::<Vec<_>>();
env.client.lock_unspent(&outpoints_to_lock)?;
env.rpc_client().lock_unspent(&outpoints_to_lock)?;

let _ = env.mine_blocks(1, None)?;
}
Expand All @@ -367,7 +389,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> {

// perform reorgs with different depths
for reorg_count in 1..=ADDITIONAL_COUNT {
env.reorg_empty_blocks(reorg_count)?;
env.reorg_empty_blocks(reorg_count, &env.bitcoind)?;
sync_from_emitter(&mut recv_chain, &mut recv_graph, &mut emitter)?;

assert_eq!(
Expand Down Expand Up @@ -396,10 +418,13 @@ 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();
let addr = env
.rpc_client()
.get_new_address(None, None)?
.assume_checked();
env.mine_blocks(BLOCKS_TO_MINE, Some(addr.clone()))?;
while emitter.next_header()?.is_some() {}

Expand Down Expand Up @@ -451,10 +476,13 @@ 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();
let addr = env
.rpc_client()
.get_new_address(None, None)?
.assume_checked();
env.mine_blocks(PREMINE_COUNT, Some(addr.clone()))?;
while emitter.next_header()?.is_some() {}

Expand Down Expand Up @@ -528,10 +556,13 @@ 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();
let addr = env
.rpc_client()
.get_new_address(None, None)?
.assume_checked();
env.mine_blocks(PREMINE_COUNT, Some(addr.clone()))?;

// introduce mempool tx at each block extension
Expand All @@ -549,7 +580,7 @@ fn mempool_during_reorg() -> anyhow::Result<()> {
.into_iter()
.map(|(tx, _)| tx.txid())
.collect::<BTreeSet<_>>(),
env.client
env.rpc_client()
.get_raw_mempool()?
.into_iter()
.collect::<BTreeSet<_>>(),
Expand All @@ -560,15 +591,15 @@ fn mempool_during_reorg() -> anyhow::Result<()> {
// mempool
for reorg_count in 1..TIP_DIFF {
println!("REORG COUNT: {}", reorg_count);
env.reorg_empty_blocks(reorg_count)?;
env.reorg_empty_blocks(reorg_count, &env.bitcoind)?;

// This is a map of mempool txids to tip height where the tx was introduced to the mempool
// we recalculate this at every loop as reorgs may evict transactions from mempool. We use
// the introduction height to determine whether we expect a tx to appear in a mempool
// 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))
Expand Down Expand Up @@ -643,7 +674,7 @@ 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)?;
Expand All @@ -658,12 +689,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.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)?;
Expand Down
7 changes: 6 additions & 1 deletion crates/electrum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ readme = "README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bdk_chain = { path = "../chain", version = "0.6.0", default-features = false }
bdk_chain = { path = "../chain", version = "0.6.0", features = [ "miniscript" ] }
electrum-client = { version = "0.18" }
#rustls = { version = "=0.21.1", optional = true, features = ["dangerous_configuration"] }

[dev-dependencies]
testenv = { path = "../testenv", version = "0.1.0", default-features = false }
electrsd = { version= "0.25.0", features = ["bitcoind_25_0", "esplora_a33e97e1", "legacy"] }
anyhow = "1"

0 comments on commit 6bb1a40

Please sign in to comment.