titan_types/
block.rs

1use {
2    crate::{rune_id::RuneId, SerializedTxid},
3    bitcoin::{
4        block::{Header, Version},
5        hashes::Hash,
6        BlockHash, CompactTarget, TxMerkleNode,
7    },
8    borsh::{BorshDeserialize, BorshSerialize},
9    serde::{Deserialize, Serialize},
10    std::io::{Read, Result, Write},
11};
12
13#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
14pub struct Block {
15    pub height: u64,
16    pub header: Header,
17    pub tx_ids: Vec<SerializedTxid>,
18    pub etched_runes: Vec<RuneId>,
19}
20
21impl BorshSerialize for Block {
22    fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
23        // 1) Serialize `height`
24        BorshSerialize::serialize(&self.height, writer)?;
25
26        // 2) Serialize `header` manually
27        BorshSerialize::serialize(&self.header.version.to_consensus(), writer)?;
28
29        // `as_raw_hash()` returns a &sha256d::Hash, which we can then convert
30        // into a 32-byte array. That array is what Borsh will see.
31        BorshSerialize::serialize(
32            &self.header.prev_blockhash.as_raw_hash().as_byte_array(),
33            writer,
34        )?;
35
36        BorshSerialize::serialize(
37            &self.header.merkle_root.as_raw_hash().as_byte_array(),
38            writer,
39        )?;
40        BorshSerialize::serialize(&self.header.time, writer)?;
41        BorshSerialize::serialize(&self.header.bits.to_consensus(), writer)?;
42        BorshSerialize::serialize(&self.header.nonce, writer)?;
43
44        // 3) Serialize `tx_ids` as a Vec<SerializedTxid>
45        //    (Vec<SerializedTxid> already implements BorshSerialize)
46        BorshSerialize::serialize(&self.tx_ids, writer)?;
47
48        // 3) Serialize `etched_runes` manually:
49        //    Borsh doesn't know about `RuneId`, so we store it ourselves:
50        //
51        //    - First, write the length of the vector
52        //    - Then for each `RuneId`, write out (block, tx)
53
54        let etched_len = self.etched_runes.len() as u64;
55        BorshSerialize::serialize(&etched_len, writer)?;
56
57        for rune_id in &self.etched_runes {
58            BorshSerialize::serialize(&rune_id.block, writer)?;
59            BorshSerialize::serialize(&rune_id.tx, writer)?;
60        }
61
62        Ok(())
63    }
64}
65
66impl BorshDeserialize for Block {
67    fn deserialize_reader<R: Read>(reader: &mut R) -> std::io::Result<Self> {
68        // 1) Deserialize `height`
69        let height = u64::deserialize_reader(reader)?;
70
71        // 2) Deserialize `header` manually
72        let version = Version::from_consensus(i32::deserialize_reader(reader)?);
73
74        let mut prev_hash = [0u8; 32];
75        reader.read_exact(&mut prev_hash)?;
76        let prev_blockhash =
77            BlockHash::from_raw_hash(Hash::from_slice(&prev_hash).map_err(|_| {
78                std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid prev_blockhash")
79            })?);
80
81        let mut merkle = [0u8; 32];
82        reader.read_exact(&mut merkle)?;
83        let merkle_root = TxMerkleNode::from_raw_hash(Hash::from_slice(&merkle).map_err(|_| {
84            std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid merkle_root")
85        })?);
86
87        let time = u32::deserialize_reader(reader)?;
88        let bits = CompactTarget::from_consensus(u32::deserialize_reader(reader)?);
89        let nonce = u32::deserialize_reader(reader)?;
90
91        let header = Header {
92            version,
93            prev_blockhash,
94            merkle_root,
95            time,
96            bits,
97            nonce,
98        };
99
100        // 3) Deserialize `tx_ids` (Vec<String>)
101        let tx_ids = Vec::<SerializedTxid>::deserialize_reader(reader)?;
102
103        // 3) Deserialize `etched_runes` manually:
104        //    - Read the length
105        //    - For each entry, read `block` (u64) then `tx` (u32)
106
107        let etched_len = u64::deserialize_reader(reader)?;
108        let mut etched_runes = Vec::with_capacity(etched_len as usize);
109
110        for _ in 0..etched_len {
111            let block = u64::deserialize_reader(reader)?;
112            let tx = u32::deserialize_reader(reader)?;
113            etched_runes.push(RuneId::new(block, tx));
114        }
115
116        Ok(Self {
117            height,
118            header,
119            tx_ids,
120            etched_runes,
121        })
122    }
123}
124
125impl Block {
126    pub fn empty_block(height: u64, header: Header) -> Self {
127        Self {
128            height,
129            header,
130            tx_ids: vec![],
131            etched_runes: vec![],
132        }
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139    use crate::rune_id::RuneId;
140    use bitcoin::{block::Version, hashes::Hash, BlockHash, CompactTarget, TxMerkleNode};
141    use borsh::BorshDeserialize;
142
143    /// Helper function to create a sample Header for testing
144    fn create_test_header() -> Header {
145        Header {
146            version: Version::from_consensus(1),
147            prev_blockhash: BlockHash::from_raw_hash(Hash::from_slice(&[1u8; 32]).unwrap()),
148            merkle_root: TxMerkleNode::from_raw_hash(Hash::from_slice(&[2u8; 32]).unwrap()),
149            time: 1640995200, // 2022-01-01 00:00:00 UTC
150            bits: CompactTarget::from_consensus(0x1d00ffff),
151            nonce: 42,
152        }
153    }
154
155    /// Helper function to create test SerializedTxids
156    fn create_test_txids() -> Vec<SerializedTxid> {
157        vec![
158            SerializedTxid::from([3u8; 32]),
159            SerializedTxid::from([4u8; 32]),
160            SerializedTxid::from([5u8; 32]),
161        ]
162    }
163
164    /// Helper function to create test RuneIds
165    fn create_test_rune_ids() -> Vec<RuneId> {
166        vec![
167            RuneId::new(100, 0),
168            RuneId::new(101, 1),
169            RuneId::new(102, 2),
170        ]
171    }
172
173    #[test]
174    fn test_empty_block_serialization_roundtrip() {
175        let header = create_test_header();
176        let block = Block::empty_block(12345, header.clone());
177
178        // Serialize
179        let serialized = borsh::to_vec(&block).expect("Failed to serialize empty block");
180
181        // Deserialize
182        let deserialized =
183            Block::try_from_slice(&serialized).expect("Failed to deserialize empty block");
184
185        // Verify all fields match
186        assert_eq!(block.height, deserialized.height);
187        assert_eq!(block.header, deserialized.header);
188        assert_eq!(block.tx_ids, deserialized.tx_ids);
189        assert_eq!(block.etched_runes, deserialized.etched_runes);
190        assert_eq!(block, deserialized);
191    }
192
193    #[test]
194    fn test_full_block_serialization_roundtrip() {
195        let header = create_test_header();
196        let tx_ids = create_test_txids();
197        let etched_runes = create_test_rune_ids();
198
199        let block = Block {
200            height: 67890,
201            header: header.clone(),
202            tx_ids: tx_ids.clone(),
203            etched_runes: etched_runes.clone(),
204        };
205
206        // Serialize
207        let serialized = borsh::to_vec(&block).expect("Failed to serialize full block");
208
209        // Deserialize
210        let deserialized =
211            Block::try_from_slice(&serialized).expect("Failed to deserialize full block");
212
213        // Verify all fields match
214        assert_eq!(block.height, deserialized.height);
215        assert_eq!(block.header, deserialized.header);
216        assert_eq!(block.tx_ids, deserialized.tx_ids);
217        assert_eq!(block.etched_runes, deserialized.etched_runes);
218        assert_eq!(block, deserialized);
219    }
220
221    #[test]
222    fn test_block_with_single_tx_serialization() {
223        let header = create_test_header();
224        let tx_ids = vec![SerializedTxid::from([6u8; 32])];
225
226        let block = Block {
227            height: 1,
228            header: header.clone(),
229            tx_ids: tx_ids.clone(),
230            etched_runes: vec![],
231        };
232
233        // Serialize
234        let serialized = borsh::to_vec(&block).expect("Failed to serialize single tx block");
235
236        // Deserialize
237        let deserialized =
238            Block::try_from_slice(&serialized).expect("Failed to deserialize single tx block");
239
240        assert_eq!(block, deserialized);
241        assert_eq!(deserialized.tx_ids.len(), 1);
242        assert_eq!(deserialized.etched_runes.len(), 0);
243    }
244
245    #[test]
246    fn test_block_with_single_rune_serialization() {
247        let header = create_test_header();
248        let etched_runes = vec![RuneId::new(999, 888)];
249
250        let block = Block {
251            height: 2,
252            header: header.clone(),
253            tx_ids: vec![],
254            etched_runes: etched_runes.clone(),
255        };
256
257        // Serialize
258        let serialized = borsh::to_vec(&block).expect("Failed to serialize single rune block");
259
260        // Deserialize
261        let deserialized =
262            Block::try_from_slice(&serialized).expect("Failed to deserialize single rune block");
263
264        assert_eq!(block, deserialized);
265        assert_eq!(deserialized.tx_ids.len(), 0);
266        assert_eq!(deserialized.etched_runes.len(), 1);
267        assert_eq!(deserialized.etched_runes[0].block, 999);
268        assert_eq!(deserialized.etched_runes[0].tx, 888);
269    }
270
271    #[test]
272    fn test_block_with_edge_case_values() {
273        let header = Header {
274            version: Version::from_consensus(i32::MAX),
275            prev_blockhash: BlockHash::from_raw_hash(Hash::from_slice(&[0u8; 32]).unwrap()),
276            merkle_root: TxMerkleNode::from_raw_hash(Hash::from_slice(&[255u8; 32]).unwrap()),
277            time: u32::MAX,
278            bits: CompactTarget::from_consensus(u32::MAX),
279            nonce: u32::MAX,
280        };
281
282        let block = Block {
283            height: u64::MAX,
284            header: header.clone(),
285            tx_ids: vec![SerializedTxid::all_zeros()],
286            etched_runes: vec![RuneId::new(u64::MAX, u32::MAX)],
287        };
288
289        // Serialize
290        let serialized = borsh::to_vec(&block).expect("Failed to serialize edge case block");
291
292        // Deserialize
293        let deserialized =
294            Block::try_from_slice(&serialized).expect("Failed to deserialize edge case block");
295
296        assert_eq!(block, deserialized);
297        assert_eq!(deserialized.height, u64::MAX);
298        assert_eq!(deserialized.header.time, u32::MAX);
299        assert_eq!(deserialized.etched_runes[0].block, u64::MAX);
300        assert_eq!(deserialized.etched_runes[0].tx, u32::MAX);
301    }
302
303    #[test]
304    fn test_large_vectors_serialization() {
305        let header = create_test_header();
306
307        // Create large vectors to test performance and correctness
308        let large_tx_ids: Vec<SerializedTxid> = (0..1000)
309            .map(|i| {
310                let mut bytes = [0u8; 32];
311                bytes[0..4].copy_from_slice(&(i as u32).to_le_bytes());
312                SerializedTxid::from(bytes)
313            })
314            .collect();
315
316        let large_etched_runes: Vec<RuneId> = (0..500)
317            .map(|i| RuneId::new(i as u64, (i * 2) as u32))
318            .collect();
319
320        let block = Block {
321            height: 500000,
322            header: header.clone(),
323            tx_ids: large_tx_ids.clone(),
324            etched_runes: large_etched_runes.clone(),
325        };
326
327        // Serialize
328        let serialized = borsh::to_vec(&block).expect("Failed to serialize large block");
329
330        // Deserialize
331        let deserialized =
332            Block::try_from_slice(&serialized).expect("Failed to deserialize large block");
333
334        assert_eq!(block, deserialized);
335        assert_eq!(deserialized.tx_ids.len(), 1000);
336        assert_eq!(deserialized.etched_runes.len(), 500);
337
338        // Verify first and last elements
339        assert_eq!(deserialized.tx_ids[0], large_tx_ids[0]);
340        assert_eq!(deserialized.tx_ids[999], large_tx_ids[999]);
341        assert_eq!(deserialized.etched_runes[0], large_etched_runes[0]);
342        assert_eq!(deserialized.etched_runes[499], large_etched_runes[499]);
343    }
344
345    #[test]
346    fn test_serialized_data_integrity() {
347        let header = create_test_header();
348        let block = Block {
349            height: 123456,
350            header: header.clone(),
351            tx_ids: create_test_txids(),
352            etched_runes: create_test_rune_ids(),
353        };
354
355        // Serialize twice and ensure identical results
356        let serialized1 = borsh::to_vec(&block).expect("Failed first serialization");
357        let serialized2 = borsh::to_vec(&block).expect("Failed second serialization");
358
359        assert_eq!(
360            serialized1, serialized2,
361            "Serialization should be deterministic"
362        );
363
364        // Verify serialized data is not empty
365        assert!(
366            !serialized1.is_empty(),
367            "Serialized data should not be empty"
368        );
369
370        // Deserialize and re-serialize to test full roundtrip
371        let deserialized = Block::try_from_slice(&serialized1).expect("Failed to deserialize");
372        let re_serialized = borsh::to_vec(&deserialized).expect("Failed to re-serialize");
373
374        assert_eq!(
375            serialized1, re_serialized,
376            "Re-serialization should match original"
377        );
378    }
379
380    #[test]
381    fn test_header_fields_preservation() {
382        // Test various header configurations to ensure all fields are preserved
383        let headers = vec![
384            Header {
385                version: Version::from_consensus(1),
386                prev_blockhash: BlockHash::from_raw_hash(Hash::from_slice(&[1u8; 32]).unwrap()),
387                merkle_root: TxMerkleNode::from_raw_hash(Hash::from_slice(&[2u8; 32]).unwrap()),
388                time: 1000000,
389                bits: CompactTarget::from_consensus(0x1d00ffff),
390                nonce: 12345,
391            },
392            Header {
393                version: Version::from_consensus(536870912), // Version 2
394                prev_blockhash: BlockHash::from_raw_hash(Hash::from_slice(&[255u8; 32]).unwrap()),
395                merkle_root: TxMerkleNode::from_raw_hash(Hash::from_slice(&[0u8; 32]).unwrap()),
396                time: 0,
397                bits: CompactTarget::from_consensus(0x1d00ffff),
398                nonce: 0,
399            },
400        ];
401
402        for (i, header) in headers.iter().enumerate() {
403            let block = Block {
404                height: i as u64,
405                header: *header,
406                tx_ids: vec![],
407                etched_runes: vec![],
408            };
409
410            let serialized =
411                borsh::to_vec(&block).expect(&format!("Failed to serialize header test {}", i));
412            let deserialized = Block::try_from_slice(&serialized)
413                .expect(&format!("Failed to deserialize header test {}", i));
414
415            assert_eq!(block.header.version, deserialized.header.version);
416            assert_eq!(
417                block.header.prev_blockhash,
418                deserialized.header.prev_blockhash
419            );
420            assert_eq!(block.header.merkle_root, deserialized.header.merkle_root);
421            assert_eq!(block.header.time, deserialized.header.time);
422            assert_eq!(block.header.bits, deserialized.header.bits);
423            assert_eq!(block.header.nonce, deserialized.header.nonce);
424        }
425    }
426
427    #[test]
428    fn test_invalid_data_handling() {
429        // Test with incomplete data
430        let incomplete_data = vec![1, 2, 3, 4, 5];
431        assert!(Block::try_from_slice(&incomplete_data).is_err());
432
433        // Test with empty data
434        let empty_data = vec![];
435        assert!(Block::try_from_slice(&empty_data).is_err());
436    }
437}