use ckb_app_config::{ExitCode, StatsArgs};
use ckb_async_runtime::Handle;
use ckb_shared::{Shared, SharedBuilder};
use ckb_store::ChainStore;
use ckb_types::{
core::{BlockNumber, ScriptHashType},
packed::CellbaseWitness,
prelude::*,
};
use std::cmp::max;
use std::collections::HashMap;
use std::convert::TryFrom;
pub fn stats(args: StatsArgs, async_handle: Handle) -> Result<(), ExitCode> {
let stats = Statics::build(args, async_handle)?;
stats.print_uncle_rate()?;
stats.print_miner_statics()?;
Ok(())
}
struct Statics {
shared: Shared,
from: BlockNumber,
to: BlockNumber,
}
impl Statics {
pub fn build(args: StatsArgs, async_handle: Handle) -> Result<Self, ExitCode> {
let shared_builder = SharedBuilder::new(
&args.config.bin_name,
args.config.root_dir.as_path(),
&args.config.db,
None,
async_handle,
args.consensus,
)?;
let (shared, _) = shared_builder.build()?;
let tip_number = shared.snapshot().tip_number();
let from = args.from.unwrap_or(0);
let to = args.to.unwrap_or(tip_number);
if from >= to {
return Err(ExitCode::Cli);
}
Ok(Statics { shared, from, to })
}
pub fn print_uncle_rate(&self) -> Result<(), ExitCode> {
let store = self.shared.store();
let to_ext = store
.get_block_hash(self.to)
.and_then(|hash| store.get_block_ext(&hash))
.ok_or(ExitCode::IO)?;
let from_ext = store
.get_block_hash(self.from)
.and_then(|hash| store.get_block_ext(&hash))
.ok_or(ExitCode::IO)?;
let block_nums = self.to - self.from;
let uncle_nums = to_ext.total_uncles_count - from_ext.total_uncles_count;
println!(
"uncle_rate: {}/{}({})",
uncle_nums,
block_nums,
uncle_nums as f64 / block_nums as f64
);
Ok(())
}
fn print_miner_statics(&self) -> Result<(), ExitCode> {
let store = self.shared.store();
let mut by_miner_script = HashMap::new();
let mut by_miner_message = HashMap::new();
let from = max(self.to.saturating_sub(999), self.from);
for i in from..=self.to {
let cellbase = store
.get_block_hash(i)
.and_then(|hash| store.get_cellbase(&hash))
.ok_or(ExitCode::IO)?;
let cellbase_witness = cellbase
.witnesses()
.get(0)
.and_then(|witness| CellbaseWitness::from_slice(&witness.raw_data()).ok())
.expect("cellbase witness should be ok");
by_miner_script
.entry(cellbase_witness.lock())
.and_modify(|e| *e += 1)
.or_insert(1);
by_miner_message
.entry(cellbase_witness.message().raw_data())
.and_modify(|e| *e += 1)
.or_insert(1);
}
let total = self.to - from;
let mut by_miner_script_vec: Vec<_> = by_miner_script.into_iter().collect();
by_miner_script_vec.sort_by_key(|(_, v)| *v);
println!("by_miner_script:");
println!(
"{0: <10} | {1: <5} | {2: <40} | {3: <64} | {4: <9} ",
"percentage", "total", "args", "code_hash", "hash_type"
);
for (script, count) in by_miner_script_vec.iter().rev() {
println!(
"{0: <10} | {1: <5} | {2: <40x} | {3: <64x} | {4: <9?}",
format!("{:.1}%", 100.0 * (*count as f64) / (total as f64)),
count,
script.args().raw_data(),
script.code_hash(),
ScriptHashType::try_from(script.hash_type()).expect("checked script hash type"),
);
}
let mut by_miner_message_vec: Vec<_> = by_miner_message.into_iter().collect();
by_miner_message_vec.sort_by_key(|(_, v)| *v);
println!("by_miner_message:");
println!(
"{0: <10} | {1: <5} | {2: <50}",
"percentage", "total", "messages"
);
for (message, count) in by_miner_message_vec.iter().rev() {
println!(
"{0: <10} | {1: <5} | {2: <50?}",
format!("{:.1}%", 100.0 * (*count as f64) / (total as f64)),
count,
message,
);
}
Ok(())
}
}