bitcoin_blk_reader/
blk_reader.rs

1
2use std::io::prelude::*;
3use std::io::BufReader;
4//use std::time::SystemTime;
5use std::fs::File;
6use std::sync::{
7    Arc,
8    RwLock,
9};
10use std::collections::{
11    HashMap,
12};
13use bytes::Bytes;
14
15use crate::{
16    block_to_block_hash,
17    BitcoinRest,
18};
19
20#[derive(Debug)]
21pub struct BlkFileReader {
22    reader: BufReader<File>,
23    xor: [u8; 8],
24    position: u64,
25}
26
27impl BlkFileReader {
28    pub fn new(path: &str, xor: [u8; 8]) -> Result<Self, std::io::Error> {
29        let file = File::open(path)?;
30        let reader = BufReader::new(file);
31        Ok(Self {
32            reader,
33            xor,
34            position: 0,
35        })
36    }
37}
38
39impl Read for BlkFileReader {
40    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
41        let read = self.reader.read(buf)?;
42        for i in 0..read {
43            buf[i] ^= self.xor[(self.position % 8) as usize];
44            self.position += 1;
45        }
46        Ok(read)
47    }
48}
49
50#[derive(Debug, Clone)]
51pub struct BlkReaderData {
52    // Block height -> (block, magic),
53    blocks: HashMap<u32, (Bytes, [u8; 4])>,
54    block_height_by_hash: HashMap<[u8; 32], u32>,
55    next_blk_index: u32,
56    next_height: u32,
57    all_read: bool,
58}
59
60impl BlkReaderData {
61    pub fn new() -> Self {
62        Self {
63            blocks: HashMap::new(),
64            block_height_by_hash: HashMap::new(),
65            next_blk_index: 0,
66            next_height: 0,
67            all_read: false,
68        }
69    }
70}
71
72#[derive(Debug, Clone)]
73pub struct BlkReader {
74    rest_endpoint: String,
75    blocks_dir: String,
76    end_height: u32,
77    xor: [u8; 8],
78    data: Arc<RwLock<BlkReaderData>>,
79}
80
81impl BlkReader {
82    pub fn new(rest_endpoint: String, blocks_dir: String) -> Self {
83        Self {
84            rest_endpoint,
85            blocks_dir,
86            end_height: 0,
87            xor: [0u8; 8],
88            data: Arc::new(RwLock::new(BlkReaderData::new())),
89        }
90    }
91    pub async fn init(&mut self, starting_height: u32) -> Result<(), Box<dyn std::error::Error>> {
92        self.xor = self.read_xor()?;
93        //println!("XOR: {}", hex::encode(self.xor));
94        let bitcoin_rest = BitcoinRest::new(self.rest_endpoint.clone());
95        // Get starting block hash.
96        let start_block_hash = bitcoin_rest.get_blockhashbyheight(starting_height).await?;
97        //println!("Starting block hash: {}", hex::encode(start_block_hash));
98        // Download all block headers.
99        //println!("Fetching all block headers...");
100        //let start_time = SystemTime::now();
101        let headers = bitcoin_rest.get_all_headers(start_block_hash, None).await?;
102        //let blocks_len = headers.len();
103        //println!("Fetched {} block headers in {}ms.", blocks_len, start_time.elapsed().unwrap().as_millis());
104        // Convert to block_height_by_hash.
105        for (offset, header) in headers.iter().enumerate() {
106            let block_hash = block_to_block_hash(header);
107            let height = starting_height + offset as u32;
108            self.data.write().unwrap().block_height_by_hash.insert(block_hash, height);
109        }
110        self.data.write().unwrap().next_height = starting_height;
111        self.end_height = starting_height + headers.len() as u32 - 1;
112        Ok(())
113    }
114    pub fn is_all_read(&self) -> bool {
115        self.data.read().unwrap().all_read
116    }
117    pub fn get_registered_block_count(&self) -> usize {
118        self.data.read().unwrap().blocks.len()
119    }
120    pub fn get_next_height(&self) -> u32 {
121        self.data.read().unwrap().next_height
122    }
123    pub fn read_xor(&self) -> Result<[u8; 8], std::io::Error> {
124        let path = format!("{}/xor.dat", self.blocks_dir);
125        let file = File::open(&path);
126        if let Err(e) = file {
127            if e.kind() == std::io::ErrorKind::NotFound {
128                return Ok([0u8; 8]);
129            }
130            return Err(e);
131        }
132        let mut file = file.unwrap();
133        let mut xor: Vec<u8> = vec![];
134        file.read_to_end(&mut xor)?;
135        if xor.len() != 8 {
136            panic!("Invalid xor.dat length.");
137        }
138        Ok(xor.try_into().unwrap())
139    }
140    fn read_file(&mut self, index: u32) -> Result<u32, ()> {
141        let path = format!("{}/blk{:05}.dat", self.blocks_dir, index);
142        //println!("Reading: {}", path);
143        let block_reader = BlkFileReader::new(&path, self.xor);
144        if block_reader.is_err() {
145            self.data.write().unwrap().all_read = true;
146            return Err(());
147        }
148        let mut block_reader = block_reader.unwrap();
149        let mut block_count = 0;
150        loop {
151            // Read magic bytes.
152            let mut magic = [0u8; 4];
153            if block_reader.read_exact(&mut magic).is_err() {
154                return Ok(block_count);
155            }
156            //println!("Magic bytes: {}", hex::encode(magic));
157            // Read block size.
158            let mut size = [0u8; 4];
159            if block_reader.read_exact(&mut size).is_err() {
160                return Ok(block_count);
161            }
162            let size = u32::from_le_bytes(size);
163            if size <= 80 {
164                return Ok(block_count);
165            }
166            //println!("Block size: {}", size);
167            // Read block.
168            let mut block_vec = vec![0u8; size as usize];
169            if block_reader.read_exact(&mut block_vec).is_err() {
170                return Ok(block_count);
171            }
172            block_count += 1;
173            // Compute block hash.
174            let block_hash = block_to_block_hash(&block_vec);
175            let block_height = self.data.read().unwrap().block_height_by_hash.get(&block_hash).cloned();
176            if block_height.is_none() {
177                //println!("Block height not found for hash: {}", hex::encode(block_hash));
178                continue;
179            }
180            let block_height = block_height.unwrap();
181            //println!("Block height: {}", block_height);
182            // Save blcok.
183            self.data.write().unwrap().blocks.insert(block_height, (Bytes::from(block_vec), magic));
184        }
185    }
186    pub fn read_next_file(&mut self) -> Result<u32, ()> {
187        let next_blk_index = {
188            let mut data = self.data.write().unwrap();
189            let next_blk_index = data.next_blk_index;
190            data.next_blk_index += 1;
191            next_blk_index
192        };
193        let block_count = self.read_file(next_blk_index);
194        if block_count.is_err() {
195            return Err(());
196        }
197        Ok(block_count.unwrap())
198    }
199    pub fn try_get_next_block(&mut self) -> Option<(u32, Bytes, [u8; 4])> {
200        let mut data = self.data.write().unwrap();
201        let next_height = data.next_height;
202        if let Some(block) = data.blocks.remove(&next_height) {
203            let height = data.next_height;
204            data.next_height += 1;
205            return Some((height, block.0, block.1));
206        }
207        None
208    }
209    pub fn get_next_block(&mut self) -> Option<(u32, Bytes, [u8; 4])> {
210        if self.data.read().unwrap().next_height > self.end_height {
211            return None;
212        }
213        loop {
214            let data = self.try_get_next_block();
215            if data.is_some() {
216                return data;
217            }
218            if self.data.read().unwrap().all_read {
219                return None;
220            }
221            if self.read_next_file().is_err() {
222                return None;
223            }
224        }
225    }
226}
227
228impl Iterator for BlkReader {
229    type Item = (u32, Bytes, [u8; 4]);
230    fn next(&mut self) -> Option<Self::Item> {
231        self.get_next_block()
232    }
233}