2017-06-25 04:21:13 -07:00
|
|
|
use std::path::{PathBuf, Path};
|
|
|
|
use std::fs;
|
|
|
|
use std::io::Read;
|
2017-08-01 02:36:48 -07:00
|
|
|
use std::time::Duration;
|
2017-08-02 03:45:15 -07:00
|
|
|
use error::{ResultExt, Error};
|
2017-06-25 04:21:13 -07:00
|
|
|
use toml;
|
2017-06-13 01:28:04 -07:00
|
|
|
|
2017-08-01 02:36:48 -07:00
|
|
|
const DEFAULT_POLL_INTERVAL: u64 = 1;
|
|
|
|
const DEFAULT_CONFIRMATIONS: u64 = 12;
|
|
|
|
|
2017-06-25 04:21:13 -07:00
|
|
|
/// Application config.
|
|
|
|
#[derive(Debug, PartialEq)]
|
2017-06-13 01:28:04 -07:00
|
|
|
pub struct Config {
|
2017-08-01 02:36:48 -07:00
|
|
|
pub mainnet: Node,
|
|
|
|
pub testnet: Node,
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Config {
|
|
|
|
fn default() -> Self {
|
|
|
|
Config {
|
|
|
|
mainnet: Node::default_mainnet(),
|
|
|
|
testnet: Node::default_testnet(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<load::Config> for Config {
|
|
|
|
fn from(config: load::Config) -> Self {
|
|
|
|
let default = Self::default();
|
|
|
|
Config {
|
|
|
|
mainnet: Node {
|
2017-08-01 02:36:48 -07:00
|
|
|
account: config.mainnet.account,
|
2017-06-25 04:21:13 -07:00
|
|
|
ipc: config.mainnet.ipc.unwrap_or(default.mainnet.ipc),
|
2017-08-01 02:36:48 -07:00
|
|
|
deploy_tx: default.mainnet.deploy_tx.with(config.mainnet.deploy_tx),
|
|
|
|
poll_interval: config.mainnet.poll_interval.map(Duration::from_secs).unwrap_or(default.mainnet.poll_interval),
|
|
|
|
required_confirmations: config.mainnet.required_confirmations.unwrap_or(default.mainnet.required_confirmations),
|
2017-06-25 04:21:13 -07:00
|
|
|
},
|
|
|
|
testnet: Node {
|
2017-08-01 02:36:48 -07:00
|
|
|
account: config.testnet.account,
|
2017-06-25 04:21:13 -07:00
|
|
|
ipc: config.testnet.ipc.unwrap_or(default.testnet.ipc),
|
2017-08-01 02:36:48 -07:00
|
|
|
deploy_tx: default.testnet.deploy_tx.with(config.testnet.deploy_tx),
|
|
|
|
poll_interval: config.testnet.poll_interval.map(Duration::from_secs).unwrap_or(default.testnet.poll_interval),
|
|
|
|
required_confirmations: config.testnet.required_confirmations.unwrap_or(default.testnet.required_confirmations),
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Config {
|
2017-08-02 03:45:15 -07:00
|
|
|
pub fn load<P: AsRef<Path>>(path: P) -> Result<Config, Error> {
|
|
|
|
let mut file = fs::File::open(path).chain_err(|| "Cannot open config")?;
|
2017-06-25 04:21:13 -07:00
|
|
|
let mut buffer = String::new();
|
|
|
|
file.read_to_string(&mut buffer);
|
|
|
|
Self::load_from_str(&buffer)
|
|
|
|
}
|
|
|
|
|
2017-08-02 03:45:15 -07:00
|
|
|
fn load_from_str(s: &str) -> Result<Config, Error> {
|
|
|
|
let config: load::Config = toml::from_str(s).chain_err(|| "Cannot parse config")?;
|
2017-06-25 04:21:13 -07:00
|
|
|
Ok(config.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
2017-08-01 02:36:48 -07:00
|
|
|
pub struct Node {
|
|
|
|
// TODO: address
|
|
|
|
pub account: String,
|
|
|
|
pub ipc: PathBuf,
|
|
|
|
pub deploy_tx: TransactionConfig,
|
|
|
|
pub poll_interval: Duration,
|
|
|
|
pub required_confirmations: u64,
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Node {
|
|
|
|
fn default_mainnet() -> Self {
|
|
|
|
// TODO: hardcode default mainnet ipc path
|
|
|
|
Node {
|
2017-08-01 02:36:48 -07:00
|
|
|
account: "".into(),
|
2017-06-25 04:21:13 -07:00
|
|
|
ipc: "".into(),
|
2017-08-01 02:36:48 -07:00
|
|
|
deploy_tx: TransactionConfig::deploy_mainnet(),
|
|
|
|
poll_interval: Duration::from_secs(DEFAULT_POLL_INTERVAL),
|
|
|
|
required_confirmations: DEFAULT_CONFIRMATIONS,
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_testnet() -> Self {
|
|
|
|
// TODO: hardcode default testnet ipc path
|
|
|
|
Node {
|
2017-08-01 02:36:48 -07:00
|
|
|
account: "".into(),
|
2017-06-25 04:21:13 -07:00
|
|
|
ipc: "".into(),
|
2017-08-01 02:36:48 -07:00
|
|
|
deploy_tx: TransactionConfig::deploy_testnet(),
|
|
|
|
poll_interval: Duration::from_secs(DEFAULT_POLL_INTERVAL),
|
|
|
|
required_confirmations: DEFAULT_CONFIRMATIONS,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub struct TransactionConfig {
|
|
|
|
pub gas: u64,
|
|
|
|
pub gas_price: u64,
|
|
|
|
pub value: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TransactionConfig {
|
|
|
|
fn with(&self, loaded: Option<load::TransactionConfig>) -> Self {
|
|
|
|
let loaded_ref = loaded.as_ref();
|
|
|
|
TransactionConfig {
|
|
|
|
gas: loaded_ref.and_then(|tx| tx.gas).unwrap_or(self.gas),
|
|
|
|
gas_price: loaded_ref.and_then(|tx| tx.gas_price).unwrap_or(self.gas_price),
|
|
|
|
value: loaded_ref.and_then(|tx| tx.value).unwrap_or(self.value),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn deploy_mainnet() -> Self {
|
|
|
|
// TODO: values
|
|
|
|
TransactionConfig {
|
|
|
|
gas: 0,
|
|
|
|
gas_price: 1,
|
|
|
|
value: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn deploy_testnet() -> Self {
|
|
|
|
// TODO: values
|
|
|
|
TransactionConfig {
|
|
|
|
gas: 2,
|
|
|
|
gas_price: 3,
|
|
|
|
value: 0,
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Some config values may not be defined in `toml` file, but they should be specified at runtime.
|
|
|
|
/// `load` module separates `Config` representation in file with optional from the one used
|
|
|
|
/// in application.
|
|
|
|
mod load {
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
pub struct Config {
|
|
|
|
pub mainnet: Node,
|
|
|
|
pub testnet: Node,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
pub struct Node {
|
2017-08-01 02:36:48 -07:00
|
|
|
// TODO: replace with address
|
|
|
|
pub account: String,
|
2017-06-25 04:21:13 -07:00
|
|
|
pub ipc: Option<PathBuf>,
|
2017-08-01 02:36:48 -07:00
|
|
|
pub deploy_tx: Option<TransactionConfig>,
|
|
|
|
pub poll_interval: Option<u64>,
|
|
|
|
pub required_confirmations: Option<u64>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
pub struct TransactionConfig {
|
|
|
|
pub gas: Option<u64>,
|
|
|
|
pub gas_price: Option<u64>,
|
|
|
|
pub value: Option<u64>,
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2017-08-01 02:36:48 -07:00
|
|
|
use std::time::Duration;
|
|
|
|
use super::{Config, Node, TransactionConfig};
|
2017-06-25 04:21:13 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn load_full_setup_from_str() {
|
|
|
|
let toml = r#"
|
|
|
|
[mainnet]
|
2017-08-01 02:36:48 -07:00
|
|
|
account = "1B68Cb0B50181FC4006Ce572cF346e596E51818b"
|
2017-06-25 04:21:13 -07:00
|
|
|
ipc = "/mainnet.ipc"
|
2017-08-01 02:36:48 -07:00
|
|
|
poll_interval = 2
|
|
|
|
required_confirmations = 100
|
2017-06-25 04:21:13 -07:00
|
|
|
|
|
|
|
[testnet]
|
2017-08-01 02:36:48 -07:00
|
|
|
account = "0x0000000000000000000000000000000000000001"
|
2017-06-25 04:21:13 -07:00
|
|
|
ipc = "/testnet.ipc"
|
2017-08-01 02:36:48 -07:00
|
|
|
deploy_tx = { gas = 20, value = 15 }
|
2017-06-25 04:21:13 -07:00
|
|
|
"#;
|
2017-08-01 02:36:48 -07:00
|
|
|
|
2017-06-25 04:21:13 -07:00
|
|
|
let expected = Config {
|
|
|
|
mainnet: Node {
|
2017-08-01 02:36:48 -07:00
|
|
|
account: "1B68Cb0B50181FC4006Ce572cF346e596E51818b".into(),
|
2017-06-25 04:21:13 -07:00
|
|
|
ipc: "/mainnet.ipc".into(),
|
2017-08-01 02:36:48 -07:00
|
|
|
deploy_tx: TransactionConfig {
|
|
|
|
gas: 0,
|
|
|
|
gas_price: 1,
|
|
|
|
value: 0,
|
|
|
|
},
|
|
|
|
poll_interval: Duration::from_secs(2),
|
|
|
|
required_confirmations: 100,
|
2017-06-25 04:21:13 -07:00
|
|
|
},
|
|
|
|
testnet: Node {
|
2017-08-01 02:36:48 -07:00
|
|
|
account: "0x0000000000000000000000000000000000000001".into(),
|
2017-06-25 04:21:13 -07:00
|
|
|
ipc: "/testnet.ipc".into(),
|
2017-08-01 02:36:48 -07:00
|
|
|
deploy_tx: TransactionConfig {
|
|
|
|
gas: 20,
|
|
|
|
gas_price: 3,
|
|
|
|
value: 15,
|
|
|
|
},
|
|
|
|
poll_interval: Duration::from_secs(1),
|
|
|
|
required_confirmations: 12,
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
|
|
|
};
|
2017-08-01 02:36:48 -07:00
|
|
|
|
2017-06-25 04:21:13 -07:00
|
|
|
let config = Config::load_from_str(toml).unwrap();
|
|
|
|
assert_eq!(expected, config);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn laod_minimal_setup_from_str() {
|
|
|
|
let toml = r#"
|
|
|
|
[mainnet]
|
2017-08-01 02:36:48 -07:00
|
|
|
account = "1B68Cb0B50181FC4006Ce572cF346e596E51818b"
|
2017-06-25 04:21:13 -07:00
|
|
|
[testnet]
|
2017-08-01 02:36:48 -07:00
|
|
|
account = "0000000000000000000000000000000000000001"
|
2017-06-25 04:21:13 -07:00
|
|
|
"#;
|
2017-08-01 02:36:48 -07:00
|
|
|
let mut expected = Config::default();
|
|
|
|
expected.mainnet.account = "1B68Cb0B50181FC4006Ce572cF346e596E51818b".into();
|
|
|
|
expected.testnet.account = "0000000000000000000000000000000000000001".into();
|
2017-06-25 04:21:13 -07:00
|
|
|
let config = Config::load_from_str(toml).unwrap();
|
|
|
|
assert_eq!(expected, config);
|
|
|
|
}
|
2017-06-13 01:28:04 -07:00
|
|
|
}
|