use std::process;
use std::time::{Duration, Instant};
use crate::blockchain::parser::chain::ChainStorage;
use crate::blockchain::proto::block::Block;
use crate::callbacks::Callback;
use crate::common::Result;
use crate::ParserOptions;
mod blkfile;
pub mod chain;
mod index;
pub mod reader;
pub mod types;
struct WorkerStats {
pub started_at: Instant,
pub last_log: Instant,
pub last_height: u64,
pub measure_frame: Duration,
}
impl WorkerStats {
fn new(start_range: u64) -> Self {
Self {
started_at: Instant::now(),
last_log: Instant::now(),
last_height: start_range,
measure_frame: Duration::from_secs(10),
}
}
}
pub struct BlockchainParser {
chain_storage: ChainStorage, stats: WorkerStats, callback: Box<dyn Callback>,
cur_height: u64,
}
impl BlockchainParser {
pub fn new(options: ParserOptions, chain_storage: ChainStorage) -> Self {
info!(target: "parser", "Parsing {} blockchain ...", options.coin.name);
Self {
chain_storage,
stats: WorkerStats::new(options.range.start),
callback: options.callback,
cur_height: options.range.start,
}
}
pub fn start(&mut self) -> Result<()> {
debug!(target: "parser", "Starting worker ...");
self.on_start(self.cur_height)?;
for height in self.cur_height..self.chain_storage.max_height() {
let block = match self.chain_storage.get_block(height) {
Ok(block) => match block {
Some(block) => block,
None => break,
},
Err(e) => {
error!(target: "parser", "Error at height {}: {}", height, e);
process::exit(1);
}
};
self.on_block(&block, height)?;
self.cur_height = height + 1;
}
self.on_complete(self.cur_height.saturating_sub(1))
}
#[inline]
pub const fn remaining(&self) -> u64 {
self.chain_storage
.max_height()
.saturating_sub(self.cur_height)
}
fn on_start(&mut self, height: u64) -> Result<()> {
let now = Instant::now();
self.stats.started_at = now;
self.stats.last_log = now;
info!(target: "parser", "Processing blocks starting from height {} ...", height);
self.callback.on_start(height)?;
trace!(target: "parser", "on_start() called");
Ok(())
}
fn on_block(&mut self, block: &Block, height: u64) -> Result<()> {
self.callback.on_block(block, height)?;
trace!(target: "parser", "on_block(height={}) called", height);
if self.callback.show_progress() {
self.print_progress(height);
}
Ok(())
}
fn on_complete(&mut self, height: u64) -> Result<()> {
info!(target: "parser", "Done. Processed blocks up to height {} in {:.2} minutes.",
height, (Instant::now() - self.stats.started_at).as_secs_f32() / 60.0);
self.callback.on_complete(height)?;
trace!(target: "parser", "on_complete() called");
Ok(())
}
fn print_progress(&mut self, height: u64) {
let now = Instant::now();
let blocks_speed = (height - self.stats.last_height) / self.stats.measure_frame.as_secs();
if now - self.stats.last_log > self.stats.measure_frame {
info!(target: "parser", "Status: {:7} Blocks processed. (remaining: {:7}, speed: {:5.2} blocks/s)",
height, self.remaining(), blocks_speed);
self.stats.last_log = now;
self.stats.last_height = height;
}
}
}