bitcoin_blk_reader/
blk_reader.rs1
2use std::io::prelude::*;
3use std::io::BufReader;
4use 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 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 let bitcoin_rest = BitcoinRest::new(self.rest_endpoint.clone());
95 let start_block_hash = bitcoin_rest.get_blockhashbyheight(starting_height).await?;
97 let headers = bitcoin_rest.get_all_headers(start_block_hash, None).await?;
102 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 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 let mut magic = [0u8; 4];
153 if block_reader.read_exact(&mut magic).is_err() {
154 return Ok(block_count);
155 }
156 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 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 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 continue;
179 }
180 let block_height = block_height.unwrap();
181 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}