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-10-01 10:16:44 -07:00
|
|
|
use rustc_hex::FromHex;
|
2017-08-10 08:55:46 -07:00
|
|
|
use web3::types::{Address, Bytes};
|
2017-08-02 03:45:15 -07:00
|
|
|
use error::{ResultExt, Error};
|
2017-08-21 08:32:37 -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-08-31 08:32:34 -07:00
|
|
|
const DEFAULT_TIMEOUT: u64 = 5;
|
2017-08-01 02:36:48 -07:00
|
|
|
|
2017-06-25 04:21:13 -07:00
|
|
|
/// Application config.
|
2017-08-13 11:44:41 -07:00
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
2017-06-13 01:28:04 -07:00
|
|
|
pub struct Config {
|
2017-10-10 02:02:46 -07:00
|
|
|
pub home: Node,
|
|
|
|
pub foreign: Node,
|
2017-08-13 12:00:11 -07:00
|
|
|
pub authorities: Authorities,
|
2017-08-24 09:20:37 -07:00
|
|
|
pub txs: Transactions,
|
2018-01-09 03:53:50 -08:00
|
|
|
pub estimated_gas_cost_of_withdraw: u32,
|
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();
|
2017-08-13 04:09:50 -07:00
|
|
|
file.read_to_string(&mut buffer).expect("TODO");
|
2017-06-25 04:21:13 -07:00
|
|
|
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-08-10 08:55:46 -07:00
|
|
|
Config::from_load_struct(config)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn from_load_struct(config: load::Config) -> Result<Config, Error> {
|
|
|
|
let result = Config {
|
2017-10-10 02:02:46 -07:00
|
|
|
home: Node::from_load_struct(config.home)?,
|
|
|
|
foreign: Node::from_load_struct(config.foreign)?,
|
2017-08-13 12:00:11 -07:00
|
|
|
authorities: Authorities {
|
|
|
|
accounts: config.authorities.accounts,
|
|
|
|
required_signatures: config.authorities.required_signatures,
|
2017-08-24 09:20:37 -07:00
|
|
|
},
|
|
|
|
txs: config.transactions.map(Transactions::from_load_struct).unwrap_or_default(),
|
2018-01-09 03:53:50 -08:00
|
|
|
estimated_gas_cost_of_withdraw: config.estimated_gas_cost_of_withdraw,
|
2017-08-10 08:55:46 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
Ok(result)
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-13 11:44:41 -07:00
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
2017-08-01 02:36:48 -07:00
|
|
|
pub struct Node {
|
2017-08-03 15:48:12 -07:00
|
|
|
pub account: Address,
|
2017-08-10 08:55:46 -07:00
|
|
|
pub contract: ContractConfig,
|
2017-08-01 02:36:48 -07:00
|
|
|
pub ipc: PathBuf,
|
2017-08-31 08:32:34 -07:00
|
|
|
pub request_timeout: Duration,
|
2017-08-01 02:36:48 -07:00
|
|
|
pub poll_interval: Duration,
|
|
|
|
pub required_confirmations: u64,
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
|
|
|
|
2017-08-10 08:55:46 -07:00
|
|
|
impl Node {
|
2017-08-24 09:38:56 -07:00
|
|
|
fn from_load_struct(node: load::Node) -> Result<Node, Error> {
|
2017-08-10 08:55:46 -07:00
|
|
|
let result = Node {
|
|
|
|
account: node.account,
|
|
|
|
contract: ContractConfig {
|
2017-10-01 10:16:44 -07:00
|
|
|
bin: {
|
|
|
|
let mut read = String::new();
|
|
|
|
let mut file = fs::File::open(node.contract.bin)?;
|
|
|
|
file.read_to_string(&mut read)?;
|
|
|
|
Bytes(read.from_hex()?)
|
|
|
|
}
|
2017-08-10 08:55:46 -07:00
|
|
|
},
|
2017-08-24 09:38:56 -07:00
|
|
|
ipc: node.ipc,
|
2017-08-31 08:32:34 -07:00
|
|
|
request_timeout: Duration::from_secs(node.request_timeout.unwrap_or(DEFAULT_TIMEOUT)),
|
2017-08-10 08:55:46 -07:00
|
|
|
poll_interval: Duration::from_secs(node.poll_interval.unwrap_or(DEFAULT_POLL_INTERVAL)),
|
|
|
|
required_confirmations: node.required_confirmations.unwrap_or(DEFAULT_CONFIRMATIONS),
|
|
|
|
};
|
2017-08-24 09:38:56 -07:00
|
|
|
|
2017-08-10 08:55:46 -07:00
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-13 11:44:41 -07:00
|
|
|
#[derive(Debug, PartialEq, Default, Clone)]
|
2017-08-12 11:03:48 -07:00
|
|
|
pub struct Transactions {
|
2017-10-10 02:02:46 -07:00
|
|
|
pub home_deploy: TransactionConfig,
|
|
|
|
pub foreign_deploy: TransactionConfig,
|
2017-08-24 09:20:37 -07:00
|
|
|
pub deposit_relay: TransactionConfig,
|
|
|
|
pub withdraw_confirm: TransactionConfig,
|
|
|
|
pub withdraw_relay: TransactionConfig,
|
2017-08-12 11:03:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Transactions {
|
|
|
|
fn from_load_struct(cfg: load::Transactions) -> Self {
|
|
|
|
Transactions {
|
2017-10-10 02:02:46 -07:00
|
|
|
home_deploy: cfg.home_deploy.map(TransactionConfig::from_load_struct).unwrap_or_default(),
|
|
|
|
foreign_deploy: cfg.foreign_deploy.map(TransactionConfig::from_load_struct).unwrap_or_default(),
|
2017-08-24 09:20:37 -07:00
|
|
|
deposit_relay: cfg.deposit_relay.map(TransactionConfig::from_load_struct).unwrap_or_default(),
|
|
|
|
withdraw_confirm: cfg.withdraw_confirm.map(TransactionConfig::from_load_struct).unwrap_or_default(),
|
|
|
|
withdraw_relay: cfg.withdraw_relay.map(TransactionConfig::from_load_struct).unwrap_or_default(),
|
2017-08-12 11:03:48 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-13 11:44:41 -07:00
|
|
|
#[derive(Debug, PartialEq, Default, Clone)]
|
2017-08-01 02:36:48 -07:00
|
|
|
pub struct TransactionConfig {
|
|
|
|
pub gas: u64,
|
|
|
|
pub gas_price: u64,
|
|
|
|
}
|
|
|
|
|
2017-08-12 11:03:48 -07:00
|
|
|
impl TransactionConfig {
|
|
|
|
fn from_load_struct(cfg: load::TransactionConfig) -> Self {
|
|
|
|
TransactionConfig {
|
|
|
|
gas: cfg.gas.unwrap_or_default(),
|
|
|
|
gas_price: cfg.gas_price.unwrap_or_default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-13 11:44:41 -07:00
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
2017-08-10 08:55:46 -07:00
|
|
|
pub struct ContractConfig {
|
|
|
|
pub bin: Bytes,
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
|
|
|
|
2017-08-13 12:00:11 -07:00
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
|
|
pub struct Authorities {
|
|
|
|
pub accounts: Vec<Address>,
|
2017-08-21 08:32:37 -07:00
|
|
|
pub required_signatures: u32,
|
2017-08-13 12:00:11 -07:00
|
|
|
}
|
|
|
|
|
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.
|
2017-08-24 09:38:56 -07:00
|
|
|
/// `load` module separates `Config` representation in file with optional from the one used
|
2017-06-25 04:21:13 -07:00
|
|
|
/// in application.
|
|
|
|
mod load {
|
|
|
|
use std::path::PathBuf;
|
2017-08-03 15:48:12 -07:00
|
|
|
use web3::types::Address;
|
2017-06-25 04:21:13 -07:00
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
2017-08-03 15:48:12 -07:00
|
|
|
#[serde(deny_unknown_fields)]
|
2017-06-25 04:21:13 -07:00
|
|
|
pub struct Config {
|
2017-10-10 02:02:46 -07:00
|
|
|
pub home: Node,
|
|
|
|
pub foreign: Node,
|
2017-08-13 12:00:11 -07:00
|
|
|
pub authorities: Authorities,
|
2017-08-24 09:20:37 -07:00
|
|
|
pub transactions: Option<Transactions>,
|
2018-01-09 03:53:50 -08:00
|
|
|
pub estimated_gas_cost_of_withdraw: u32,
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
2017-08-24 09:20:37 -07:00
|
|
|
#[serde(deny_unknown_fields)]
|
2017-06-25 04:21:13 -07:00
|
|
|
pub struct Node {
|
2017-08-03 15:48:12 -07:00
|
|
|
pub account: Address,
|
2017-08-10 08:55:46 -07:00
|
|
|
pub contract: ContractConfig,
|
2017-08-24 09:38:56 -07:00
|
|
|
pub ipc: PathBuf,
|
2017-08-31 08:32:34 -07:00
|
|
|
pub request_timeout: Option<u64>,
|
2017-08-01 02:36:48 -07:00
|
|
|
pub poll_interval: Option<u64>,
|
|
|
|
pub required_confirmations: Option<u64>,
|
|
|
|
}
|
|
|
|
|
2017-08-12 11:03:48 -07:00
|
|
|
#[derive(Deserialize)]
|
2017-08-24 09:20:37 -07:00
|
|
|
#[serde(deny_unknown_fields)]
|
2017-08-12 11:03:48 -07:00
|
|
|
pub struct Transactions {
|
2017-10-10 02:02:46 -07:00
|
|
|
pub home_deploy: Option<TransactionConfig>,
|
|
|
|
pub foreign_deploy: Option<TransactionConfig>,
|
2017-08-24 09:20:37 -07:00
|
|
|
pub deposit_relay: Option<TransactionConfig>,
|
|
|
|
pub withdraw_confirm: Option<TransactionConfig>,
|
|
|
|
pub withdraw_relay: Option<TransactionConfig>,
|
2017-08-12 11:03:48 -07:00
|
|
|
}
|
|
|
|
|
2017-08-01 02:36:48 -07:00
|
|
|
#[derive(Deserialize)]
|
2017-08-24 09:20:37 -07:00
|
|
|
#[serde(deny_unknown_fields)]
|
2017-08-01 02:36:48 -07:00
|
|
|
pub struct TransactionConfig {
|
|
|
|
pub gas: Option<u64>,
|
|
|
|
pub gas_price: Option<u64>,
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
2017-08-10 08:55:46 -07:00
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
2017-08-24 09:20:37 -07:00
|
|
|
#[serde(deny_unknown_fields)]
|
2017-08-10 08:55:46 -07:00
|
|
|
pub struct ContractConfig {
|
|
|
|
pub bin: PathBuf,
|
|
|
|
}
|
2017-08-13 12:00:11 -07:00
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
2017-08-24 09:20:37 -07:00
|
|
|
#[serde(deny_unknown_fields)]
|
2017-08-13 12:00:11 -07:00
|
|
|
pub struct Authorities {
|
|
|
|
pub accounts: Vec<Address>,
|
2017-08-21 08:32:37 -07:00
|
|
|
pub required_signatures: u32,
|
2017-08-13 12:00:11 -07:00
|
|
|
}
|
2017-06-25 04:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2017-08-01 02:36:48 -07:00
|
|
|
use std::time::Duration;
|
2017-10-01 10:16:44 -07:00
|
|
|
use rustc_hex::FromHex;
|
2017-08-24 09:20:37 -07:00
|
|
|
use super::{Config, Node, ContractConfig, Transactions, Authorities, TransactionConfig};
|
2017-06-25 04:21:13 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn load_full_setup_from_str() {
|
|
|
|
let toml = r#"
|
2018-01-09 03:53:50 -08:00
|
|
|
estimated_gas_cost_of_withdraw = 100000
|
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
[home]
|
2017-08-03 15:48:12 -07:00
|
|
|
account = "0x1B68Cb0B50181FC4006Ce572cF346e596E51818b"
|
2017-10-10 02:02:46 -07:00
|
|
|
ipc = "/home.ipc"
|
2017-08-01 02:36:48 -07:00
|
|
|
poll_interval = 2
|
|
|
|
required_confirmations = 100
|
2017-06-25 04:21:13 -07:00
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
[home.contract]
|
|
|
|
bin = "../contracts/HomeBridge.bin"
|
2017-08-10 08:55:46 -07:00
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
[foreign]
|
2017-08-01 02:36:48 -07:00
|
|
|
account = "0x0000000000000000000000000000000000000001"
|
2017-10-10 02:02:46 -07:00
|
|
|
ipc = "/foreign.ipc"
|
2017-08-12 11:03:48 -07:00
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
[foreign.contract]
|
|
|
|
bin = "../contracts/ForeignBridge.bin"
|
2017-08-13 12:00:11 -07:00
|
|
|
|
|
|
|
[authorities]
|
|
|
|
accounts = [
|
|
|
|
"0x0000000000000000000000000000000000000001",
|
|
|
|
"0x0000000000000000000000000000000000000002",
|
|
|
|
"0x0000000000000000000000000000000000000003"
|
|
|
|
]
|
|
|
|
required_signatures = 2
|
2017-08-24 09:20:37 -07:00
|
|
|
|
|
|
|
[transactions]
|
2017-10-10 02:02:46 -07:00
|
|
|
home_deploy = { gas = 20 }
|
2017-06-25 04:21:13 -07:00
|
|
|
"#;
|
2017-08-01 02:36:48 -07:00
|
|
|
|
2017-08-24 09:20:37 -07:00
|
|
|
let mut expected = Config {
|
|
|
|
txs: Transactions::default(),
|
2017-10-10 02:02:46 -07:00
|
|
|
home: Node {
|
2017-08-03 15:48:12 -07:00
|
|
|
account: "0x1B68Cb0B50181FC4006Ce572cF346e596E51818b".parse().unwrap(),
|
2017-10-10 02:02:46 -07:00
|
|
|
ipc: "/home.ipc".into(),
|
2017-08-10 08:55:46 -07:00
|
|
|
contract: ContractConfig {
|
2017-10-10 02:02:46 -07:00
|
|
|
bin: include_str!("../../contracts/HomeBridge.bin").from_hex().unwrap().into(),
|
2017-08-10 08:55:46 -07:00
|
|
|
},
|
2017-08-01 02:36:48 -07:00
|
|
|
poll_interval: Duration::from_secs(2),
|
2017-08-31 08:32:34 -07:00
|
|
|
request_timeout: Duration::from_secs(5),
|
2017-08-01 02:36:48 -07:00
|
|
|
required_confirmations: 100,
|
2017-06-25 04:21:13 -07:00
|
|
|
},
|
2017-10-10 02:02:46 -07:00
|
|
|
foreign: Node {
|
2017-08-03 15:48:12 -07:00
|
|
|
account: "0x0000000000000000000000000000000000000001".parse().unwrap(),
|
2017-08-10 08:55:46 -07:00
|
|
|
contract: ContractConfig {
|
2017-10-10 02:02:46 -07:00
|
|
|
bin: include_str!("../../contracts/ForeignBridge.bin").from_hex().unwrap().into(),
|
2017-08-10 08:55:46 -07:00
|
|
|
},
|
2017-10-10 02:02:46 -07:00
|
|
|
ipc: "/foreign.ipc".into(),
|
2017-08-01 02:36:48 -07:00
|
|
|
poll_interval: Duration::from_secs(1),
|
2017-08-31 08:32:34 -07:00
|
|
|
request_timeout: Duration::from_secs(5),
|
2017-08-01 02:36:48 -07:00
|
|
|
required_confirmations: 12,
|
2017-08-13 12:00:11 -07:00
|
|
|
},
|
|
|
|
authorities: Authorities {
|
|
|
|
accounts: vec![
|
2017-08-24 09:38:56 -07:00
|
|
|
"0x0000000000000000000000000000000000000001".parse().unwrap(),
|
|
|
|
"0x0000000000000000000000000000000000000002".parse().unwrap(),
|
|
|
|
"0x0000000000000000000000000000000000000003".parse().unwrap(),
|
2017-08-13 12:00:11 -07:00
|
|
|
],
|
|
|
|
required_signatures: 2,
|
2018-01-09 03:53:50 -08:00
|
|
|
},
|
|
|
|
estimated_gas_cost_of_withdraw: 100_000,
|
2017-06-25 04:21:13 -07:00
|
|
|
};
|
2017-08-01 02:36:48 -07:00
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
expected.txs.home_deploy = TransactionConfig {
|
2017-08-24 09:20:37 -07:00
|
|
|
gas: 20,
|
|
|
|
gas_price: 0,
|
|
|
|
};
|
|
|
|
|
2017-06-25 04:21:13 -07:00
|
|
|
let config = Config::load_from_str(toml).unwrap();
|
|
|
|
assert_eq!(expected, config);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2017-09-28 15:59:19 -07:00
|
|
|
fn load_minimal_setup_from_str() {
|
2017-06-25 04:21:13 -07:00
|
|
|
let toml = r#"
|
2018-01-09 03:53:50 -08:00
|
|
|
estimated_gas_cost_of_withdraw = 200000000
|
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
[home]
|
2017-08-03 15:48:12 -07:00
|
|
|
account = "0x1B68Cb0B50181FC4006Ce572cF346e596E51818b"
|
2017-08-24 09:38:56 -07:00
|
|
|
ipc = ""
|
2017-08-10 08:55:46 -07:00
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
[home.contract]
|
|
|
|
bin = "../contracts/HomeBridge.bin"
|
2017-08-10 08:55:46 -07:00
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
[foreign]
|
2017-08-03 15:48:12 -07:00
|
|
|
account = "0x0000000000000000000000000000000000000001"
|
2017-08-24 09:38:56 -07:00
|
|
|
ipc = ""
|
2017-08-10 08:55:46 -07:00
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
[foreign.contract]
|
|
|
|
bin = "../contracts/ForeignBridge.bin"
|
2017-08-13 12:00:11 -07:00
|
|
|
|
|
|
|
[authorities]
|
|
|
|
accounts = [
|
|
|
|
"0x0000000000000000000000000000000000000001",
|
|
|
|
"0x0000000000000000000000000000000000000002",
|
|
|
|
"0x0000000000000000000000000000000000000003"
|
|
|
|
]
|
|
|
|
required_signatures = 2
|
2017-06-25 04:21:13 -07:00
|
|
|
"#;
|
2017-08-10 08:55:46 -07:00
|
|
|
let expected = Config {
|
2017-08-24 09:20:37 -07:00
|
|
|
txs: Transactions::default(),
|
2017-10-10 02:02:46 -07:00
|
|
|
home: Node {
|
2017-08-10 08:55:46 -07:00
|
|
|
account: "0x1B68Cb0B50181FC4006Ce572cF346e596E51818b".parse().unwrap(),
|
|
|
|
ipc: "".into(),
|
|
|
|
contract: ContractConfig {
|
2017-10-10 02:02:46 -07:00
|
|
|
bin: include_str!("../../contracts/HomeBridge.bin").from_hex().unwrap().into(),
|
2017-08-10 08:55:46 -07:00
|
|
|
},
|
|
|
|
poll_interval: Duration::from_secs(1),
|
2017-08-31 08:32:34 -07:00
|
|
|
request_timeout: Duration::from_secs(5),
|
2017-08-10 08:55:46 -07:00
|
|
|
required_confirmations: 12,
|
|
|
|
},
|
2017-10-10 02:02:46 -07:00
|
|
|
foreign: Node {
|
2017-08-10 08:55:46 -07:00
|
|
|
account: "0x0000000000000000000000000000000000000001".parse().unwrap(),
|
|
|
|
ipc: "".into(),
|
|
|
|
contract: ContractConfig {
|
2017-10-10 02:02:46 -07:00
|
|
|
bin: include_str!("../../contracts/ForeignBridge.bin").from_hex().unwrap().into(),
|
2017-08-10 08:55:46 -07:00
|
|
|
},
|
|
|
|
poll_interval: Duration::from_secs(1),
|
2017-08-31 08:32:34 -07:00
|
|
|
request_timeout: Duration::from_secs(5),
|
2017-08-10 08:55:46 -07:00
|
|
|
required_confirmations: 12,
|
2017-08-13 12:00:11 -07:00
|
|
|
},
|
|
|
|
authorities: Authorities {
|
|
|
|
accounts: vec![
|
2017-08-24 09:38:56 -07:00
|
|
|
"0x0000000000000000000000000000000000000001".parse().unwrap(),
|
|
|
|
"0x0000000000000000000000000000000000000002".parse().unwrap(),
|
|
|
|
"0x0000000000000000000000000000000000000003".parse().unwrap(),
|
2017-08-13 12:00:11 -07:00
|
|
|
],
|
|
|
|
required_signatures: 2,
|
2018-01-09 03:53:50 -08:00
|
|
|
},
|
|
|
|
estimated_gas_cost_of_withdraw: 200_000_000,
|
2017-08-10 08:55:46 -07:00
|
|
|
};
|
|
|
|
|
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
|
|
|
}
|