2018-04-03 23:42:16 -07:00
|
|
|
use std::sync::{Arc, RwLock};
|
2017-08-13 07:13:03 -07:00
|
|
|
use std::ops;
|
2018-06-01 18:31:47 -07:00
|
|
|
use futures::{self, Future, Stream, stream::{Collect, FuturesUnordered, futures_unordered}, Poll};
|
2017-08-12 11:03:48 -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, H520, Address, Bytes, FilterBuilder};
|
|
|
|
use api::{self, LogStream};
|
2017-08-13 07:13:03 -07:00
|
|
|
use app::App;
|
2018-02-02 06:59:54 -08:00
|
|
|
use contracts::foreign;
|
2017-08-24 09:42:19 -07:00
|
|
|
use util::web3_filter;
|
2017-08-13 07:15:14 -07:00
|
|
|
use database::Database;
|
2018-04-03 23:42:16 -07:00
|
|
|
use error::{Error, ErrorKind};
|
2018-02-02 06:59:54 -08:00
|
|
|
use message_to_mainnet::{MessageToMainnet, MESSAGE_LENGTH};
|
2018-04-25 03:12:39 -07:00
|
|
|
use ethcore_transaction::{Transaction, Action};
|
|
|
|
use itertools::Itertools;
|
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;
|
2017-08-12 11:03:48 -07:00
|
|
|
|
2017-10-10 02:02:46 -07:00
|
|
|
fn withdraws_filter(foreign: &foreign::ForeignBridge, address: Address) -> FilterBuilder {
|
|
|
|
let filter = foreign.events().withdraw().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
|
|
|
}
|
|
|
|
|
2018-02-02 06:59:54 -08:00
|
|
|
fn withdraw_submit_signature_payload(foreign: &foreign::ForeignBridge, withdraw_message: Vec<u8>, signature: H520) -> Bytes {
|
|
|
|
assert_eq!(withdraw_message.len(), MESSAGE_LENGTH, "ForeignBridge never accepts messages with len != {} bytes; qed", MESSAGE_LENGTH);
|
|
|
|
foreign.functions().submit_signature().input(signature.0.to_vec(), withdraw_message).into()
|
2017-08-21 08:32:37 -07:00
|
|
|
}
|
|
|
|
|
2017-08-13 07:15:14 -07:00
|
|
|
/// State of withdraw confirmation.
|
|
|
|
enum WithdrawConfirmState<T: Transport> {
|
|
|
|
/// Withdraw confirm is waiting for logs.
|
2017-08-12 11:03:48 -07:00
|
|
|
Wait,
|
2017-08-13 07:15:14 -07:00
|
|
|
/// Confirming withdraws.
|
2017-08-13 07:13:03 -07:00
|
|
|
ConfirmWithdraws {
|
2018-06-01 18:31:47 -07:00
|
|
|
future: Collect<FuturesUnordered<NonceCheck<T, SendRawTransaction<T>>>>,
|
2017-08-12 11:03:48 -07:00
|
|
|
block: u64,
|
|
|
|
},
|
2017-08-13 07:15:14 -07:00
|
|
|
/// All withdraws till given block has been confirmed.
|
2017-08-12 11:03:48 -07:00
|
|
|
Yield(Option<u64>),
|
|
|
|
}
|
|
|
|
|
2018-05-23 17:42:13 -07:00
|
|
|
pub fn create_withdraw_confirm<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>>) -> WithdrawConfirm<T> {
|
2017-08-13 07:15:14 -07:00
|
|
|
let logs_init = api::LogStreamInit {
|
|
|
|
after: init.checked_withdraw_confirm,
|
2017-10-10 02:02:46 -07:00
|
|
|
request_timeout: app.config.foreign.request_timeout,
|
|
|
|
poll_interval: app.config.foreign.poll_interval,
|
|
|
|
confirmations: app.config.foreign.required_confirmations,
|
|
|
|
filter: withdraws_filter(&app.foreign_bridge, init.foreign_contract_address.clone()),
|
2017-08-13 07:15:14 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
WithdrawConfirm {
|
2017-10-10 02:02:46 -07:00
|
|
|
logs: api::log_stream(app.connections.foreign.clone(), app.timer.clone(), logs_init),
|
2018-02-07 05:58:50 -08:00
|
|
|
foreign_contract: init.foreign_contract_address,
|
2017-08-13 07:15:14 -07:00
|
|
|
state: WithdrawConfirmState::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 07:15:14 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-12 11:03:48 -07:00
|
|
|
pub struct WithdrawConfirm<T: Transport> {
|
2017-08-13 07:13:03 -07:00
|
|
|
app: Arc<App<T>>,
|
|
|
|
logs: LogStream<T>,
|
2017-08-12 11:03:48 -07:00
|
|
|
state: WithdrawConfirmState<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 11:03:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Transport> Stream for WithdrawConfirm<T> {
|
2018-05-25 00:24:16 -07:00
|
|
|
type Item = BridgeChecked;
|
2017-08-12 11:03:48 -07:00
|
|
|
type Error = Error;
|
|
|
|
|
|
|
|
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
2018-04-25 03:12:39 -07:00
|
|
|
// borrow checker...
|
|
|
|
let app = &self.app;
|
|
|
|
let gas = self.app.config.txs.withdraw_confirm.gas.into();
|
2018-05-23 17:42:13 -07:00
|
|
|
let gas_price = U256::from(*self.foreign_gas_price.read().unwrap());
|
2018-04-25 03:12:39 -07:00
|
|
|
let contract = self.foreign_contract.clone();
|
2017-08-13 07:13:03 -07:00
|
|
|
loop {
|
|
|
|
let next_state = match self.state {
|
|
|
|
WithdrawConfirmState::Wait => {
|
2018-04-28 10:03:38 -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 foreign for withdrawals")));
|
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 withdraws to sign", len);
|
2018-04-28 10:03:38 -07:00
|
|
|
let mut messages = item.logs
|
2017-08-13 07:13:03 -07:00
|
|
|
.into_iter()
|
2018-01-19 05:47:13 -08:00
|
|
|
.map(|log| {
|
|
|
|
info!("withdraw is ready for signature submission. tx hash {}", log.transaction_hash.unwrap());
|
2018-02-02 06:59:54 -08:00
|
|
|
Ok(MessageToMainnet::from_log(log)?.to_bytes())
|
2018-01-19 05:47:13 -08:00
|
|
|
})
|
2018-02-02 06:59:54 -08:00
|
|
|
.collect::<Result<Vec<_>, Error>>()?;
|
2017-08-13 07:13:03 -07:00
|
|
|
|
2018-04-28 10:03:38 -07:00
|
|
|
info!("signing");
|
|
|
|
|
|
|
|
let signatures = messages.clone()
|
2017-08-21 08:32:37 -07:00
|
|
|
.into_iter()
|
2018-04-28 10:03:38 -07:00
|
|
|
.map(|message|
|
|
|
|
app.keystore.sign(self.app.config.foreign.account, None, api::eth_data_hash(message)))
|
|
|
|
.map_results(|sig| H520::from(sig.into_electrum()))
|
|
|
|
.fold_results(vec![], |mut acc, sig| {
|
|
|
|
acc.push(sig);
|
|
|
|
acc
|
2017-08-31 08:32:34 -07:00
|
|
|
})
|
2018-04-28 10:03:38 -07:00
|
|
|
.map_err(|e| ErrorKind::SignError(e))?;
|
2017-08-13 07:13:03 -07:00
|
|
|
|
2018-04-28 10:03:38 -07:00
|
|
|
let block = item.to;
|
2018-04-03 23:42:16 -07:00
|
|
|
|
2018-05-23 17:42:13 -07:00
|
|
|
let balance_required = gas * gas_price * U256::from(signatures.len());
|
2018-04-03 23:42:16 -07:00
|
|
|
if balance_required > *foreign_balance.as_ref().unwrap() {
|
|
|
|
return Err(ErrorKind::InsufficientFunds.into())
|
|
|
|
}
|
|
|
|
|
2018-01-19 05:47:13 -08:00
|
|
|
info!("signing complete");
|
2018-02-02 06:59:54 -08:00
|
|
|
let confirmations = messages
|
2017-08-13 07:13:03 -07:00
|
|
|
.drain(ops::RangeFull)
|
|
|
|
.zip(signatures.into_iter())
|
2018-02-02 06:59:54 -08:00
|
|
|
.map(|(withdraw_message, signature)| {
|
|
|
|
withdraw_submit_signature_payload(&app.foreign_bridge, withdraw_message, signature)
|
|
|
|
})
|
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(contract),
|
|
|
|
};
|
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-13 07:13:03 -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!("submitting {} signatures", len);
|
2017-08-13 07:13:03 -07:00
|
|
|
WithdrawConfirmState::ConfirmWithdraws {
|
2018-06-01 18:31:47 -07:00
|
|
|
future: futures_unordered(confirmations).collect(),
|
2017-08-13 07:13:03 -07:00
|
|
|
block,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
WithdrawConfirmState::ConfirmWithdraws { 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), "sending signature submissions to foreign")));
|
2018-01-19 05:47:13 -08:00
|
|
|
info!("submitting signatures complete");
|
2017-08-13 07:13:03 -07:00
|
|
|
WithdrawConfirmState::Yield(Some(block))
|
|
|
|
},
|
|
|
|
WithdrawConfirmState::Yield(ref mut block) => match block.take() {
|
2018-01-19 05:47:13 -08:00
|
|
|
None => {
|
|
|
|
info!("waiting for new withdraws that should get signed");
|
|
|
|
WithdrawConfirmState::Wait
|
|
|
|
},
|
2018-05-25 00:24:16 -07:00
|
|
|
Some(v) => return Ok(Some(BridgeChecked::WithdrawConfirm(v)).into()),
|
2017-08-13 07:13:03 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
self.state = next_state;
|
|
|
|
}
|
2017-08-12 11:03:48 -07:00
|
|
|
}
|
|
|
|
}
|