2018-04-03 23:42:16 -07:00
|
|
|
use std::sync::{Arc, RwLock};
|
2018-06-01 18:31:47 -07:00
|
|
|
use futures::{self, Future, Stream, stream::{Collect, FuturesUnordered, futures_unordered}, Poll};
|
2017-08-12 07:57:07 -07:00
|
|
|
use web3::Transport;
|
Problem: nonce reuse
Unfortunately, bridge will still reuse nonce very often.
Specifically when trying to send more than one transaction at
a time, clearly a faulty behaviour.
Solution: chain retrieving a nonce with subsequent sending
of the transaction.
However, chaining these is not enough as it'll still fail.
This is happening because bridge module is polling all its components
(deposit_relay, withdraw_confirm, withdraw_relay) sequentially,
and some of them maybe waiting on their transactions to go through.
However, those transactions are also done as composed futures of nonce
retrieval and transaction sending. This means that it is very often
that first, these futures will go through the nonce acquisition process,
get the same values, and then submit transactions with the same nonce.
This patch makes NonceCheck future check if the transaction failed
with this specific issue of nonce reuse and effectively restarts from
the beginning in that case, repeating nonce acquisition process... until
it succeeeds.
2018-04-29 13:20:42 -07:00
|
|
|
use web3::types::{U256, Address, Bytes, Log, FilterBuilder};
|
2017-08-21 08:32:37 -07:00
|
|
|
use ethabi::RawLog;
|
Problem: nonce reuse
Unfortunately, bridge will still reuse nonce very often.
Specifically when trying to send more than one transaction at
a time, clearly a faulty behaviour.
Solution: chain retrieving a nonce with subsequent sending
of the transaction.
However, chaining these is not enough as it'll still fail.
This is happening because bridge module is polling all its components
(deposit_relay, withdraw_confirm, withdraw_relay) sequentially,
and some of them maybe waiting on their transactions to go through.
However, those transactions are also done as composed futures of nonce
retrieval and transaction sending. This means that it is very often
that first, these futures will go through the nonce acquisition process,
get the same values, and then submit transactions with the same nonce.
This patch makes NonceCheck future check if the transaction failed
with this specific issue of nonce reuse and effectively restarts from
the beginning in that case, repeating nonce acquisition process... until
it succeeeds.
2018-04-29 13:20:42 -07:00
|
|
|
use api::{LogStream, self};
|
2018-04-03 23:42:16 -07:00
|
|
|
use error::{Error, ErrorKind, Result};
|
2017-08-12 11:03:48 -07:00
|
|
|
use database::Database;
|
2017-10-10 02:02:46 -07:00
|
|
|
use contracts::{home, foreign};
|
2017-08-24 09:42:19 -07:00
|
|
|
use util::web3_filter;
|
2017-08-12 07:57:07 -07:00
|
|
|
use app::App;
|
2018-04-25 03:12:39 -07:00
|
|
|
use ethcore_transaction::{Transaction, Action};
|
Problem: nonce reuse
Unfortunately, bridge will still reuse nonce very often.
Specifically when trying to send more than one transaction at
a time, clearly a faulty behaviour.
Solution: chain retrieving a nonce with subsequent sending
of the transaction.
However, chaining these is not enough as it'll still fail.
This is happening because bridge module is polling all its components
(deposit_relay, withdraw_confirm, withdraw_relay) sequentially,
and some of them maybe waiting on their transactions to go through.
However, those transactions are also done as composed futures of nonce
retrieval and transaction sending. This means that it is very often
that first, these futures will go through the nonce acquisition process,
get the same values, and then submit transactions with the same nonce.
This patch makes NonceCheck future check if the transaction failed
with this specific issue of nonce reuse and effectively restarts from
the beginning in that case, repeating nonce acquisition process... until
it succeeeds.
2018-04-29 13:20:42 -07:00
|
|
|
use super::nonce::{NonceCheck, SendRawTransaction};
|
2018-05-25 00:24:16 -07:00
|
|
|
use super::BridgeChecked;
|
2018-04-25 03:12:39 -07:00
|
|
|
use itertools::Itertools;
|
2017-08-10 08:55:46 -07:00
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
fn deposits_filter(home: &home::HomeBridge, address: Address) -> FilterBuilder {
|
|
|
|
let filter = home.events().deposit().create_filter();
|
2018-05-19 08:25:39 -07:00
|
|
|
web3_filter(filter, ::std::iter::once(address))
|
2017-08-21 08:32:37 -07:00
|
|
|
}
|
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
fn deposit_relay_payload(home: &home::HomeBridge, foreign: &foreign::ForeignBridge, log: Log) -> Result<Bytes> {
|
2017-08-21 08:32:37 -07:00
|
|
|
let raw_log = RawLog {
|
2018-02-07 05:58:50 -08:00
|
|
|
topics: log.topics,
|
2017-08-21 08:32:37 -07:00
|
|
|
data: log.data.0,
|
|
|
|
};
|
2017-10-10 02:02:46 -07:00
|
|
|
let deposit_log = home.events().deposit().parse_log(raw_log)?;
|
2017-08-21 08:32:37 -07:00
|
|
|
let hash = log.transaction_hash.expect("log to be mined and contain `transaction_hash`");
|
2017-10-10 02:02:46 -07:00
|
|
|
let payload = foreign.functions().deposit().input(deposit_log.recipient, deposit_log.value, hash.0);
|
2017-08-21 08:32:37 -07:00
|
|
|
Ok(payload.into())
|
|
|
|
}
|
|
|
|
|
2017-08-13 04:09:28 -07:00
|
|
|
/// State of deposits relay.
|
|
|
|
enum DepositRelayState<T: Transport> {
|
|
|
|
/// Deposit relay is waiting for logs.
|
2017-08-12 07:57:07 -07:00
|
|
|
Wait,
|
2017-08-13 04:09:28 -07:00
|
|
|
/// Relaying deposits in progress.
|
2017-08-12 07:57:07 -07:00
|
|
|
RelayDeposits {
|
2018-06-01 18:31:47 -07:00
|
|
|
future: Collect<FuturesUnordered<NonceCheck<T, SendRawTransaction<T>>>>,
|
2017-08-12 07:57:07 -07:00
|
|
|
block: u64,
|
|
|
|
},
|
2017-08-13 07:15:14 -07:00
|
|
|
/// All deposits till given block has been relayed.
|
2017-08-12 07:57:07 -07:00
|
|
|
Yield(Option<u64>),
|
|
|
|
}
|
|
|
|
|
2018-05-23 17:42:13 -07:00
|
|
|
pub fn create_deposit_relay<T: Transport + Clone>(app: Arc<App<T>>, init: &Database, foreign_balance: Arc<RwLock<Option<U256>>>, foreign_chain_id: u64, foreign_gas_price: Arc<RwLock<u64>>) -> DepositRelay<T> {
|
2017-08-13 04:09:28 -07:00
|
|
|
let logs_init = api::LogStreamInit {
|
|
|
|
after: init.checked_deposit_relay,
|
2017-10-10 02:02:46 -07:00
|
|
|
request_timeout: app.config.home.request_timeout,
|
|
|
|
poll_interval: app.config.home.poll_interval,
|
|
|
|
confirmations: app.config.home.required_confirmations,
|
2018-02-07 05:58:50 -08:00
|
|
|
filter: deposits_filter(&app.home_bridge, init.home_contract_address),
|
2017-08-13 04:09:28 -07:00
|
|
|
};
|
|
|
|
DepositRelay {
|
2017-10-10 02:02:46 -07:00
|
|
|
logs: api::log_stream(app.connections.home.clone(), app.timer.clone(), logs_init),
|
2018-02-07 05:58:50 -08:00
|
|
|
foreign_contract: init.foreign_contract_address,
|
2017-08-13 04:09:28 -07:00
|
|
|
state: DepositRelayState::Wait,
|
|
|
|
app,
|
2018-04-03 23:42:16 -07:00
|
|
|
foreign_balance,
|
2018-04-25 03:12:39 -07:00
|
|
|
foreign_chain_id,
|
2018-05-23 17:42:13 -07:00
|
|
|
foreign_gas_price,
|
2017-08-13 04:09:28 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-12 07:57:07 -07:00
|
|
|
pub struct DepositRelay<T: Transport> {
|
|
|
|
app: Arc<App<T>>,
|
|
|
|
logs: LogStream<T>,
|
|
|
|
state: DepositRelayState<T>,
|
2017-10-10 02:02:46 -07:00
|
|
|
foreign_contract: Address,
|
2018-04-03 23:42:16 -07:00
|
|
|
foreign_balance: Arc<RwLock<Option<U256>>>,
|
2018-04-25 03:12:39 -07:00
|
|
|
foreign_chain_id: u64,
|
2018-05-23 17:42:13 -07:00
|
|
|
foreign_gas_price: Arc<RwLock<u64>>,
|
2017-08-12 07:57:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Transport> Stream for DepositRelay<T> {
|
2018-05-25 00:24:16 -07:00
|
|
|
type Item = BridgeChecked;
|
2017-08-12 07:57:07 -07:00
|
|
|
type Error = Error;
|
|
|
|
|
|
|
|
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
|
|
|
loop {
|
|
|
|
let next_state = match self.state {
|
2017-08-12 11:03:48 -07:00
|
|
|
DepositRelayState::Wait => {
|
2018-04-03 23:42:16 -07:00
|
|
|
let foreign_balance = self.foreign_balance.read().unwrap();
|
|
|
|
if foreign_balance.is_none() {
|
|
|
|
warn!("foreign contract balance is unknown");
|
|
|
|
return Ok(futures::Async::NotReady);
|
|
|
|
}
|
2018-05-07 16:34:03 -07:00
|
|
|
let item = try_stream!(self.logs.poll().map_err(|e| ErrorKind::ContextualizedError(Box::new(e), "polling home for deposits")));
|
Problem: nonce reuse
Unfortunately, bridge will still reuse nonce very often.
Specifically when trying to send more than one transaction at
a time, clearly a faulty behaviour.
Solution: chain retrieving a nonce with subsequent sending
of the transaction.
However, chaining these is not enough as it'll still fail.
This is happening because bridge module is polling all its components
(deposit_relay, withdraw_confirm, withdraw_relay) sequentially,
and some of them maybe waiting on their transactions to go through.
However, those transactions are also done as composed futures of nonce
retrieval and transaction sending. This means that it is very often
that first, these futures will go through the nonce acquisition process,
get the same values, and then submit transactions with the same nonce.
This patch makes NonceCheck future check if the transaction failed
with this specific issue of nonce reuse and effectively restarts from
the beginning in that case, repeating nonce acquisition process... until
it succeeeds.
2018-04-29 13:20:42 -07:00
|
|
|
let len = item.logs.len();
|
|
|
|
info!("got {} new deposits to relay", len);
|
2018-05-23 17:42:13 -07:00
|
|
|
|
|
|
|
let gas = U256::from(self.app.config.txs.deposit_relay.gas);
|
|
|
|
let gas_price = U256::from(*self.foreign_gas_price.read().unwrap());
|
|
|
|
let balance_required = gas * gas_price * U256::from(item.logs.len());
|
|
|
|
|
2018-04-03 23:42:16 -07:00
|
|
|
if balance_required > *foreign_balance.as_ref().unwrap() {
|
|
|
|
return Err(ErrorKind::InsufficientFunds.into())
|
|
|
|
}
|
2017-08-12 11:03:48 -07:00
|
|
|
let deposits = item.logs
|
|
|
|
.into_iter()
|
2017-10-10 02:02:46 -07:00
|
|
|
.map(|log| deposit_relay_payload(&self.app.home_bridge, &self.app.foreign_bridge, log))
|
2017-08-21 08:32:37 -07:00
|
|
|
.collect::<Result<Vec<_>>>()?
|
2017-08-12 11:03:48 -07:00
|
|
|
.into_iter()
|
2018-04-25 03:12:39 -07:00
|
|
|
.map(|payload| {
|
|
|
|
let tx = Transaction {
|
2018-05-23 17:42:13 -07:00
|
|
|
gas,
|
|
|
|
gas_price,
|
2018-04-25 03:12:39 -07:00
|
|
|
value: U256::zero(),
|
|
|
|
data: payload.0,
|
Problem: nonce reuse
Unfortunately, bridge will still reuse nonce very often.
Specifically when trying to send more than one transaction at
a time, clearly a faulty behaviour.
Solution: chain retrieving a nonce with subsequent sending
of the transaction.
However, chaining these is not enough as it'll still fail.
This is happening because bridge module is polling all its components
(deposit_relay, withdraw_confirm, withdraw_relay) sequentially,
and some of them maybe waiting on their transactions to go through.
However, those transactions are also done as composed futures of nonce
retrieval and transaction sending. This means that it is very often
that first, these futures will go through the nonce acquisition process,
get the same values, and then submit transactions with the same nonce.
This patch makes NonceCheck future check if the transaction failed
with this specific issue of nonce reuse and effectively restarts from
the beginning in that case, repeating nonce acquisition process... until
it succeeeds.
2018-04-29 13:20:42 -07:00
|
|
|
nonce: U256::zero(),
|
2018-04-25 03:12:39 -07:00
|
|
|
action: Action::Call(self.foreign_contract.clone()),
|
|
|
|
};
|
Problem: nonce reuse
Unfortunately, bridge will still reuse nonce very often.
Specifically when trying to send more than one transaction at
a time, clearly a faulty behaviour.
Solution: chain retrieving a nonce with subsequent sending
of the transaction.
However, chaining these is not enough as it'll still fail.
This is happening because bridge module is polling all its components
(deposit_relay, withdraw_confirm, withdraw_relay) sequentially,
and some of them maybe waiting on their transactions to go through.
However, those transactions are also done as composed futures of nonce
retrieval and transaction sending. This means that it is very often
that first, these futures will go through the nonce acquisition process,
get the same values, and then submit transactions with the same nonce.
This patch makes NonceCheck future check if the transaction failed
with this specific issue of nonce reuse and effectively restarts from
the beginning in that case, repeating nonce acquisition process... until
it succeeeds.
2018-04-29 13:20:42 -07:00
|
|
|
api::send_transaction_with_nonce(self.app.connections.foreign.clone(), self.app.clone(), self.app.config.foreign.clone(),
|
|
|
|
tx, self.foreign_chain_id, SendRawTransaction(self.app.connections.foreign.clone()))
|
|
|
|
}).collect_vec();
|
2017-08-12 07:57:07 -07:00
|
|
|
|
Problem: nonce reuse
Unfortunately, bridge will still reuse nonce very often.
Specifically when trying to send more than one transaction at
a time, clearly a faulty behaviour.
Solution: chain retrieving a nonce with subsequent sending
of the transaction.
However, chaining these is not enough as it'll still fail.
This is happening because bridge module is polling all its components
(deposit_relay, withdraw_confirm, withdraw_relay) sequentially,
and some of them maybe waiting on their transactions to go through.
However, those transactions are also done as composed futures of nonce
retrieval and transaction sending. This means that it is very often
that first, these futures will go through the nonce acquisition process,
get the same values, and then submit transactions with the same nonce.
This patch makes NonceCheck future check if the transaction failed
with this specific issue of nonce reuse and effectively restarts from
the beginning in that case, repeating nonce acquisition process... until
it succeeeds.
2018-04-29 13:20:42 -07:00
|
|
|
info!("relaying {} deposits", len);
|
2017-08-12 11:03:48 -07:00
|
|
|
DepositRelayState::RelayDeposits {
|
2018-06-01 18:31:47 -07:00
|
|
|
future: futures_unordered(deposits).collect(),
|
2017-08-13 07:13:03 -07:00
|
|
|
block: item.to,
|
2017-08-12 11:03:48 -07:00
|
|
|
}
|
2017-08-12 07:57:07 -07:00
|
|
|
},
|
|
|
|
DepositRelayState::RelayDeposits { ref mut future, block } => {
|
2018-05-07 16:34:03 -07:00
|
|
|
let _ = try_ready!(future.poll().map_err(|e| ErrorKind::ContextualizedError(Box::new(e), "relaying deposit to foreign")));
|
2018-01-24 06:07:22 -08:00
|
|
|
info!("deposit relay completed");
|
2017-08-12 07:57:07 -07:00
|
|
|
DepositRelayState::Yield(Some(block))
|
|
|
|
},
|
|
|
|
DepositRelayState::Yield(ref mut block) => match block.take() {
|
|
|
|
None => DepositRelayState::Wait,
|
2018-05-25 00:24:16 -07:00
|
|
|
Some(v) => return Ok(Some(BridgeChecked::DepositRelay(v)).into()),
|
2017-08-12 07:57:07 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
self.state = next_state;
|
|
|
|
}
|
|
|
|
}
|
2017-08-10 08:55:46 -07:00
|
|
|
}
|
2017-08-25 06:01:47 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use rustc_hex::FromHex;
|
2018-05-01 10:02:01 -07:00
|
|
|
use web3::types::{Log, Bytes, Address};
|
2017-10-10 02:02:46 -07:00
|
|
|
use contracts::{home, foreign};
|
2017-08-25 06:01:47 -07:00
|
|
|
use super::deposit_relay_payload;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_deposit_relay_payload() {
|
2017-10-10 02:02:46 -07:00
|
|
|
let home = home::HomeBridge::default();
|
|
|
|
let foreign = foreign::ForeignBridge::default();
|
2017-08-25 06:01:47 -07:00
|
|
|
|
|
|
|
let data = "000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0".from_hex().unwrap();
|
|
|
|
let log = Log {
|
|
|
|
data: data.into(),
|
2018-02-07 05:50:49 -08:00
|
|
|
topics: vec!["e1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c".into()],
|
|
|
|
transaction_hash: Some("884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into()),
|
2018-05-01 10:02:01 -07:00
|
|
|
address: Address::zero(),
|
|
|
|
block_hash: None,
|
|
|
|
transaction_index: None,
|
|
|
|
log_index: None,
|
|
|
|
transaction_log_index: None,
|
|
|
|
log_type: None,
|
|
|
|
block_number: None,
|
|
|
|
removed: None,
|
2017-08-25 06:01:47 -07:00
|
|
|
};
|
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
let payload = deposit_relay_payload(&home, &foreign, log).unwrap();
|
2017-08-25 06:01:47 -07:00
|
|
|
let expected: Bytes = "26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".from_hex().unwrap().into();
|
|
|
|
assert_eq!(expected, payload);
|
|
|
|
}
|
|
|
|
}
|