Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update poa-bridge to use v2 contracts #103

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ Bridge forces TLS for RPC connections by default. However, in some limited scena
this might be undesirable. In this case, you can use `--allow-insecure-rpc-endpoints` option to allow non-TLS
endpoints to be used. Ensure, however, that this option is not going to be used in production.


#### Exit Status Codes

| Code | Meaning |
Expand Down Expand Up @@ -102,9 +101,9 @@ password = "foreign_password.txt"
required_signatures = 2

[transactions]
deposit_confirm = { gas = 3000000 }
deposit_relay = { gas = 3000000 }
withdraw_relay = { gas = 3000000 }
withdraw_confirm = { gas = 3000000 }
```

#### Options
Expand Down Expand Up @@ -133,24 +132,24 @@ withdraw_confirm = { gas = 3000000 }

#### transaction options

- `transaction.deposit_confirm.gas` - specify how much gas should be consumed for each call to the Home contract's `submitSignature()` function
- `transaction.deposit_relay.gas` - specify how much gas should be consumed by deposit relay
- `transaction.withdraw_confirm.gas` - specify how much gas should be consumed by withdraw confirm
- `transaction.withdraw_relay.gas` - specify how much gas should be consumed by withdraw relay

### Database file format

```toml
home_contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db7"
foreign_contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"
checked_deposit_confirm = 120
checked_deposit_relay = 120
checked_withdraw_relay = 121
checked_withdraw_confirm = 121
```

**all fields are required**

- `home_contract_address` - address of the bridge contract on home chain
- `foreign_contract_address` - address of the bridge contract on foreign chain
- `checked_deposit_confirm` - number of the last block for which an authority has confirmed deposit
- `checked_deposit_relay` - number of the last block for which an authority has relayed deposits to the foreign
- `checked_withdraw_relay` - number of the last block for which an authority has relayed withdraws to the home
- `checked_withdraw_confirm` - number of the last block for which an authority has confirmed withdraw
2 changes: 2 additions & 0 deletions bridge/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ fn check_rustc_version() {
fn main() {
check_rustc_version();

/*
// rerun build script if bridge contract has changed.
// without this cargo doesn't since the bridge contract
// is outside the crate directories
Expand Down Expand Up @@ -53,4 +54,5 @@ fn main() {
}
}
}
*/
}
2 changes: 1 addition & 1 deletion bridge/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub struct Connections<T> where T: Transport {
impl Connections<Http> {
pub fn new_http(handle: &Handle, home: &str, home_concurrent_connections: usize, foreign: &str, foreign_concurrent_connections: usize) -> Result<Self, Error> {

let home = Http::with_event_loop(home, handle,home_concurrent_connections)
let home = Http::with_event_loop(home, handle,home_concurrent_connections)
.map_err(ErrorKind::Web3)
.map_err(Error::from)
.chain_err(||"Cannot connect to home node rpc")?;
Expand Down
3 changes: 3 additions & 0 deletions bridge/src/bridge/deploy.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/*
use std::sync::Arc;
use futures::{Future, Poll};
#[cfg(feature = "deploy")]
Expand Down Expand Up @@ -125,6 +126,7 @@ impl<T: Transport + Clone> Future for Deploy<T> {
home_deploy: Some(main_receipt.block_number.low_u64()),
foreign_deploy: Some(test_receipt.block_number.low_u64()),
checked_deposit_relay: main_receipt.block_number.low_u64(),
checked_deposit_confirm: main_receipt.block_number.low_u64(),
checked_withdraw_relay: test_receipt.block_number.low_u64(),
checked_withdraw_confirm: test_receipt.block_number.low_u64(),
};
Expand All @@ -137,3 +139,4 @@ impl<T: Transport + Clone> Future for Deploy<T> {
}
}
}
/*
226 changes: 226 additions & 0 deletions bridge/src/bridge/deposit_confirm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
use std::ops::RangeFull;
use std::sync::{Arc, RwLock};

use ethcore_transaction::{Action, Transaction};
use futures::{Async, Future, Poll, Stream};
use futures::stream::{Collect, FuturesUnordered, futures_unordered};
use itertools::Itertools;
use web3::Transport;
use web3::types::{Address, Bytes, FilterBuilder, H520, U256};

use api::{eth_data_hash, LogStream, LogStreamInit, LogStreamItem, log_stream};
use app::App;
use contracts::home::HomeBridge;
use database::Database;
use error::{Error, ErrorKind};
use message_to_mainnet::MessageToMainnet;
use super::BridgeChecked;
use super::nonce::{NonceCheck, SendRawTransaction, send_transaction_with_nonce};
use util::web3_filter;

// A future representing all currently open calls to the Home
// contract's `submitSignature()` function.
type SubmitSignaturesFuture<T: Transport> =
Collect<FuturesUnordered<NonceCheck<T, SendRawTransaction<T>>>>;

fn create_deposit_filter(contract: &HomeBridge, contract_address: Address) -> FilterBuilder {
let filter = contract.events().deposit().create_filter();
web3_filter(filter, contract_address)
}

fn create_submit_signature_payload(
home_contract: &HomeBridge,
deposit_message: Vec<u8>,
signature: H520
) -> Bytes
{
home_contract.functions().submit_signature()
.input(signature.0.to_vec(), deposit_message)
.into()
}

// Represents each possible state for the `DepositConfirm`.
enum State<T: Transport> {
// Monitoring the Foreign chain for new `Deposit` events.
Initial,
// Waiting for all calls to the Home contract's `submitSignature()`
// function to finish.
WaitingOnSubmitSignatures {
future: SubmitSignaturesFuture<T>,
last_block_checked: u64,
},
// All calls to the Home Contract's `submitSignature()` function
// have finished. Yields the block number for the last block
// checked for `Deposit` events on the Foreign chain.
Yield(Option<u64>),
}

pub struct DepositConfirm<T: Transport> {
app: Arc<App<T>>,
logs: LogStream<T>,
state: State<T>,
home_contract_address: Address,
home_balance: Arc<RwLock<Option<U256>>>,
home_chain_id: u64,
home_gas_price: Arc<RwLock<u64>>,
}

pub fn create_deposit_confirm<T: Transport + Clone>(
app: Arc<App<T>>,
init: &Database,
home_balance: Arc<RwLock<Option<U256>>>,
home_chain_id: u64,
home_gas_price: Arc<RwLock<u64>>
) -> DepositConfirm<T>
{
let deposit_log_filter = create_deposit_filter(
&app.home_bridge,
init.home_contract_address
);

let logs_init = LogStreamInit {
after: init.checked_deposit_confirm,
request_timeout: app.config.home.request_timeout,
poll_interval: app.config.home.poll_interval,
confirmations: app.config.home.required_confirmations,
filter: deposit_log_filter,
};

let deposit_log_stream = log_stream(
app.connections.home.clone(),
app.timer.clone(),
logs_init
);

DepositConfirm {
logs: deposit_log_stream,
home_contract_address: init.home_contract_address,
state: State::Initial,
app,
home_balance,
home_chain_id,
home_gas_price,
}
}

impl<T: Transport> Stream for DepositConfirm<T> {
type Item = BridgeChecked;
type Error = Error;

fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let app = &self.app;
let home_config = &app.config.home;
let home_conn = &app.connections.home;
let home_contract = &app.home_bridge;
let home_contract_address = self.home_contract_address;
let home_chain_id = self.home_chain_id;
let my_home_address = app.config.home.account;
let gas = app.config.txs.deposit_confirm.gas.into();
let gas_price = U256::from(*self.home_gas_price.read().unwrap());

loop {
let next_state = match self.state {
State::Initial => {
let home_balance = self.home_balance.read().unwrap();
if home_balance.is_none() {
warn!("home contract balance is unknown");
return Ok(Async::NotReady);
}

let LogStreamItem { to: last_block_checked, logs, .. } =
try_stream!(
self.logs.poll().map_err(|e| {
let context = "polling Home contract for Depoist event logs";
ErrorKind::ContextualizedError(Box::new(e), context)
})
);

let n_new_deposits = logs.len();
info!("got {} new deposits to sign", n_new_deposits);

let mut messages = logs.into_iter()
.map(|log| {
info!(
"deposit is ready for signature submission. tx hash {}",
log.transaction_hash.unwrap()
);

MessageToMainnet::from_deposit_log(log)
.map(|msg| msg.to_bytes())
})
.collect::<Result<Vec<Vec<u8>>, Error>>()?;

let signatures = messages.iter()
.map(|message| {
let signed_message = eth_data_hash(message.clone());
app.keystore.sign(my_home_address, None, signed_message)
})
.map_results(|sig| H520::from(sig.into_electrum()))
.fold_results(vec![], |mut acc, sig| {
acc.push(sig);
acc
})
.map_err(ErrorKind::SignError)?;

let balance_required = gas * gas_price * U256::from(signatures.len());
if balance_required > *home_balance.as_ref().unwrap() {
return Err(ErrorKind::InsufficientFunds.into());
}

let submit_signature_calls = messages.drain(RangeFull)
.zip(signatures.into_iter())
.map(|(message, signature)| create_submit_signature_payload(
home_contract,
message,
signature
))
.map(|payload| {
let tx = Transaction {
gas,
gas_price,
value: U256::zero(),
data: payload.0,
nonce: U256::zero(),
action: Action::Call(home_contract_address),
};

send_transaction_with_nonce(
home_conn.clone(),
app.clone(),
home_config.clone(),
tx,
home_chain_id,
SendRawTransaction(home_conn.clone())
)
})
.collect_vec();

State::WaitingOnSubmitSignatures {
future: futures_unordered(submit_signature_calls).collect(),
last_block_checked,
}
},
State::WaitingOnSubmitSignatures { ref mut future, last_block_checked } => {
let _ = try_ready!(
future.poll().map_err(|e| {
let context = "sending signature submissions to home";
ErrorKind::ContextualizedError(Box::new(e), context)
})
);
info!("submitting signatures to home complete");
State::Yield(Some(last_block_checked))
},
State::Yield(ref mut block) => match block.take() {
Some(block) => {
let checked = BridgeChecked::DepositConfirm(block);
return Ok(Async::Ready(Some(checked)));
},
None => State::Initial,
},
};

self.state = next_state;
}
}
}

Loading