test(esplora): introduce test cases for update_local_chain
This commit is contained in:
parent
f05e8502e6
commit
6f824cf325
@ -1,15 +1,31 @@
|
|||||||
|
use bdk_chain::local_chain::LocalChain;
|
||||||
|
use bdk_chain::BlockId;
|
||||||
use bdk_esplora::EsploraExt;
|
use bdk_esplora::EsploraExt;
|
||||||
use electrsd::bitcoind::bitcoincore_rpc::RpcApi;
|
use electrsd::bitcoind::bitcoincore_rpc::RpcApi;
|
||||||
use electrsd::bitcoind::{self, anyhow, BitcoinD};
|
use electrsd::bitcoind::{self, anyhow, BitcoinD};
|
||||||
use electrsd::{Conf, ElectrsD};
|
use electrsd::{Conf, ElectrsD};
|
||||||
use esplora_client::{self, BlockingClient, Builder};
|
use esplora_client::{self, BlockingClient, Builder};
|
||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use bdk_chain::bitcoin::{Address, Amount, BlockHash, Txid};
|
use bdk_chain::bitcoin::{Address, Amount, BlockHash, Txid};
|
||||||
|
|
||||||
|
macro_rules! h {
|
||||||
|
($index:literal) => {{
|
||||||
|
bdk_chain::bitcoin::hashes::Hash::hash($index.as_bytes())
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! local_chain {
|
||||||
|
[ $(($height:expr, $block_hash:expr)), * ] => {{
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
bdk_chain::local_chain::LocalChain::from_blocks([$(($height, $block_hash).into()),*].into_iter().collect())
|
||||||
|
.expect("chain must have genesis block")
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
struct TestEnv {
|
struct TestEnv {
|
||||||
bitcoind: BitcoinD,
|
bitcoind: BitcoinD,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -39,6 +55,20 @@ impl TestEnv {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reset_electrsd(mut self) -> anyhow::Result<Self> {
|
||||||
|
let mut electrs_conf = Conf::default();
|
||||||
|
electrs_conf.http_enabled = true;
|
||||||
|
let electrs_exe =
|
||||||
|
electrsd::downloaded_exe_path().expect("electrs version feature must be enabled");
|
||||||
|
let electrsd = ElectrsD::with_conf(electrs_exe, &self.bitcoind, &electrs_conf)?;
|
||||||
|
|
||||||
|
let base_url = format!("http://{}", &electrsd.esplora_url.clone().unwrap());
|
||||||
|
let client = Builder::new(base_url.as_str()).build_blocking()?;
|
||||||
|
self.electrsd = electrsd;
|
||||||
|
self.client = client;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
fn mine_blocks(
|
fn mine_blocks(
|
||||||
&self,
|
&self,
|
||||||
count: usize,
|
count: usize,
|
||||||
@ -202,3 +232,161 @@ pub fn test_update_tx_graph_gap_limit() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn update_local_chain() -> anyhow::Result<()> {
|
||||||
|
const TIP_HEIGHT: u32 = 50;
|
||||||
|
|
||||||
|
let env = TestEnv::new()?;
|
||||||
|
let b = {
|
||||||
|
let bdc = &env.bitcoind.client;
|
||||||
|
assert_eq!(bdc.get_block_count()?, 1);
|
||||||
|
[(0, bdc.get_block_hash(0)?), (1, bdc.get_block_hash(1)?)]
|
||||||
|
.into_iter()
|
||||||
|
.chain((2..).zip(env.mine_blocks((TIP_HEIGHT - 1) as usize, None)?))
|
||||||
|
.collect::<BTreeMap<_, _>>()
|
||||||
|
};
|
||||||
|
// so new blocks can be seen by Electrs
|
||||||
|
let env = env.reset_electrsd()?;
|
||||||
|
|
||||||
|
struct TestCase {
|
||||||
|
name: &'static str,
|
||||||
|
chain: LocalChain,
|
||||||
|
heights: &'static [u32],
|
||||||
|
exp_update_heights: &'static [u32],
|
||||||
|
}
|
||||||
|
|
||||||
|
let test_cases = [
|
||||||
|
TestCase {
|
||||||
|
name: "request_later_blocks",
|
||||||
|
chain: local_chain![(0, b[&0]), (21, b[&21])],
|
||||||
|
heights: &[22, 25, 28],
|
||||||
|
exp_update_heights: &[21, 22, 25, 28],
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
name: "request_prev_blocks",
|
||||||
|
chain: local_chain![(0, b[&0]), (1, b[&1]), (5, b[&5])],
|
||||||
|
heights: &[4],
|
||||||
|
exp_update_heights: &[4, 5],
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
name: "request_prev_blocks_2",
|
||||||
|
chain: local_chain![(0, b[&0]), (1, b[&1]), (10, b[&10])],
|
||||||
|
heights: &[4, 6],
|
||||||
|
exp_update_heights: &[4, 6, 10],
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
name: "request_later_and_prev_blocks",
|
||||||
|
chain: local_chain![(0, b[&0]), (7, b[&7]), (11, b[&11])],
|
||||||
|
heights: &[8, 9, 15],
|
||||||
|
exp_update_heights: &[8, 9, 11, 15],
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
name: "request_tip_only",
|
||||||
|
chain: local_chain![(0, b[&0]), (5, b[&5]), (49, b[&49])],
|
||||||
|
heights: &[TIP_HEIGHT],
|
||||||
|
exp_update_heights: &[49],
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
name: "request_nothing",
|
||||||
|
chain: local_chain![(0, b[&0]), (13, b[&13]), (23, b[&23])],
|
||||||
|
heights: &[],
|
||||||
|
exp_update_heights: &[23],
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
name: "request_nothing_during_reorg",
|
||||||
|
chain: local_chain![(0, b[&0]), (13, b[&13]), (23, h!("23"))],
|
||||||
|
heights: &[],
|
||||||
|
exp_update_heights: &[13, 23],
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
name: "request_nothing_during_reorg_2",
|
||||||
|
chain: local_chain![(0, b[&0]), (21, b[&21]), (22, h!("22")), (23, h!("23"))],
|
||||||
|
heights: &[],
|
||||||
|
exp_update_heights: &[21, 22, 23],
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
name: "request_prev_blocks_during_reorg",
|
||||||
|
chain: local_chain![(0, b[&0]), (21, b[&21]), (22, h!("22")), (23, h!("23"))],
|
||||||
|
heights: &[17, 20],
|
||||||
|
exp_update_heights: &[17, 20, 21, 22, 23],
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
name: "request_later_blocks_during_reorg",
|
||||||
|
chain: local_chain![(0, b[&0]), (9, b[&9]), (22, h!("22")), (23, h!("23"))],
|
||||||
|
heights: &[25, 27],
|
||||||
|
exp_update_heights: &[9, 22, 23, 25, 27],
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
name: "request_later_blocks_during_reorg_2",
|
||||||
|
chain: local_chain![(0, b[&0]), (9, h!("9"))],
|
||||||
|
heights: &[10],
|
||||||
|
exp_update_heights: &[0, 9, 10],
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
name: "request_later_and_prev_blocks_during_reorg",
|
||||||
|
chain: local_chain![(0, b[&0]), (1, b[&1]), (9, h!("9"))],
|
||||||
|
heights: &[8, 11],
|
||||||
|
exp_update_heights: &[1, 8, 9, 11],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (i, t) in test_cases.into_iter().enumerate() {
|
||||||
|
println!("Case {}: {}", i, t.name);
|
||||||
|
let mut chain = t.chain;
|
||||||
|
|
||||||
|
let update = env
|
||||||
|
.client
|
||||||
|
.update_local_chain(chain.tip(), t.heights.iter().copied())
|
||||||
|
.map_err(|err| {
|
||||||
|
anyhow::format_err!("[{}:{}] `update_local_chain` failed: {}", i, t.name, err)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let update_blocks = update
|
||||||
|
.tip
|
||||||
|
.iter()
|
||||||
|
.map(|cp| cp.block_id())
|
||||||
|
.collect::<BTreeSet<_>>();
|
||||||
|
|
||||||
|
let exp_update_blocks = t
|
||||||
|
.exp_update_heights
|
||||||
|
.iter()
|
||||||
|
.map(|&height| {
|
||||||
|
let hash = b[&height];
|
||||||
|
BlockId { height, hash }
|
||||||
|
})
|
||||||
|
.chain(
|
||||||
|
// Electrs Esplora `get_block` call fetches 10 blocks which is included in the
|
||||||
|
// update
|
||||||
|
b.range(TIP_HEIGHT - 9..)
|
||||||
|
.map(|(&height, &hash)| BlockId { height, hash }),
|
||||||
|
)
|
||||||
|
.collect::<BTreeSet<_>>();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
update_blocks, exp_update_blocks,
|
||||||
|
"[{}:{}] unexpected update",
|
||||||
|
i, t.name
|
||||||
|
);
|
||||||
|
|
||||||
|
let _ = chain
|
||||||
|
.apply_update(update)
|
||||||
|
.unwrap_or_else(|err| panic!("[{}:{}] update failed to apply: {}", i, t.name, err));
|
||||||
|
|
||||||
|
// all requested heights must exist in the final chain
|
||||||
|
for height in t.heights {
|
||||||
|
let exp_blockhash = b.get(height).expect("block must exist in bitcoind");
|
||||||
|
assert_eq!(
|
||||||
|
chain.blocks().get(height),
|
||||||
|
Some(exp_blockhash),
|
||||||
|
"[{}:{}] block {}:{} must exist in final chain",
|
||||||
|
i,
|
||||||
|
t.name,
|
||||||
|
height,
|
||||||
|
exp_blockhash
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user