mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
Addition of configuration file (#79)
* addition of configuration file to grin executable, instructions on how it's picked up are in grin.toml * Starting to add a configuration file, and change some elements of configuration structures to make them more consistent
This commit is contained in:
parent
e2ebd854e1
commit
1ede61d2a2
13 changed files with 531 additions and 77 deletions
|
@ -4,11 +4,12 @@ version = "0.1.0"
|
||||||
authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
|
authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["api", "chain", "core", "grin", "p2p", "store", "util", "pool", "wallet"]
|
members = ["api", "chain", "config", "core", "grin", "p2p", "store", "util", "pool", "wallet"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
grin_api = { path = "./api" }
|
grin_api = { path = "./api" }
|
||||||
grin_wallet = { path = "./wallet" }
|
grin_wallet = { path = "./wallet" }
|
||||||
|
grin_config = { path = "./config" }
|
||||||
secp256k1zkp = { path = "./secp256k1zkp" }
|
secp256k1zkp = { path = "./secp256k1zkp" }
|
||||||
|
|
||||||
clap = "^2.23.3"
|
clap = "^2.23.3"
|
||||||
|
|
14
config/Cargo.toml
Normal file
14
config/Cargo.toml
Normal file
|
@ -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"}
|
212
config/src/config.rs
Normal file
212
config/src/config.rs
Normal file
|
@ -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<GlobalConfig, ConfigError> {
|
||||||
|
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<GlobalConfig, ConfigError> {
|
||||||
|
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<ConfigMembers, toml::de::Error> = 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<String, ConfigError> {
|
||||||
|
let encoded:Result<String, toml::ser::Error> = 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");
|
||||||
|
}
|
36
config/src/lib.rs
Normal file
36
config/src/lib.rs
Normal file
|
@ -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};
|
103
config/src/types.rs
Normal file
103
config/src/types.rs
Normal file
|
@ -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<io::Error> 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<PathBuf>,
|
||||||
|
//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<ConfigMembers>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<MinerConfig>,
|
||||||
|
//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<WalletConfig>
|
||||||
|
}
|
60
grin.toml
Normal file
60
grin.toml
Normal file
|
@ -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
|
|
@ -95,14 +95,15 @@ impl Miner {
|
||||||
let mut sol = None;
|
let mut sol = None;
|
||||||
debug!("(Server ID: {}) Mining at Cuckoo{} for at most 2 secs on block {} at difficulty {}.",
|
debug!("(Server ID: {}) Mining at Cuckoo{} for at most 2 secs on block {} at difficulty {}.",
|
||||||
self.debug_output_id,
|
self.debug_output_id,
|
||||||
self.config.cuckoo_size,
|
self.config.cuckoo_size.unwrap(),
|
||||||
latest_hash,
|
latest_hash,
|
||||||
b.header.difficulty);
|
b.header.difficulty);
|
||||||
let mut iter_count = 0;
|
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.",
|
debug!("(Server ID: {}) Artificially slowing down loop by {}ms per iteration.",
|
||||||
self.debug_output_id,
|
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 {
|
while head.hash() == latest_hash && time::get_time().sec < deadline {
|
||||||
let pow_hash = b.hash();
|
let pow_hash = b.hash();
|
||||||
|
@ -123,8 +124,8 @@ impl Miner {
|
||||||
iter_count += 1;
|
iter_count += 1;
|
||||||
|
|
||||||
//Artificial slow down
|
//Artificial slow down
|
||||||
if self.config.slow_down_in_millis > 0 {
|
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));
|
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 {}.",
|
info!("(Server ID: {}) Found valid proof of work, adding block {}.",
|
||||||
self.debug_output_id, b.hash());
|
self.debug_output_id, b.hash());
|
||||||
b.header.pow = proof;
|
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
|
chain::EASY_POW
|
||||||
} else {
|
} else {
|
||||||
chain::NONE
|
chain::NONE
|
||||||
|
|
|
@ -62,10 +62,10 @@ impl Server {
|
||||||
check_config(&mut config);
|
check_config(&mut config);
|
||||||
let mut evtlp = reactor::Core::new().unwrap();
|
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())?;
|
let serv = Server::future(config, &evtlp.handle())?;
|
||||||
if mining_config.enable_mining {
|
if mining_config.as_mut().unwrap().enable_mining {
|
||||||
serv.start_miner(mining_config);
|
serv.start_miner(mining_config.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
let forever = Timer::default()
|
let forever = Timer::default()
|
||||||
|
@ -99,14 +99,14 @@ impl Server {
|
||||||
tx_pool.clone(),
|
tx_pool.clone(),
|
||||||
peer_store.clone()));
|
peer_store.clone()));
|
||||||
let p2p_server =
|
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());
|
chain_adapter.init(p2p_server.clone());
|
||||||
|
|
||||||
let seed = seed::Seeder::new(config.capabilities, peer_store.clone(), p2p_server.clone());
|
let seed = seed::Seeder::new(config.capabilities, peer_store.clone(), p2p_server.clone());
|
||||||
match config.seeding_type.clone() {
|
match config.seeding_type.clone() {
|
||||||
Seeding::None => {}
|
Seeding::None => {}
|
||||||
Seeding::List(seeds) => {
|
Seeding::List => {
|
||||||
seed.connect_and_monitor(evt_handle.clone(), seed::predefined_seeds(seeds));
|
seed.connect_and_monitor(evt_handle.clone(), seed::predefined_seeds(config.seeds.as_mut().unwrap().clone()));
|
||||||
}
|
}
|
||||||
Seeding::WebStatic => {
|
Seeding::WebStatic => {
|
||||||
seed.connect_and_monitor(evt_handle.clone(), seed::web_seeds(evt_handle.clone()));
|
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"))]
|
#[cfg(not(feature = "use-cuckoo-miner"))]
|
||||||
pub fn start_miner(&self, config: MinerConfig) {
|
pub fn start_miner(&self, config: MinerConfig) {
|
||||||
let mut miner = miner::Miner::new(config.clone(), self.chain.clone(), self.tx_pool.clone());
|
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 || {
|
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);
|
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());
|
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.port));
|
||||||
thread::spawn(move || {
|
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);
|
miner.run_loop(test_cuckoo_miner);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -186,9 +186,9 @@ impl Server {
|
||||||
|
|
||||||
fn check_config(config: &mut ServerConfig) {
|
fn check_config(config: &mut ServerConfig) {
|
||||||
// applying test/normal config
|
// applying test/normal config
|
||||||
config.mining_config.cuckoo_size = if config.test_mode {
|
config.mining_config.as_mut().unwrap().cuckoo_size = if config.test_mode {
|
||||||
consensus::TEST_SIZESHIFT as u32
|
Some(consensus::TEST_SIZESHIFT as u32)
|
||||||
} else {
|
} else {
|
||||||
consensus::DEFAULT_SIZESHIFT as u32
|
Some(consensus::DEFAULT_SIZESHIFT as u32)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ pub enum Seeding {
|
||||||
/// No seeding, mostly for tests that programmatically connect
|
/// No seeding, mostly for tests that programmatically connect
|
||||||
None,
|
None,
|
||||||
/// A list of seed addresses provided to the server
|
/// A list of seed addresses provided to the server
|
||||||
List(Vec<String>),
|
List,
|
||||||
/// Automatically download a text file with a list of server addresses
|
/// Automatically download a text file with a list of server addresses
|
||||||
WebStatic,
|
WebStatic,
|
||||||
}
|
}
|
||||||
|
@ -77,21 +77,24 @@ pub struct ServerConfig {
|
||||||
/// Network address for the Rest API HTTP server.
|
/// Network address for the Rest API HTTP server.
|
||||||
pub api_http_addr: String,
|
pub api_http_addr: String,
|
||||||
|
|
||||||
/// Capabilities expose by this node, also conditions which other peers this
|
/// Setup the server for tests and testnet
|
||||||
/// node will have an affinity toward when connection.
|
pub test_mode: bool,
|
||||||
pub capabilities: p2p::Capabilities,
|
|
||||||
|
|
||||||
/// Method used to get the list of seed nodes for initial bootstrap.
|
/// Method used to get the list of seed nodes for initial bootstrap.
|
||||||
pub seeding_type: Seeding,
|
pub seeding_type: Seeding,
|
||||||
|
|
||||||
|
/// The list of seed nodes, if using Seeding as a seed type
|
||||||
|
pub seeds: Option<Vec<String>>,
|
||||||
|
|
||||||
|
/// Capabilities expose by this node, also conditions which other peers this
|
||||||
|
/// node will have an affinity toward when connection.
|
||||||
|
pub capabilities: p2p::Capabilities,
|
||||||
|
|
||||||
/// Configuration for the peer-to-peer server
|
/// Configuration for the peer-to-peer server
|
||||||
pub p2p_config: p2p::P2PConfig,
|
pub p2p_config: Option<p2p::P2PConfig>,
|
||||||
|
|
||||||
/// Configuration for the mining daemon
|
/// Configuration for the mining daemon
|
||||||
pub mining_config: MinerConfig,
|
pub mining_config: Option<MinerConfig>,
|
||||||
|
|
||||||
/// Setup the server for tests and testnet
|
|
||||||
pub test_mode: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mining configuration
|
/// Mining configuration
|
||||||
|
@ -109,10 +112,10 @@ pub struct MinerConfig {
|
||||||
|
|
||||||
/// a testing attribute for the time being that artifically slows down the
|
/// a testing attribute for the time being that artifically slows down the
|
||||||
/// mining loop by adding a sleep to the thread
|
/// mining loop by adding a sleep to the thread
|
||||||
pub slow_down_in_millis: u64,
|
pub slow_down_in_millis: Option<u64>,
|
||||||
|
|
||||||
/// Size of Cuckoo Cycle to mine on
|
/// Size of Cuckoo Cycle to mine on
|
||||||
pub cuckoo_size: u32,
|
pub cuckoo_size: Option<u32>,
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -124,8 +127,9 @@ impl Default for ServerConfig {
|
||||||
api_http_addr: "127.0.0.1:13415".to_string(),
|
api_http_addr: "127.0.0.1:13415".to_string(),
|
||||||
capabilities: p2p::FULL_NODE,
|
capabilities: p2p::FULL_NODE,
|
||||||
seeding_type: Seeding::None,
|
seeding_type: Seeding::None,
|
||||||
p2p_config: p2p::P2PConfig::default(),
|
seeds: None,
|
||||||
mining_config: MinerConfig::default(),
|
p2p_config: Some(p2p::P2PConfig::default()),
|
||||||
|
mining_config: Some(MinerConfig::default()),
|
||||||
test_mode: true,
|
test_mode: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,8 +141,8 @@ impl Default for MinerConfig {
|
||||||
enable_mining: false,
|
enable_mining: false,
|
||||||
wallet_receiver_url: "http://localhost:13416".to_string(),
|
wallet_receiver_url: "http://localhost:13416".to_string(),
|
||||||
burn_reward: false,
|
burn_reward: false,
|
||||||
slow_down_in_millis: 0,
|
slow_down_in_millis: Some(0),
|
||||||
cuckoo_size: 0
|
cuckoo_size: Some(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,9 +203,11 @@ impl LocalServerContainer {
|
||||||
let api_addr = format!("{}:{}", self.config.base_addr, self.config.api_server_port);
|
let api_addr = format!("{}:{}", self.config.base_addr, self.config.api_server_port);
|
||||||
|
|
||||||
let mut seeding_type=grin::Seeding::None;
|
let mut seeding_type=grin::Seeding::None;
|
||||||
|
let mut seeds=Vec::new();
|
||||||
|
|
||||||
if self.config.seed_addr.len()>0{
|
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{
|
grin::ServerConfig{
|
||||||
api_http_addr: api_addr,
|
api_http_addr: api_addr,
|
||||||
db_root: format!("{}/.grin", self.working_dir),
|
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,
|
seeding_type: seeding_type,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}, &event_loop.handle()).unwrap();
|
}, &event_loop.handle()).unwrap();
|
||||||
|
@ -229,9 +232,9 @@ impl LocalServerContainer {
|
||||||
let mut miner_config = grin::MinerConfig {
|
let mut miner_config = grin::MinerConfig {
|
||||||
enable_mining: self.config.start_miner,
|
enable_mining: self.config.start_miner,
|
||||||
burn_reward: self.config.burn_mining_rewards,
|
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(),
|
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()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -199,7 +199,7 @@ fn simulate_block_propagation() {
|
||||||
let miner_config = grin::MinerConfig{
|
let miner_config = grin::MinerConfig{
|
||||||
enable_mining: true,
|
enable_mining: true,
|
||||||
burn_reward: true,
|
burn_reward: true,
|
||||||
cuckoo_size: consensus::TEST_SIZESHIFT as u32,
|
cuckoo_size: Some(consensus::TEST_SIZESHIFT as u32),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ fn simulate_block_propagation() {
|
||||||
grin::ServerConfig{
|
grin::ServerConfig{
|
||||||
api_http_addr: format!("127.0.0.1:{}", 20000+n),
|
api_http_addr: format!("127.0.0.1:{}", 20000+n),
|
||||||
db_root: format!("target/{}/grin-prop-{}", test_name_dir, 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()
|
..Default::default()
|
||||||
}, &handle).unwrap();
|
}, &handle).unwrap();
|
||||||
servers.push(s);
|
servers.push(s);
|
||||||
|
@ -256,7 +256,7 @@ fn simulate_full_sync() {
|
||||||
let miner_config = grin::MinerConfig{
|
let miner_config = grin::MinerConfig{
|
||||||
enable_mining: true,
|
enable_mining: true,
|
||||||
burn_reward: true,
|
burn_reward: true,
|
||||||
cuckoo_size: consensus::TEST_SIZESHIFT as u32,
|
cuckoo_size: Some(consensus::TEST_SIZESHIFT as u32),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ fn simulate_full_sync() {
|
||||||
let s = grin::Server::future(
|
let s = grin::Server::future(
|
||||||
grin::ServerConfig{
|
grin::ServerConfig{
|
||||||
db_root: format!("target/{}/grin-sync-{}", test_name_dir, n),
|
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()
|
..Default::default()
|
||||||
}, &handle).unwrap();
|
}, &handle).unwrap();
|
||||||
servers.push(s);
|
servers.push(s);
|
||||||
|
|
|
@ -26,10 +26,9 @@ extern crate tiny_keccak;
|
||||||
extern crate grin_api as api;
|
extern crate grin_api as api;
|
||||||
extern crate grin_grin as grin;
|
extern crate grin_grin as grin;
|
||||||
extern crate grin_wallet as wallet;
|
extern crate grin_wallet as wallet;
|
||||||
|
extern crate grin_config as config;
|
||||||
extern crate secp256k1zkp as secp;
|
extern crate secp256k1zkp as secp;
|
||||||
|
|
||||||
const GRIN_HOME: &'static str = ".grin";
|
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
@ -44,6 +43,16 @@ use secp::Secp256k1;
|
||||||
|
|
||||||
use wallet::WalletConfig;
|
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() {
|
fn main() {
|
||||||
env_logger::init().unwrap();
|
env_logger::init().unwrap();
|
||||||
|
|
||||||
|
@ -80,12 +89,6 @@ fn main() {
|
||||||
.long("wallet_url")
|
.long("wallet_url")
|
||||||
.help("A listening wallet receiver to which mining rewards will be sent")
|
.help("A listening wallet receiver to which mining rewards will be sent")
|
||||||
.takes_value(true))
|
.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")
|
.subcommand(SubCommand::with_name("start")
|
||||||
.about("Start the Grin server as a daemon"))
|
.about("Start the Grin server as a daemon"))
|
||||||
.subcommand(SubCommand::with_name("stop")
|
.subcommand(SubCommand::with_name("stop")
|
||||||
|
@ -162,7 +165,34 @@ fn main() {
|
||||||
wallet_command(wallet_args);
|
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) {
|
fn server_command(server_args: &ArgMatches) {
|
||||||
info!("Starting the Grin server...");
|
info!("Starting the Grin server...");
|
||||||
|
|
||||||
// configuration wrangling
|
// just get defaults from the global config
|
||||||
let mut server_config = read_config();
|
let mut server_config = GlobalConfig::default().members.unwrap().server;
|
||||||
|
|
||||||
if let Some(port) = server_args.value_of("port") {
|
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") {
|
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") {
|
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") {
|
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") {
|
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)
|
// start the server in the different run modes (interactive or daemon)
|
||||||
match server_args.subcommand() {
|
match server_args.subcommand() {
|
||||||
("run", _) => {
|
("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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -76,8 +76,10 @@ impl From<api::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct WalletConfig {
|
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
|
//The api address that this api server (i.e. this wallet) will run
|
||||||
pub api_http_addr: String,
|
pub api_http_addr: String,
|
||||||
//The api address of a running server node, against which transaction inputs will be checked
|
//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 {
|
impl Default for WalletConfig {
|
||||||
fn default() -> WalletConfig {
|
fn default() -> WalletConfig {
|
||||||
WalletConfig {
|
WalletConfig {
|
||||||
|
enable_wallet: false,
|
||||||
api_http_addr: "http://127.0.0.1:13415".to_string(),
|
api_http_addr: "http://127.0.0.1:13415".to_string(),
|
||||||
check_node_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(),
|
data_file_dir: ".".to_string(),
|
||||||
|
|
Loading…
Reference in a new issue