/* 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::{BlockOrderStore, BlockStore, HeaderStore}, slot_checkpoint_store::SlotCheckpointStore, tx_store::TxStore, Blockchain, }, cli_desc, consensus::{ block::{Block, Header}, constants::{EPOCH_LENGTH, TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP}, lead_info::LeadInfo, }, 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 LeadInfoInfo { _signature: String, _public_key: String, _public_inputs: Vec, _coin_slot: u64, _coin_eta: String, _proof: String, _leaders: u64, } impl LeadInfoInfo { pub fn new(lead_info: &LeadInfo) -> LeadInfoInfo { let _signature = format!("{:?}", lead_info.signature); let _public_key = lead_info.public_key.to_string(); let mut _public_inputs = vec![]; for public_input in &lead_info.public_inputs { _public_inputs.push(format!("{:?}", public_input)); } let _coin_slot = lead_info.coin_slot; let _coin_eta = format!("{:?}", lead_info.coin_eta); let _proof = format!("{:?}", lead_info.proof); let _leaders = lead_info.leaders; LeadInfoInfo { _signature, _public_key, _public_inputs, _coin_slot, _coin_eta, _proof, _leaders, } } } #[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, _lead_info: LeadInfoInfo, } 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 _lead_info = LeadInfoInfo::new(&block.lead_info); BlockInfo { _hash, _magic, _header, _txs, _lead_info } } } #[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 SlotCheckpointInfo { _slot: u64, _eta: String, _sigma1: String, _sigma2: String, } impl SlotCheckpointInfo { pub fn new(_slot: u64, _eta: String, _sigma1: String, _sigma2: String) -> SlotCheckpointInfo { SlotCheckpointInfo { _slot, _eta, _sigma1, _sigma2 } } } #[derive(Debug)] struct SlotCheckpointStoreInfo { _slot_checkpoints: Vec, } impl SlotCheckpointStoreInfo { pub fn new(slotcheckpointstore: &SlotCheckpointStore) -> SlotCheckpointStoreInfo { let mut _slot_checkpoints = Vec::new(); let result = slotcheckpointstore.get_all(); match result { Ok(iter) => { for slot_checkpoint in iter.iter() { _slot_checkpoints.push(SlotCheckpointInfo::new( slot_checkpoint.slot, format!("{:?}", slot_checkpoint.eta), format!("{:?}", slot_checkpoint.sigma1), format!("{:?}", slot_checkpoint.sigma2), )); } } Err(e) => println!("Error: {:?}", e), } SlotCheckpointStoreInfo { _slot_checkpoints } } } #[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, _slot_checkpoints: SlotCheckpointStoreInfo, _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 _slot_checkpoints = SlotCheckpointStoreInfo::new(&blockchain.slot_checkpoints); let _transactions = TxStoreInfo::new(&blockchain.transactions); BlockchainInfo { _headers, _blocks, _order, _slot_checkpoints, _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, *TESTNET_GENESIS_TIMESTAMP, *TESTNET_GENESIS_HASH_BYTES)?; let slot = blockchain.last_slot_checkpoint()?.slot; 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, *TESTNET_GENESIS_TIMESTAMP, *TESTNET_GENESIS_HASH_BYTES)?; 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(()) }