1use std::borrow::Cow;
2
3use bitcoin::hashes::{Hash, HashEngine};
4use derive_more::Deref;
5
6use crate::BlkMetadata;
7
8use super::{BlockHash, Height};
9
10#[derive(Debug)]
13struct RawBlockData {
14 bytes: Vec<u8>,
15 tx_offsets: Vec<u32>,
17}
18
19#[derive(Debug, Deref)]
20pub struct Block {
21 height: Height,
22 hash: BlockHash,
23 #[deref]
24 block: bitcoin::Block,
25 raw: Option<RawBlockData>,
26}
27
28impl Block {
29 pub fn height(&self) -> Height {
30 self.height
31 }
32
33 pub fn hash(&self) -> &BlockHash {
34 &self.hash
35 }
36
37 pub fn total_size_and_weight(&self) -> (usize, usize) {
41 let overhead =
42 bitcoin::block::Header::SIZE + bitcoin::VarInt::from(self.txdata.len()).size();
43 let mut total_size = overhead;
44 let mut weight_wu = overhead * 4;
45 for (i, tx) in self.txdata.iter().enumerate() {
46 let base = tx.base_size();
47 let total = self
48 .raw_tx_bytes(i)
49 .map_or_else(|| tx.total_size(), |raw| raw.len());
50 total_size += total;
51 weight_wu += base * 3 + total;
52 }
53 (total_size, weight_wu)
54 }
55
56 pub fn set_raw_data(&mut self, bytes: Vec<u8>, tx_offsets: Vec<u32>) {
57 self.raw = Some(RawBlockData { bytes, tx_offsets });
58 }
59
60 pub fn compute_tx_id_and_sizes(&self, index: usize) -> (bitcoin::Txid, u32, u32) {
63 let tx = &self.txdata[index];
64 if let Some(raw) = self.raw_tx_bytes(index) {
65 let total_size = raw.len() as u32;
66 let is_segwit = raw[4] == 0x00;
67 let base_size = if is_segwit {
68 tx.base_size() as u32
69 } else {
70 total_size
71 };
72 let txid = Self::hash_raw_tx(raw, base_size);
73 debug_assert_eq!(txid, tx.compute_txid(), "raw txid mismatch at tx {index}");
74 (txid, base_size, total_size)
75 } else {
76 (
77 tx.compute_txid(),
78 tx.base_size() as u32,
79 tx.total_size() as u32,
80 )
81 }
82 }
83
84 fn raw_tx_bytes(&self, index: usize) -> Option<&[u8]> {
86 let raw = self.raw.as_ref()?;
87 let start = raw.tx_offsets[index] as usize;
88 let end = raw
89 .tx_offsets
90 .get(index + 1)
91 .map_or(raw.bytes.len(), |&off| off as usize);
92 Some(&raw.bytes[start..end])
93 }
94
95 fn hash_raw_tx(raw: &[u8], base_size: u32) -> bitcoin::Txid {
101 let mut engine = bitcoin::Txid::engine();
102 if raw[4] == 0x00 {
103 let io_len = base_size as usize - 8;
104 engine.input(&raw[..4]);
105 engine.input(&raw[6..6 + io_len]);
106 engine.input(&raw[raw.len() - 4..]);
107 } else {
108 engine.input(raw);
109 }
110 bitcoin::Txid::from_engine(engine)
111 }
112
113 pub fn coinbase_tag(&self) -> Cow<'_, str> {
114 String::from_utf8_lossy(
115 self.txdata
116 .first()
117 .and_then(|tx| tx.input.first())
118 .unwrap()
119 .script_sig
120 .as_bytes(),
121 )
122 }
123}
124
125impl From<(Height, bitcoin::Block)> for Block {
126 #[inline]
127 fn from((height, block): (Height, bitcoin::Block)) -> Self {
128 Self::from((height, block.block_hash(), block))
129 }
130}
131
132impl From<(Height, bitcoin::BlockHash, bitcoin::Block)> for Block {
133 #[inline]
134 fn from((height, hash, block): (Height, bitcoin::BlockHash, bitcoin::Block)) -> Self {
135 Self::from((height, BlockHash::from(hash), block))
136 }
137}
138
139impl From<(Height, BlockHash, bitcoin::Block)> for Block {
140 #[inline]
141 fn from((height, hash, block): (Height, BlockHash, bitcoin::Block)) -> Self {
142 Self {
143 height,
144 hash,
145 block,
146 raw: None,
147 }
148 }
149}
150
151impl From<ReadBlock> for Block {
152 #[inline]
153 fn from(value: ReadBlock) -> Self {
154 value.block
155 }
156}
157
158#[derive(Debug, Deref)]
159pub struct ReadBlock {
160 #[deref]
161 block: Block,
162 metadata: BlkMetadata,
163 tx_metadata: Vec<BlkMetadata>,
164}
165
166impl From<(Block, BlkMetadata, Vec<BlkMetadata>)> for ReadBlock {
167 #[inline]
168 fn from((block, metadata, tx_metadata): (Block, BlkMetadata, Vec<BlkMetadata>)) -> Self {
169 Self {
170 block,
171 metadata,
172 tx_metadata,
173 }
174 }
175}
176
177impl ReadBlock {
178 pub fn metadata(&self) -> &BlkMetadata {
179 &self.metadata
180 }
181
182 pub fn tx_metadata(&self) -> &Vec<BlkMetadata> {
183 &self.tx_metadata
184 }
185
186 pub fn inner(self) -> Block {
187 self.block
188 }
189}