use std::borrow::Cow;
use bitcoin::hashes::{Hash, HashEngine};
use derive_more::Deref;
use crate::BlkMetadata;
use super::{BlockHash, Height};
#[derive(Debug)]
struct RawBlockData {
bytes: Vec<u8>,
tx_offsets: Vec<u32>,
}
#[derive(Debug, Deref)]
pub struct Block {
height: Height,
hash: BlockHash,
#[deref]
block: bitcoin::Block,
raw: Option<RawBlockData>,
}
impl Block {
pub fn height(&self) -> Height {
self.height
}
pub fn hash(&self) -> &BlockHash {
&self.hash
}
pub fn total_size_and_weight(&self) -> (usize, usize) {
let overhead =
bitcoin::block::Header::SIZE + bitcoin::VarInt::from(self.txdata.len()).size();
let mut total_size = overhead;
let mut weight_wu = overhead * 4;
for (i, tx) in self.txdata.iter().enumerate() {
let base = tx.base_size();
let total = self
.raw_tx_bytes(i)
.map_or_else(|| tx.total_size(), |raw| raw.len());
total_size += total;
weight_wu += base * 3 + total;
}
(total_size, weight_wu)
}
pub fn set_raw_data(&mut self, bytes: Vec<u8>, tx_offsets: Vec<u32>) {
self.raw = Some(RawBlockData { bytes, tx_offsets });
}
pub fn compute_tx_id_and_sizes(&self, index: usize) -> (bitcoin::Txid, u32, u32) {
let tx = &self.txdata[index];
if let Some(raw) = self.raw_tx_bytes(index) {
let total_size = raw.len() as u32;
let is_segwit = raw[4] == 0x00;
let base_size = if is_segwit {
tx.base_size() as u32
} else {
total_size
};
let txid = Self::hash_raw_tx(raw, base_size);
debug_assert_eq!(txid, tx.compute_txid(), "raw txid mismatch at tx {index}");
(txid, base_size, total_size)
} else {
(
tx.compute_txid(),
tx.base_size() as u32,
tx.total_size() as u32,
)
}
}
fn raw_tx_bytes(&self, index: usize) -> Option<&[u8]> {
let raw = self.raw.as_ref()?;
let start = raw.tx_offsets[index] as usize;
let end = raw
.tx_offsets
.get(index + 1)
.map_or(raw.bytes.len(), |&off| off as usize);
Some(&raw.bytes[start..end])
}
fn hash_raw_tx(raw: &[u8], base_size: u32) -> bitcoin::Txid {
let mut engine = bitcoin::Txid::engine();
if raw[4] == 0x00 {
let io_len = base_size as usize - 8;
engine.input(&raw[..4]);
engine.input(&raw[6..6 + io_len]);
engine.input(&raw[raw.len() - 4..]);
} else {
engine.input(raw);
}
bitcoin::Txid::from_engine(engine)
}
pub fn coinbase_tag(&self) -> Cow<'_, str> {
String::from_utf8_lossy(
self.txdata
.first()
.and_then(|tx| tx.input.first())
.unwrap()
.script_sig
.as_bytes(),
)
}
}
impl From<(Height, bitcoin::Block)> for Block {
#[inline]
fn from((height, block): (Height, bitcoin::Block)) -> Self {
Self::from((height, block.block_hash(), block))
}
}
impl From<(Height, bitcoin::BlockHash, bitcoin::Block)> for Block {
#[inline]
fn from((height, hash, block): (Height, bitcoin::BlockHash, bitcoin::Block)) -> Self {
Self::from((height, BlockHash::from(hash), block))
}
}
impl From<(Height, BlockHash, bitcoin::Block)> for Block {
#[inline]
fn from((height, hash, block): (Height, BlockHash, bitcoin::Block)) -> Self {
Self {
height,
hash,
block,
raw: None,
}
}
}
impl From<ReadBlock> for Block {
#[inline]
fn from(value: ReadBlock) -> Self {
value.block
}
}
#[derive(Debug, Deref)]
pub struct ReadBlock {
#[deref]
block: Block,
metadata: BlkMetadata,
tx_metadata: Vec<BlkMetadata>,
}
impl From<(Block, BlkMetadata, Vec<BlkMetadata>)> for ReadBlock {
#[inline]
fn from((block, metadata, tx_metadata): (Block, BlkMetadata, Vec<BlkMetadata>)) -> Self {
Self {
block,
metadata,
tx_metadata,
}
}
}
impl ReadBlock {
pub fn metadata(&self) -> &BlkMetadata {
&self.metadata
}
pub fn tx_metadata(&self) -> &Vec<BlkMetadata> {
&self.tx_metadata
}
pub fn inner(self) -> Block {
self.block
}
}