Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 45d643c

Browse files
committedJun 19, 2018
Move bridge contact addresses to primary configuration file.
* Allow creation of `Database` from either a stored database configuration containing a `home_contract_address` and `foreign_contract_address` or a user defined configuration without contract address fields. * User defined database configuration files are parsed through the `database::parsed::UserDefinedDatabase` struct. * Stored database configuration files are parsed through the `database::parsed::StoredDatabase` struct. * Deprecate `Database::save` in favor of `Deprecate::store`. * Add the `Database::load_user_defined` method to handle creation of a `Database` from a user defined configuration containing no contract addresses. * Deprecate `Database::load` in favor of `Database::load_stored`. `Database::load_stored` will create a new `Database` from a stored database configuration file *containing* contract addresses. * Remove the `FromStr` impl for `Database`. * Rename the `config::load` module to `parse`. * Update tests.
1 parent 69474ce commit 45d643c

File tree

7 files changed

+281
-82
lines changed

7 files changed

+281
-82
lines changed
 

‎README.md

+5-6
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ and they can convert them back as well.
3030

3131
![withdraw](./res/withdraw.png)
3232

33-
### How to build
33+
### How to build
3434

3535
Requires `rust` and `cargo`: [installation instructions.](https://www.rust-lang.org/en-US/install.html)
3636

@@ -83,6 +83,7 @@ keystore = "/path/to/keystore"
8383

8484
[home]
8585
account = "0x006e27b6a72e1f34c626762f3c4761547aff1421"
86+
contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db7"
8687
rpc_host = "http://localhost"
8788
rpc_port = 8545
8889
required_confirmations = 0
@@ -91,6 +92,7 @@ default_gas_price = 1_000_000_000 # 1 GWEI
9192

9293
[foreign]
9394
account = "0x006e27b6a72e1f34c626762f3c4761547aff1421"
95+
contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"
9496
rpc_host = "http://localhost"
9597
rpc_port = 9545
9698
required_confirmations = 0
@@ -110,11 +112,12 @@ withdraw_confirm = { gas = 3000000 }
110112

111113
#### Options
112114

113-
- `keystore` - path to a keystore directory with JSON keys
115+
- `keystore` - path to a keystore directory with JSON keys
114116

115117
#### home/foreign options
116118

117119
- `home/foreign.account` - authority address on the home (**required**)
120+
- `home/foreign.contract_address` - The address of the bridge contract on home/foreign chain. (**required** unless the configuration is being used in a deploy build [FIXME: clarify this description]).
118121
- `home/foreign.rpc_host` - RPC host (**required**)
119122
- `home/foreign.rpc_port` - RPC port (**defaults to 8545**)
120123
- `home/foreign.required_confirmations` - number of confirmation required to consider transaction final on home (default: **12**)
@@ -141,17 +144,13 @@ withdraw_confirm = { gas = 3000000 }
141144
### Database file format
142145

143146
```toml
144-
home_contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db7"
145-
foreign_contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"
146147
checked_deposit_relay = 120
147148
checked_withdraw_relay = 121
148149
checked_withdraw_confirm = 121
149150
```
150151

151152
**all fields are required**
152153

153-
- `home_contract_address` - address of the bridge contract on home chain
154-
- `foreign_contract_address` - address of the bridge contract on foreign chain
155154
- `checked_deposit_relay` - number of the last block for which an authority has relayed deposits to the foreign
156155
- `checked_withdraw_relay` - number of the last block for which an authority has relayed withdraws to the home
157156
- `checked_withdraw_confirm` - number of the last block for which an authority has confirmed withdraw

‎bridge/src/bridge/deploy.rs

+58-46
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub enum Deployed {
2222
Existing(Database),
2323
}
2424

25+
// FIXME: Needs documentation. What do these states signify?
2526
#[cfg(feature = "deploy")]
2627
enum DeployState<T: Transport + Clone> {
2728
CheckIfNeeded,
@@ -64,61 +65,72 @@ impl<T: Transport + Clone> Future for Deploy<T> {
6465
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
6566
loop {
6667
let _next_state = match self.state {
67-
DeployState::CheckIfNeeded => match Database::load(&self.app.database_path).map_err(ErrorKind::from) {
68-
Ok(database) => return Ok(Deployed::Existing(database).into()),
69-
Err(ErrorKind::MissingFile(_e)) => {
70-
#[cfg(feature = "deploy")] {
71-
println!("deploy");
72-
let main_data = self.app.home_bridge.constructor(
73-
self.app.config.home.contract.bin.clone().0,
74-
self.app.config.authorities.required_signatures,
75-
self.app.config.authorities.accounts.clone(),
76-
self.app.config.estimated_gas_cost_of_withdraw
77-
);
78-
let test_data = self.app.foreign_bridge.constructor(
79-
self.app.config.foreign.contract.bin.clone().0,
80-
self.app.config.authorities.required_signatures,
81-
self.app.config.authorities.accounts.clone(),
82-
self.app.config.estimated_gas_cost_of_withdraw
83-
);
68+
DeployState::CheckIfNeeded => {
69+
let db = Database::load_user_defined(
70+
&self.app.database_path,
71+
self.app.config.home.contract_address.expect("Home contract \
72+
address must be specified within config toml."),
73+
self.app.config.foreign.contract_address.expect("Foreign contract \
74+
address must be specified within config toml."))
75+
.map_err(ErrorKind::from);
8476

85-
let main_tx = Transaction {
86-
nonce: U256::zero(),
87-
gas_price: self.app.config.txs.home_deploy.gas_price.into(),
88-
gas: self.app.config.txs.home_deploy.gas.into(),
89-
action: Action::Create,
90-
value: U256::zero(),
91-
data: main_data.into(),
92-
};
77+
match db {
78+
Ok(database) => return Ok(Deployed::Existing(database).into()),
79+
Err(ErrorKind::MissingFile(_e)) => {
80+
#[cfg(feature = "deploy")] {
81+
println!("deploy");
82+
let main_data = self.app.home_bridge.constructor(
83+
self.app.config.home.contract.bin.clone().0,
84+
self.app.config.authorities.required_signatures,
85+
self.app.config.authorities.accounts.clone(),
86+
self.app.config.estimated_gas_cost_of_withdraw
87+
);
88+
let test_data = self.app.foreign_bridge.constructor(
89+
self.app.config.foreign.contract.bin.clone().0,
90+
self.app.config.authorities.required_signatures,
91+
self.app.config.authorities.accounts.clone(),
92+
self.app.config.estimated_gas_cost_of_withdraw
93+
);
9394

94-
let test_tx = Transaction {
95-
nonce: U256::zero(),
96-
gas_price: self.app.config.txs.foreign_deploy.gas_price.into(),
97-
gas: self.app.config.txs.foreign_deploy.gas.into(),
98-
action: Action::Create,
99-
value: U256::zero(),
100-
data: test_data.into(),
101-
};
95+
let main_tx = Transaction {
96+
nonce: U256::zero(),
97+
gas_price: self.app.config.txs.home_deploy.gas_price.into(),
98+
gas: self.app.config.txs.home_deploy.gas.into(),
99+
action: Action::Create,
100+
value: U256::zero(),
101+
data: main_data.into(),
102+
};
102103

103-
let main_future = api::send_transaction_with_nonce(self.app.connections.home.clone(), self.app.clone(),
104-
self.app.config.home.clone(), main_tx, self.home_chain_id,
105-
TransactionWithConfirmation(self.app.connections.home.clone(), self.app.config.home.poll_interval, self.app.config.home.required_confirmations));
104+
let test_tx = Transaction {
105+
nonce: U256::zero(),
106+
gas_price: self.app.config.txs.foreign_deploy.gas_price.into(),
107+
gas: self.app.config.txs.foreign_deploy.gas.into(),
108+
action: Action::Create,
109+
value: U256::zero(),
110+
data: test_data.into(),
111+
};
106112

107-
let test_future = api::send_transaction_with_nonce(self.app.connections.foreign.clone(), self.app.clone(),
108-
self.app.config.foreign.clone(), test_tx, self.foreign_chain_id,
109-
TransactionWithConfirmation(self.app.connections.foreign.clone(), self.app.config.foreign.poll_interval, self.app.config.foreign.required_confirmations));
113+
let main_future = api::send_transaction_with_nonce(self.app.connections.home.clone(), self.app.clone(),
114+
self.app.config.home.clone(), main_tx, self.home_chain_id,
115+
TransactionWithConfirmation(self.app.connections.home.clone(), self.app.config.home.poll_interval, self.app.config.home.required_confirmations));
110116

111-
DeployState::Deploying(main_future.join(test_future))
112-
}
113-
#[cfg(not(feature = "deploy"))] {
114-
return Err(ErrorKind::MissingFile(_e).into())
115-
}
116-
},
117-
Err(err) => return Err(err.into()),
117+
let test_future = api::send_transaction_with_nonce(self.app.connections.foreign.clone(), self.app.clone(),
118+
self.app.config.foreign.clone(), test_tx, self.foreign_chain_id,
119+
TransactionWithConfirmation(self.app.connections.foreign.clone(), self.app.config.foreign.poll_interval, self.app.config.foreign.required_confirmations));
120+
121+
DeployState::Deploying(main_future.join(test_future))
122+
}
123+
#[cfg(not(feature = "deploy"))] {
124+
return Err(ErrorKind::MissingFile(_e).into())
125+
}
126+
},
127+
Err(err) => return Err(err.into()),
128+
}
118129
},
119130
#[cfg(feature = "deploy")]
120131
DeployState::Deploying(ref mut future) => {
121132
let (main_receipt, test_receipt) = try_ready!(future.poll());
133+
122134
let database = Database {
123135
home_contract_address: main_receipt.contract_address.expect("contract creation receipt must have an address; qed"),
124136
foreign_contract_address: test_receipt.contract_address.expect("contract creation receipt must have an address; qed"),

‎bridge/src/bridge/gas_price.rs

+5
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ mod tests {
165165
fn errored_request() {
166166
let node = Node {
167167
account: Address::new(),
168+
contract_address: Some("49edf201c1e139282643d5e7c6fb0c7219ad1db7".into()),
168169
request_timeout: Duration::from_secs(5),
169170
poll_interval: Duration::from_secs(1),
170171
required_confirmations: 0,
@@ -208,6 +209,7 @@ mod tests {
208209
fn bad_json() {
209210
let node = Node {
210211
account: Address::new(),
212+
contract_address: Some("49edf201c1e139282643d5e7c6fb0c7219ad1db7".into()),
211213
request_timeout: Duration::from_secs(5),
212214
poll_interval: Duration::from_secs(1),
213215
required_confirmations: 0,
@@ -251,6 +253,7 @@ mod tests {
251253
fn unexpected_json() {
252254
let node = Node {
253255
account: Address::new(),
256+
contract_address: Some("49edf201c1e139282643d5e7c6fb0c7219ad1db7".into()),
254257
request_timeout: Duration::from_secs(5),
255258
poll_interval: Duration::from_secs(1),
256259
required_confirmations: 0,
@@ -293,6 +296,7 @@ mod tests {
293296
fn non_object_json() {
294297
let node = Node {
295298
account: Address::new(),
299+
contract_address: Some("49edf201c1e139282643d5e7c6fb0c7219ad1db7".into()),
296300
request_timeout: Duration::from_secs(5),
297301
poll_interval: Duration::from_secs(1),
298302
required_confirmations: 0,
@@ -335,6 +339,7 @@ mod tests {
335339
fn correct_json() {
336340
let node = Node {
337341
account: Address::new(),
342+
contract_address: Some("49edf201c1e139282643d5e7c6fb0c7219ad1db7".into()),
338343
request_timeout: Duration::from_secs(5),
339344
poll_interval: Duration::from_secs(1),
340345
required_confirmations: 0,

‎bridge/src/bridge/mod.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ impl<ES: Stream<Item = BridgeChecked, Error = Error>> Stream for Bridge<ES> {
6262
.create(true)
6363
.open(&self.path)?;
6464

65-
self.database.save(file)?;
65+
self.database.store(file)?;
6666
Ok(Async::Ready(Some(())))
6767
}
6868
}
@@ -221,6 +221,7 @@ impl<'a, T: Transport + 'a> Stream for BridgeEventStream<'a, T> {
221221
#[cfg(test)]
222222
mod tests {
223223
extern crate tempdir;
224+
224225
use self::tempdir::TempDir;
225226
use database::Database;
226227
use super::{Bridge, BridgeChecked};
@@ -243,21 +244,24 @@ mod tests {
243244
let mut event_loop = Core::new().unwrap();
244245
let _ = event_loop.run(bridge.collect());
245246

246-
let db = Database::load(&path).unwrap();
247+
let db = Database::load_stored(&path).unwrap();
248+
247249
assert_eq!(1, db.checked_deposit_relay);
248250
assert_eq!(0, db.checked_withdraw_confirm);
249251
assert_eq!(0, db.checked_withdraw_relay);
250252

251253
let bridge = Bridge {
252254
path: path.clone(),
253255
database: Database::default(),
254-
event_stream: stream::iter_ok::<_, Error>(vec![BridgeChecked::DepositRelay(2), BridgeChecked::WithdrawConfirm(3), BridgeChecked::WithdrawRelay(2)]),
256+
event_stream: stream::iter_ok::<_, Error>(vec![BridgeChecked::DepositRelay(2),
257+
BridgeChecked::WithdrawConfirm(3), BridgeChecked::WithdrawRelay(2)]),
255258
};
256259

257260
let mut event_loop = Core::new().unwrap();
258261
let _ = event_loop.run(bridge.collect());
259262

260-
let db = Database::load(&path).unwrap();
263+
let db = Database::load_stored(&path).unwrap();
264+
261265
assert_eq!(2, db.checked_deposit_relay);
262266
assert_eq!(3, db.checked_withdraw_confirm);
263267
assert_eq!(2, db.checked_withdraw_relay);

‎bridge/src/config.rs

+28-10
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ impl Config {
4141
}
4242

4343
fn load_from_str(s: &str, allow_insecure_rpc_endpoints: bool) -> Result<Config, Error> {
44-
let config: load::Config = toml::from_str(s).chain_err(|| "Cannot parse config")?;
44+
let config: parsed::Config = toml::from_str(s).chain_err(|| "Cannot parse config")?;
4545
Config::from_load_struct(config, allow_insecure_rpc_endpoints)
4646
}
4747

48-
fn from_load_struct(config: load::Config, allow_insecure_rpc_endpoints: bool) -> Result<Config, Error> {
49-
let result = Config {
48+
fn from_load_struct(config: parsed::Config, allow_insecure_rpc_endpoints: bool) -> Result<Config, Error> {
49+
let config = Config {
5050
home: Node::from_load_struct(config.home, allow_insecure_rpc_endpoints)?,
5151
foreign: Node::from_load_struct(config.foreign, allow_insecure_rpc_endpoints)?,
5252
authorities: Authorities {
@@ -60,7 +60,7 @@ impl Config {
6060
keystore: config.keystore,
6161
};
6262

63-
Ok(result)
63+
Ok(config)
6464
}
6565
}
6666

@@ -69,6 +69,7 @@ pub struct Node {
6969
pub account: Address,
7070
#[cfg(feature = "deploy")]
7171
pub contract: ContractConfig,
72+
pub contract_address: Option<Address>,
7273
pub request_timeout: Duration,
7374
pub poll_interval: Duration,
7475
pub required_confirmations: usize,
@@ -106,7 +107,7 @@ impl PartialEq for NodeInfo {
106107
}
107108

108109
impl Node {
109-
fn from_load_struct(node: load::Node, allow_insecure_rpc_endpoints: bool) -> Result<Node, Error> {
110+
fn from_load_struct(node: parsed::Node, allow_insecure_rpc_endpoints: bool) -> Result<Node, Error> {
110111
let gas_price_oracle_url = node.gas_price_oracle_url.clone();
111112

112113
let gas_price_speed = match node.gas_price_speed {
@@ -132,7 +133,7 @@ impl Node {
132133
}
133134
}
134135

135-
let result = Node {
136+
let node = Node {
136137
account: node.account,
137138
#[cfg(feature = "deploy")]
138139
contract: ContractConfig {
@@ -143,6 +144,7 @@ impl Node {
143144
Bytes(read.from_hex()?)
144145
}
145146
},
147+
contract_address: node.contract_address,
146148
request_timeout: Duration::from_secs(node.request_timeout.unwrap_or(DEFAULT_TIMEOUT)),
147149
poll_interval: Duration::from_secs(node.poll_interval.unwrap_or(DEFAULT_POLL_INTERVAL)),
148150
required_confirmations: node.required_confirmations.unwrap_or(DEFAULT_CONFIRMATIONS),
@@ -157,7 +159,14 @@ impl Node {
157159
concurrent_http_requests,
158160
};
159161

160-
Ok(result)
162+
// Ensure that the contract address is specified for non-deploy builds:
163+
if cfg!(not(feature = "deploy")) && node.contract_address.is_none() {
164+
return Err("Contract address not specified. Please define the 'contract_address' \
165+
key within both the '[home]' and '[foreign]' tables in the toml config file. \
166+
See 'https://github.com/poanetwork/poa-bridge/blob/master/README.md' for more.".into())
167+
}
168+
169+
Ok(node)
161170
}
162171

163172
pub fn password(&self) -> Result<String, Error> {
@@ -182,7 +191,7 @@ pub struct Transactions {
182191
}
183192

184193
impl Transactions {
185-
fn from_load_struct(cfg: load::Transactions) -> Self {
194+
fn from_load_struct(cfg: parsed::Transactions) -> Self {
186195
Transactions {
187196
#[cfg(feature = "deploy")]
188197
home_deploy: cfg.home_deploy.map(TransactionConfig::from_load_struct).unwrap_or_default(),
@@ -202,7 +211,7 @@ pub struct TransactionConfig {
202211
}
203212

204213
impl TransactionConfig {
205-
fn from_load_struct(cfg: load::TransactionConfig) -> Self {
214+
fn from_load_struct(cfg: parsed::TransactionConfig) -> Self {
206215
TransactionConfig {
207216
gas: cfg.gas.unwrap_or_default(),
208217
gas_price: cfg.gas_price.unwrap_or_default(),
@@ -260,7 +269,7 @@ impl GasPriceSpeed {
260269
/// Some config values may not be defined in `toml` file, but they should be specified at runtime.
261270
/// `load` module separates `Config` representation in file with optional from the one used
262271
/// in application.
263-
mod load {
272+
mod parsed {
264273
use std::path::PathBuf;
265274
use web3::types::Address;
266275

@@ -282,6 +291,7 @@ mod load {
282291
pub account: Address,
283292
#[cfg(feature = "deploy")]
284293
pub contract: ContractConfig,
294+
pub contract_address: Option<Address>,
285295
pub request_timeout: Option<u64>,
286296
pub poll_interval: Option<u64>,
287297
pub required_confirmations: Option<usize>,
@@ -348,6 +358,7 @@ keystore = "/keys"
348358
349359
[home]
350360
account = "0x1B68Cb0B50181FC4006Ce572cF346e596E51818b"
361+
contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db7"
351362
poll_interval = 2
352363
required_confirmations = 100
353364
rpc_host = "127.0.0.1"
@@ -356,6 +367,7 @@ password = "password"
356367
357368
[foreign]
358369
account = "0x0000000000000000000000000000000000000001"
370+
contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"
359371
rpc_host = "127.0.0.1"
360372
rpc_port = 8545
361373
password = "password"
@@ -371,6 +383,7 @@ required_signatures = 2
371383
txs: Transactions::default(),
372384
home: Node {
373385
account: "1B68Cb0B50181FC4006Ce572cF346e596E51818b".into(),
386+
contract_address: Some("49edf201c1e139282643d5e7c6fb0c7219ad1db7".into()),
374387
poll_interval: Duration::from_secs(2),
375388
request_timeout: Duration::from_secs(DEFAULT_TIMEOUT),
376389
required_confirmations: 100,
@@ -386,6 +399,7 @@ required_signatures = 2
386399
},
387400
foreign: Node {
388401
account: "0000000000000000000000000000000000000001".into(),
402+
contract_address: Some("49edf201c1e139282643d5e7c6fb0c7219ad1db8".into()),
389403
poll_interval: Duration::from_secs(1),
390404
request_timeout: Duration::from_secs(DEFAULT_TIMEOUT),
391405
required_confirmations: 12,
@@ -419,11 +433,13 @@ keystore = "/keys/"
419433
420434
[home]
421435
account = "0x1B68Cb0B50181FC4006Ce572cF346e596E51818b"
436+
contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db7"
422437
rpc_host = ""
423438
password = "password"
424439
425440
[foreign]
426441
account = "0x0000000000000000000000000000000000000001"
442+
contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"
427443
rpc_host = ""
428444
password = "password"
429445
@@ -434,6 +450,7 @@ required_signatures = 2
434450
txs: Transactions::default(),
435451
home: Node {
436452
account: "1B68Cb0B50181FC4006Ce572cF346e596E51818b".into(),
453+
contract_address: Some("49edf201c1e139282643d5e7c6fb0c7219ad1db7".into()),
437454
poll_interval: Duration::from_secs(1),
438455
request_timeout: Duration::from_secs(DEFAULT_TIMEOUT),
439456
required_confirmations: 12,
@@ -449,6 +466,7 @@ required_signatures = 2
449466
},
450467
foreign: Node {
451468
account: "0000000000000000000000000000000000000001".into(),
469+
contract_address: Some("49edf201c1e139282643d5e7c6fb0c7219ad1db8".into()),
452470
poll_interval: Duration::from_secs(1),
453471
request_timeout: Duration::from_secs(DEFAULT_TIMEOUT),
454472
required_confirmations: 12,

‎bridge/src/database.rs

+175-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use web3::types::Address;
55
use toml;
66
use error::{Error, ResultExt, ErrorKind};
77

8+
89
/// Application "database".
910
#[derive(Debug, PartialEq, Deserialize, Serialize, Default, Clone)]
1011
pub struct Database {
@@ -24,11 +25,17 @@ pub struct Database {
2425
pub checked_withdraw_confirm: u64,
2526
}
2627

27-
impl str::FromStr for Database {
28-
type Err = Error;
29-
30-
fn from_str(s: &str) -> Result<Self, Self::Err> {
31-
toml::from_str(s).chain_err(|| "Cannot parse database")
28+
impl From<parsed::StoredDatabase> for Database {
29+
fn from(db_parsed: parsed::StoredDatabase) -> Database {
30+
Database {
31+
home_contract_address: db_parsed.home_contract_address,
32+
foreign_contract_address: db_parsed.foreign_contract_address,
33+
home_deploy: db_parsed.home_deploy,
34+
foreign_deploy: db_parsed.foreign_deploy,
35+
checked_deposit_relay: db_parsed.checked_deposit_relay,
36+
checked_withdraw_relay: db_parsed.checked_withdraw_relay,
37+
checked_withdraw_confirm: db_parsed.checked_withdraw_confirm,
38+
}
3239
}
3340
}
3441

@@ -39,30 +46,156 @@ impl fmt::Display for Database {
3946
}
4047

4148
impl Database {
49+
/// Loads the toml file specified by `path` and returns a new `Database`
50+
/// containing its parsed contents.
51+
#[deprecated(note = "Use '::load_stored' instead.")]
4252
pub fn load<P: AsRef<Path>>(path: P) -> Result<Database, Error> {
53+
Self::load_stored(path)
54+
}
55+
56+
/// Loads the toml file specified by `path` and returns a new `Database`
57+
/// containing its parsed contents.
58+
pub fn load_stored<P: AsRef<Path>>(path: P) -> Result<Database, Error> {
59+
let mut file = match fs::File::open(&path) {
60+
Ok(file) => file,
61+
Err(ref err) if err.kind() == io::ErrorKind::NotFound =>
62+
return Err(ErrorKind::MissingFile(format!("{:?}", path.as_ref())).into()),
63+
Err(err) => return Err(err).chain_err(|| "Cannot open database file"),
64+
};
65+
66+
let mut buffer = String::new();
67+
file.read_to_string(&mut buffer)?;
68+
Ok(toml::from_str::<parsed::StoredDatabase>(&buffer)?.into())
69+
70+
}
71+
72+
/// Loads a user defined toml file specified by `path` and returns a new
73+
/// `Database` containing its parsed contents.
74+
pub fn load_user_defined<P: AsRef<Path>>(path: P, home_contract_address: Address,
75+
foreign_contract_address: Address)
76+
-> Result<Database, Error> {
4377
let mut file = match fs::File::open(&path) {
4478
Ok(file) => file,
45-
Err(ref err) if err.kind() == io::ErrorKind::NotFound => return Err(ErrorKind::MissingFile(format!("{:?}", path.as_ref())).into()),
46-
Err(err) => return Err(err).chain_err(|| "Cannot open database"),
79+
Err(ref err) if err.kind() == io::ErrorKind::NotFound =>
80+
return Err(ErrorKind::MissingFile(format!("{:?}", path.as_ref())).into()),
81+
Err(err) => return Err(err).chain_err(|| "Cannot open database file"),
4782
};
4883

4984
let mut buffer = String::new();
5085
file.read_to_string(&mut buffer)?;
51-
buffer.parse()
86+
Database::from_str_user_defined(&buffer, home_contract_address, foreign_contract_address)
5287
}
5388

54-
pub fn save<W: Write>(&self, mut write: W) -> Result<(), Error> {
55-
write.write_all(self.to_string().as_bytes())?;
89+
/// Returns a new `Database` constructed from a stored toml string
90+
/// containing keys for 'home_contract_address' and
91+
/// 'foreign_contract_address'.
92+
#[cfg(test)]
93+
fn from_str_stored<S: AsRef<str>>(s: S) -> Result<Database, Error> {
94+
toml::from_str::<parsed::StoredDatabase>(s.as_ref())
95+
.map(Database::from)
96+
.map_err(Error::from)
97+
}
98+
99+
/// Returns a new `Database` constructed from the parsed string `s` and
100+
/// the provided addresses.
101+
///
102+
/// An error will be returned if the `s` contains keys for
103+
/// 'home_contract_address' or 'foreign_contract_address'.
104+
fn from_str_user_defined<S: AsRef<str>>(s: S, home_contract_address: Address,
105+
foreign_contract_address: Address) -> Result<Database, Error> {
106+
let db_parsed: parsed::UserDefinedDatabase = toml::from_str(s.as_ref())
107+
.chain_err(|| "Cannot parse database file")?;
108+
109+
Ok(Database {
110+
home_contract_address,
111+
foreign_contract_address,
112+
home_deploy: db_parsed.home_deploy,
113+
foreign_deploy: db_parsed.foreign_deploy,
114+
checked_deposit_relay: db_parsed.checked_deposit_relay,
115+
checked_withdraw_relay: db_parsed.checked_withdraw_relay,
116+
checked_withdraw_confirm: db_parsed.checked_withdraw_confirm,
117+
})
118+
}
119+
120+
/// Writes a serialized `Database` to a writer.
121+
#[deprecated(note = "Use '::store' instead.")]
122+
pub fn save<W: Write>(&self, writer: W) -> Result<(), Error> {
123+
self.store(writer)
124+
}
125+
126+
/// Writes a serialized `Database` to a writer.
127+
pub fn store<W: Write>(&self, mut writer: W) -> Result<(), Error> {
128+
writer.write_all(self.to_string().as_bytes())?;
56129
Ok(())
57130
}
58131
}
59132

133+
mod parsed {
134+
#[cfg(test)]
135+
use std::fmt;
136+
#[cfg(test)]
137+
use toml;
138+
139+
use super::Address;
140+
141+
/// Parsed application "database".
142+
#[derive(Debug, Deserialize, Serialize)]
143+
#[serde(deny_unknown_fields)]
144+
pub struct StoredDatabase {
145+
/// Address of home contract.
146+
pub home_contract_address: Address,
147+
/// Address of foreign contract.
148+
pub foreign_contract_address: Address,
149+
/// Number of block at which home contract has been deployed.
150+
pub home_deploy: Option<u64>,
151+
/// Number of block at which foreign contract has been deployed.
152+
pub foreign_deploy: Option<u64>,
153+
/// Number of last block which has been checked for deposit relays.
154+
pub checked_deposit_relay: u64,
155+
/// Number of last block which has been checked for withdraw relays.
156+
pub checked_withdraw_relay: u64,
157+
/// Number of last block which has been checked for withdraw confirms.
158+
pub checked_withdraw_confirm: u64,
159+
}
160+
161+
#[cfg(test)]
162+
impl fmt::Display for StoredDatabase {
163+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164+
f.write_str(&toml::to_string(self).expect("serialization can't fail; qed"))
165+
}
166+
}
167+
168+
/// Parsed application "database".
169+
#[derive(Debug, Deserialize, Serialize)]
170+
#[serde(deny_unknown_fields)]
171+
pub struct UserDefinedDatabase {
172+
/// Number of block at which home contract has been deployed.
173+
pub home_deploy: Option<u64>,
174+
/// Number of block at which foreign contract has been deployed.
175+
pub foreign_deploy: Option<u64>,
176+
/// Number of last block which has been checked for deposit relays.
177+
pub checked_deposit_relay: u64,
178+
/// Number of last block which has been checked for withdraw relays.
179+
pub checked_withdraw_relay: u64,
180+
/// Number of last block which has been checked for withdraw confirms.
181+
pub checked_withdraw_confirm: u64,
182+
}
183+
184+
#[cfg(test)]
185+
impl fmt::Display for UserDefinedDatabase {
186+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187+
f.write_str(&toml::to_string(self).expect("serialization can't fail; qed"))
188+
}
189+
}
190+
}
191+
192+
60193
#[cfg(test)]
61194
mod tests {
62195
use super::Database;
63196

64197
#[test]
65-
fn database_to_and_from_str() {
198+
fn database_to_and_from_str_stored() {
66199
let toml =
67200
r#"home_contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db7"
68201
foreign_contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"
@@ -83,9 +216,39 @@ checked_withdraw_confirm = 121
83216
checked_withdraw_confirm: 121,
84217
};
85218

86-
let database = toml.parse().unwrap();
219+
let database = Database::from_str_stored(toml).unwrap();
87220
assert_eq!(expected, database);
88221
let s = database.to_string();
89222
assert_eq!(s, toml);
90223
}
224+
225+
#[test]
226+
fn database_to_and_from_str_user_defined() {
227+
let toml =
228+
r#"home_deploy = 100
229+
foreign_deploy = 101
230+
checked_deposit_relay = 120
231+
checked_withdraw_relay = 121
232+
checked_withdraw_confirm = 121
233+
"#;
234+
235+
let home_contract_address = "49edf201c1e139282643d5e7c6fb0c7219ad1db7".into();
236+
let foreign_contract_address = "49edf201c1e139282643d5e7c6fb0c7219ad1db8".into();
237+
238+
let expected = Database {
239+
home_contract_address,
240+
foreign_contract_address,
241+
home_deploy: Some(100),
242+
foreign_deploy: Some(101),
243+
checked_deposit_relay: 120,
244+
checked_withdraw_relay: 121,
245+
checked_withdraw_confirm: 121,
246+
};
247+
248+
let database = Database::from_str_user_defined(toml,
249+
home_contract_address, foreign_contract_address).unwrap();
250+
assert_eq!(expected, database);
251+
let s = database.to_string();
252+
assert!(s.contains(toml));
253+
}
91254
}

‎cli/src/main.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -143,15 +143,13 @@ fn execute<S, I>(command: I, running: Arc<AtomicBool>) -> Result<String, UserFac
143143

144144
info!(target:"bridge", " using RPC connection");
145145
let app = match App::new_http(config.clone(), &args.arg_database, &handle, running.clone()) {
146-
Ok(app) => app,
146+
Ok(app) => Arc::new(app),
147147
Err(e) => {
148148
warn!("Can't establish an RPC connection: {:?}", e);
149149
return Err((ERR_CANNOT_CONNECT, e).into());
150150
},
151151
};
152152

153-
let app = Arc::new(app);
154-
155153
info!(target: "bridge", "Acquiring home & foreign chain ids");
156154
let home_chain_id = event_loop.run(create_chain_id_retrieval(app.clone(), app.connections.home.clone(), app.config.home.clone())).expect("can't retrieve home chain_id");
157155
let foreign_chain_id = event_loop.run(create_chain_id_retrieval(app.clone(), app.connections.foreign.clone(), app.config.foreign.clone())).expect("can't retrieve foreign chain_id");
@@ -178,7 +176,7 @@ fn execute<S, I>(command: I, running: Arc<AtomicBool>) -> Result<String, UserFac
178176
Deployed::New(database) => {
179177
info!(target: "bridge", "Deployed new bridge contracts");
180178
info!(target: "bridge", "\n\n{}\n", database);
181-
database.save(fs::File::create(&app.database_path)?)?;
179+
database.store(fs::File::create(&app.database_path)?)?;
182180
database
183181
},
184182
Deployed::Existing(database) => {

0 commit comments

Comments
 (0)
Please sign in to comment.