diff --git a/Cargo.toml b/Cargo.toml index fedeb79..8c6096d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alfis" -version = "0.4.7" +version = "0.4.8" authors = ["Revertron "] edition = "2018" build = "build.rs" @@ -12,7 +12,7 @@ repository = "https://github.com/Revertron/Alfis" [dependencies] getopts = "0.2.21" log = "0.4.14" -simple_logger = "1.11.0" +simplelog = "0.10" toml = "0.5.8" digest = "0.9.0" sha2 = "0.9.3" diff --git a/src/commons/constants.rs b/src/commons/constants.rs index 45d5e89..dbc1914 100644 --- a/src/commons/constants.rs +++ b/src/commons/constants.rs @@ -31,4 +31,9 @@ pub const ZONE_MAX_LENGTH: usize = 10; pub const MAX_RECONNECTS: u32 = 5; pub const CLASS_ZONE: &str = "zone"; -pub const CLASS_DOMAIN: &str = "domain"; \ No newline at end of file +pub const CLASS_DOMAIN: &str = "domain"; + +/// Public nodes listen port +pub const LISTEN_PORT: u16 = 4244; +pub const UI_REFRESH_DELAY_MS: u128 = 250; +pub const LOG_REFRESH_DELAY_SEC: u64 = 60; diff --git a/src/commons/mod.rs b/src/commons/mod.rs index 9d8c0ea..f7f33e2 100644 --- a/src/commons/mod.rs +++ b/src/commons/mod.rs @@ -1,14 +1,17 @@ -use std::num; -use rand::Rng; - -pub mod constants; -pub use constants::*; use std::net::IpAddr; +use std::num; +use mio::Token; +use rand::Rng; #[cfg(not(target_os = "macos"))] use thread_priority::*; + +pub use constants::*; + use crate::dns::protocol::DnsRecord; +pub mod constants; + /// Convert bytes array to HEX format pub fn to_hex(buf: &[u8]) -> String { let mut result = String::new(); @@ -95,6 +98,13 @@ pub fn is_yggdrasil(addr: &IpAddr) -> bool { false } +/// Gets new token from old token, mutating the last +pub fn next(current: &mut Token) -> Token { + let next = current.0; + current.0 += 1; + Token(next) +} + /// Checks if this record has IP from Yggdrasil network /// https://yggdrasil-network.github.io pub fn is_yggdrasil_record(record: &DnsRecord) -> bool { @@ -134,8 +144,8 @@ pub fn setup_miner_thread(cpu: u32) { #[cfg(test)] mod test { - use crate::{check_domain, is_yggdrasil}; use std::net::IpAddr; + use crate::{check_domain, is_yggdrasil}; #[test] fn test_check_domain() { diff --git a/src/main.rs b/src/main.rs index 7747d38..461ac1f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,14 +9,17 @@ use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; -use getopts::Options; +use getopts::{Options, Matches}; #[allow(unused_imports)] use log::{debug, error, info, trace, warn, LevelFilter}; -use simple_logger::SimpleLogger; +use simplelog::*; #[cfg(windows)] use winapi::um::wincon::{ATTACH_PARENT_PROCESS, AttachConsole, FreeConsole}; use alfis::{Block, Bytes, Chain, Miner, Context, Network, Settings, dns_utils, Keystore, ZONE_DIFFICULTY}; +use std::fs::OpenOptions; +use std::process::exit; +use std::io::{Seek, SeekFrom}; #[cfg(feature = "webgui")] mod web_ui; @@ -42,8 +45,9 @@ fn main() { opts.optflag("n", "nogui", "Run without graphic user interface (default for no gui builds)"); opts.optflag("v", "verbose", "Show more debug messages"); opts.optflag("d", "debug", "Show trace messages, more than debug"); - opts.optflag("l", "list", "List blocks from DB and exit"); + opts.optflag("b", "blocks", "List blocks from DB and exit"); opts.optflag("g", "generate", "Generate new config file. Generated config will be printed to console."); + opts.optopt("l", "log", "Write log to file", "FILE"); opts.optopt("c", "config", "Path to config file", "FILE"); opts.optopt("w", "work-dir", "Path to working directory", "DIRECTORY"); opts.optopt("u", "upgrade", "Path to config file that you want to upgrade. Upgraded config will be printed to console.", "FILE"); @@ -82,13 +86,6 @@ fn main() { #[cfg(not(feature = "webgui"))] let no_gui = true; - let mut level = LevelFilter::Info; - if opt_matches.opt_present("v") { - level = LevelFilter::Debug; - } - if opt_matches.opt_present("d") { - level = LevelFilter::Trace; - } let config_name = match opt_matches.opt_str("c") { None => { SETTINGS_FILENAME.to_owned() } Some(path) => { path } @@ -97,18 +94,14 @@ fn main() { env::set_current_dir(Path::new(&path)).expect(&format!("Unable to change working directory to '{}'", &path)); } - SimpleLogger::new() - .with_level(level) - .with_module_level("mio::poll", LevelFilter::Warn) - .init() - .unwrap(); + setup_logger(&opt_matches); info!(target: LOG_TARGET_MAIN, "Starting ALFIS {}", env!("CARGO_PKG_VERSION")); let settings = Settings::load(&config_name).expect(&format!("Cannot load settings from {}!", &config_name)); info!(target: LOG_TARGET_MAIN, "Loaded settings: {:?}", &settings); let keystore = Keystore::from_file(&settings.key_file, ""); let chain: Chain = Chain::new(&settings); - if opt_matches.opt_present("l") { + if opt_matches.opt_present("b") { for i in 1..(chain.height() + 1) { if let Some(block) = chain.get_block(i) { info!(target: LOG_TARGET_MAIN, "{:?}", &block); @@ -152,12 +145,58 @@ fn main() { } } +/// Sets up logger in accordance with command line options +fn setup_logger(opt_matches: &Matches) { + let mut level = LevelFilter::Info; + if opt_matches.opt_present("v") { + level = LevelFilter::Debug; + } + if opt_matches.opt_present("d") { + level = LevelFilter::Trace; + } + let config = ConfigBuilder::new() + .add_filter_ignore_str("mio::poll") + .set_thread_level(LevelFilter::Off) + .set_location_level(LevelFilter::Off) + .set_target_level(LevelFilter::Error) + .set_time_level(LevelFilter::Error) + .set_time_to_local(true) + .build(); + match opt_matches.opt_str("l") { + None => { + if let Err(e) = TermLogger::init(level, config, TerminalMode::Stdout, ColorChoice::Auto) { + println!("Unable to initialize logger!\n{}", e); + } + } + Some(path) => { + let file = match OpenOptions::new().write(true).create(true).open(&path) { + Ok(mut file) => { + file.seek(SeekFrom::End(0)).unwrap(); + file + } + Err(e) => { + println!("Could not open log file '{}' for writing!\n{}", &path, e); + exit(1); + } + }; + CombinedLogger::init( + vec![ + TermLogger::new(level, config.clone(), TerminalMode::Stdout, ColorChoice::Auto), + WriteLogger::new(level, config, file), + ] + ).unwrap(); + } + } +} + +/// Gets own domains by current loaded keystore and writes them to log fn print_my_domains(context: &Arc>) { let context = context.lock().unwrap(); let domains = context.chain.get_my_domains(&context.keystore); debug!("Domains: {:?}", &domains); } +/// Creates genesis (origin) block if `origin` is empty in config and we don't have any blocks in DB fn create_genesis_if_needed(context: &Arc>, miner: &Arc>) { // If there is no origin in settings and no blockchain in DB, generate genesis block let context = context.lock().unwrap(); diff --git a/src/p2p/network.rs b/src/p2p/network.rs index cdcbe7c..5dee5ab 100644 --- a/src/p2p/network.rs +++ b/src/p2p/network.rs @@ -3,26 +3,25 @@ extern crate serde_json; use std::{io, thread}; use std::io::{Read, Write}; +use std::net::{IpAddr, Shutdown, SocketAddr, SocketAddrV4}; use std::sync::{Arc, Mutex, MutexGuard}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::time::{Duration, Instant}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; use mio::{Events, Interest, Poll, Registry, Token}; use mio::event::Event; use mio::net::{TcpListener, TcpStream}; -#[allow(unused_imports)] -use log::{trace, debug, info, warn, error}; - -use std::net::{SocketAddr, IpAddr, SocketAddrV4, Shutdown}; -use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers, is_yggdrasil}; -use crate::blockchain::types::BlockQuality; -use crate::commons::CHAIN_VERSION; -use std::sync::atomic::{AtomicBool, Ordering}; use rand::random; +use crate::{Block, Context, p2p::Message, p2p::Peer, p2p::Peers, p2p::State}; +use crate::blockchain::types::BlockQuality; +use crate::commons::*; + const SERVER: Token = Token(0); const POLL_TIMEOUT: Option = Some(Duration::from_millis(1000)); -pub const LISTEN_PORT: u16 = 4244; const MAX_PACKET_SIZE: usize = 1 * 1024 * 1024; // 1 Mb const MAX_READ_BLOCK_TIME: u128 = 500; @@ -63,7 +62,8 @@ impl Network { // Starting peer connections to bootstrap nodes peers.connect_peers(&peers_addrs, &poll.registry(), &mut unique_token, yggdrasil_only); - let mut peers_timer = Instant::now(); + let mut ui_timer = Instant::now(); + let mut log_timer = Instant::now(); let mut bootstrap_timer = Instant::now(); loop { if peers.get_peers_count() == 0 && bootstrap_timer.elapsed().as_secs() > 60 { @@ -138,7 +138,7 @@ impl Network { } events.clear(); - if peers_timer.elapsed().as_millis() > 250 { + if ui_timer.elapsed().as_millis() > UI_REFRESH_DELAY_MS { // Send pings to idle peers let (height, hash) = { let mut context = context.lock().unwrap(); @@ -147,11 +147,15 @@ impl Network { if nodes > 0 { context.bus.post(crate::event::Event::NetworkStatus { nodes, blocks: height }); } + if log_timer.elapsed().as_secs() > LOG_REFRESH_DELAY_SEC { + info!("Active nodes count: {}, blocks count: {}", nodes, height); + log_timer = Instant::now(); + } (height, context.chain.last_hash()) }; peers.update(poll.registry(), height, hash); peers.connect_new_peers(poll.registry(), &mut unique_token, yggdrasil_only); - peers_timer = Instant::now(); + ui_timer = Instant::now(); } } if !running.load(Ordering::SeqCst) { @@ -557,12 +561,6 @@ fn check_block(block: &Block, prev: &Block) -> bool { prev.index == block.index - 1 && prev.hash == block.prev_block_hash } -pub(crate) fn next(current: &mut Token) -> Token { - let next = current.0; - current.0 += 1; - Token(next) -} - fn would_block(err: &io::Error) -> bool { err.kind() == io::ErrorKind::WouldBlock } diff --git a/src/p2p/peers.rs b/src/p2p/peers.rs index 021c65f..3a75684 100644 --- a/src/p2p/peers.rs +++ b/src/p2p/peers.rs @@ -1,17 +1,18 @@ use std::collections::{HashMap, HashSet}; -use std::net::{SocketAddr, IpAddr, Shutdown, ToSocketAddrs}; -use mio::{Token, Interest, Registry}; +use std::net::{IpAddr, Shutdown, SocketAddr, ToSocketAddrs}; + +use chrono::Utc; +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; +use mio::{Interest, Registry, Token}; use mio::net::TcpStream; -use crate::p2p::{Peer, State, Message}; -use crate::p2p::network::LISTEN_PORT; -use crate::p2p::network::next; use rand::random; use rand::seq::IteratorRandom; -#[allow(unused_imports)] -use log::{trace, debug, info, warn, error}; -use crate::{Bytes, is_yggdrasil, commons}; -use crate::commons::MAX_RECONNECTS; -use chrono::Utc; + +use crate::{Bytes, commons}; +use crate::commons::*; +use crate::p2p::{Message, Peer, State}; +use crate::commons::next; pub struct Peers { peers: HashMap, @@ -90,7 +91,7 @@ impl Peers { peers.insert(peer.to_owned()); peers }); - debug!("Got {} peers", peers.len()); + debug!("Got {} peers from exchange", peers.len()); //debug!("Got {} peers: {:?}", peers.len(), &peers); // TODO make it return error if these peers are wrong and seem like an attack for peer in peers.iter() {