From 9971fda5367cfa69917445a11c4b8c9d6de3f713 Mon Sep 17 00:00:00 2001 From: valued mammal Date: Wed, 14 Jan 2026 21:16:57 -0500 Subject: [PATCH] test(chain): Test `apply_update` with a single `CheckPoint
` --- crates/chain/tests/test_local_chain.rs | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/crates/chain/tests/test_local_chain.rs b/crates/chain/tests/test_local_chain.rs index 8f27dc93..33f64073 100644 --- a/crates/chain/tests/test_local_chain.rs +++ b/crates/chain/tests/test_local_chain.rs @@ -11,6 +11,7 @@ use bdk_chain::{ BlockId, }; use bdk_testenv::{chain_update, hash, local_chain}; +use bitcoin::consensus::encode::deserialize_hex; use bitcoin::{block::Header, hashes::Hash, BlockHash}; use proptest::prelude::*; @@ -553,6 +554,53 @@ fn local_chain_disconnect_from() { } } +// Test that `apply_update` can connect 1 `Header` at a time +// and fails if a `prev_blockhash` conflict is detected. +#[test] +fn test_apply_update_single_header() { + let headers: Vec
= [ + "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff7f2002000000", + "0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f1c96cc459dbb0c7bbc722af14f913da868779290ad48ff87ee314ebb8ae08f384b166069ffff7f2001000000", + "0000002078ce518c7dcfd99ad5859c35bd2be15794c0e5dc8e60c1fea3b0461c45da181ca80f828504f88c645ea46cfcf93156269807e3bd409e1317271a1546d238e9b24c166069ffff7f2001000000", + "000000200dfd6c5af6ea3cb341a08db344f93743d94b630d122867faff855f6310e58864e6e0c859fda703d66d051b86f16f09b0cead182f3cbe13767cd91b6371ec252c4c166069ffff7f2000000000", + ] + .into_iter() + .map(|s| deserialize_hex::
(s).expect("failed to deserialize header")) + .collect(); + + let header_0 = headers[0]; + let header_1 = headers[1]; + let header_2 = headers[2]; + let header_3 = headers[3]; + + let (mut chain, _) = LocalChain::from_genesis(header_0); + + // Apply 1 `CheckPoint
` at a time + for (height, header) in (1..).zip([header_1, header_2, header_3]) { + let changeset = chain.apply_update(CheckPoint::new(height, header)).unwrap(); + assert_eq!( + changeset, + ChangeSet { + blocks: [(height, Some(headers[height as usize]))].into() + }, + ); + } + assert_eq!(chain.tip().iter().count(), 4); + for height in 0..4 { + assert!(chain + .get(height) + .is_some_and(|cp| cp.hash() == headers[height as usize].block_hash())) + } + + // `apply_update` should error if update does not connect. + // Reset chain for error test. + chain = LocalChain::from_blocks([(0, header_0), (1, header_1)].into()).unwrap(); + let mut header_2_alt = header_2; + header_2_alt.prev_blockhash = hash!("header_1_new"); + let result = chain.apply_update(CheckPoint::new(2, header_2_alt)); + assert!(result.is_err(), "Failed to detect prev_blockhash conflict"); +} + #[test] fn checkpoint_from_block_ids() { struct TestCase<'a> { -- 2.49.0