// Copyright 2021 The Grin Developers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Main for building the binary of a Grin peer-to-peer node. #[macro_use] extern crate clap; #[macro_use] extern crate log; use crate::config::config::SERVER_CONFIG_FILE_NAME; use crate::core::global; use crate::tools::check_seeds; use crate::util::init_logger; use clap::App; use futures::channel::oneshot; use grin_api as api; use grin_chain as chain; use grin_config as config; use grin_core as core; use grin_p2p as p2p; use grin_servers as servers; use grin_util as util; use grin_util::logger::LogEntry; use std::fs::File; use std::io::Write; use std::sync::mpsc; #[macro_use] extern crate serde_derive; extern crate serde_json; mod cmd; mod tools; pub mod tui; // include build information pub mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } pub fn info_strings() -> (String, String) { ( format!( "This is Grin version {}{}, built for {} by {}.", built_info::PKG_VERSION, built_info::GIT_COMMIT_HASH.map_or_else(|| "".to_owned(), |v| format!(" (git {})", v)), built_info::TARGET, built_info::RUSTC_VERSION, ), format!( "Built with profile \"{}\", features \"{}\".", built_info::PROFILE, built_info::FEATURES_STR, ), ) } fn log_build_info() { let (basic_info, detailed_info) = info_strings(); info!("{}", basic_info); debug!("{}", detailed_info); } fn log_feature_flags() { info!("Feature: NRD kernel enabled: {}", global::is_nrd_enabled()); } fn main() { let exit_code = real_main(); std::process::exit(exit_code); } fn real_main() -> i32 { let yml = load_yaml!("grin.yml"); let args = App::from_yaml(yml) .version(built_info::PKG_VERSION) .get_matches(); let mut node_config; let chain_type = if args.is_present("testnet") { global::ChainTypes::Testnet } else if args.is_present("usernet") { global::ChainTypes::UserTesting } else { global::ChainTypes::Mainnet }; // Deal with configuration file creation if let ("server", Some(server_args)) = args.subcommand() { // If it's just a server config command, do it and exit if let ("config", Some(_)) = server_args.subcommand() { cmd::config_command_server(&chain_type, SERVER_CONFIG_FILE_NAME); return 0; } } // Load relevant config match args.subcommand() { // When the subscommand is 'server' take into account the 'config_file' flag ("server", Some(server_args)) => { if let Some(_path) = server_args.value_of("config_file") { node_config = Some(config::GlobalConfig::new(_path).unwrap_or_else(|e| { panic!("Error loading server configuration: {}", e); })); } else { node_config = Some( config::initial_setup_server(&chain_type).unwrap_or_else(|e| { panic!("Error loading server configuration: {}", e); }), ); } } // Otherwise load up the node config as usual _ => { node_config = Some( config::initial_setup_server(&chain_type).unwrap_or_else(|e| { panic!("Error loading server configuration: {}", e); }), ); } } let config = node_config.clone().unwrap(); let mut logging_config = config.members.as_ref().unwrap().logging.clone().unwrap(); logging_config.tui_running = config.members.as_ref().unwrap().server.run_tui; // Handle global --no-tui flag and GRIN_NO_TUI environment variable let no_tui = args.is_present("no-tui") || std::env::var("GRIN_NO_TUI") .map(|v| v == "1" || v.eq_ignore_ascii_case("true")) .unwrap_or(false); if no_tui { // Deactivate logger TUI logging_config.tui_running = Some(false); // Deactivate Server TUI in node config, so // cmd::server_command(...) will not use it if let Some(gc) = node_config.as_mut() { if let Some(members) = gc.members.as_mut() { members.server.run_tui = Some(false); } } } let api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>) = Box::leak(Box::new(oneshot::channel::<()>())); let (logs_tx, logs_rx) = if logging_config.tui_running.unwrap() { let (logs_tx, logs_rx) = mpsc::sync_channel::(200); (Some(logs_tx), Some(logs_rx)) } else { (None, None) }; init_logger(Some(logging_config), logs_tx); if let Some(file_path) = &config.config_file_path { info!( "Using configuration file at {}", file_path.to_str().unwrap() ); } else { info!("Node configuration file not found, using default"); }; log_build_info(); // Initialize our global chain_type, feature flags (NRD kernel support currently), accept_fee_base, and future_time_limit. // These are read via global and not read from config beyond this point. global::init_global_chain_type(config.members.as_ref().unwrap().server.chain_type); info!("Chain: {:?}", global::get_chain_type()); match global::get_chain_type() { global::ChainTypes::Mainnet => { // Set various mainnet specific feature flags. global::init_global_nrd_enabled(false); } _ => { // Set various non-mainnet feature flags. global::init_global_nrd_enabled(true); } } let afb = config .members .as_ref() .unwrap() .server .pool_config .accept_fee_base; global::init_global_accept_fee_base(afb); info!("Accept Fee Base: {:?}", global::get_accept_fee_base()); global::init_global_future_time_limit(config.members.unwrap().server.future_time_limit); info!("Future Time Limit: {:?}", global::get_future_time_limit()); log_feature_flags(); // Execute subcommand match args.subcommand() { // server commands and options ("server", Some(server_args)) => { cmd::server_command(Some(server_args), node_config.unwrap(), logs_rx, api_chan) } // client commands and options ("client", Some(client_args)) => cmd::client_command(client_args, node_config.unwrap()), // clean command ("clean", _) => { let db_root_path = node_config.unwrap().members.unwrap().server.db_root; println!("Cleaning chain data directory: {}", db_root_path); match std::fs::remove_dir_all(db_root_path) { Ok(_) => 0, Err(_) => 1, } } // seedcheck command ("seedcheck", Some(seedcheck_args)) => { let is_testnet = seedcheck_args.is_present("testnet"); let results = check_seeds(is_testnet); let output = serde_json::to_string_pretty(&results).expect("Unable to serialize results"); if let Some(output_file) = seedcheck_args.value_of("output") { let mut file = File::create(output_file).expect("Unable to create file"); writeln!(file, "{}", output).expect("Unable to write data"); println!("Results written to {}", output_file); } else { println!("{}", output); } 0 } // If nothing is specified, try to just use the config file instead // this could possibly become the way to configure most things // with most command line options being phased out _ => cmd::server_command(None, node_config.unwrap(), logs_rx, api_chan), } }