use anyhow::{bail, Result};
use bitcoin::{
hashes::{sha256d, Hash},
BlockHash,
};
pub struct CfHeaderChain {
pub tip_height: u32,
pub tip_hash: BlockHash,
}
impl CfHeaderChain {
pub fn new_from_store(prev: Option<(u32, BlockHash)>) -> Self {
match prev {
Some((h, hh)) if h > 0 => Self {
tip_height: h,
tip_hash: hh,
},
_ => Self {
tip_height: 0,
tip_hash: BlockHash::all_zeros(),
},
}
}
pub fn apply_batch(
&mut self,
start_height: u32,
headers: &[[u8; 32]],
checkpoints: &[(u32, BlockHash)],
) -> Result<()> {
let expected = self.tip_height.saturating_add(1);
if start_height != expected {
bail!("cfheaders batch start mismatch: got {start_height}, expected {expected}");
}
let mut rolling = self.tip_hash;
for (i, fh_bytes) in headers.iter().enumerate() {
let h = start_height + i as u32;
let cur = {
let mut data = Vec::with_capacity(64);
data.extend_from_slice(rolling.as_ref()); data.extend_from_slice(fh_bytes); let d = sha256d::Hash::hash(&data);
BlockHash::from_byte_array(*d.as_ref())
};
if let Some((_, chk)) = checkpoints.iter().find(|(hh, _)| *hh == h) {
if &cur != chk {
bail!("cfheaders checkpoint mismatch @{}!", h);
}
}
rolling = cur;
self.tip_height = h;
self.tip_hash = rolling;
}
Ok(())
}
}