mod connected;
use crate::parser::blk_file::BlkFile;
use crate::parser::errors::{OpError, OpResult};
use crate::parser::script::{evaluate_script, ScriptInfo};
use crate::parser::tx_index::TxDB;
use std::ops::Deref;
use std::path::Path;
use std::sync::Arc;
pub use crate::iter::{BlockIter, ConnectedBlockIter};
pub use crate::parser::block_index::{BlockIndex, BlockIndexRecord};
pub use crate::parser::proto::connected_proto::{
ConnectedBlock, ConnectedTx, FConnectedBlock, FConnectedTransaction, SConnectedBlock,
SConnectedTransaction,
};
pub use crate::parser::proto::full_proto::{FBlock, FBlockHeader, FTransaction, FTxOut};
pub use crate::parser::proto::simple_proto::{SBlock, SBlockHeader, STransaction, STxOut};
pub use bitcoin::hashes::hex::{FromHex, ToHex};
pub use bitcoin::{Address, Block, BlockHash, BlockHeader, Network, Script, Transaction, Txid};
#[deprecated(since = "1.2.7", note = "use `get_addresses_from_script` instead")]
pub fn parse_script(script_pub_key: &str) -> OpResult<ScriptInfo> {
get_addresses_from_script(script_pub_key)
}
#[inline]
pub fn get_addresses_from_script(script_pub_key: &str) -> OpResult<ScriptInfo> {
let script = Script::from_hex(script_pub_key)?;
Ok(evaluate_script(&script, Network::Bitcoin))
}
pub struct InnerDB {
pub block_index: BlockIndex,
pub blk_file: BlkFile,
pub tx_db: TxDB,
}
#[derive(Clone)]
pub struct BitcoinDB(Arc<InnerDB>);
impl Deref for BitcoinDB {
type Target = InnerDB;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl BitcoinDB {
pub fn new(p: &Path, tx_index: bool) -> OpResult<BitcoinDB> {
if !p.exists() {
return Err(OpError::from("data_dir does not exist"));
}
let blk_path = p.join("blocks");
let index_path = blk_path.join("index");
let block_index = BlockIndex::new(index_path.as_path())?;
let tx_db = if tx_index {
let tx_index_path = p.join("indexes").join("txindex");
TxDB::new(&tx_index_path, &block_index)
} else {
TxDB::null()
};
let inner = InnerDB {
block_index,
blk_file: BlkFile::new(blk_path.as_path())?,
tx_db,
};
Ok(BitcoinDB(Arc::new(inner)))
}
#[deprecated(since = "1.2.6", note = "use `get_block_count()` instead")]
pub fn get_max_height(&self) -> usize {
self.block_index.records.len()
}
pub fn get_block_count(&self) -> usize {
let records = self.block_index.records.len();
for h in 0..records {
if self.block_index.records.get(h).unwrap().n_tx == 0 {
return h;
}
}
records
}
pub fn get_header(&self, height: usize) -> OpResult<&BlockIndexRecord> {
if let Some(header) = self.block_index.records.get(height) {
Ok(header)
} else {
Err(OpError::from("height not found"))
}
}
pub fn get_hash_from_height(&self, height: usize) -> OpResult<BlockHash> {
match self.block_index.records.get(height) {
None => Err(OpError::from("height not found")),
Some(s) => Ok(s.block_header.block_hash()),
}
}
pub fn get_height_from_hash(&self, hash: &BlockHash) -> OpResult<usize> {
match self.block_index.hash_to_height.get(hash) {
None => Err(OpError::from("hash not found")),
Some(h) => Ok(*h as usize),
}
}
pub fn get_raw_block(&self, height: usize) -> OpResult<Vec<u8>> {
if let Some(index) = self.block_index.records.get(height) {
let blk = self
.blk_file
.read_raw_block(index.n_file, index.n_data_pos)?;
Ok(blk)
} else {
Err(OpError::from("height not found"))
}
}
pub fn get_block<T: From<Block>>(&self, height: usize) -> OpResult<T> {
if let Some(index) = self.block_index.records.get(height) {
let blk = self.blk_file.read_block(index.n_file, index.n_data_pos)?;
Ok(blk.into())
} else {
Err(OpError::from("height not found"))
}
}
pub fn get_transaction<T: From<Transaction>>(&self, txid: &Txid) -> OpResult<T> {
if !self.tx_db.is_open() {
return Err(OpError::from("TxDB not open"));
}
if self.tx_db.is_genesis_tx(txid) {
return Ok(self.get_block::<Block>(0)?.txdata.swap_remove(0).into());
}
let record = self.tx_db.get_tx_record(txid)?;
let tx = self
.blk_file
.read_transaction(record.n_file, record.n_pos, record.n_tx_offset)?;
Ok(tx.into())
}
pub fn get_height_of_transaction(&self, txid: &Txid) -> OpResult<usize> {
if !self.tx_db.is_open() {
return Err(OpError::from("TxDB not open"));
}
self.tx_db.get_block_height_of_tx(txid)
}
pub fn iter_block<T>(&self, start: usize, end: usize) -> BlockIter<T>
where
T: From<Block> + Send + 'static,
{
BlockIter::from_range(self, start, end)
}
pub fn iter_heights<T, TIter>(&self, heights: TIter) -> BlockIter<T>
where
T: 'static + From<Block> + Send,
TIter: IntoIterator<Item = usize> + Send + 'static,
<TIter as IntoIterator>::IntoIter: Send + 'static,
{
BlockIter::new(self, heights)
}
}