From f001fd45ad55ec8b9108214d2e41b2a7973e8ba9 Mon Sep 17 00:00:00 2001 From: valued mammal <valuedmammal@protonmail.com> Date: Sun, 15 Sep 2024 17:42:16 -0400 Subject: [PATCH] test(rpc): add bip158 tests --- crates/bitcoind_rpc/tests/bip158.rs | 114 ++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 crates/bitcoind_rpc/tests/bip158.rs diff --git a/crates/bitcoind_rpc/tests/bip158.rs b/crates/bitcoind_rpc/tests/bip158.rs new file mode 100644 index 000000000..ed0cb0c45 --- /dev/null +++ b/crates/bitcoind_rpc/tests/bip158.rs @@ -0,0 +1,114 @@ +use bitcoin::{constants, Network}; + +use bdk_bitcoind_rpc::bip158::FilterIter; +use bdk_core::{BlockId, CheckPoint}; +use bdk_testenv::{anyhow, bitcoind, TestEnv}; +use bitcoincore_rpc::RpcApi; + +macro_rules! block_id { + ($height:expr, $hash:literal) => {{ + BlockId { + height: $height, + hash: bdk_core::bitcoin::hashes::Hash::hash($hash.as_bytes()), + } + }}; +} + +// Test the result of `chain_update` given a local checkpoint. +// +// new blocks +// 2--3--4--5--6--7--8--9--10--11 +// +// case 1: base below new blocks +// 0- +// case 2: base overlaps with new blocks +// 0--1--2--3--4 +// case 3: stale tip (with overlap) +// 0--1--2--3--4--x +// case 4: stale tip (no overlap) +// 0--x +#[test] +fn get_tip_and_chain_update() -> anyhow::Result<()> { + let mut conf = bitcoind::Conf::default(); + conf.args.push("-blockfilterindex=1"); + conf.args.push("-peerblockfilters=1"); + let env = TestEnv::new_with_config(bdk_testenv::Config { + bitcoind: conf, + ..Default::default() + })?; + + // Start by mining ten blocks + let hash = env.rpc_client().get_best_block_hash()?; + let header = env.rpc_client().get_block_header_info(&hash)?; + assert_eq!(header.height, 1); + let block_1 = BlockId { + height: header.height as u32, + hash, + }; + + let genesis_hash = constants::genesis_block(Network::Regtest).block_hash(); + let genesis = BlockId { + height: 0, + hash: genesis_hash, + }; + + // `FilterIter` will try to return up to ten recent blocks + // so we keep them for reference + let new_blocks: Vec<BlockId> = (2..=11) + .zip(env.mine_blocks(10, None)?) + .map(BlockId::from) + .collect(); + + struct TestCase { + // name + name: &'static str, + // local blocks + chain: Vec<BlockId>, + // expected blocks + exp: Vec<BlockId>, + } + + // For each test we create a new `FilterIter` with the checkpoint given + // by the blocks in the test chain. Then we sync to the remote tip and + // check the blocks that are returned in the chain update. + [ + TestCase { + name: "point of agreement below new blocks, expect base + new", + chain: vec![genesis, block_1], + exp: [block_1].into_iter().chain(new_blocks.clone()).collect(), + }, + TestCase { + name: "point of agreement genesis, expect base + new", + chain: vec![genesis], + exp: [genesis].into_iter().chain(new_blocks.clone()).collect(), + }, + TestCase { + name: "point of agreement within new blocks, expect base + remaining", + chain: new_blocks[..=2].to_vec(), + exp: new_blocks[2..].to_vec(), + }, + TestCase { + name: "stale tip within new blocks, expect base + corrected + remaining", + // base height: 4, stale height: 5 + chain: vec![new_blocks[2], block_id!(5, "E")], + exp: new_blocks[2..].to_vec(), + }, + TestCase { + name: "stale tip below new blocks, expect base + corrected + new", + chain: vec![genesis, block_id!(1, "A")], + exp: [genesis, block_1].into_iter().chain(new_blocks).collect(), + }, + ] + .into_iter() + .for_each(|test| { + let cp = CheckPoint::from_block_ids(test.chain).unwrap(); + let mut it = FilterIter::new_with_checkpoint(env.rpc_client(), cp); + let _ = it.get_tip().unwrap(); + let update_cp = it.chain_update().unwrap().unwrap(); + let mut update_blocks: Vec<_> = update_cp.iter().map(|cp| cp.block_id()).collect(); + update_blocks.reverse(); + assert_eq!(update_blocks, test.exp, "{}", test.name); + }); + + Ok(()) +}