2018-02-02 06:59:54 -08:00
|
|
|
use ethereum_types::{Address, U256, H256};
|
|
|
|
use contracts::foreign::events::Withdraw;
|
|
|
|
use web3::types::Log;
|
2018-02-12 05:54:46 -08:00
|
|
|
use ethabi;
|
2018-02-15 02:33:59 -08:00
|
|
|
use error::Error;
|
2018-02-02 06:59:54 -08:00
|
|
|
|
2018-02-14 03:13:40 -08:00
|
|
|
/// the message that is relayed from side to main.
|
|
|
|
/// contains all the information required for the relay.
|
|
|
|
/// validators sign off on this message.
|
2018-02-12 10:32:09 -08:00
|
|
|
#[derive(PartialEq, Debug)]
|
2018-02-02 06:59:54 -08:00
|
|
|
pub struct MessageToMainnet {
|
|
|
|
pub recipient: Address,
|
|
|
|
pub value: U256,
|
|
|
|
pub sidenet_transaction_hash: H256,
|
|
|
|
pub mainnet_gas_price: U256,
|
|
|
|
}
|
|
|
|
|
2018-02-14 03:13:40 -08:00
|
|
|
/// length of a `MessageToMainnet.to_bytes()` in bytes
|
2018-02-02 06:59:54 -08:00
|
|
|
pub const MESSAGE_LENGTH: usize = 116;
|
|
|
|
|
|
|
|
impl MessageToMainnet {
|
2018-02-14 03:13:40 -08:00
|
|
|
/// parses message from a byte slice
|
2018-02-02 06:59:54 -08:00
|
|
|
pub fn from_bytes(bytes: &[u8]) -> Self {
|
|
|
|
assert_eq!(bytes.len(), MESSAGE_LENGTH);
|
|
|
|
|
2018-02-24 13:21:23 -08:00
|
|
|
Self {
|
2018-02-02 06:59:54 -08:00
|
|
|
recipient: bytes[0..20].into(),
|
2018-02-24 13:21:23 -08:00
|
|
|
value: (&bytes[20..52]).into(),
|
2018-02-02 06:59:54 -08:00
|
|
|
sidenet_transaction_hash: bytes[52..84].into(),
|
2018-02-24 13:21:23 -08:00
|
|
|
mainnet_gas_price: (&bytes[84..MESSAGE_LENGTH]).into(),
|
2018-02-02 06:59:54 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-14 03:13:40 -08:00
|
|
|
/// construct a message from a `Withdraw` event that was logged on `foreign`
|
2018-02-02 06:59:54 -08:00
|
|
|
pub fn from_log(web3_log: Log) -> Result<Self, Error> {
|
2018-02-12 05:54:46 -08:00
|
|
|
let ethabi_raw_log = ethabi::RawLog {
|
2018-02-12 01:11:39 -08:00
|
|
|
topics: web3_log.topics,
|
2018-02-02 06:59:54 -08:00
|
|
|
data: web3_log.data.0,
|
|
|
|
};
|
|
|
|
let withdraw_log = Withdraw::default().parse_log(ethabi_raw_log)?;
|
2018-02-15 02:33:59 -08:00
|
|
|
let hash = web3_log.transaction_hash.ok_or_else(|| "`log` must be mined and contain `transaction_hash`")?;
|
2018-02-02 06:59:54 -08:00
|
|
|
Ok(Self {
|
2018-02-15 02:17:42 -08:00
|
|
|
recipient: withdraw_log.recipient,
|
|
|
|
value: withdraw_log.value,
|
|
|
|
sidenet_transaction_hash: hash,
|
|
|
|
mainnet_gas_price: withdraw_log.home_gas_price,
|
2018-02-02 06:59:54 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-02-14 03:13:40 -08:00
|
|
|
/// serializes message to a byte vector.
|
|
|
|
/// mainly used to construct the message byte vector that is then signed
|
|
|
|
/// and passed to `ForeignBridge.submitSignature`
|
2018-02-02 06:59:54 -08:00
|
|
|
pub fn to_bytes(&self) -> Vec<u8> {
|
|
|
|
let mut result = vec![0u8; MESSAGE_LENGTH];
|
|
|
|
result[0..20].copy_from_slice(&self.recipient.0[..]);
|
2018-02-24 13:21:23 -08:00
|
|
|
result[20..52].copy_from_slice(&H256::from(self.value));
|
2018-02-02 06:59:54 -08:00
|
|
|
result[52..84].copy_from_slice(&self.sidenet_transaction_hash.0[..]);
|
2018-02-24 13:21:23 -08:00
|
|
|
result[84..MESSAGE_LENGTH].copy_from_slice(&H256::from(self.mainnet_gas_price));
|
2018-02-02 06:59:54 -08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-02-14 03:13:40 -08:00
|
|
|
/// serializes message to an ethabi payload
|
2018-02-12 05:54:46 -08:00
|
|
|
pub fn to_payload(&self) -> Vec<u8> {
|
|
|
|
ethabi::encode(&[ethabi::Token::Bytes(self.to_bytes())])
|
2018-02-02 06:59:54 -08:00
|
|
|
}
|
|
|
|
}
|
2018-02-12 10:32:09 -08:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use quickcheck::TestResult;
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
quickcheck! {
|
|
|
|
fn quickcheck_message_to_mainnet_roundtrips_to_bytes(
|
|
|
|
recipient_raw: Vec<u8>,
|
|
|
|
value_raw: u64,
|
|
|
|
sidenet_transaction_hash_raw: Vec<u8>,
|
|
|
|
mainnet_gas_price_raw: u64
|
|
|
|
) -> TestResult {
|
|
|
|
if recipient_raw.len() != 20 || sidenet_transaction_hash_raw.len() != 32 {
|
|
|
|
return TestResult::discard();
|
|
|
|
}
|
|
|
|
|
|
|
|
let recipient: Address = recipient_raw.as_slice().into();
|
|
|
|
let value: U256 = value_raw.into();
|
|
|
|
let sidenet_transaction_hash: H256 = sidenet_transaction_hash_raw.as_slice().into();
|
|
|
|
let mainnet_gas_price: U256 = mainnet_gas_price_raw.into();
|
|
|
|
|
|
|
|
let message = MessageToMainnet {
|
|
|
|
recipient,
|
|
|
|
value,
|
|
|
|
sidenet_transaction_hash,
|
|
|
|
mainnet_gas_price
|
|
|
|
};
|
|
|
|
|
|
|
|
let bytes = message.to_bytes();
|
|
|
|
assert_eq!(message, MessageToMainnet::from_bytes(bytes.as_slice()));
|
|
|
|
|
|
|
|
let payload = message.to_payload();
|
|
|
|
let mut tokens = ethabi::decode(&[ethabi::ParamType::Bytes], payload.as_slice())
|
|
|
|
.unwrap();
|
|
|
|
let decoded = tokens.pop().unwrap().to_bytes().unwrap();
|
|
|
|
assert_eq!(message, MessageToMainnet::from_bytes(decoded.as_slice()));
|
|
|
|
|
|
|
|
TestResult::passed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|