diff --git a/Cargo.lock b/Cargo.lock index 0677d6efa..233e90ea6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1677,6 +1677,11 @@ dependencies = [ "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "nan-preserving-float" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "net2" version = "0.2.31" @@ -2485,7 +2490,7 @@ dependencies = [ [[package]] name = "pwasm-utils" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3433,18 +3438,19 @@ dependencies = [ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.27.5 (registry+https://github.com/rust-lang/crates.io-index)", - "pwasm-utils 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pwasm-utils 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "vm 0.1.0", - "wasmi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmi" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.27.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3673,6 +3679,7 @@ dependencies = [ "checksum msdos_time 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "65ba9d75bcea84e07812618fedf284a64776c2f2ea0cad6bca7f69739695a958" "checksum multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9c35dac080fd6e16a99924c8dfdef0af89d797dd851adab25feaffacf7850d6" "checksum multihash 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d49add5f49eb08bfc4d01ff286b84a48f53d45314f165c2d6efe477222d24f3" +"checksum nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34d4f00fcc2f4c9efa8cc971db0da9e28290e28e97af47585e48691ef10ff31f" "checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum ntp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "143149743832c6543b60a8ef2a26cd9122dfecec2b767158e852a7beecf6d7a0" @@ -3713,7 +3720,7 @@ dependencies = [ "checksum primal-sieve 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c0911abe7b63ddec27527ba7579c3017f645eb992be6ddbfad605e34aca01876" "checksum protobuf 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "568a15e4d572d9a5e63ae3a55f84328c984842887db179b40b4cc6a608bac6a4" "checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07" -"checksum pwasm-utils 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "54d440c3b56eee028aa5d4f18cbed8c6e0c9ae23563b93f344beb7e73854ea02" +"checksum pwasm-utils 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d51e9954a77aab7b4b606dc315a49cbed187924f163b6750cdf6d5677dbf0839" "checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3" "checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4" "checksum quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29cec87bc2816766d7e4168302d505dd06b0a825aed41b00633d296e922e02dd" @@ -3814,7 +3821,7 @@ dependencies = [ "checksum vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c3365f36c57e5df714a34be40902b27a992eeddb9996eca52d0584611cf885d" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum wasmi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d19da510b59247935ad5f598357b3cc739912666d75d3d28318026478d95bbdb" +"checksum wasmi 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46df76793c28cd8f590d5667f540a81c1c245440a17b03560e381226e27cf348" "checksum webpki 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e1622384bcb5458c6a3e3fa572f53ea8fef1cc85e535a2983dea87e9154fac2" "checksum webpki-roots 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "155d4060e5befdf3a6076bd28c22513473d9900b763c9e4521acc6f78a75415c" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" diff --git a/ethcore/res/ethereum/ellaism.json b/ethcore/res/ethereum/ellaism.json index 3036992eb..74bbf3641 100644 --- a/ethcore/res/ethereum/ellaism.json +++ b/ethcore/res/ethereum/ellaism.json @@ -13,9 +13,9 @@ "eip150Transition": "0x0", "eip160Transition": "0x0", "ecip1017EraRounds": 10000000, - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff" + "eip161dTransition": "0x7fffffffffffffff", + "eip100bTransition": 2000000 } } }, @@ -29,7 +29,12 @@ "chainID": "0x40", "eip155Transition": "0x0", "eip98Transition": "0x7fffffffffffff", - "eip86Transition": "0x7fffffffffffff" + "eip86Transition": "0x7fffffffffffff", + "wasmActivationTransition": 2000000, + "eip140Transition": 2000000, + "eip211Transition": 2000000, + "eip214Transition": 2000000, + "eip658Transition": 2000000 }, "genesis": { "seal": { @@ -67,6 +72,10 @@ "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } + "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": 2000000, "pricing": { "modexp": { "divisor": 20 } } } }, + "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": 2000000, "pricing": { "linear": { "base": 500, "word": 0 } } } }, + "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": 2000000, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, + "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": 2000000, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } } } } diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index af7e300bf..91647cdab 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -57,6 +57,12 @@ pub trait BlockProvider { /// (though not necessarily a part of the canon chain). fn is_known(&self, hash: &H256) -> bool; + /// Returns true if the given block is known and in the canon chain. + fn is_canon(&self, hash: &H256) -> bool { + let is_canon = || Some(hash == &self.block_hash(self.block_number(hash)?)?); + is_canon().unwrap_or(false) + } + /// Get the first block of the best part of the chain. /// Return `None` if there is no gap and the first block is the genesis. /// Any queries of blocks which precede this one are not guaranteed to @@ -153,7 +159,7 @@ pub trait BlockProvider { fn blocks_with_bloom(&self, bloom: &Bloom, from_block: BlockNumber, to_block: BlockNumber) -> Vec; /// Returns logs matching given filter. - fn logs(&self, blocks: Vec, matches: F, limit: Option) -> Vec + fn logs(&self, blocks: Vec, matches: F, limit: Option) -> Vec where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized; } @@ -360,16 +366,18 @@ impl BlockProvider for BlockChain { .collect() } - fn logs(&self, mut blocks: Vec, matches: F, limit: Option) -> Vec + /// Returns logs matching given filter. The order of logs returned will be the same as the order of the blocks + /// provided. And it's the callers responsibility to sort blocks provided in advance. + fn logs(&self, mut blocks: Vec, matches: F, limit: Option) -> Vec where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized { // sort in reverse order - blocks.sort_by(|a, b| b.cmp(a)); + blocks.reverse(); let mut logs = blocks .chunks(128) .flat_map(move |blocks_chunk| { blocks_chunk.into_par_iter() - .filter_map(|number| self.block_hash(*number).map(|hash| (*number, hash))) + .filter_map(|hash| self.block_number(&hash).map(|r| (r, hash))) .filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts))) .filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, b.transaction_hashes()))) .flat_map(|(number, hash, mut receipts, mut hashes)| { @@ -396,7 +404,7 @@ impl BlockProvider for BlockChain { .enumerate() .map(move |(i, log)| LocalizedLogEntry { entry: log, - block_hash: hash, + block_hash: *hash, block_number: number, transaction_hash: tx_hash, // iterating in reverse order @@ -1957,17 +1965,33 @@ mod tests { value: 103.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), }.sign(&secret(), None); + let t4 = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 104.into(), + data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), + }.sign(&secret(), None); let tx_hash1 = t1.hash(); let tx_hash2 = t2.hash(); let tx_hash3 = t3.hash(); + let tx_hash4 = t4.hash(); let genesis = BlockBuilder::genesis(); let b1 = genesis.add_block_with_transactions(vec![t1, t2]); let b2 = b1.add_block_with_transactions(iter::once(t3)); + let b3 = genesis.add_block_with(|| BlockOptions { + transactions: vec![t4.clone()], + difficulty: U256::from(9), + ..Default::default() + }); // Branch block let b1_hash = b1.last().hash(); let b1_number = b1.last().number(); let b2_hash = b2.last().hash(); let b2_number = b2.last().number(); + let b3_hash = b3.last().hash(); + let b3_number = b3.last().number(); let db = new_db(); let bc = new_chain(&genesis.last().encoded(), db.clone()); @@ -1998,10 +2022,21 @@ mod tests { ], } ]); + insert_block(&db, &bc, &b3.last().encoded(), vec![ + Receipt { + outcome: TransactionOutcome::StateRoot(H256::default()), + gas_used: 10_000.into(), + log_bloom: Default::default(), + logs: vec![ + LogEntry { address: Default::default(), topics: vec![], data: vec![5], }, + ], + } + ]); // when - let logs1 = bc.logs(vec![1, 2], |_| true, None); - let logs2 = bc.logs(vec![1, 2], |_| true, Some(1)); + let logs1 = bc.logs(vec![b1_hash, b2_hash], |_| true, None); + let logs2 = bc.logs(vec![b1_hash, b2_hash], |_| true, Some(1)); + let logs3 = bc.logs(vec![b3_hash], |_| true, None); // then assert_eq!(logs1, vec![ @@ -2053,6 +2088,17 @@ mod tests { log_index: 0, } ]); + assert_eq!(logs3, vec![ + LocalizedLogEntry { + entry: LogEntry { address: Default::default(), topics: vec![], data: vec![5] }, + block_hash: b3_hash, + block_number: b3_number, + transaction_hash: tx_hash4, + transaction_index: 0, + transaction_log_index: 0, + log_index: 0, + } + ]); } #[test] diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 907d98fff..9e10aa410 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::collections::{HashSet, HashMap, BTreeMap, VecDeque}; +use std::collections::{HashSet, HashMap, BTreeMap, BTreeSet, VecDeque}; use std::str::FromStr; use std::sync::{Arc, Weak}; use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; @@ -1666,23 +1666,83 @@ impl BlockChainClient for Client { } fn logs(&self, filter: Filter) -> Vec { - let (from, to) = match (self.block_number_ref(&filter.from_block), self.block_number_ref(&filter.to_block)) { - (Some(from), Some(to)) => (from, to), - _ => return Vec::new(), + // Wrap the logic inside a closure so that we can take advantage of question mark syntax. + let fetch_logs = || { + let chain = self.chain.read(); + + // First, check whether `filter.from_block` and `filter.to_block` is on the canon chain. If so, we can use the + // optimized version. + let is_canon = |id| { + match id { + &BlockId::Pending => true, + // If it is referred by number, then it is always on the canon chain. + &BlockId::Earliest | &BlockId::Latest | &BlockId::Number(_) => true, + // If it is referred by hash, we see whether a hash -> number -> hash conversion gives us the same + // result. + &BlockId::Hash(ref hash) => chain.is_canon(hash), + } + }; + + let blocks = if is_canon(&filter.from_block) && is_canon(&filter.to_block) { + // If we are on the canon chain, use bloom filter to fetch required hashes. + let from = self.block_number_ref(&filter.from_block)?; + let to = self.block_number_ref(&filter.to_block)?; + + filter.bloom_possibilities().iter() + .map(|bloom| { + chain.blocks_with_bloom(bloom, from, to) + }) + .flat_map(|m| m) + // remove duplicate elements + .collect::>() + .into_iter() + .filter_map(|n| chain.block_hash(n)) + .collect::>() + + } else { + // Otherwise, we use a slower version that finds a link between from_block and to_block. + let from_hash = Self::block_hash(&chain, &*self.miner, filter.from_block)?; + let from_number = chain.block_number(&from_hash)?; + let to_hash = Self::block_hash(&chain, &*self.miner, filter.from_block)?; + + let blooms = filter.bloom_possibilities(); + let bloom_match = |header: &encoded::Header| { + blooms.iter().any(|bloom| header.log_bloom().contains_bloom(bloom)) + }; + + let (blocks, last_hash) = { + let mut blocks = Vec::new(); + let mut current_hash = to_hash; + + loop { + let header = chain.block_header_data(¤t_hash)?; + if bloom_match(&header) { + blocks.push(current_hash); + } + + // Stop if `from` block is reached. + if header.number() <= from_number { + break; + } + current_hash = header.parent_hash(); + } + + blocks.reverse(); + (blocks, current_hash) + }; + + // Check if we've actually reached the expected `from` block. + if last_hash != from_hash || blocks.is_empty() { + return None; + } + + blocks + }; + + Some(self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit)) }; - let chain = self.chain.read(); - let blocks = filter.bloom_possibilities().iter() - .map(move |bloom| { - chain.blocks_with_bloom(bloom, from, to) - }) - .flat_map(|m| m) - // remove duplicate elements - .collect::>() - .into_iter() - .collect::>(); - - self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit) + fetch_logs().unwrap_or_default() } fn filter_traces(&self, filter: TraceFilter) -> Option> { diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 4b2f8f324..6d754efed 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -21,7 +21,7 @@ use std::cell::{RefCell, RefMut}; use std::collections::hash_map::Entry; -use std::collections::{HashMap, BTreeMap, HashSet}; +use std::collections::{HashMap, BTreeMap, BTreeSet, HashSet}; use std::fmt; use std::sync::Arc; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY}; @@ -833,40 +833,65 @@ impl State { })) } - // Return a list of all touched addresses in cache. - fn touched_addresses(&self) -> Vec
{ + /// Populate a PodAccount map from this state, with another state as the account and storage query. + pub fn to_pod_diff(&mut self, query: &State) -> trie::Result { assert!(self.checkpoints.borrow().is_empty()); - self.cache.borrow().iter().map(|(add, _)| *add).collect() - } - fn query_pod(&mut self, query: &PodState, touched_addresses: &[Address]) -> trie::Result<()> { - let pod = query.get(); + // Merge PodAccount::to_pod for cache of self and `query`. + let all_addresses = self.cache.borrow().keys().cloned() + .chain(query.cache.borrow().keys().cloned()) + .collect::>(); - for address in touched_addresses { - if !self.ensure_cached(address, RequireCache::Code, true, |a| a.is_some())? { - continue + Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: trie::Result<_>, address| { + let mut m = m?; + + let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| { + acc.map(|acc| { + // Merge all modified storage keys. + let all_keys = { + let self_keys = acc.storage_changes().keys().cloned() + .collect::>(); + + if let Some(ref query_storage) = query.cache.borrow().get(&address) + .and_then(|opt| { + Some(opt.account.as_ref()?.storage_changes().keys().cloned() + .collect::>()) + }) + { + self_keys.union(&query_storage).cloned().collect::>() + } else { + self_keys.into_iter().collect::>() + } + }; + + // Storage must be fetched after ensure_cached to avoid borrow problem. + (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec())) + }) + })?; + + if let Some((balance, nonce, storage_keys, code)) = account { + let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: trie::Result<_>, key| { + let mut s = s?; + + s.insert(key, self.storage_at(&address, &key)?); + Ok(s) + })?; + + m.insert(address, PodAccount { + balance, nonce, storage, code + }); } - if let Some(pod_account) = pod.get(address) { - // needs to be split into two parts for the refcell code here - // to work. - for key in pod_account.storage.keys() { - self.storage_at(address, key)?; - } - } - } - - Ok(()) + Ok(m) + })?)) } /// Returns a `StateDiff` describing the difference from `orig` to `self`. /// Consumes self. - pub fn diff_from(&self, orig: State) -> trie::Result { - let addresses_post = self.touched_addresses(); + pub fn diff_from(&self, mut orig: State) -> trie::Result { let pod_state_post = self.to_pod(); - let mut state_pre = orig; - state_pre.query_pod(&pod_state_post, &addresses_post)?; - Ok(pod_state::diff_pod(&state_pre.to_pod(), &pod_state_post)) + let pod_state_pre = orig.to_pod_diff(self)?; + Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post)) } // load required account data from the databases. @@ -2209,9 +2234,6 @@ mod tests { let original = state.clone(); state.kill_account(&a); - assert_eq!(original.touched_addresses(), vec![]); - assert_eq!(state.touched_addresses(), vec![a]); - let diff = state.diff_from(original).unwrap(); let diff_map = diff.get(); assert_eq!(diff_map.len(), 1); @@ -2224,4 +2246,42 @@ mod tests { storage: Default::default() }), None).as_ref()); } + + #[test] + fn should_trace_diff_unmodified_storage() { + use pod_account; + + let a = 10.into(); + let db = get_temp_state_db(); + + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(20u64))).unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + let original = state.clone(); + state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(100u64))).unwrap(); + + let diff = state.diff_from(original).unwrap(); + let diff_map = diff.get(); + assert_eq!(diff_map.len(), 1); + assert!(diff_map.get(&a).is_some()); + assert_eq!(diff_map.get(&a), + pod_account::diff_pod(Some(&PodAccount { + balance: U256::zero(), + nonce: U256::zero(), + code: Some(Default::default()), + storage: vec![(H256::from(&U256::from(1u64)), H256::from(&U256::from(20u64)))] + .into_iter().collect(), + }), Some(&PodAccount { + balance: U256::zero(), + nonce: U256::zero(), + code: Some(Default::default()), + storage: vec![(H256::from(&U256::from(1u64)), H256::from(&U256::from(100u64)))] + .into_iter().collect(), + })).as_ref()); + } } diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 7452ff46e..d9f901ddf 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -458,7 +458,7 @@ mod tests { unimplemented!() } - fn logs(&self, _blocks: Vec, _matches: F, _limit: Option) -> Vec + fn logs(&self, _blocks: Vec, _matches: F, _limit: Option) -> Vec where F: Fn(&LogEntry) -> bool, Self: Sized { unimplemented!() } diff --git a/ethcore/wasm/Cargo.toml b/ethcore/wasm/Cargo.toml index 6e60580e7..e39bc7cd1 100644 --- a/ethcore/wasm/Cargo.toml +++ b/ethcore/wasm/Cargo.toml @@ -11,5 +11,5 @@ parity-wasm = "0.27" libc = "0.2" pwasm-utils = "0.1" vm = { path = "../vm" } -wasmi = { version = "0.1.3", features = ["opt-in-32bit"] } ethcore-logger = { path = "../../logger" } +wasmi = { version = "0.2" }