/* 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::{read_dir, read_to_string}, io::{stdin, Read}, }; use anyhow::Result; use clap::{Parser, Subcommand}; use darkfi::{ blockchain::{BlockInfo, Blockchain, BlockchainOverlay}, cli_desc, tx::Transaction, util::{ path::expand_path, time::{TimeKeeper, Timestamp}, }, validator::{utils::genesis_txs_total, verification::verify_genesis_block}, }; use darkfi_contract_test_harness::vks; use darkfi_serial::{deserialize, serialize}; #[derive(Parser)] #[command(about = cli_desc!())] struct Args { #[command(subcommand)] command: Subcmd, } #[derive(Subcommand)] enum Subcmd { /// Read a Darkfi genesis block from stdin and display it Display, /// Generate a Darkfi genesis block and write it to stdin Generate { #[arg(short, long, default_value = "genesis_txs")] /// Path to folder containing the genesis transactions txs_folder: String, #[arg(short, long)] /// Genesis timestamp to use, instead of current one genesis_timestamp: Option, }, /// Read a Darkfi genesis block from stdin and verify it Verify, } /// Auxiliary function to read a bs58 genesis block from stdin fn read_block() -> Result { eprintln!("Reading genesis block from stdin..."); let mut buf = String::new(); stdin().read_to_string(&mut buf)?; let bytes = bs58::decode(&buf.trim()).into_vec()?; let block = deserialize(&bytes)?; Ok(block) } #[async_std::main] async fn main() -> Result<()> { // Parse arguments let args = Args::parse(); // Execute a subcommand match args.command { Subcmd::Display => { let genesis_block = read_block()?; println!("{genesis_block:#?}"); Ok(()) } Subcmd::Generate { txs_folder, genesis_timestamp } => { // Grab genesis transactions from folder let txs_folder = expand_path(&txs_folder).unwrap(); let mut genesis_txs: Vec = vec![]; for file in read_dir(txs_folder)? { let bytes = bs58::decode(&read_to_string(file?.path())?.trim()).into_vec()?; let tx = deserialize(&bytes)?; genesis_txs.push(tx); } // Generate the genesis block let mut genesis_block = BlockInfo::default(); // Update timestamp if one was provided if let Some(timestamp) = genesis_timestamp { genesis_block.header.timestamp = Timestamp(timestamp); } // Append genesis transactions if !genesis_txs.is_empty() { // Retrieve genesis producer transaction let producer_tx = genesis_block.txs.pop().unwrap(); // Append genesis transactions and calculate their total genesis_block.txs.append(&mut genesis_txs); genesis_block.txs.push(producer_tx); let genesis_txs_total = genesis_txs_total(&genesis_block.txs)?; genesis_block.slots[0].total_tokens = genesis_txs_total; } // Write generated genesis block to stdin let encoded = bs58::encode(&serialize(&genesis_block)).into_string(); println!("{}", encoded); Ok(()) } Subcmd::Verify => { let genesis_block = read_block()?; let hash = genesis_block.hash()?; println!("Verifying genesis block: {hash}"); // Initialize a temporary sled database let sled_db = sled::Config::new().temporary(true).open()?; let (_, vks) = vks::read_or_gen_vks_and_pks()?; vks::inject(&sled_db, &vks)?; // Create an overlay over whole blockchain let blockchain = Blockchain::new(&sled_db)?; let overlay = BlockchainOverlay::new(&blockchain)?; // Generate a dummy time keeper let time_keeper = TimeKeeper::new(genesis_block.header.timestamp, 10, 90, 0); // Grab block txs total let genesis_txs_total = genesis_txs_total(&genesis_block.txs)?; verify_genesis_block(&overlay, &time_keeper, &genesis_block, genesis_txs_total).await?; println!("Genesis block {hash} verified successfully!"); Ok(()) } } }