use std::collections::HashMap;
use anyhow::{Context, Result};
use bitcoin_hashes::{hex::ToHex, Hash};
use bitcoincash::consensus::{deserialize, serialize};
use futures_util::StreamExt;
use crate::{
chaindef::{BlockHash, BlockHeader},
store::{db_decode, db_encode, DBStore, Row},
};
use super::DBRow;
const HEADERINDEX_CF: &str = "header";
#[derive(serde::Serialize, serde::Deserialize)]
pub struct HeaderRow {
header: Vec<u8>,
}
impl HeaderRow {
pub fn new(header: &BlockHeader) -> Self {
Self {
header: serialize(&header),
}
}
pub(crate) async fn read_headers_unsorted(store: &DBStore) -> Vec<BlockHeader> {
store
.iter(HEADERINDEX_CF, false)
.await
.map(|r| Self::from_row(&r))
.map(|h| h.header())
.collect()
.await
}
pub(crate) async fn headers_up_to_tip(
store: &DBStore,
tip_hash: &BlockHash,
) -> Result<Vec<BlockHeader>> {
info!("Building header chain");
let mut headers: HashMap<BlockHash, BlockHeader> = Self::read_headers_unsorted(store)
.await
.into_iter()
.map(|h| (h.block_hash(), h))
.collect();
let tip = headers.remove(tip_hash).context(anyhow!(
"Tip {} not found in header database",
tip_hash.to_hex()
))?;
let mut header_chain: Vec<BlockHeader> = Vec::with_capacity(headers.len());
header_chain.push(tip);
loop {
let parent_header = header_chain.last().unwrap();
let prev = &parent_header.prev_blockhash;
if prev == &BlockHash::all_zeros() {
break;
}
let prev = headers
.remove(prev)
.context(anyhow!("Header {} missing from database", prev.to_hex()))?;
header_chain.push(prev);
}
header_chain.reverse();
info!("Done building chain");
Ok(header_chain)
}
pub(crate) fn header(&self) -> BlockHeader {
deserialize(&self.header).expect("failed to decode header")
}
}
impl DBRow for HeaderRow {
const CF: &'static str = HEADERINDEX_CF;
fn to_row(&self) -> Row {
Row {
key: db_encode(&self).unwrap().into_boxed_slice(),
value: vec![].into_boxed_slice(),
}
}
fn from_row(row: &Row) -> HeaderRow {
db_decode(&row.key).expect("failed to parse HeaderRow")
}
}