/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2024 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, BlockDifficulty, BlockDifficultyStore, BlockOrderStore, BlockRanks, BlockStore, }, contract_store::{ContractStateStore, WasmStore}, header_store::{Header, HeaderStore}, tx_store::{PendingTxOrderStore, PendingTxStore, TxStore}, Blockchain, }, cli_desc, tx::Transaction, util::{path::expand_path, time::Timestamp}, Result, }; use darkfi_sdk::{ blockchain::block_epoch, crypto::{ContractId, MerkleTree}, }; use num_bigint::BigUint; #[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 = ["darkfid"])] /// Node folder name (supports multiple values) node: Vec, #[arg(short, long, default_value = "")] /// Node blockchain folder blockchain: String, #[arg(short, long)] /// Export all contents into a JSON file export: bool, } #[derive(Debug)] struct HeaderInfo { _hash: blake3::Hash, _version: u8, _previous: blake3::Hash, _height: u64, _timestamp: Timestamp, _nonce: u64, _tree: MerkleTree, } impl HeaderInfo { pub fn new(_hash: blake3::Hash, header: &Header) -> HeaderInfo { HeaderInfo { _hash, _version: header.version, _previous: header.previous, _height: header.height, _timestamp: header.timestamp, _nonce: header.nonce, _tree: header.tree.clone(), } } } #[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, header)); } } Err(e) => println!("Error: {:?}", e), } HeaderStoreInfo { _headers } } } #[derive(Debug)] struct BlockInfo { _hash: blake3::Hash, _header: blake3::Hash, _txs: Vec, _signature: String, } impl BlockInfo { pub fn new(_hash: blake3::Hash, block: &Block) -> BlockInfo { BlockInfo { _hash, _header: block.header, _txs: block.txs.clone(), _signature: format!("{:?}", block.signature), } } } #[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, block)); } } Err(e) => println!("Error: {:?}", e), } BlockInfoChain { _blocks } } } #[derive(Debug)] struct OrderInfo { _height: u64, _hash: blake3::Hash, } impl OrderInfo { pub fn new(_height: u64, _hash: blake3::Hash) -> OrderInfo { OrderInfo { _height, _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 (height, hash) in iter.iter() { _order.push(OrderInfo::new(*height, *hash)); } } Err(e) => println!("Error: {:?}", e), } BlockOrderStoreInfo { _order } } } #[derive(Debug)] struct BlockRanksInfo { _target_rank: BigUint, _targets_rank: BigUint, _hash_rank: BigUint, _hashes_rank: BigUint, } impl BlockRanksInfo { pub fn new(ranks: &BlockRanks) -> BlockRanksInfo { BlockRanksInfo { _target_rank: ranks.target_rank.clone(), _targets_rank: ranks.targets_rank.clone(), _hash_rank: ranks.hash_rank.clone(), _hashes_rank: ranks.hashes_rank.clone(), } } } #[derive(Debug)] struct BlockDifficultyInfo { _height: u64, _timestamp: Timestamp, _difficulty: BigUint, _cummulative_difficulty: BigUint, _ranks: BlockRanksInfo, } impl BlockDifficultyInfo { pub fn new(difficulty: &BlockDifficulty) -> BlockDifficultyInfo { BlockDifficultyInfo { _height: difficulty.height, _timestamp: difficulty.timestamp, _difficulty: difficulty.difficulty.clone(), _cummulative_difficulty: difficulty.cummulative_difficulty.clone(), _ranks: BlockRanksInfo::new(&difficulty.ranks), } } } #[derive(Debug)] struct BlockDifficultyStoreInfo { _difficulties: Vec, } impl BlockDifficultyStoreInfo { pub fn new(difficultiesstore: &BlockDifficultyStore) -> BlockDifficultyStoreInfo { let mut _difficulties = Vec::new(); let result = difficultiesstore.get_all(); match result { Ok(iter) => { for (_, difficulty) in iter.iter() { _difficulties.push(BlockDifficultyInfo::new(difficulty)); } } Err(e) => println!("Error: {:?}", e), } BlockDifficultyStoreInfo { _difficulties } } } #[derive(Debug)] struct TxInfo { _hash: blake3::Hash, _payload: Transaction, } impl TxInfo { pub fn new(_hash: blake3::Hash, tx: &Transaction) -> TxInfo { TxInfo { _hash, _payload: tx.clone() } } } #[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, tx)); } } Err(e) => println!("Error: {:?}", e), } TxStoreInfo { _transactions } } } #[derive(Debug)] struct PendingTxStoreInfo { _transactions: Vec, } impl PendingTxStoreInfo { pub fn new(pendingtxstore: &PendingTxStore) -> PendingTxStoreInfo { let mut _transactions = Vec::new(); let result = pendingtxstore.get_all(); match result { Ok(iter) => { for (hash, tx) in iter.iter() { _transactions.push(TxInfo::new(*hash, tx)); } } Err(e) => println!("Error: {:?}", e), } PendingTxStoreInfo { _transactions } } } #[derive(Debug)] struct PendingTxOrderStoreInfo { _order: Vec, } impl PendingTxOrderStoreInfo { pub fn new(orderstore: &PendingTxOrderStore) -> PendingTxOrderStoreInfo { let mut _order = Vec::new(); let result = orderstore.get_all(); match result { Ok(iter) => { for (height, hash) in iter.iter() { _order.push(OrderInfo::new(*height, *hash)); } } Err(e) => println!("Error: {:?}", e), } PendingTxOrderStoreInfo { _order } } } #[derive(Debug)] struct ContractStateInfo { _id: ContractId, _state_hashes: Vec, } impl ContractStateInfo { pub fn new(_id: ContractId, state_hashes: &[blake3::Hash]) -> ContractStateInfo { ContractStateInfo { _id, _state_hashes: state_hashes.to_vec() } } } #[derive(Debug)] struct ContractStateStoreInfo { _contracts: Vec, } impl ContractStateStoreInfo { pub fn new(contractsstore: &ContractStateStore) -> ContractStateStoreInfo { let mut _contracts = Vec::new(); let result = contractsstore.get_all(); match result { Ok(iter) => { for (id, state_hash) in iter.iter() { _contracts.push(ContractStateInfo::new(*id, state_hash)); } } Err(e) => println!("Error: {:?}", e), } ContractStateStoreInfo { _contracts } } } #[derive(Debug)] struct WasmInfo { _id: ContractId, _bincode_hash: blake3::Hash, } impl WasmInfo { pub fn new(_id: ContractId, bincode: &[u8]) -> WasmInfo { let _bincode_hash = blake3::hash(bincode); WasmInfo { _id, _bincode_hash } } } #[derive(Debug)] struct WasmStoreInfo { _wasm_bincodes: Vec, } impl WasmStoreInfo { pub fn new(wasmstore: &WasmStore) -> WasmStoreInfo { let mut _wasm_bincodes = Vec::new(); let result = wasmstore.get_all(); match result { Ok(iter) => { for (id, bincode) in iter.iter() { _wasm_bincodes.push(WasmInfo::new(*id, bincode)); } } Err(e) => println!("Error: {:?}", e), } WasmStoreInfo { _wasm_bincodes } } } #[derive(Debug)] struct BlockchainInfo { _headers: HeaderStoreInfo, _blocks: BlockInfoChain, _order: BlockOrderStoreInfo, _difficulties: BlockDifficultyStoreInfo, _transactions: TxStoreInfo, _pending_txs: PendingTxStoreInfo, _pending_txs_order: PendingTxOrderStoreInfo, _contracts: ContractStateStoreInfo, _wasm_bincode: WasmStoreInfo, } impl BlockchainInfo { pub fn new(blockchain: &Blockchain) -> BlockchainInfo { BlockchainInfo { _headers: HeaderStoreInfo::new(&blockchain.headers), _blocks: BlockInfoChain::new(&blockchain.blocks), _order: BlockOrderStoreInfo::new(&blockchain.order), _difficulties: BlockDifficultyStoreInfo::new(&blockchain.difficulties), _transactions: TxStoreInfo::new(&blockchain.transactions), _pending_txs: PendingTxStoreInfo::new(&blockchain.pending_txs), _pending_txs_order: PendingTxOrderStoreInfo::new(&blockchain.pending_txs_order), _contracts: ContractStateStoreInfo::new(&blockchain.contracts), _wasm_bincode: WasmStoreInfo::new(&blockchain.wasm_bincode), } } } 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 (height, block) = blockchain.last()?; let epoch = block_epoch(height); let blocks = blockchain.len(); let txs = blockchain.txs_len(); drop(sled_db); // Print statistics println!("Latest height: {height}"); 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_all(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(()) }