Problem: required_signatures is static
Validators' information is completely configured through validators contracts and does not depend on `authorities.required_signatures` parameter of bridge's configuration. The number of validators also could be changed during run-time and therefore `authorities.required_signatures` parameter will not reflect the actual number of signatures required for transaction validation. Solution: retrieve required_signatures from RequiredSignaturesChanged event and requiredSignatures() method Closes #74
This commit is contained in:
parent
cc4147c9cc
commit
8a56c5cafb
|
@ -96,7 +96,6 @@ accounts = [
|
|||
"0x006e27b6a72e1f34c626762f3c4761547aff1421",
|
||||
"0x006e27b6a72e1f34c626762f3c4761547aff1421"
|
||||
]
|
||||
required_signatures = 2
|
||||
|
||||
[transactions]
|
||||
deposit_relay = { gas = 3000000, gas_price = 1000000000 }
|
||||
|
@ -121,7 +120,6 @@ withdraw_confirm = { gas = 3000000, gas_price = 1000000000 }
|
|||
#### authorities options
|
||||
|
||||
- `authorities.account` - all authorities (**required**)
|
||||
- `authorities.required_signatures` - number of authorities signatures required to consider action final (**required**)
|
||||
|
||||
#### transaction options
|
||||
|
||||
|
|
|
@ -142,6 +142,23 @@ pub fn call<T: Transport>(transport: T, address: Address, payload: Bytes) -> Api
|
|||
}
|
||||
}
|
||||
|
||||
pub fn call_at<T: Transport>(transport: T, address: Address, payload: Bytes, block: Option<BlockNumber>) -> ApiCall<Bytes, T::Out> {
|
||||
let future = api::Eth::new(transport).call(CallRequest {
|
||||
from: None,
|
||||
to: address,
|
||||
gas: None,
|
||||
gas_price: None,
|
||||
value: None,
|
||||
data: Some(payload),
|
||||
}, block);
|
||||
|
||||
ApiCall {
|
||||
future,
|
||||
message: "eth_call",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns a eth_sign-compatible hash of data to sign.
|
||||
/// The data is prepended with special message to prevent
|
||||
/// chosen-plaintext attacks.
|
||||
|
|
|
@ -127,6 +127,7 @@ impl<T: Transport + Clone> Future for Deploy<T> {
|
|||
checked_deposit_relay: main_receipt.block_number.low_u64(),
|
||||
checked_withdraw_relay: test_receipt.block_number.low_u64(),
|
||||
checked_withdraw_confirm: test_receipt.block_number.low_u64(),
|
||||
withdraw_relay_required_signatures: Some(self.app.config.authorities.required_signatures),
|
||||
};
|
||||
return Ok(Deployed::New(database).into())
|
||||
},
|
||||
|
|
|
@ -15,7 +15,7 @@ use itertools::Itertools;
|
|||
|
||||
fn deposits_filter(home: &home::HomeBridge, address: Address) -> FilterBuilder {
|
||||
let filter = home.events().deposit().create_filter();
|
||||
web3_filter(filter, address)
|
||||
web3_filter(filter, ::std::iter::once(address))
|
||||
}
|
||||
|
||||
fn deposit_relay_payload(home: &home::HomeBridge, foreign: &foreign::ForeignBridge, log: Log) -> Result<Bytes> {
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::sync::{Arc, RwLock};
|
|||
use std::path::PathBuf;
|
||||
use futures::{Stream, Poll, Async};
|
||||
use web3::Transport;
|
||||
use web3::types::U256;
|
||||
use web3::types::{U256, Address};
|
||||
use app::App;
|
||||
use database::Database;
|
||||
use error::{Error, ErrorKind, Result};
|
||||
|
@ -27,7 +27,7 @@ pub use self::withdraw_confirm::{WithdrawConfirm, create_withdraw_confirm};
|
|||
#[derive(Clone, Copy)]
|
||||
pub enum BridgeChecked {
|
||||
DepositRelay(u64),
|
||||
WithdrawRelay(u64),
|
||||
WithdrawRelay((u64, u32)),
|
||||
WithdrawConfirm(u64),
|
||||
}
|
||||
|
||||
|
@ -47,8 +47,9 @@ impl BridgeBackend for FileBackend {
|
|||
BridgeChecked::DepositRelay(n) => {
|
||||
self.database.checked_deposit_relay = n;
|
||||
},
|
||||
BridgeChecked::WithdrawRelay(n) => {
|
||||
BridgeChecked::WithdrawRelay((n, sigs)) => {
|
||||
self.database.checked_withdraw_relay = n;
|
||||
self.database.withdraw_relay_required_signatures = Some(sigs);
|
||||
},
|
||||
BridgeChecked::WithdrawConfirm(n) => {
|
||||
self.database.checked_withdraw_confirm = n;
|
||||
|
@ -71,17 +72,18 @@ enum BridgeStatus {
|
|||
}
|
||||
|
||||
/// Creates new bridge.
|
||||
pub fn create_bridge<T: Transport + Clone>(app: Arc<App<T>>, init: &Database, home_chain_id: u64, foreign_chain_id: u64) -> Bridge<T, FileBackend> {
|
||||
pub fn create_bridge<T: Transport + Clone>(app: Arc<App<T>>, init: &Database, home_chain_id: u64, foreign_chain_id: u64, foreign_validator_contract: Address) -> Bridge<T, FileBackend> {
|
||||
let backend = FileBackend {
|
||||
path: app.database_path.clone(),
|
||||
database: init.clone(),
|
||||
};
|
||||
|
||||
create_bridge_backed_by(app, init, backend, home_chain_id, foreign_chain_id)
|
||||
create_bridge_backed_by(app, init, backend, home_chain_id, foreign_chain_id, foreign_validator_contract)
|
||||
}
|
||||
|
||||
/// Creates new bridge writing to custom backend.
|
||||
pub fn create_bridge_backed_by<T: Transport + Clone, F: BridgeBackend>(app: Arc<App<T>>, init: &Database, backend: F, home_chain_id: u64, foreign_chain_id: u64) -> Bridge<T, F> {
|
||||
pub fn create_bridge_backed_by<T: Transport + Clone, F: BridgeBackend>(app: Arc<App<T>>, init: &Database, backend: F, home_chain_id: u64, foreign_chain_id: u64,
|
||||
foreign_validator_contract: Address) -> Bridge<T, F> {
|
||||
let home_balance = Arc::new(RwLock::new(None));
|
||||
let foreign_balance = Arc::new(RwLock::new(None));
|
||||
Bridge {
|
||||
|
@ -90,7 +92,7 @@ pub fn create_bridge_backed_by<T: Transport + Clone, F: BridgeBackend>(app: Arc<
|
|||
foreign_balance: foreign_balance.clone(),
|
||||
home_balance: home_balance.clone(),
|
||||
deposit_relay: create_deposit_relay(app.clone(), init, foreign_balance.clone(), foreign_chain_id),
|
||||
withdraw_relay: create_withdraw_relay(app.clone(), init, home_balance.clone(), home_chain_id),
|
||||
withdraw_relay: create_withdraw_relay(app.clone(), init, home_balance.clone(), home_chain_id, foreign_validator_contract),
|
||||
withdraw_confirm: create_withdraw_confirm(app.clone(), init, foreign_balance.clone(), foreign_chain_id),
|
||||
state: BridgeStatus::Wait,
|
||||
backend,
|
||||
|
@ -168,6 +170,7 @@ impl<T: Transport, F: BridgeBackend> Stream for Bridge<T, F> {
|
|||
self.check_balances()?;
|
||||
}
|
||||
|
||||
|
||||
let w_relay = try_bridge!(self.withdraw_relay.poll().map_err(|e| ErrorKind::ContextualizedError(Box::new(e), "withdraw_relay"))).
|
||||
map(BridgeChecked::WithdrawRelay);
|
||||
|
||||
|
@ -175,6 +178,7 @@ impl<T: Transport, F: BridgeBackend> Stream for Bridge<T, F> {
|
|||
self.check_balances()?;
|
||||
}
|
||||
|
||||
|
||||
let w_confirm = try_bridge!(self.withdraw_confirm.poll().map_err(|e| ErrorKind::ContextualizedError(Box::new(e), "withdraw_confirm"))).
|
||||
map(BridgeChecked::WithdrawConfirm);
|
||||
|
||||
|
@ -226,10 +230,11 @@ mod tests {
|
|||
assert_eq!(1, backend.database.checked_deposit_relay);
|
||||
assert_eq!(0, backend.database.checked_withdraw_confirm);
|
||||
assert_eq!(0, backend.database.checked_withdraw_relay);
|
||||
backend.save(vec![BridgeChecked::DepositRelay(2), BridgeChecked::WithdrawConfirm(3), BridgeChecked::WithdrawRelay(2)]).unwrap();
|
||||
backend.save(vec![BridgeChecked::DepositRelay(2), BridgeChecked::WithdrawConfirm(3), BridgeChecked::WithdrawRelay((2, 1))]).unwrap();
|
||||
assert_eq!(2, backend.database.checked_deposit_relay);
|
||||
assert_eq!(3, backend.database.checked_withdraw_confirm);
|
||||
assert_eq!(2, backend.database.checked_withdraw_relay);
|
||||
assert_eq!(1, backend.database.withdraw_relay_required_signatures.unwrap());
|
||||
|
||||
let loaded = Database::load(path).unwrap();
|
||||
assert_eq!(backend.database, loaded);
|
||||
|
|
|
@ -16,7 +16,7 @@ use super::nonce::{NonceCheck, SendRawTransaction};
|
|||
|
||||
fn withdraws_filter(foreign: &foreign::ForeignBridge, address: Address) -> FilterBuilder {
|
||||
let filter = foreign.events().withdraw().create_filter();
|
||||
web3_filter(filter, address)
|
||||
web3_filter(filter, ::std::iter::once(address))
|
||||
}
|
||||
|
||||
fn withdraw_submit_signature_payload(foreign: &foreign::ForeignBridge, withdraw_message: Vec<u8>, signature: H520) -> Bytes {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::sync::{Arc, RwLock};
|
||||
use futures::{self, Future, Stream, stream::{Collect, iter_ok, IterOk, Buffered}, Poll};
|
||||
use futures::{self, Async, Future, Stream, stream::{Collect, iter_ok, IterOk, Buffered}, Poll};
|
||||
use futures::future::{JoinAll, join_all, Join};
|
||||
use tokio_timer::Timeout;
|
||||
use web3::Transport;
|
||||
use web3::types::{U256, Address, FilterBuilder, Log, Bytes};
|
||||
use ethabi::{RawLog, self};
|
||||
use ethabi::{RawLog, Topic, self};
|
||||
use app::App;
|
||||
use api::{self, LogStream, ApiCall};
|
||||
use contracts::foreign;
|
||||
|
@ -18,9 +18,18 @@ use super::nonce::{NonceCheck, SendRawTransaction};
|
|||
use itertools::Itertools;
|
||||
|
||||
/// returns a filter for `ForeignBridge.CollectedSignatures` events
|
||||
fn collected_signatures_filter(foreign: &foreign::ForeignBridge, address: Address) -> FilterBuilder {
|
||||
let filter = foreign.events().collected_signatures().create_filter();
|
||||
web3_filter(filter, address)
|
||||
fn collected_signatures_filter<I: IntoIterator<Item = Address>>(foreign: &foreign::ForeignBridge, addresses: I) -> FilterBuilder {
|
||||
let mut filter = foreign.events().collected_signatures().create_filter();
|
||||
let sig_filter = foreign.events().required_signatures_changed().create_filter();
|
||||
// Combine with the `RequiredSignaturesChanged` event
|
||||
match filter.topic0 {
|
||||
Topic::This(t) => filter.topic0 = Topic::OneOf(vec![t]),
|
||||
Topic::OneOf(ref mut vec) => {
|
||||
vec.append(&mut sig_filter.topic0.into());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
web3_filter(filter, addresses)
|
||||
}
|
||||
|
||||
/// payloads for calls to `ForeignBridge.signature` and `ForeignBridge.message`
|
||||
|
@ -33,7 +42,12 @@ struct RelayAssignment {
|
|||
message_payload: Bytes,
|
||||
}
|
||||
|
||||
fn signatures_payload(foreign: &foreign::ForeignBridge, required_signatures: u32, my_address: Address, log: Log) -> error::Result<Option<RelayAssignment>> {
|
||||
fn signatures_payload(foreign: &foreign::ForeignBridge, required_signatures: u32, my_address: Address, log: Log) -> error::Result<(Option<RelayAssignment>, u32)> {
|
||||
// check if this is a RequiredSignaturesChanged event
|
||||
match get_required_signatures(foreign, log.clone()) {
|
||||
Some(signatures) => return Ok((None, signatures)),
|
||||
None => (),
|
||||
}
|
||||
// convert web3::Log to ethabi::RawLog since ethabi events can
|
||||
// only be parsed from the latter
|
||||
let raw_log = RawLog {
|
||||
|
@ -45,7 +59,7 @@ fn signatures_payload(foreign: &foreign::ForeignBridge, required_signatures: u32
|
|||
info!("bridge not responsible for relaying transaction to home. tx hash: {}", log.transaction_hash.unwrap());
|
||||
// this authority is not responsible for relaying this transaction.
|
||||
// someone else will relay this transaction to home.
|
||||
return Ok(None);
|
||||
return Ok((None, required_signatures));
|
||||
}
|
||||
let signature_payloads = (0..required_signatures).into_iter()
|
||||
.map(|index| foreign.functions().signature().input(collected_signatures.message_hash, index))
|
||||
|
@ -53,14 +67,27 @@ fn signatures_payload(foreign: &foreign::ForeignBridge, required_signatures: u32
|
|||
.collect();
|
||||
let message_payload = foreign.functions().message().input(collected_signatures.message_hash).into();
|
||||
|
||||
Ok(Some(RelayAssignment {
|
||||
Ok((Some(RelayAssignment {
|
||||
signature_payloads,
|
||||
message_payload,
|
||||
}))
|
||||
}), required_signatures))
|
||||
}
|
||||
|
||||
fn get_required_signatures(foreign: &foreign::ForeignBridge, log: Log) -> Option<u32> {
|
||||
// convert web3::Log to ethabi::RawLog since ethabi events can
|
||||
// only be parsed from the latter
|
||||
let raw_log = RawLog {
|
||||
topics: log.topics.into_iter().map(|t| t.0.into()).collect(),
|
||||
data: log.data.0,
|
||||
};
|
||||
foreign.events().required_signatures_changed().parse_log(raw_log)
|
||||
.ok().map(|v| v.required_signatures.low_u32())
|
||||
}
|
||||
|
||||
|
||||
/// state of the withdraw relay state machine
|
||||
pub enum WithdrawRelayState<T: Transport> {
|
||||
CheckRequiredSignatures(Timeout<ApiCall<Bytes, T::Out>>),
|
||||
Wait,
|
||||
FetchMessagesSignatures {
|
||||
future: Join<
|
||||
|
@ -76,20 +103,31 @@ pub enum WithdrawRelayState<T: Transport> {
|
|||
Yield(Option<u64>),
|
||||
}
|
||||
|
||||
pub fn create_withdraw_relay<T: Transport + Clone>(app: Arc<App<T>>, init: &Database, home_balance: Arc<RwLock<Option<U256>>>, home_chain_id: u64) -> WithdrawRelay<T> {
|
||||
pub fn create_withdraw_relay<T: Transport + Clone>(app: Arc<App<T>>, init: &Database, home_balance: Arc<RwLock<Option<U256>>>, home_chain_id: u64,
|
||||
foreign_validator_contract: Address) -> WithdrawRelay<T> {
|
||||
let logs_init = api::LogStreamInit {
|
||||
after: init.checked_withdraw_relay,
|
||||
request_timeout: app.config.foreign.request_timeout,
|
||||
poll_interval: app.config.foreign.poll_interval,
|
||||
confirmations: app.config.foreign.required_confirmations,
|
||||
filter: collected_signatures_filter(&app.foreign_bridge, init.foreign_contract_address),
|
||||
filter: collected_signatures_filter(&app.foreign_bridge, vec![init.foreign_contract_address, foreign_validator_contract]),
|
||||
};
|
||||
|
||||
let state = if init.withdraw_relay_required_signatures.is_none() {
|
||||
let call = app.timer.timeout(api::call_at(app.connections.foreign.clone(), foreign_validator_contract,
|
||||
app.foreign_bridge.functions().required_signatures().input().into(),
|
||||
Some(init.checked_withdraw_relay.into())), app.config.foreign.request_timeout);
|
||||
WithdrawRelayState::CheckRequiredSignatures(call)
|
||||
} else {
|
||||
WithdrawRelayState::Wait
|
||||
};
|
||||
|
||||
WithdrawRelay {
|
||||
logs: api::log_stream(app.connections.foreign.clone(), app.timer.clone(), logs_init),
|
||||
home_contract: init.home_contract_address,
|
||||
foreign_contract: init.foreign_contract_address,
|
||||
state: WithdrawRelayState::Wait,
|
||||
required_signatures: init.withdraw_relay_required_signatures.clone().unwrap_or(app.config.authorities.accounts.len() as u32),
|
||||
state,
|
||||
app,
|
||||
home_balance,
|
||||
home_chain_id,
|
||||
|
@ -104,10 +142,11 @@ pub struct WithdrawRelay<T: Transport> {
|
|||
home_contract: Address,
|
||||
home_balance: Arc<RwLock<Option<U256>>>,
|
||||
home_chain_id: u64,
|
||||
required_signatures: u32,
|
||||
}
|
||||
|
||||
impl<T: Transport> Stream for WithdrawRelay<T> {
|
||||
type Item = u64;
|
||||
type Item = (u64, u32);
|
||||
type Error = Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
|
@ -116,24 +155,54 @@ impl<T: Transport> Stream for WithdrawRelay<T> {
|
|||
let contract = self.home_contract.clone();
|
||||
let home = &self.app.config.home;
|
||||
let t = &self.app.connections.home;
|
||||
let foreign = &self.app.connections.foreign;
|
||||
let chain_id = self.home_chain_id;
|
||||
let foreign_bridge = &self.app.foreign_bridge;
|
||||
let foreign_account = self.app.config.foreign.account;
|
||||
let timer = &self.app.timer;
|
||||
let foreign_contract = self.foreign_contract;
|
||||
let foreign_request_timeout = self.app.config.foreign.request_timeout;
|
||||
|
||||
loop {
|
||||
let required_signatures = self.required_signatures;
|
||||
let next_state = match self.state {
|
||||
WithdrawRelayState::CheckRequiredSignatures(ref mut logs) => {
|
||||
let mut required_signatures = try_ready!(logs.poll().map_err(|e| ErrorKind::ContextualizedError(Box::new(e), "checking foreign for requiredSignatures value")));
|
||||
self.required_signatures = U256::from(required_signatures.0.as_slice()).low_u32();
|
||||
info!("Required signatures: {}", self.required_signatures);
|
||||
WithdrawRelayState::Wait
|
||||
},
|
||||
WithdrawRelayState::Wait => {
|
||||
let item = try_stream!(self.logs.poll().map_err(|e| ErrorKind::ContextualizedError(Box::new(e), "polling foreign for collected signatures")));
|
||||
info!("got {} new signed withdraws to relay", item.logs.len());
|
||||
let assignments = item.logs
|
||||
.into_iter()
|
||||
.map(|log| {
|
||||
.fold((self.required_signatures, vec![]), |mut acc, log| {
|
||||
info!("collected signature is ready for relay: tx hash: {}", log.transaction_hash.unwrap());
|
||||
signatures_payload(
|
||||
&self.app.foreign_bridge,
|
||||
self.app.config.authorities.required_signatures,
|
||||
self.app.config.foreign.account,
|
||||
log)
|
||||
})
|
||||
.collect::<error::Result<Vec<_>>>()?;
|
||||
let res = signatures_payload(
|
||||
foreign_bridge,
|
||||
acc.0,
|
||||
foreign_account,
|
||||
log);
|
||||
match res {
|
||||
Ok((value, required_signatures)) => {
|
||||
acc.1.push(Ok(value));
|
||||
(required_signatures, acc.1)
|
||||
},
|
||||
Err(err) => {
|
||||
acc.1.push(Err(err));
|
||||
(acc.0, acc.1)
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if assignments.0 != self.required_signatures {
|
||||
self.required_signatures = assignments.0;
|
||||
info!("Required signatures: {} (block #{})", self.required_signatures, item.to);
|
||||
}
|
||||
|
||||
let assignments = assignments.1.into_iter().collect::<error::Result<Vec<_>>>()?;
|
||||
|
||||
let (signatures, messages): (Vec<_>, Vec<_>) = assignments.into_iter()
|
||||
.filter_map(|a| a)
|
||||
|
@ -142,9 +211,9 @@ impl<T: Transport> Stream for WithdrawRelay<T> {
|
|||
|
||||
let message_calls = messages.into_iter()
|
||||
.map(|payload| {
|
||||
self.app.timer.timeout(
|
||||
api::call(&self.app.connections.foreign, self.foreign_contract.clone(), payload),
|
||||
self.app.config.foreign.request_timeout)
|
||||
timer.timeout(
|
||||
api::call(foreign, foreign_contract.clone(), payload),
|
||||
foreign_request_timeout)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
@ -152,9 +221,9 @@ impl<T: Transport> Stream for WithdrawRelay<T> {
|
|||
.map(|payloads| {
|
||||
payloads.into_iter()
|
||||
.map(|payload| {
|
||||
self.app.timer.timeout(
|
||||
api::call(&self.app.connections.foreign, self.foreign_contract.clone(), payload),
|
||||
self.app.config.foreign.request_timeout)
|
||||
timer.timeout(
|
||||
api::call(foreign, foreign_contract.clone(), payload),
|
||||
foreign_request_timeout)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
|
@ -246,7 +315,7 @@ impl<T: Transport> Stream for WithdrawRelay<T> {
|
|||
info!("waiting for signed withdraws to relay");
|
||||
WithdrawRelayState::Wait
|
||||
},
|
||||
some => return Ok(some.into()),
|
||||
Some(block) => return Ok(Async::Ready(Some((block, required_signatures)))),
|
||||
}
|
||||
};
|
||||
self.state = next_state;
|
||||
|
@ -282,7 +351,7 @@ mod tests {
|
|||
removed: None,
|
||||
};
|
||||
|
||||
let assignment = signatures_payload(&foreign, 2, my_address, log).unwrap().unwrap();
|
||||
let assignment = signatures_payload(&foreign, 2, my_address, log).unwrap().0.unwrap();
|
||||
let expected_message: Bytes = "490a32c600000000000000000000000000000000000000000000000000000000000000f0".from_hex().unwrap().into();
|
||||
let expected_signatures: Vec<Bytes> = vec![
|
||||
"1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap().into(),
|
||||
|
@ -314,6 +383,6 @@ mod tests {
|
|||
};
|
||||
|
||||
let assignment = signatures_payload(&foreign, 2, my_address, log).unwrap();
|
||||
assert_eq!(None, assignment);
|
||||
assert_eq!(None, assignment.0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ impl Config {
|
|||
foreign: Node::from_load_struct(config.foreign)?,
|
||||
authorities: Authorities {
|
||||
accounts: config.authorities.accounts,
|
||||
#[cfg(feature = "deploy")]
|
||||
required_signatures: config.authorities.required_signatures,
|
||||
},
|
||||
txs: config.transactions.map(Transactions::from_load_struct).unwrap_or_default(),
|
||||
|
@ -181,6 +182,7 @@ pub struct ContractConfig {
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Authorities {
|
||||
pub accounts: Vec<Address>,
|
||||
#[cfg(feature = "deploy")]
|
||||
pub required_signatures: u32,
|
||||
}
|
||||
|
||||
|
@ -241,9 +243,9 @@ mod load {
|
|||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Authorities {
|
||||
pub accounts: Vec<Address>,
|
||||
#[cfg(feature = "deploy")]
|
||||
pub required_signatures: u32,
|
||||
}
|
||||
}
|
||||
|
@ -292,7 +294,6 @@ accounts = [
|
|||
"0x0000000000000000000000000000000000000002",
|
||||
"0x0000000000000000000000000000000000000003"
|
||||
]
|
||||
required_signatures = 2
|
||||
|
||||
[transactions]
|
||||
home_deploy = { gas = 20 }
|
||||
|
@ -335,7 +336,6 @@ home_deploy = { gas = 20 }
|
|||
"0000000000000000000000000000000000000002".into(),
|
||||
"0000000000000000000000000000000000000003".into(),
|
||||
],
|
||||
required_signatures: 2,
|
||||
},
|
||||
#[cfg(feature = "deploy")]
|
||||
estimated_gas_cost_of_withdraw: 100_000,
|
||||
|
@ -381,7 +381,6 @@ accounts = [
|
|||
"0x0000000000000000000000000000000000000002",
|
||||
"0x0000000000000000000000000000000000000003"
|
||||
]
|
||||
required_signatures = 2
|
||||
"#;
|
||||
let expected = Config {
|
||||
txs: Transactions::default(),
|
||||
|
@ -419,7 +418,6 @@ required_signatures = 2
|
|||
"0000000000000000000000000000000000000002".into(),
|
||||
"0000000000000000000000000000000000000003".into(),
|
||||
],
|
||||
required_signatures: 2,
|
||||
},
|
||||
#[cfg(feature = "deploy")]
|
||||
estimated_gas_cost_of_withdraw: 200_000_000,
|
||||
|
|
|
@ -20,6 +20,8 @@ pub struct Database {
|
|||
pub checked_deposit_relay: u64,
|
||||
/// Number of last block which has been checked for withdraw relays.
|
||||
pub checked_withdraw_relay: u64,
|
||||
/// Number of required signatures on the foreign side
|
||||
pub withdraw_relay_required_signatures: Option<u32>,
|
||||
/// Number of last block which has been checked for withdraw confirms.
|
||||
pub checked_withdraw_confirm: u64,
|
||||
}
|
||||
|
@ -70,6 +72,7 @@ home_deploy = 100
|
|||
foreign_deploy = 101
|
||||
checked_deposit_relay = 120
|
||||
checked_withdraw_relay = 121
|
||||
withdraw_relay_required_signatures = 2
|
||||
checked_withdraw_confirm = 121
|
||||
"#;
|
||||
|
||||
|
@ -81,6 +84,7 @@ checked_withdraw_confirm = 121
|
|||
checked_deposit_relay: 120,
|
||||
checked_withdraw_relay: 121,
|
||||
checked_withdraw_confirm: 121,
|
||||
withdraw_relay_required_signatures: Some(2),
|
||||
};
|
||||
|
||||
let database = toml.parse().unwrap();
|
||||
|
|
|
@ -22,6 +22,9 @@ error_chain! {
|
|||
errors {
|
||||
ShutdownRequested
|
||||
InsufficientFunds
|
||||
NoRequiredSignaturesChanged {
|
||||
description("No RequiredSignaturesChanged has been observed")
|
||||
}
|
||||
// api timeout
|
||||
Timeout(request: &'static str) {
|
||||
description("Request timeout"),
|
||||
|
|
|
@ -11,12 +11,12 @@ fn web3_topic(topic: ethabi::Topic<ethabi::Hash>) -> Option<Vec<H256>> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn web3_filter(filter: ethabi::TopicFilter, address: Address) -> FilterBuilder {
|
||||
pub fn web3_filter<I: IntoIterator<Item = Address>>(filter: ethabi::TopicFilter, addresses: I) -> FilterBuilder {
|
||||
let t0 = web3_topic(filter.topic0);
|
||||
let t1 = web3_topic(filter.topic1);
|
||||
let t2 = web3_topic(filter.topic2);
|
||||
let t3 = web3_topic(filter.topic3);
|
||||
FilterBuilder::default()
|
||||
.address(vec![address])
|
||||
.address(addresses.into_iter().collect())
|
||||
.topics(t0, t1, t2, t3)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ use docopt::Docopt;
|
|||
use futures::{Stream, future};
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
use bridge::api;
|
||||
use bridge::app::App;
|
||||
use bridge::bridge::{create_bridge, create_deploy, create_chain_id_retrieval, Deployed};
|
||||
use bridge::config::Config;
|
||||
|
@ -175,8 +176,15 @@ fn execute<S, I>(command: I, running: Arc<AtomicBool>) -> Result<String, UserFac
|
|||
},
|
||||
};
|
||||
|
||||
let foreign_validator_contract: web3::types::Address = event_loop.run(app.timer.timeout(api::call_at(app.connections.foreign.clone(), database.foreign_contract_address,
|
||||
app.foreign_bridge.functions().validator_contract().input().into(),
|
||||
Some(database.checked_withdraw_relay.into())), app.config.foreign.request_timeout)).unwrap().0.as_slice().into();
|
||||
|
||||
info!(target: "bridge", "Foreign validator contract: {}", foreign_validator_contract);
|
||||
|
||||
info!(target: "bridge", "Starting listening to events");
|
||||
let bridge = create_bridge(app.clone(), &database, home_chain_id, foreign_chain_id).and_then(|_| future::ok(true)).collect();
|
||||
let bridge = create_bridge(app.clone(), &database, home_chain_id, foreign_chain_id, foreign_validator_contract)
|
||||
.and_then(|_| future::ok(true)).collect();
|
||||
let mut result = event_loop.run(bridge);
|
||||
loop {
|
||||
match result {
|
||||
|
|
|
@ -253,6 +253,10 @@ contract HomeBridge is BridgeDeploymentAddressStorage,
|
|||
/// Must be lesser than number of authorities.
|
||||
uint256 public requiredSignatures;
|
||||
|
||||
event RequiredSignaturesChanged (uint256 requiredSignatures);
|
||||
|
||||
address public validatorContract;
|
||||
|
||||
/// The gas cost of calling `HomeBridge.withdraw`.
|
||||
///
|
||||
/// Is subtracted from `value` on withdraw.
|
||||
|
@ -284,6 +288,8 @@ contract HomeBridge is BridgeDeploymentAddressStorage,
|
|||
requiredSignatures = requiredSignaturesParam;
|
||||
authorities = authoritiesParam;
|
||||
estimatedGasCostOfWithdraw = estimatedGasCostOfWithdrawParam;
|
||||
validatorContract = this;
|
||||
RequiredSignaturesChanged(requiredSignatures);
|
||||
}
|
||||
|
||||
/// Should be used to deposit money.
|
||||
|
@ -357,9 +363,12 @@ contract ForeignBridge is BridgeDeploymentAddressStorage,
|
|||
///
|
||||
/// Must be less than number of authorities.
|
||||
uint256 public requiredSignatures;
|
||||
event RequiredSignaturesChanged (uint256 requiredSignatures);
|
||||
|
||||
uint256 public estimatedGasCostOfWithdraw;
|
||||
|
||||
address public validatorContract;
|
||||
|
||||
// Original parity-bridge assumes that anyone could forward final
|
||||
// withdraw confirmation to the HomeBridge contract. That's why
|
||||
// they need to make sure that no one is trying to steal funds by
|
||||
|
@ -416,11 +425,15 @@ contract ForeignBridge is BridgeDeploymentAddressStorage,
|
|||
require(_requiredSignatures <= _authorities.length);
|
||||
requiredSignatures = _requiredSignatures;
|
||||
|
||||
validatorContract = this;
|
||||
|
||||
for (uint i = 0; i < _authorities.length; i++) {
|
||||
authorities[_authorities[i]] = true;
|
||||
}
|
||||
|
||||
estimatedGasCostOfWithdraw = _estimatedGasCostOfWithdraw;
|
||||
|
||||
RequiredSignaturesChanged(requiredSignatures);
|
||||
}
|
||||
|
||||
/// require that sender is an authority
|
||||
|
|
|
@ -17,7 +17,6 @@ rpc_port = 443
|
|||
accounts = [
|
||||
"0x006e27b6a72e1f34c626762f3c4761547aff1421",
|
||||
]
|
||||
required_signatures = 1
|
||||
|
||||
[transactions]
|
||||
home_deploy = { gas = 1000000, gas_price = 1000000000 }
|
||||
|
|
Loading…
Reference in New Issue