/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2023 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
use std::{fs::File, io::Write};
use clap::Parser;
use darkfi::{
blockchain::{
block_store::{Block, BlockOrderStore, BlockProducer, BlockStore},
header_store::{Header, HeaderStore},
slot_store::SlotStore,
tx_store::TxStore,
Blockchain,
},
cli_desc,
consensus::constants::EPOCH_LENGTH,
tx::Transaction,
util::{path::expand_path, time::Timestamp},
Result,
};
use darkfi_sdk::crypto::MerkleNode;
#[derive(Parser)]
#[command(about = cli_desc!())]
struct Args {
#[arg(short, long, default_value = "../../../contrib/localnet/darkfid-single-node/")]
/// Path containing the node folders
path: String,
#[arg(short, long, default_values = ["darkfid0", "faucetd"])]
/// Node folder name (supports multiple values)
node: Vec,
#[arg(short, long, default_value = "/blockchain/testnet")]
/// Node blockchain folder
blockchain: String,
#[arg(short, long)]
/// Export all contents into a JSON file
export: bool,
}
#[derive(Debug)]
struct BlockProducerInfo {
_signature: String,
_proposal: Transaction,
}
impl BlockProducerInfo {
pub fn new(producer: &BlockProducer) -> BlockProducerInfo {
let _signature = format!("{:?}", producer.signature);
let _proposal = producer.proposal.clone();
BlockProducerInfo { _signature, _proposal }
}
}
#[derive(Debug)]
struct HeaderInfo {
_hash: blake3::Hash,
_version: u8,
_previous: blake3::Hash,
_epoch: u64,
_slot: u64,
_timestamp: Timestamp,
_root: MerkleNode,
}
impl HeaderInfo {
pub fn new(_hash: blake3::Hash, header: &Header) -> HeaderInfo {
let _version = header.version;
let _previous = header.previous;
let _epoch = header.epoch;
let _slot = header.slot;
let _timestamp = header.timestamp;
let _root = header.root;
HeaderInfo { _hash, _version, _previous, _epoch, _slot, _timestamp, _root }
}
}
#[derive(Debug)]
struct HeaderStoreInfo {
_headers: Vec,
}
impl HeaderStoreInfo {
pub fn new(headerstore: &HeaderStore) -> HeaderStoreInfo {
let mut _headers = Vec::new();
let result = headerstore.get_all();
match result {
Ok(iter) => {
for (hash, header) in iter.iter() {
_headers.push(HeaderInfo::new(hash.clone(), &header));
}
}
Err(e) => println!("Error: {:?}", e),
}
HeaderStoreInfo { _headers }
}
}
#[derive(Debug)]
struct BlockInfo {
_hash: blake3::Hash,
_magic: [u8; 4],
_header: blake3::Hash,
_txs: Vec,
_producer: BlockProducerInfo,
_slots: Vec,
}
impl BlockInfo {
pub fn new(_hash: blake3::Hash, block: &Block) -> BlockInfo {
let _magic = block.magic;
let _header = block.header;
let _txs = block.txs.clone();
let _producer = BlockProducerInfo::new(&block.producer);
let _slots = block.slots.clone();
BlockInfo { _hash, _magic, _header, _txs, _producer, _slots }
}
}
#[derive(Debug)]
struct BlockInfoChain {
_blocks: Vec,
}
impl BlockInfoChain {
pub fn new(blockstore: &BlockStore) -> BlockInfoChain {
let mut _blocks = Vec::new();
let result = blockstore.get_all();
match result {
Ok(iter) => {
for (hash, block) in iter.iter() {
_blocks.push(BlockInfo::new(hash.clone(), &block));
}
}
Err(e) => println!("Error: {:?}", e),
}
BlockInfoChain { _blocks }
}
}
#[derive(Debug)]
struct OrderInfo {
_slot: u64,
_hash: blake3::Hash,
}
impl OrderInfo {
pub fn new(_slot: u64, _hash: blake3::Hash) -> OrderInfo {
OrderInfo { _slot, _hash }
}
}
#[derive(Debug)]
struct BlockOrderStoreInfo {
_order: Vec,
}
impl BlockOrderStoreInfo {
pub fn new(orderstore: &BlockOrderStore) -> BlockOrderStoreInfo {
let mut _order = Vec::new();
let result = orderstore.get_all();
match result {
Ok(iter) => {
for (slot, hash) in iter.iter() {
_order.push(OrderInfo::new(slot.clone(), hash.clone()));
}
}
Err(e) => println!("Error: {:?}", e),
}
BlockOrderStoreInfo { _order }
}
}
#[derive(Debug)]
struct SlotInfo {
_id: u64,
_eta: String,
_sigma1: String,
_sigma2: String,
}
impl SlotInfo {
pub fn new(_id: u64, _eta: String, _sigma1: String, _sigma2: String) -> SlotInfo {
SlotInfo { _id, _eta, _sigma1, _sigma2 }
}
}
#[derive(Debug)]
struct SlotStoreInfo {
_slots: Vec,
}
impl SlotStoreInfo {
pub fn new(slot_store: &SlotStore) -> SlotStoreInfo {
let mut _slots = Vec::new();
let result = slot_store.get_all();
match result {
Ok(iter) => {
for slot in iter.iter() {
_slots.push(SlotInfo::new(
slot.id,
format!("{:?}", slot.previous_eta),
format!("{:?}", slot.sigma1),
format!("{:?}", slot.sigma2),
));
}
}
Err(e) => println!("Error: {:?}", e),
}
SlotStoreInfo { _slots }
}
}
#[derive(Debug)]
struct TxInfo {
_hash: blake3::Hash,
_payload: Transaction,
}
impl TxInfo {
pub fn new(_hash: blake3::Hash, tx: &Transaction) -> TxInfo {
let _payload = tx.clone();
TxInfo { _hash, _payload }
}
}
#[derive(Debug)]
struct TxStoreInfo {
_transactions: Vec,
}
impl TxStoreInfo {
pub fn new(txstore: &TxStore) -> TxStoreInfo {
let mut _transactions = Vec::new();
let result = txstore.get_all();
match result {
Ok(iter) => {
for (hash, tx) in iter.iter() {
_transactions.push(TxInfo::new(hash.clone(), &tx));
}
}
Err(e) => println!("Error: {:?}", e),
}
TxStoreInfo { _transactions }
}
}
#[derive(Debug)]
struct BlockchainInfo {
_headers: HeaderStoreInfo,
_blocks: BlockInfoChain,
_order: BlockOrderStoreInfo,
_slots: SlotStoreInfo,
_transactions: TxStoreInfo,
}
impl BlockchainInfo {
pub fn new(blockchain: &Blockchain) -> BlockchainInfo {
let _headers = HeaderStoreInfo::new(&blockchain.headers);
let _blocks = BlockInfoChain::new(&blockchain.blocks);
let _order = BlockOrderStoreInfo::new(&blockchain.order);
let _slots = SlotStoreInfo::new(&blockchain.slots);
let _transactions = TxStoreInfo::new(&blockchain.transactions);
BlockchainInfo { _headers, _blocks, _order, _slots, _transactions }
}
}
fn statistics(folder: &str, node: &str, blockchain: &str) -> Result<()> {
println!("Retrieving blockchain statistics for {node}...");
// Node folder
let folder = folder.to_owned() + node;
// Initialize or load sled database
let path = folder.to_owned() + blockchain;
let db_path = expand_path(&path).unwrap();
let sled_db = sled::open(&db_path)?;
// Retrieve statistics
let blockchain = Blockchain::new(&sled_db)?;
let slot = blockchain.last_slot()?.id;
let epoch = slot / EPOCH_LENGTH as u64;
let (_, block) = blockchain.last()?;
let blocks = blockchain.len();
let txs = blockchain.txs_len();
drop(sled_db);
// Print statistics
println!("Latest slot: {slot}");
println!("Epoch: {epoch}");
println!("Latest block: {block}");
println!("Total blocks: {blocks}");
println!("Total transactions: {txs}");
Ok(())
}
fn export(folder: &str, node: &str, blockchain: &str) -> Result<()> {
println!("Exporting data for {node}...");
// Node folder
let folder = folder.to_owned() + node;
// Initialize or load sled database
let path = folder.to_owned() + blockchain;
let db_path = expand_path(&path).unwrap();
let sled_db = sled::open(&db_path)?;
// Data export
let blockchain = Blockchain::new(&sled_db)?;
let info = BlockchainInfo::new(&blockchain);
let info_string = format!("{:#?}", info);
let file_name = node.to_owned() + "_db";
let mut file = File::create(file_name.clone())?;
file.write(info_string.as_bytes())?;
drop(sled_db);
println!("Data exported to file: {file_name}");
Ok(())
}
fn main() -> Result<()> {
// Parse arguments
let args = Args::parse();
println!("Node folder path: {}", args.path);
// Export data for each node
for node in args.node {
if args.export {
export(&args.path, &node, &args.blockchain)?;
continue
}
statistics(&args.path, &node, &args.blockchain)?;
}
Ok(())
}