diff --git a/Cargo.toml b/Cargo.toml index 52e8ea684..59f08788d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,12 @@ version = "0.1.0" authors = ["Ignotus Peverell "] [workspace] -members = ["api", "chain", "core", "grin", "p2p", "store", "util", "pool", "wallet"] +members = ["api", "chain", "config", "core", "grin", "p2p", "store", "util", "pool", "wallet"] [dependencies] grin_api = { path = "./api" } grin_wallet = { path = "./wallet" } +grin_config = { path = "./config" } secp256k1zkp = { path = "./secp256k1zkp" } clap = "^2.23.3" diff --git a/config/Cargo.toml b/config/Cargo.toml new file mode 100644 index 000000000..f8853e543 --- /dev/null +++ b/config/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "grin_config" +version = "0.1.0" +authors = [" yeastplume"] +workspace = ".." + +[dependencies] +serde = "~1.0.8" +serde_derive = "~1.0.8" +toml = "0.4" + +grin_grin = { path = "../grin" } +grin_p2p = { path = "../p2p" } +grin_wallet = { path = "../wallet"} diff --git a/config/src/config.rs b/config/src/config.rs new file mode 100644 index 000000000..be84be799 --- /dev/null +++ b/config/src/config.rs @@ -0,0 +1,212 @@ +// Copyright 2017 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. + +//! Configuration file management + +use std::env; +use std::io::Read; +use std::path::PathBuf; +use std::fs::File; + +use toml; +use grin::{ServerConfig, + MinerConfig}; +use wallet::WalletConfig; + +use types::{ConfigMembers, + GlobalConfig, + ConfigError}; + +/// The default file name to use when trying to derive +/// the config file location + +const CONFIG_FILE_NAME: &'static str = "grin.toml"; +const GRIN_HOME: &'static str = ".grin"; + +/// Returns the defaults, as strewn throughout the code + +impl Default for ConfigMembers { + fn default() -> ConfigMembers { + ConfigMembers { + server: ServerConfig::default(), + mining: Some(MinerConfig::default()), + //wallet: Some(WalletConfig::default()), + } + } +} + +impl Default for GlobalConfig { + fn default() -> GlobalConfig{ + GlobalConfig { + config_file_path: None, + using_config_file: false, + members: Some(ConfigMembers::default()) + } + } +} + +impl GlobalConfig { + + /// Need to decide on rules where to read the config file from, + /// but will take a stab at logic for now + + fn derive_config_location(&mut self) -> Result<(), ConfigError> { + //First, check working directory + let mut config_path = env::current_dir().unwrap(); + config_path.push(CONFIG_FILE_NAME); + if config_path.exists() { + self.config_file_path = Some(config_path); + return Ok(()) + } + //Next, look in directory of executable + let mut config_path=env::current_exe().unwrap(); + config_path.pop(); + config_path.push(CONFIG_FILE_NAME); + if config_path.exists() { + self.config_file_path = Some(config_path); + return Ok(()) + } + //Then look in {user_home}/.grin + let config_path = env::home_dir(); + if let Some(mut p) = config_path { + p.push(GRIN_HOME); + p.push(CONFIG_FILE_NAME); + if p.exists() { + self.config_file_path = Some(p); + return Ok(()) + } + } + + // Give up + Err(ConfigError::FileNotFoundError(String::from(""))) + + } + + /// Takes the path to a config file, or if NONE, tries + /// to determine a config file based on rules in + /// derive_config_location + + pub fn new(file_path:Option<&str>) -> Result { + let mut return_value = GlobalConfig::default(); + if let Some(fp) = file_path { + return_value.config_file_path = Some(PathBuf::from(&fp)); + } else { + return_value.derive_config_location(); + } + + //No attempt at a config file, just return defaults + if let None = return_value.config_file_path { + return Ok(return_value); + } + + //Config file path is given but not valid + if !return_value.config_file_path.as_mut().unwrap().exists() { + return Err( + ConfigError::FileNotFoundError(String::from(return_value.config_file_path.as_mut() + .unwrap().to_str().unwrap().clone())) + ); + } + + //Try to parse the config file if it exists + //explode if it does exist but something's wrong + //with it + return_value.read_config() + } + + pub fn read_config(mut self) -> Result { + let mut file = File::open(self.config_file_path.as_mut().unwrap())?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + let decoded:Result = toml::from_str(&contents); + match decoded { + Ok(mut gc) => { + //Put the struct back together, because the config + //file was flattened a bit + gc.server.mining_config = gc.mining.clone(); + self.using_config_file = true; + self.members = Some(gc); + return Ok(self) + }, + Err (e) => { + return Err( + ConfigError::ParseError(String::from(self.config_file_path.as_mut() + .unwrap().to_str().unwrap().clone()), + String::from(format!("{}", e)) + ) + ); + } + } + } + + pub fn ser_config(&mut self) -> Result { + let encoded:Result = toml::to_string(self.members.as_mut().unwrap()); + match encoded { + Ok(enc) => { + return Ok(enc) + }, + Err (e) => { + return Err( + ConfigError::SerializationError( + String::from(format!("{}", e)) + ) + ); + } + } + } + + /*pub fn wallet_enabled(&mut self) -> bool { + return self.members.as_mut().unwrap().wallet.as_mut().unwrap().enable_wallet; + }*/ + + pub fn mining_enabled(&mut self) -> bool { + return self.members.as_mut().unwrap().mining.as_mut().unwrap().enable_mining; + } +} + +#[test] +fn test_read_config() { + let toml_str = r#" + #Section is optional, if not here or enable_server is false, will only run wallet + [server] + enable_server = true + api_http_addr = "127.0.0.1" + db_root = "." + seeding_type = "None" + test_mode = false + #7 = FULL_NODE, not sure how to serialise this properly to use constants + capabilities = [7] + + [server.p2p_config] + host = "127.0.0.1" + port = 13414 + + #Mining section is optional, if it's not here it will default to not mining + [mining] + enable_mining = true + wallet_receiver_url = "http://127.0.0.1:13415" + burn_reward = false + #testing value, optional + #slow_down_in_millis = 30 + #testing value, should really be removed and read from consensus instead, optional + #cuckoo_size = 12 + + + "#; + + let mut decoded: GlobalConfig = toml::from_str(toml_str).unwrap(); + decoded.server.as_mut().unwrap().mining_config = decoded.mining; + println!("Decoded.server: {:?}", decoded.server); + println!("Decoded wallet: {:?}", decoded.wallet); + panic!("panic"); +} \ No newline at end of file diff --git a/config/src/lib.rs b/config/src/lib.rs new file mode 100644 index 000000000..c4ecf78b9 --- /dev/null +++ b/config/src/lib.rs @@ -0,0 +1,36 @@ +// Copyright 2017 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. + +//! Crate wrapping up the Grin binary and configuration file + +#![deny(non_upper_case_globals)] +#![deny(non_camel_case_types)] +#![deny(non_snake_case)] +#![deny(unused_mut)] +#![warn(missing_docs)] + + +extern crate serde; +#[macro_use] +extern crate serde_derive; +extern crate toml; + +extern crate grin_grin as grin; +extern crate grin_p2p as p2p; +extern crate grin_wallet as wallet; + +pub mod config; +pub mod types; + +pub use types::{GlobalConfig, ConfigMembers, ConfigError}; \ No newline at end of file diff --git a/config/src/types.rs b/config/src/types.rs new file mode 100644 index 000000000..157deaed9 --- /dev/null +++ b/config/src/types.rs @@ -0,0 +1,103 @@ +// Copyright 2017 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. + +//! Public types for config modules + +use std::path::PathBuf; +use std::io; +use std::fmt; + +use grin::{ServerConfig, + MinerConfig}; +use wallet::WalletConfig; + + +/// Error type wrapping config errors. +#[derive(Debug)] +pub enum ConfigError { + /// Error with parsing of config file + ParseError (String, String), + + /// Error with fileIO while reading config file + FileIOError (String, String), + + /// No file found + FileNotFoundError (String), + + /// Error serializing config values + SerializationError (String), +} + +impl fmt::Display for ConfigError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ConfigError::ParseError(ref file_name, ref message) => { + write!(f, "Error parsing configuration file at {} - {}",file_name, message) + } + ConfigError::FileIOError(ref file_name, ref message) => { + write!(f, "{} {}", message, file_name) + } + ConfigError::FileNotFoundError(ref file_name) => { + write!(f, "Configuration file not found: {}", file_name) + } + ConfigError::SerializationError(ref message) => { + write!(f, "Error serializing configuration: {}", message) + } + } + } +} + +impl From for ConfigError { + fn from(error: io::Error) -> ConfigError { + ConfigError::FileIOError( + String::from(""), + String::from(format!("Error loading config file: {}",error)), + ) + } +} + +/// Going to hold all of the various configuration types +/// separately for now, then put them together as a single +/// ServerConfig object afterwards. This is to flatten +/// out the configuration file into logical sections, +/// as they tend to be quite nested in the code +/// Most structs optional, as they may or may not +/// be needed depending on what's being run + +#[derive(Debug, Serialize, Deserialize)] +pub struct GlobalConfig { + //Keep track of the file we've read + pub config_file_path: Option, + //keep track of whether we're using + //a config file or just the defaults + //for each member + pub using_config_file: bool, + pub members: Option, +} + +/// Keeping an 'inner' structure here, as the top +/// level GlobalConfigContainer options might want to keep +/// internal state that we don't necessarily +/// want serialised or deserialised + +#[derive(Debug, Serialize, Deserialize)] +pub struct ConfigMembers { + pub server: ServerConfig, + pub mining: Option, + //removing wallet from here for now, + //as its concerns are separate from the server's, really + //given it needs to manage keys. It should probably + //stay command line only for the time being + //pub wallet: Option +} \ No newline at end of file diff --git a/grin.toml b/grin.toml new file mode 100644 index 000000000..c63dedca4 --- /dev/null +++ b/grin.toml @@ -0,0 +1,60 @@ +# Sample Server Configuration File for Grin +# +# When running the grin executable without specifying any command line +# arguments, it will look for this file in three places, in the following +# order: +# +# -The working directory +# -The directory in which the executable resides +# -[user home]/.grin +# + +#Server connection details +[server] + +#the address on which services will listen, e.g. Transaction Pool +api_http_addr = "127.0.0.1:13413" + +#the directory, relative to current, in which the grin blockchain +#is stored +db_root = ".grin" + +#How to seed this server, can be None, List or WebStatic +seeding_type = "None" + +#if seeding_type = List, the list of peers to connect to. +#seeds = ["192.168.0.1:8080","192.168.0.2:8080"] + +#Whether to run in test mode, which at the moment affects cuckoo_size +#if this is false tries to use a slow cuckoo30 at the moment, not +#recommended +test_mode = true + +#7 = Bit flags for FULL_NODE, this structure needs to be changed +#internally to make it more configurable +capabilities = [7] + +#The P2P server details (i.e. the server that communicates with other +#grin server nodes +[server.p2p_config] +host = "127.0.0.1" +port = 13414 + +#Mining details. This section is optional. If it's not here, the server +#will default to not mining. + +[mining] +#flag whether mining is enabled +enable_mining = true + +#the wallet reciever to which coinbase rewards will be sent +wallet_receiver_url = "http://127.0.0.1:13415" + +#whether to ignore the reward (mostly for testing) +burn_reward = true + +#testing value, optional +#slow_down_in_millis = 30 + +#testing value, should really be removed and read from consensus instead, optional +#cuckoo_size = 12 \ No newline at end of file diff --git a/grin/src/miner.rs b/grin/src/miner.rs index 9ab1b8239..f82d605ab 100644 --- a/grin/src/miner.rs +++ b/grin/src/miner.rs @@ -95,14 +95,15 @@ impl Miner { let mut sol = None; debug!("(Server ID: {}) Mining at Cuckoo{} for at most 2 secs on block {} at difficulty {}.", self.debug_output_id, - self.config.cuckoo_size, + self.config.cuckoo_size.unwrap(), latest_hash, b.header.difficulty); let mut iter_count = 0; - if self.config.slow_down_in_millis > 0 { + + if self.config.slow_down_in_millis != None && self.config.slow_down_in_millis.unwrap() > 0 { debug!("(Server ID: {}) Artificially slowing down loop by {}ms per iteration.", self.debug_output_id, - self.config.slow_down_in_millis); + self.config.slow_down_in_millis.unwrap()); } while head.hash() == latest_hash && time::get_time().sec < deadline { let pow_hash = b.hash(); @@ -123,8 +124,8 @@ impl Miner { iter_count += 1; //Artificial slow down - if self.config.slow_down_in_millis > 0 { - thread::sleep(std::time::Duration::from_millis(self.config.slow_down_in_millis)); + if self.config.slow_down_in_millis != None && self.config.slow_down_in_millis.unwrap() > 0 { + thread::sleep(std::time::Duration::from_millis(self.config.slow_down_in_millis.unwrap())); } } @@ -133,7 +134,7 @@ impl Miner { info!("(Server ID: {}) Found valid proof of work, adding block {}.", self.debug_output_id, b.hash()); b.header.pow = proof; - let opts = if self.config.cuckoo_size < consensus::DEFAULT_SIZESHIFT as u32 { + let opts = if self.config.cuckoo_size.unwrap() < consensus::DEFAULT_SIZESHIFT as u32 { chain::EASY_POW } else { chain::NONE diff --git a/grin/src/server.rs b/grin/src/server.rs index 98e3d6202..e20fbcf85 100644 --- a/grin/src/server.rs +++ b/grin/src/server.rs @@ -62,10 +62,10 @@ impl Server { check_config(&mut config); let mut evtlp = reactor::Core::new().unwrap(); - let mining_config = config.mining_config.clone(); + let mut mining_config = config.mining_config.clone(); let serv = Server::future(config, &evtlp.handle())?; - if mining_config.enable_mining { - serv.start_miner(mining_config); + if mining_config.as_mut().unwrap().enable_mining { + serv.start_miner(mining_config.unwrap()); } let forever = Timer::default() @@ -99,14 +99,14 @@ impl Server { tx_pool.clone(), peer_store.clone())); let p2p_server = - Arc::new(p2p::Server::new(config.capabilities, config.p2p_config, net_adapter.clone())); + Arc::new(p2p::Server::new(config.capabilities, config.p2p_config.unwrap(), net_adapter.clone())); chain_adapter.init(p2p_server.clone()); let seed = seed::Seeder::new(config.capabilities, peer_store.clone(), p2p_server.clone()); match config.seeding_type.clone() { Seeding::None => {} - Seeding::List(seeds) => { - seed.connect_and_monitor(evt_handle.clone(), seed::predefined_seeds(seeds)); + Seeding::List => { + seed.connect_and_monitor(evt_handle.clone(), seed::predefined_seeds(config.seeds.as_mut().unwrap().clone())); } Seeding::WebStatic => { seed.connect_and_monitor(evt_handle.clone(), seed::web_seeds(evt_handle.clone())); @@ -150,9 +150,9 @@ impl Server { #[cfg(not(feature = "use-cuckoo-miner"))] pub fn start_miner(&self, config: MinerConfig) { let mut miner = miner::Miner::new(config.clone(), self.chain.clone(), self.tx_pool.clone()); - miner.set_debug_output_id(format!("Port {}",self.config.p2p_config.port)); + miner.set_debug_output_id(format!("Port {}",self.config.p2p_config.unwrap().port)); thread::spawn(move || { - let test_cuckoo_miner = cuckoo::Miner::new(consensus::EASINESS, config.cuckoo_size.clone()); + let test_cuckoo_miner = cuckoo::Miner::new(consensus::EASINESS, config.cuckoo_size.unwrap().clone()); miner.run_loop(test_cuckoo_miner); }); } @@ -163,7 +163,7 @@ impl Server { let mut miner = miner::Miner::new(config.clone(), self.chain.clone(), self.tx_pool.clone()); miner.set_debug_output_id(format!("Port {}",self.config.p2p_config.port)); thread::spawn(move || { - let test_cuckoo_miner = PluginMiner::new(consensus::EASINESS, config.cuckoo_size.clone()); + let test_cuckoo_miner = PluginMiner::new(consensus::EASINESS, config.cuckoo_size.unwrap().clone()); miner.run_loop(test_cuckoo_miner); }); } @@ -186,9 +186,9 @@ impl Server { fn check_config(config: &mut ServerConfig) { // applying test/normal config - config.mining_config.cuckoo_size = if config.test_mode { - consensus::TEST_SIZESHIFT as u32 + config.mining_config.as_mut().unwrap().cuckoo_size = if config.test_mode { + Some(consensus::TEST_SIZESHIFT as u32) } else { - consensus::DEFAULT_SIZESHIFT as u32 + Some(consensus::DEFAULT_SIZESHIFT as u32) }; } diff --git a/grin/src/types.rs b/grin/src/types.rs index 20e23e9bf..85ef1f1d5 100644 --- a/grin/src/types.rs +++ b/grin/src/types.rs @@ -62,7 +62,7 @@ pub enum Seeding { /// No seeding, mostly for tests that programmatically connect None, /// A list of seed addresses provided to the server - List(Vec), + List, /// Automatically download a text file with a list of server addresses WebStatic, } @@ -77,21 +77,24 @@ pub struct ServerConfig { /// Network address for the Rest API HTTP server. pub api_http_addr: String, + /// Setup the server for tests and testnet + pub test_mode: bool, + + /// Method used to get the list of seed nodes for initial bootstrap. + pub seeding_type: Seeding, + + /// The list of seed nodes, if using Seeding as a seed type + pub seeds: Option>, + /// Capabilities expose by this node, also conditions which other peers this /// node will have an affinity toward when connection. pub capabilities: p2p::Capabilities, - /// Method used to get the list of seed nodes for initial bootstrap. - pub seeding_type: Seeding, - /// Configuration for the peer-to-peer server - pub p2p_config: p2p::P2PConfig, + pub p2p_config: Option, /// Configuration for the mining daemon - pub mining_config: MinerConfig, - - /// Setup the server for tests and testnet - pub test_mode: bool, + pub mining_config: Option, } /// Mining configuration @@ -109,10 +112,10 @@ pub struct MinerConfig { /// a testing attribute for the time being that artifically slows down the /// mining loop by adding a sleep to the thread - pub slow_down_in_millis: u64, + pub slow_down_in_millis: Option, /// Size of Cuckoo Cycle to mine on - pub cuckoo_size: u32, + pub cuckoo_size: Option, } @@ -124,8 +127,9 @@ impl Default for ServerConfig { api_http_addr: "127.0.0.1:13415".to_string(), capabilities: p2p::FULL_NODE, seeding_type: Seeding::None, - p2p_config: p2p::P2PConfig::default(), - mining_config: MinerConfig::default(), + seeds: None, + p2p_config: Some(p2p::P2PConfig::default()), + mining_config: Some(MinerConfig::default()), test_mode: true, } } @@ -137,8 +141,8 @@ impl Default for MinerConfig { enable_mining: false, wallet_receiver_url: "http://localhost:13416".to_string(), burn_reward: false, - slow_down_in_millis: 0, - cuckoo_size: 0 + slow_down_in_millis: Some(0), + cuckoo_size: Some(0) } } } diff --git a/grin/tests/framework.rs b/grin/tests/framework.rs index 0447bf28c..002f7adfe 100644 --- a/grin/tests/framework.rs +++ b/grin/tests/framework.rs @@ -203,9 +203,11 @@ impl LocalServerContainer { let api_addr = format!("{}:{}", self.config.base_addr, self.config.api_server_port); let mut seeding_type=grin::Seeding::None; + let mut seeds=Vec::new(); if self.config.seed_addr.len()>0{ - seeding_type=grin::Seeding::List(vec![self.config.seed_addr.to_string()]); + seeding_type=grin::Seeding::List; + seeds=vec![self.config.seed_addr.to_string()]; } @@ -213,7 +215,8 @@ impl LocalServerContainer { grin::ServerConfig{ api_http_addr: api_addr, db_root: format!("{}/.grin", self.working_dir), - p2p_config: p2p::P2PConfig{port: self.config.p2p_server_port, ..p2p::P2PConfig::default()}, + p2p_config: Some(p2p::P2PConfig{port: self.config.p2p_server_port, ..p2p::P2PConfig::default()}), + seeds: Some(seeds), seeding_type: seeding_type, ..Default::default() }, &event_loop.handle()).unwrap(); @@ -229,9 +232,9 @@ impl LocalServerContainer { let mut miner_config = grin::MinerConfig { enable_mining: self.config.start_miner, burn_reward: self.config.burn_mining_rewards, - cuckoo_size: self.config.cuckoo_size, + cuckoo_size: Some(self.config.cuckoo_size), wallet_receiver_url : self.config.coinbase_wallet_address.clone(), - slow_down_in_millis: self.config.miner_slowdown_in_millis.clone(), + slow_down_in_millis: Some(self.config.miner_slowdown_in_millis.clone()), ..Default::default() }; diff --git a/grin/tests/simulnet.rs b/grin/tests/simulnet.rs index 1664dc0d2..95fe9eb7f 100644 --- a/grin/tests/simulnet.rs +++ b/grin/tests/simulnet.rs @@ -199,7 +199,7 @@ fn simulate_block_propagation() { let miner_config = grin::MinerConfig{ enable_mining: true, burn_reward: true, - cuckoo_size: consensus::TEST_SIZESHIFT as u32, + cuckoo_size: Some(consensus::TEST_SIZESHIFT as u32), ..Default::default() }; @@ -210,7 +210,7 @@ fn simulate_block_propagation() { grin::ServerConfig{ api_http_addr: format!("127.0.0.1:{}", 20000+n), db_root: format!("target/{}/grin-prop-{}", test_name_dir, n), - p2p_config: p2p::P2PConfig{port: 10000+n, ..p2p::P2PConfig::default()}, + p2p_config: Some(p2p::P2PConfig{port: 10000+n, ..p2p::P2PConfig::default()}), ..Default::default() }, &handle).unwrap(); servers.push(s); @@ -256,7 +256,7 @@ fn simulate_full_sync() { let miner_config = grin::MinerConfig{ enable_mining: true, burn_reward: true, - cuckoo_size: consensus::TEST_SIZESHIFT as u32, + cuckoo_size: Some(consensus::TEST_SIZESHIFT as u32), ..Default::default() }; @@ -266,7 +266,7 @@ fn simulate_full_sync() { let s = grin::Server::future( grin::ServerConfig{ db_root: format!("target/{}/grin-sync-{}", test_name_dir, n), - p2p_config: p2p::P2PConfig{port: 11000+n, ..p2p::P2PConfig::default()}, + p2p_config: Some(p2p::P2PConfig{port: 11000+n, ..p2p::P2PConfig::default()}), ..Default::default() }, &handle).unwrap(); servers.push(s); diff --git a/src/bin/grin.rs b/src/bin/grin.rs index 8dcf99425..c8d9090ff 100644 --- a/src/bin/grin.rs +++ b/src/bin/grin.rs @@ -26,10 +26,9 @@ extern crate tiny_keccak; extern crate grin_api as api; extern crate grin_grin as grin; extern crate grin_wallet as wallet; +extern crate grin_config as config; extern crate secp256k1zkp as secp; -const GRIN_HOME: &'static str = ".grin"; - use std::env; use std::thread; use std::io::Read; @@ -44,6 +43,16 @@ use secp::Secp256k1; use wallet::WalletConfig; +use config::{GlobalConfig, ConfigError}; + +fn start_from_config_file(mut global_config:GlobalConfig){ + info!("Starting the Grin server from configuration file at {}", global_config.config_file_path.unwrap().to_str().unwrap()); + grin::Server::start(global_config.members.as_mut().unwrap().server.clone()).unwrap(); + loop { + thread::sleep(Duration::from_secs(60)); + } +} + fn main() { env_logger::init().unwrap(); @@ -80,12 +89,6 @@ fn main() { .long("wallet_url") .help("A listening wallet receiver to which mining rewards will be sent") .takes_value(true)) - .arg(Arg::with_name("config") - .short("c") - .long("config") - .value_name("FILE.json") - .help("Sets a custom json configuration file") - .takes_value(true)) .subcommand(SubCommand::with_name("start") .about("Start the Grin server as a daemon")) .subcommand(SubCommand::with_name("stop") @@ -162,7 +165,34 @@ fn main() { wallet_command(wallet_args); } - _ => println!("Unknown command, use 'grin help' for a list of all commands"), + //If nothing is specified, try to load up and use a config file instead + //this should possibly become the way to configure most things + //with most command line options being phased out + _ => { + //This will return a global config object, + //which will either contain defaults for all + //of the config structures or a configuration + //read from a config file + + let mut global_config = GlobalConfig::new(None); + match global_config { + Ok(gc) => { + if (gc.using_config_file){ + start_from_config_file(gc); + } else { + //won't attempt to just start with defaults, + //and will reject + println!("Unknown command, and no configuration file was found."); + println!("Use 'grin help' for a list of all commands."); + } + } + Err(e) => { + println!("{}", e); + } + } + + + } } } @@ -173,10 +203,11 @@ fn main() { fn server_command(server_args: &ArgMatches) { info!("Starting the Grin server..."); - // configuration wrangling - let mut server_config = read_config(); + // just get defaults from the global config + let mut server_config = GlobalConfig::default().members.unwrap().server; + if let Some(port) = server_args.value_of("port") { - server_config.p2p_config.port = port.parse().unwrap(); + server_config.p2p_config.as_mut().unwrap().port = port.parse().unwrap(); } if let Some(api_port) = server_args.value_of("api_port") { @@ -185,17 +216,22 @@ fn server_command(server_args: &ArgMatches) { } if server_args.is_present("mine") { - server_config.mining_config.enable_mining = true; + server_config.mining_config.as_mut().unwrap().enable_mining = true; } if let Some(wallet_url) = server_args.value_of("wallet_url") { - server_config.mining_config.wallet_receiver_url = wallet_url.to_string(); + server_config.mining_config.as_mut().unwrap().wallet_receiver_url = wallet_url.to_string(); } if let Some(seeds) = server_args.values_of("seed") { - server_config.seeding_type = grin::Seeding::List(seeds.map(|s| s.to_string()).collect()); + server_config.seeding_type = grin::Seeding::List; + server_config.seeds = Some(seeds.map(|s| s.to_string()).collect()); } + /*let mut sc = GlobalConfig::default(); + sc.members.as_mut().unwrap().server = server_config.clone(); + println!("{}", sc.ser_config().unwrap());*/ + // start the server in the different run modes (interactive or daemon) match server_args.subcommand() { ("run", _) => { @@ -291,22 +327,3 @@ fn wallet_command(wallet_args: &ArgMatches) { } } -fn read_config() -> grin::ServerConfig { - let mut config_path = env::home_dir().ok_or("Failed to detect home directory!").unwrap(); - config_path.push(GRIN_HOME); - if !config_path.exists() { - return default_config(); - } - let mut config_file = File::open(config_path).unwrap(); - let mut config_content = String::new(); - config_file.read_to_string(&mut config_content).unwrap(); - serde_json::from_str(config_content.as_str()).unwrap() -} - -fn default_config() -> grin::ServerConfig { - grin::ServerConfig { - test_mode: true, - seeding_type: grin::Seeding::WebStatic, - ..Default::default() - } -} diff --git a/wallet/src/types.rs b/wallet/src/types.rs index 4e22dfb99..ad019c393 100644 --- a/wallet/src/types.rs +++ b/wallet/src/types.rs @@ -76,8 +76,10 @@ impl From for Error { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct WalletConfig { + //Whether to run a wallet + pub enable_wallet: bool, //The api address that this api server (i.e. this wallet) will run pub api_http_addr: String, //The api address of a running server node, against which transaction inputs will be checked @@ -90,6 +92,7 @@ pub struct WalletConfig { impl Default for WalletConfig { fn default() -> WalletConfig { WalletConfig { + enable_wallet: false, api_http_addr: "http://127.0.0.1:13415".to_string(), check_node_api_http_addr: "http://127.0.0.1:13415".to_string(), data_file_dir: ".".to_string(),