#![cfg(feature = "miniscript")]
+use std::collections::BTreeMap;
use std::ops::{Bound, RangeBounds};
use bdk_chain::{
}
}
+#[test]
+fn local_chain_insert_header() {
+ fn header(prev_blockhash: BlockHash) -> Header {
+ Header {
+ version: bitcoin::block::Version::default(),
+ prev_blockhash,
+ merkle_root: bitcoin::hash_types::TxMerkleNode::all_zeros(),
+ time: 0,
+ bits: bitcoin::CompactTarget::default(),
+ nonce: 0,
+ }
+ }
+
+ // Create consecutive headers of height `n`, where the genesis header is height 0.
+ fn build_headers(n: u32) -> Vec<Header> {
+ let mut headers = Vec::new();
+ let genesis = header(hash!("_"));
+ headers.push(genesis);
+ for i in 1..=n {
+ let prev = headers[(i - 1) as usize].block_hash();
+ headers.push(header(prev));
+ }
+ headers
+ }
+
+ let headers = build_headers(5);
+
+ fn local_chain(data: Vec<(u32, Header)>) -> LocalChain<Header> {
+ bdk_chain::local_chain::LocalChain::from_blocks(
+ data.into_iter().collect::<BTreeMap<_, _>>(),
+ )
+ .expect("chain must have genesis block")
+ }
+
+ struct TestCase {
+ original: LocalChain<Header>,
+ insert: (u32, Header),
+ expected_result: Result<ChangeSet<Header>, AlterCheckPointError>,
+ expected_final: LocalChain<Header>,
+ }
+
+ let test_cases = [
+ // Test case 1: start with only the genesis header and insert header at height 5.
+ TestCase {
+ original: local_chain(vec![(0, headers[0])]),
+ insert: (5, headers[5]),
+ expected_result: Ok([(5, Some(headers[5]))].into()),
+ expected_final: local_chain(vec![(0, headers[0]), (5, headers[5])]),
+ },
+ // Test case 2: start with headers at heights 0 and 3. Insert header at height 4.
+ TestCase {
+ original: local_chain(vec![(0, headers[0]), (3, headers[3])]),
+ insert: (4, headers[4]),
+ expected_result: Ok([(4, Some(headers[4]))].into()),
+ expected_final: local_chain(vec![(0, headers[0]), (3, headers[3]), (4, headers[4])]),
+ },
+ // Test case 3: start with headers at heights 0 and 4. Insert header at height 3.
+ TestCase {
+ original: local_chain(vec![(0, headers[0]), (4, headers[4])]),
+ insert: (3, headers[3]),
+ expected_result: Ok([(3, Some(headers[3]))].into()),
+ expected_final: local_chain(vec![(0, headers[0]), (3, headers[3]), (4, headers[4])]),
+ },
+ // Test case 4: start with headers at heights 0 and 2. Insert the same header at height 2.
+ TestCase {
+ original: local_chain(vec![(0, headers[0]), (2, headers[2])]),
+ insert: (2, headers[2]),
+ expected_result: Ok([].into()),
+ expected_final: local_chain(vec![(0, headers[0]), (2, headers[2])]),
+ },
+ // Test case 5: start with headers at heights 0 and 2. Insert conflicting header at height
+ // 2.
+ TestCase {
+ original: local_chain(vec![(0, headers[0]), (2, headers[2])]),
+ insert: (2, header(hash!("conflict"))),
+ expected_result: Err(AlterCheckPointError {
+ height: 2,
+ original_hash: headers[2].block_hash(),
+ update_hash: Some(header(hash!("conflict")).block_hash()),
+ }),
+ expected_final: local_chain(vec![(0, headers[0]), (2, headers[2])]),
+ },
+ ];
+
+ for (i, t) in test_cases.into_iter().enumerate() {
+ let mut chain = t.original;
+ assert_eq!(
+ chain.insert_block(t.insert.0, t.insert.1),
+ t.expected_result,
+ "[{i}] unexpected result when inserting block",
+ );
+ assert_eq!(chain, t.expected_final, "[{i}] unexpected final chain",);
+ }
+}
+
#[test]
fn local_chain_disconnect_from() {
struct TestCase {