diff --git a/rust-evmjit/src/lib.rs b/rust-evmjit/src/lib.rs index de4efd6a1..a16b0dbe8 100644 --- a/rust-evmjit/src/lib.rs +++ b/rust-evmjit/src/lib.rs @@ -125,11 +125,11 @@ pub trait Env { fn log(&mut self, beg: *const u8, - size: *const u64, - topic1: *const JitI256, - topic2: *const JitI256, - topic3: *const JitI256, - topic4: *const JitI256); + size: u64, + topic1: *const JitH256, + topic2: *const JitH256, + topic3: *const JitH256, + topic4: *const JitH256); fn extcode(&self, address: *const JitH256, size: *mut u64) -> *const u8; } @@ -222,6 +222,18 @@ pub mod ffi { } } + impl From for JitH256 { + fn from(mut i: JitI256) -> JitH256 { + unsafe { + { + let bytes: &mut [u8] = slice::from_raw_parts_mut(i.words.as_mut_ptr() as *mut u8, 32); + bytes.reverse(); + } + mem::transmute(i) + } + } + } + #[repr(C)] #[derive(Debug)] /// Jit runtime data. @@ -350,11 +362,11 @@ pub mod ffi { #[no_mangle] pub unsafe extern "C" fn env_log(env: *mut EnvHandle, beg: *const u8, - size: *const u64, - topic1: *const JitI256, - topic2: *const JitI256, - topic3: *const JitI256, - topic4: *const JitI256) { + size: u64, + topic1: *const JitH256, + topic2: *const JitH256, + topic3: *const JitH256, + topic4: *const JitH256) { let env = &mut *env; env.log(beg, size, topic1, topic2, topic3, topic4); } @@ -403,4 +415,5 @@ fn hash_to_int() { let h = H256 { words:[0x0123456789abcdef, 0, 0, 0] }; let i = I256::from(h); assert_eq!([0u64, 0, 0, 0xefcdab8967452301], i.words); + assert_eq!(H256::from(i).words, h.words); } diff --git a/src/evm/env.rs b/src/evm/env.rs index e7907086a..b067154ba 100644 --- a/src/evm/env.rs +++ b/src/evm/env.rs @@ -1,8 +1,30 @@ //! Contract execution environment. +use std::collections::HashSet; use util::hash::*; use util::uint::*; +use util::bytes::*; use state::*; +use evm::LogEntry; + +struct SubState { + // any accounts that have suicided + suicides: HashSet
, + // any logs + logs: Vec, + // refund counter of SSTORE nonzero->zero + refunds: U256 +} + +impl SubState { + fn new() -> SubState { + SubState { + suicides: HashSet::new(), + logs: vec![], + refunds: U256::zero() + } + } +} /// This structure represents contract execution environment. /// It should be initalized with `State` and contract address. @@ -22,7 +44,8 @@ use state::*; /// ``` pub struct Env { state: State, - address: Address + address: Address, + substate: SubState } impl Env { @@ -30,7 +53,8 @@ impl Env { pub fn new(state: State, address: Address) -> Env { Env { state: state, - address: address + address: address, + substate: SubState::new() } } @@ -70,14 +94,21 @@ impl Env { self.state.code(address).unwrap_or(vec![]) } - pub fn log(&self, _topics: &[H256], _data: &[u8]) { - unimplemented!(); + /// Creates log entry with given topics and data + pub fn log(&mut self, topics: Vec, data: Bytes) { + let address = self.address.clone(); + self.substate.logs.push(LogEntry::new(address, topics, data)); } - /// Drain state + /// Returns state // not sure if this is the best solution, but seems to be the easiest one, mk - pub fn state(self) -> State { - self.state + pub fn state(&self) -> &State { + &self.state + } + + /// Returns substate + pub fn logs(&self) -> &[LogEntry] { + &self.substate.logs } } diff --git a/src/evm/jit.rs b/src/evm/jit.rs index f236e4d22..81a28a4df 100644 --- a/src/evm/jit.rs +++ b/src/evm/jit.rs @@ -1,5 +1,6 @@ use std::mem; use std::ptr; +use std::slice; use evmjit; use util::hash::*; use util::uint::*; @@ -40,9 +41,15 @@ impl<'a> FromJit<&'a evmjit::I256> for Address { } } +impl<'a> FromJit<&'a evmjit::H256> for H256 { + fn from_jit(input: &'a evmjit::H256) -> Self { + H256::from_jit(&evmjit::I256::from(input.clone())) + } +} + impl<'a> FromJit<&'a evmjit::H256> for Address { fn from_jit(input: &'a evmjit::H256) -> Self { - Address::from(H256::from_jit(&evmjit::I256::from(input.clone()))) + Address::from(H256::from_jit(input)) } } @@ -165,13 +172,34 @@ impl<'a> evmjit::Env for EnvAdapter<'a> { } fn log(&mut self, - _beg: *const u8, - _size: *const u64, - _topic1: *const evmjit::I256, - _topic2: *const evmjit::I256, - _topic3: *const evmjit::I256, - _topic4: *const evmjit::I256) { - unimplemented!(); + beg: *const u8, + size: u64, + topic1: *const evmjit::H256, + topic2: *const evmjit::H256, + topic3: *const evmjit::H256, + topic4: *const evmjit::H256) { + + unsafe { + let mut topics = vec![]; + if !topic1.is_null() { + topics.push(H256::from_jit(&*topic1)); + } + + if !topic2.is_null() { + topics.push(H256::from_jit(&*topic2)); + } + + if !topic3.is_null() { + topics.push(H256::from_jit(&*topic3)); + } + + if !topic4.is_null() { + topics.push(H256::from_jit(&*topic4)); + } + + let bytes_ref: &[u8] = slice::from_raw_parts(beg, size as usize); + self.env.log(topics, bytes_ref.to_vec()); + } } fn extcode(&self, address: *const evmjit::H256, size: *mut u64) -> *const u8 { @@ -382,4 +410,53 @@ mod tests { let state = env.state(); assert_eq!(state.storage_at(&address, &H256::new()), H256::from(&U256::from(0x10))); } + + #[test] + fn test_empty_log() { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let mut data = RuntimeData::new(); + data.address = address.clone(); + data.caller = address.clone(); + data.gas = 0x174876e800; + data.code = "60006000a0".from_hex().unwrap(); + + let mut env = Env::new(State::new_temp(), address.clone()); + let evm = JitEvm; + assert_eq!(evm.exec(data, &mut env), ReturnCode::Stop); + let logs = env.logs(); + assert_eq!(logs.len(), 1); + let log = &logs[0]; + assert_eq!(log.address(), &address); + assert_eq!(log.topics().len(), 0); + assert_eq!(log.bloom(), H2048::from_str("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap()); + } + + #[test] + fn test_log_with_one_topic() { + // 60 ff - push ff + // 60 00 - push 00 + // 53 - mstore + // 33 - caller + // 60 20 - push 20 + // 60 00 - push 0 + // a1 - log with 1 topic + + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let mut data = RuntimeData::new(); + data.address = address.clone(); + data.caller = address.clone(); + data.gas = 0x174876e800; + data.code = "60ff6000533360206000a1".from_hex().unwrap(); + + let mut env = Env::new(State::new_temp(), address.clone()); + let evm = JitEvm; + assert_eq!(evm.exec(data, &mut env), ReturnCode::Stop); + let logs = env.logs(); + assert_eq!(logs.len(), 1); + let log = &logs[0]; + assert_eq!(log.address(), &address); + assert_eq!(log.topics().len(), 1); + let topic = &log.topics()[0]; + assert_eq!(topic, &H256::from(address.clone())); + } } diff --git a/src/evm/logentry.rs b/src/evm/logentry.rs new file mode 100644 index 000000000..7307dadf4 --- /dev/null +++ b/src/evm/logentry.rs @@ -0,0 +1,62 @@ +use util::hash::*; +use util::bytes::*; +use util::sha3::*; + +/// Data sturcture used to represent Evm log entry. +pub struct LogEntry { + address: Address, + topics: Vec, + data: Bytes +} + +impl LogEntry { + /// This function should be called to create new log entry. + pub fn new(address: Address, topics: Vec, data: Bytes) -> LogEntry { + LogEntry { + address: address, + topics: topics, + data: data + } + } + + /// Returns reference to address. + pub fn address(&self) -> &Address { + &self.address + } + + /// Returns reference to topics. + pub fn topics(&self) -> &[H256] { + &self.topics + } + + /// Returns reference to data. + pub fn data(&self) -> &Bytes { + &self.data + } + + /// Returns log bloom of given log entry. + pub fn bloom(&self) -> H2048 { + let mut bloom = H2048::new(); + bloom.shift_bloom(&self.address.sha3()); + for topic in self.topics.iter() { + bloom.shift_bloom(&topic.sha3()); + } + bloom + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use util::hash::*; + use util::bytes::*; + use evm::LogEntry; + + #[test] + fn test_empty_log_bloom() { + let bloom = H2048::from_str("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let log = LogEntry::new(address, vec![], vec![]); + assert_eq!(log.bloom(), bloom); + } +} diff --git a/src/evm/mod.rs b/src/evm/mod.rs index 304a4cc78..55d35aa21 100644 --- a/src/evm/mod.rs +++ b/src/evm/mod.rs @@ -4,9 +4,11 @@ pub mod env; pub mod runtime_data; pub mod evm; pub mod vmfactory; +pub mod logentry; #[cfg(feature = "jit" )] mod jit; pub use self::evm::{Evm, ReturnCode}; pub use self::env::Env; pub use self::runtime_data::RuntimeData; +pub use self::logentry::LogEntry;