bitcoin_explorer/parser/
block_index.rs1use crate::parser::errors::OpResult;
2use crate::parser::reader::BlockchainRead;
3use bitcoin::{BlockHash, BlockHeader};
4use leveldb::database::iterator::LevelDBIterator;
5use leveldb::database::Database;
6use leveldb::iterator::Iterable;
7use leveldb::options::{Options, ReadOptions};
8use log::info;
9use serde::Serialize;
10use std::collections::{BTreeMap, HashMap};
11use std::fmt;
12use std::io::Cursor;
13use std::path::Path;
14
15const BLOCK_VALID_HEADER: u32 = 1;
19const BLOCK_VALID_TREE: u32 = 2;
20const BLOCK_VALID_TRANSACTIONS: u32 = 3;
21const BLOCK_VALID_CHAIN: u32 = 4;
22const BLOCK_VALID_SCRIPTS: u32 = 5;
23const BLOCK_VALID_MASK: u32 = BLOCK_VALID_HEADER
24 | BLOCK_VALID_TREE
25 | BLOCK_VALID_TRANSACTIONS
26 | BLOCK_VALID_CHAIN
27 | BLOCK_VALID_SCRIPTS;
28const BLOCK_HAVE_DATA: u32 = 8;
29const BLOCK_HAVE_UNDO: u32 = 16;
30
31#[derive(Clone)]
36pub struct BlockIndex {
37 pub records: Box<[BlockIndexRecord]>,
38 pub hash_to_height: HashMap<BlockHash, i32>,
39}
40
41#[derive(Serialize, Clone)]
45pub struct BlockIndexRecord {
46 pub n_version: i32,
47 pub n_height: i32,
48 pub n_status: u32,
49 pub n_tx: u32,
50 pub n_file: i32,
51 pub n_data_pos: u32,
52 pub n_undo_pos: u32,
53 pub block_header: BlockHeader,
54}
55
56impl BlockIndex {
57 pub(crate) fn new(p: &Path) -> OpResult<BlockIndex> {
61 let records = load_block_index(p)?.into_boxed_slice();
62
63 let mut hash_to_height = HashMap::with_capacity(records.len());
65 for b in records.iter() {
66 hash_to_height.insert(b.block_header.block_hash(), b.n_height);
67 }
68 hash_to_height.shrink_to_fit();
69 Ok(BlockIndex {
70 records,
71 hash_to_height,
72 })
73 }
74}
75
76pub fn load_block_index(path: &Path) -> OpResult<Vec<BlockIndexRecord>> {
82 let mut block_index_by_block_hash = BTreeMap::new();
83
84 info!("Start loading block_index");
85 let mut options = Options::new();
86 options.create_if_missing = false;
87 let db: Database<BlockKey> = Database::open(path, options)?;
88 let options = ReadOptions::new();
89 let mut iter = db.iter(options);
90 let mut max_height_block_hash = Option::<(BlockHash, i32)>::None;
91
92 while iter.advance() {
93 let k = iter.key();
94 let v = iter.value();
95 if is_block_index_record(&k.key) {
96 let record = BlockIndexRecord::from(&v)?;
97 if record.n_height == 0
99 || (record.n_status & BLOCK_VALID_MASK >= BLOCK_VALID_SCRIPTS
100 && record.n_status & BLOCK_HAVE_DATA > 0)
101 {
102 let block_hash = record.block_header.block_hash();
103 if let Some((hash, height)) = max_height_block_hash.as_mut() {
105 if record.n_height > *height {
106 *hash = block_hash;
107 *height = record.n_height;
108 }
109 } else {
110 max_height_block_hash = Some((block_hash, record.n_height));
111 }
112 block_index_by_block_hash.insert(block_hash, record);
113 }
114 }
115 }
116 if let Some((hash, height)) = max_height_block_hash {
118 let mut block_index = Vec::with_capacity(height as usize + 1);
119 let mut current_hash = hash;
120 let mut current_height = height;
121 while current_height >= 0 {
123 let blk = block_index_by_block_hash
124 .remove(¤t_hash)
125 .expect("block hash not found in block index!");
126 assert_eq!(
127 current_height, blk.n_height,
128 "some block info missing from block index levelDB,\
129 delete Bitcoin folder and re-download!"
130 );
131 current_hash = blk.block_header.prev_blockhash;
132 current_height -= 1;
133 block_index.push(blk);
134 }
135 block_index.reverse();
136 Ok(block_index)
137 } else {
138 Ok(Vec::with_capacity(0))
139 }
140}
141
142struct BlockKey {
144 key: Vec<u8>,
145}
146
147impl db_key::Key for BlockKey {
149 fn from_u8(key: &[u8]) -> Self {
150 BlockKey {
151 key: Vec::from(key),
152 }
153 }
154
155 fn as_slice<T, F: Fn(&[u8]) -> T>(&self, f: F) -> T {
156 f(&self.key)
157 }
158}
159
160impl BlockIndexRecord {
161 fn from(values: &[u8]) -> OpResult<Self> {
165 let mut reader = Cursor::new(values);
166
167 let n_version = reader.read_varint()? as i32;
168 let n_height = reader.read_varint()? as i32;
169 let n_status = reader.read_varint()? as u32;
170 let n_tx = reader.read_varint()? as u32;
171 let n_file = if n_status & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO) > 0 {
172 reader.read_varint()? as i32
173 } else {
174 -1
175 };
176 let n_data_pos = if n_status & BLOCK_HAVE_DATA > 0 {
177 reader.read_varint()? as u32
178 } else {
179 u32::MAX
180 };
181 let n_undo_pos = if n_status & BLOCK_HAVE_UNDO > 0 {
182 reader.read_varint()? as u32
183 } else {
184 u32::MAX
185 };
186 let block_header = reader.read_block_header()?;
187
188 Ok(BlockIndexRecord {
189 n_version,
190 n_height,
191 n_status,
192 n_tx,
193 n_file,
194 n_data_pos,
195 n_undo_pos,
196 block_header,
197 })
198 }
199}
200
201#[inline]
202fn is_block_index_record(data: &[u8]) -> bool {
203 data.first() == Some(&b'b')
204}
205
206impl fmt::Debug for BlockIndexRecord {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 f.debug_struct("BlockIndexRecord")
209 .field("version", &self.n_version)
210 .field("height", &self.n_height)
211 .field("status", &self.n_status)
212 .field("n_tx", &self.n_tx)
213 .field("n_file", &self.n_file)
214 .field("n_data_pos", &self.n_data_pos)
215 .field("header", &self.block_header)
216 .finish()
217 }
218}