From 804ddfe31e3c3abf6209997a84c40746c91cb95b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= Date: Mon, 19 Feb 2018 15:05:21 +0000 Subject: [PATCH] [Beta] Backports (#7945) * ECIP 1041 - Remove Difficulty Bomb (#7905) Enable difficulty bomb defusion at block: - 5900000 on Ethereum Classic mainnet, - 2300000 on morden testnet. Reference: https://github.com/ethereumproject/ECIPs/blob/master/ECIPs/ECIP-1041.md * spec: Validate required divisor fields are not 0 (#7933) * Add validate_non_zero function It's used to validate that a Spec's uint field used as a divisor is not zero. * Add deserialize_with to gas_limit_bound_divisor Prevents panics due to divide-by-zero on the gas_limit_bound_divisor field. * Add deserialize_with to difficulty_bound_divisor Prevents panics due to divide-by-zero on the difficulty_bound_divisor field. * Add validate_optional_non_zero function Used to validate Option divisor fields. * Use deserialize_with on optional divisor fields. * Add #[serde(default)] attribute to divisor fields When using `#[serde(deserialize_with)]`, `#[serde(default)]` must be specified so that missing fields can be deserialized with the deserializer for `None`. * Kovan WASM fork code (#7849) * kovan fork code * introduce ethcore level vm_factory and let it fail * fix json tests * wasmcosts as option * review changes * wasm costs in parser * fix evm tests * review fixes * fix test * remove redundant json field --- Cargo.lock | 1 - ethcore/evm/src/factory.rs | 10 +-- ethcore/evm/src/interpreter/mod.rs | 11 ++- ethcore/evm/src/tests.rs | 70 ++++++++-------- ethcore/light/Cargo.toml | 1 - ethcore/light/src/lib.rs | 1 - ethcore/res/ethereum/classic.json | 3 +- ethcore/res/ethereum/kovan_wasm_test.json | 74 +++++++++++++++++ ethcore/res/ethereum/morden.json | 5 +- ethcore/src/client/client.rs | 8 +- ethcore/src/client/evm_test_client.rs | 7 +- ethcore/src/client/test_client.rs | 9 ++- ethcore/src/client/traits.rs | 8 +- ethcore/src/engines/mod.rs | 5 -- ethcore/src/ethereum/mod.rs | 3 + ethcore/src/executive.rs | 98 +++++++++++++++++------ ethcore/src/factory.rs | 34 +++++++- ethcore/src/json_tests/executive.rs | 2 +- ethcore/src/machine.rs | 5 -- ethcore/src/spec/spec.rs | 12 ++- ethcore/src/state/mod.rs | 4 +- ethcore/src/tests/helpers.rs | 2 +- ethcore/vm/src/lib.rs | 2 +- ethcore/vm/src/schedule.rs | 16 +++- ethcore/vm/src/tests.rs | 8 ++ ethcore/wasm/src/lib.rs | 14 ++-- ethcore/wasm/src/parser.rs | 16 ++-- ethcore/wasm/src/runtime.rs | 34 ++++---- ethcore/wasm/src/tests.rs | 36 ++++----- json/src/spec/ethash.rs | 19 ++++- json/src/spec/params.rs | 29 ++++++- json/src/uint.rs | 24 +++++- 32 files changed, 402 insertions(+), 169 deletions(-) create mode 100644 ethcore/res/ethereum/kovan_wasm_test.json diff --git a/Cargo.lock b/Cargo.lock index aa7a7ae4f..237070ba9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -605,7 +605,6 @@ dependencies = [ "ethcore-io 1.9.0", "ethcore-network 1.9.0", "ethcore-util 1.9.0", - "evm 0.1.0", "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/evm/src/factory.rs b/ethcore/evm/src/factory.rs index 3e9ee0e37..bae20a89d 100644 --- a/ethcore/evm/src/factory.rs +++ b/ethcore/evm/src/factory.rs @@ -33,7 +33,7 @@ impl Factory { /// Create fresh instance of VM /// Might choose implementation depending on supplied gas. #[cfg(feature = "jit")] - pub fn create(&self, gas: U256) -> Box { + pub fn create(&self, gas: &U256) -> Box { match self.evm { VMType::Jit => { Box::new(super::jit::JitEvm::default()) @@ -49,7 +49,7 @@ impl Factory { /// Create fresh instance of VM /// Might choose implementation depending on supplied gas. #[cfg(not(feature = "jit"))] - pub fn create(&self, gas: U256) -> Box { + pub fn create(&self, gas: &U256) -> Box { match self.evm { VMType::Interpreter => if Self::can_fit_in_usize(gas) { Box::new(super::interpreter::Interpreter::::new(self.evm_cache.clone())) @@ -68,8 +68,8 @@ impl Factory { } } - fn can_fit_in_usize(gas: U256) -> bool { - gas == U256::from(gas.low_u64() as usize) + fn can_fit_in_usize(gas: &U256) -> bool { + gas == &U256::from(gas.low_u64() as usize) } } @@ -95,7 +95,7 @@ impl Default for Factory { #[test] fn test_create_vm() { - let _vm = Factory::default().create(U256::zero()); + let _vm = Factory::default().create(&U256::zero()); } /// Create tests by injecting different VM factories diff --git a/ethcore/evm/src/interpreter/mod.rs b/ethcore/evm/src/interpreter/mod.rs index 6ea218ecf..3a4e67af4 100644 --- a/ethcore/evm/src/interpreter/mod.rs +++ b/ethcore/evm/src/interpreter/mod.rs @@ -914,8 +914,13 @@ mod tests { use rustc_hex::FromHex; use vmtype::VMType; use factory::Factory; - use vm::{ActionParams, ActionValue}; + use vm::{Vm, ActionParams, ActionValue}; use vm::tests::{FakeExt, test_finalize}; + use bigint::prelude::U256; + + fn interpreter(gas: &U256) -> Box { + Factory::new(VMType::Interpreter, 1).create(gas) + } #[test] fn should_not_fail_on_tracing_mem() { @@ -932,7 +937,7 @@ mod tests { ext.tracing = true; let gas_left = { - let mut vm = Factory::new(VMType::Interpreter, 1).create(params.gas); + let mut vm = interpreter(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -954,7 +959,7 @@ mod tests { ext.tracing = true; let err = { - let mut vm = Factory::new(VMType::Interpreter, 1).create(params.gas); + let mut vm = interpreter(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).err().unwrap() }; diff --git a/ethcore/evm/src/tests.rs b/ethcore/evm/src/tests.rs index 6e85e1803..c98a95b5d 100644 --- a/ethcore/evm/src/tests.rs +++ b/ethcore/evm/src/tests.rs @@ -40,7 +40,7 @@ fn test_add(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -60,7 +60,7 @@ fn test_sha3(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -80,7 +80,7 @@ fn test_address(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -102,7 +102,7 @@ fn test_origin(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -124,7 +124,7 @@ fn test_sender(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -159,7 +159,7 @@ fn test_extcodecopy(factory: super::Factory) { ext.codes.insert(sender, Arc::new(sender_code)); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -179,7 +179,7 @@ fn test_log_empty(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -211,7 +211,7 @@ fn test_log_sender(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -236,7 +236,7 @@ fn test_blockhash(factory: super::Factory) { ext.blockhashes.insert(U256::zero(), blockhash.clone()); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -258,7 +258,7 @@ fn test_calldataload(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -279,7 +279,7 @@ fn test_author(factory: super::Factory) { ext.info.author = author; let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -299,7 +299,7 @@ fn test_timestamp(factory: super::Factory) { ext.info.timestamp = timestamp; let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -319,7 +319,7 @@ fn test_number(factory: super::Factory) { ext.info.number = number; let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -339,7 +339,7 @@ fn test_difficulty(factory: super::Factory) { ext.info.difficulty = difficulty; let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -359,7 +359,7 @@ fn test_gas_limit(factory: super::Factory) { ext.info.gas_limit = gas_limit; let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -377,7 +377,7 @@ fn test_mul(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -395,7 +395,7 @@ fn test_sub(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -413,7 +413,7 @@ fn test_div(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -431,7 +431,7 @@ fn test_div_zero(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -449,7 +449,7 @@ fn test_mod(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -468,7 +468,7 @@ fn test_smod(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -487,7 +487,7 @@ fn test_sdiv(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -506,7 +506,7 @@ fn test_exp(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -526,7 +526,7 @@ fn test_comparison(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -547,7 +547,7 @@ fn test_signed_comparison(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -568,7 +568,7 @@ fn test_bitops(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -591,7 +591,7 @@ fn test_addmod_mulmod(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -612,7 +612,7 @@ fn test_byte(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -631,7 +631,7 @@ fn test_signextend(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -651,7 +651,7 @@ fn test_badinstruction_int() { let mut ext = FakeExt::new(); let err = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap_err() }; @@ -671,7 +671,7 @@ fn test_pop(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -691,7 +691,7 @@ fn test_extops(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -714,7 +714,7 @@ fn test_jumps(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -742,7 +742,7 @@ fn test_calls(factory: super::Factory) { }; let gas_left = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -781,7 +781,7 @@ fn test_create_in_staticcall(factory: super::Factory) { ext.is_static = true; let err = { - let mut vm = factory.create(params.gas); + let mut vm = factory.create(¶ms.gas); test_finalize(vm.exec(params, &mut ext)).unwrap_err() }; diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index 8e366289b..987195b8a 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -16,7 +16,6 @@ memorydb = { path = "../../util/memorydb" } patricia-trie = { path = "../../util/patricia_trie" } ethcore-network = { path = "../../util/network" } ethcore-io = { path = "../../util/io" } -evm = { path = "../evm" } heapsize = "0.4" vm = { path = "../vm" } rlp = { path = "../../util/rlp" } diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index cceb12429..e109a5278 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -60,7 +60,6 @@ extern crate ethcore_util as util; extern crate ethcore_bigint as bigint; extern crate ethcore_bytes as bytes; extern crate ethcore; -extern crate evm; extern crate heapsize; extern crate futures; extern crate itertools; diff --git a/ethcore/res/ethereum/classic.json b/ethcore/res/ethereum/classic.json index 5f6e3af83..a4c6fe9f2 100644 --- a/ethcore/res/ethereum/classic.json +++ b/ethcore/res/ethereum/classic.json @@ -15,7 +15,8 @@ "ecip1010ContinueTransition": 5000000, "ecip1017EraRounds": 5000000, "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff" + "eip161dTransition": "0x7fffffffffffffff", + "bombDefuseTransition": 5900000 } } }, diff --git a/ethcore/res/ethereum/kovan_wasm_test.json b/ethcore/res/ethereum/kovan_wasm_test.json new file mode 100644 index 000000000..9be03a769 --- /dev/null +++ b/ethcore/res/ethereum/kovan_wasm_test.json @@ -0,0 +1,74 @@ +{ + "name": "Kovan (Test)", + "dataDir": "kovan-test", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "4", + "blockReward": "0x4563918244F40000", + "validators" : { + "list": [ + "0x00D6Cc1BA9cf89BD2e58009741f4F7325BAdc0ED", + "0x00427feae2419c15b89d1c21af10d1b6650a4d3d", + "0x4Ed9B08e6354C70fE6F8CB0411b0d3246b424d6c", + "0x0020ee4Be0e2027d76603cB751eE069519bA81A1", + "0x0010f94b296a852aaac52ea6c5ac72e03afd032d", + "0x007733a1FE69CF3f2CF989F81C7b4cAc1693387A", + "0x00E6d2b931F55a3f1701c7389d592a7778897879", + "0x00e4a10650e5a6D6001C38ff8E64F97016a1645c", + "0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de" + ] + }, + "validateScoreTransition": 1000000, + "validateStepTransition": 1500000, + "maximumUncleCountTransition": 5067000, + "maximumUncleCount": 0 + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x400", + "registrar" : "0xfAb104398BBefbd47752E7702D9fE23047E1Bca3", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x2A", + "forkBlock": 4297256, + "forkCanonHash": "0x0a66d93c2f727dca618fabaf70c39b37018c73d78b939d8b11efbbd09034778f", + "validateReceiptsTransition" : 1000000, + "eip155Transition": 1000000, + "validateChainIdTransition": 1000000, + "eip140Transition": 5067000, + "eip211Transition": 5067000, + "eip214Transition": 5067000, + "eip658Transition": 5067000, + "wasmActivationTransition": 10 + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x5B8D80" + }, + "accounts": { + "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0x0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0x0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0x0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0x0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": 5067000, "pricing": { "modexp": { "divisor": 20 } } } }, + "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": 5067000, "pricing": { "linear": { "base": 500, "word": 0 } } } }, + "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": 5067000, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, + "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": 5067000, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "0x00521965e7bd230323c423d96c657db5b79d099f": { "balance": "1606938044258990275541962092341162602522202993782792835301376" } + }, + "nodes": [ + "enode://56abaf065581a5985b8c5f4f88bd202526482761ba10be9bfdcd14846dd01f652ec33fde0f8c0fd1db19b59a4c04465681fcef50e11380ca88d25996191c52de@40.71.221.215:30303", + "enode://d07827483dc47b368eaf88454fb04b41b7452cf454e194e2bd4c14f98a3278fed5d819dbecd0d010407fc7688d941ee1e58d4f9c6354d3da3be92f55c17d7ce3@52.166.117.77:30303", + "enode://8fa162563a8e5a05eef3e1cd5abc5828c71344f7277bb788a395cce4a0e30baf2b34b92fe0b2dbbba2313ee40236bae2aab3c9811941b9f5a7e8e90aaa27ecba@52.165.239.18:30303", + "enode://7e2e7f00784f516939f94e22bdc6cf96153603ca2b5df1c7cc0f90a38e7a2f218ffb1c05b156835e8b49086d11fdd1b3e2965be16baa55204167aa9bf536a4d9@52.243.47.56:30303", + "enode://0518a3d35d4a7b3e8c433e7ffd2355d84a1304ceb5ef349787b556197f0c87fad09daed760635b97d52179d645d3e6d16a37d2cc0a9945c2ddf585684beb39ac@40.68.248.100:30303" + ] +} diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index f84818d63..04c5a1244 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -14,9 +14,9 @@ "ecip1010PauseTransition": 1915000, "ecip1010ContinueTransition": 3415000, "ecip1017EraRounds": 2000000, - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff" + "eip161dTransition": "0x7fffffffffffffff", + "bombDefuseTransition": 2300000 } } }, @@ -31,7 +31,6 @@ "forkBlock": "0x1b34d8", "forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145", "eip155Transition": 1915000, - "eip98Transition": "0x7fffffffffffff", "eip86Transition": "0x7fffffffffffff" }, diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index c114a62ac..0cddfcc8a 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -50,9 +50,9 @@ use encoded; use engines::{EthEngine, EpochTransition}; use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError}; use vm::{EnvInfo, LastHashes}; -use evm::{Factory as EvmFactory, Schedule}; +use evm::Schedule; use executive::{Executive, Executed, TransactOptions, contract_address}; -use factory::Factories; +use factory::{Factories, VmFactory}; use futures::{future, Future}; use header::{BlockNumber, Header}; use io::*; @@ -189,7 +189,7 @@ impl Client { let trie_factory = TrieFactory::new(trie_spec); let factories = Factories { - vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size), + vm: VmFactory::new(config.vm_type.clone(), config.jump_table_size), trie: trie_factory, accountdb: Default::default(), }; @@ -1910,7 +1910,7 @@ impl MiningBlockChainClient for Client { block } - fn vm_factory(&self) -> &EvmFactory { + fn vm_factory(&self) -> &VmFactory { &self.factories.vm } diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index 814e119ba..4d38b1967 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -20,12 +20,11 @@ use std::fmt; use std::sync::Arc; use bigint::prelude::U256; use bigint::hash::H256; -use journaldb; -use {trie, kvdb_memorydb, bytes}; +use {factory, journaldb, trie, kvdb_memorydb, bytes}; use kvdb::{self, KeyValueDB}; use {state, state_db, client, executive, trace, transaction, db, spec, pod_state}; use factory::Factories; -use evm::{self, VMType, FinalizationResult}; +use evm::{VMType, FinalizationResult}; use vm::{self, ActionParams}; /// EVM test Error. @@ -120,7 +119,7 @@ impl<'a> EvmTestClient<'a> { fn factories() -> Factories { Factories { - vm: evm::Factory::new(VMType::Interpreter, 5 * 1024), + vm: factory::VmFactory::new(VMType::Interpreter, 5 * 1024), trie: trie::TrieFactory::new(trie::TrieSpec::Secure), accountdb: Default::default(), } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index cc2f08bab..1501757d9 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -47,7 +47,8 @@ use log_entry::LocalizedLogEntry; use receipt::{Receipt, LocalizedReceipt, TransactionOutcome}; use blockchain::extras::BlockReceipts; use error::{ImportResult, Error as EthcoreError}; -use evm::{Factory as EvmFactory, VMType}; +use evm::VMType; +use factory::VmFactory; use vm::Schedule; use miner::{Miner, MinerService, TransactionImportResult}; use spec::Spec; @@ -98,7 +99,7 @@ pub struct TestBlockChainClient { /// Spec pub spec: Spec, /// VM Factory - pub vm_factory: EvmFactory, + pub vm_factory: VmFactory, /// Timestamp assigned to latest sealed block pub latest_block_timestamp: RwLock, /// Ancient block info. @@ -169,7 +170,7 @@ impl TestBlockChainClient { queue_size: AtomicUsize::new(0), miner: Arc::new(Miner::with_spec(&spec)), spec: spec, - vm_factory: EvmFactory::new(VMType::Interpreter, 1024 * 1024), + vm_factory: VmFactory::new(VMType::Interpreter, 1024 * 1024), latest_block_timestamp: RwLock::new(10_000_000), ancient_block: RwLock::new(None), first_block: RwLock::new(None), @@ -399,7 +400,7 @@ impl MiningBlockChainClient for TestBlockChainClient { block.reopen(&*self.spec.engine) } - fn vm_factory(&self) -> &EvmFactory { + fn vm_factory(&self) -> &VmFactory { &self.vm_factory } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 58a05e4e0..12d2d76e2 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -21,9 +21,9 @@ use block::{OpenBlock, SealedBlock, ClosedBlock}; use blockchain::TreeRoute; use encoded; use vm::LastHashes; -use error::{ImportResult, CallError, Error as EthcoreError}; -use error::{TransactionImportResult, BlockImportError}; -use evm::{Factory as EvmFactory, Schedule}; +use error::{ImportResult, CallError, Error as EthcoreError, TransactionImportResult, BlockImportError}; +use evm::Schedule; +use factory::VmFactory; use executive::Executed; use filter::Filter; use header::{BlockNumber}; @@ -298,7 +298,7 @@ pub trait MiningBlockChainClient: BlockChainClient { fn reopen_block(&self, block: ClosedBlock) -> OpenBlock; /// Returns EvmFactory. - fn vm_factory(&self) -> &EvmFactory; + fn vm_factory(&self) -> &VmFactory; /// Broadcast a block proposal. fn broadcast_proposal_block(&self, block: SealedBlock); diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 88ad68f63..c2a873161 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -390,11 +390,6 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> { self.machine().verify_transaction_basic(t, header) } - /// If this machine supports wasm. - fn supports_wasm(&self) -> bool { - self.machine().supports_wasm() - } - /// Additional information. fn additional_params(&self) -> HashMap { self.machine().additional_params() diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index 2ecef4941..957e70544 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -141,6 +141,9 @@ pub fn new_constantinople_test_machine() -> EthereumMachine { load_machine(inclu /// Create a new Musicoin-MCIP3-era spec. pub fn new_mcip3_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/mcip3_test.json")) } +/// Create new Kovan spec with wasm activated at certain block +pub fn new_kovan_wasm_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/kovan_wasm_test.json")) } + #[cfg(test)] mod tests { use bigint::prelude::U256; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 2d7d9ae7f..4cd43c1d4 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -24,11 +24,12 @@ use util::*; use bytes::{Bytes, BytesRef}; use state::{Backend as StateBackend, State, Substate, CleanupMode}; use machine::EthereumMachine as Machine; -use vm::EnvInfo; use error::ExecutionError; -use evm::{CallType, Factory, Finalize, FinalizationResult}; -use vm::{self, Ext, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, ActionValue}; -use wasm; +use evm::{CallType, Finalize, FinalizationResult}; +use vm::{ + self, Ext, EnvInfo, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, + ActionValue, Schedule, +}; use externalities::*; use trace::{self, Tracer, VMTracer}; use transaction::{Action, SignedTransaction}; @@ -40,8 +41,6 @@ pub use executed::{Executed, ExecutionResult}; /// Maybe something like here: `https://github.com/ethereum/libethereum/blob/4db169b8504f2b87f7d5a481819cfb959fc65f6c/libethereum/ExtVM.cpp` const STACK_SIZE_PER_DEPTH: usize = 24*1024; -const WASM_MAGIC_NUMBER: &'static [u8; 4] = b"\0asm"; - /// Returns new address created from address, nonce, and code hash pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address, nonce: &U256, code: &[u8]) -> (Address, Option) { use rlp::RlpStream; @@ -154,14 +153,6 @@ impl TransactOptions { } } -pub fn executor(machine: &Machine, vm_factory: &Factory, params: &ActionParams) -> Box { - if machine.supports_wasm() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) { - Box::new(wasm::WasmInterpreter) - } else { - vm_factory.create(params.gas) - } -} - /// Transaction executor. pub struct Executive<'a, B: 'a + StateBackend> { state: &'a mut State, @@ -336,6 +327,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { fn exec_vm( &mut self, + schedule: Schedule, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy, @@ -351,19 +343,20 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { let vm_factory = self.state.vm_factory(); let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer, static_call); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); - return executor(self.machine, &vm_factory, ¶ms).exec(params, &mut ext).finalize(ext); + let mut vm = vm_factory.create(¶ms, &schedule); + return vm.exec(params, &mut ext).finalize(ext); } // Start in new thread to reset stack // TODO [todr] No thread builder yet, so we need to reset once for a while // https://github.com/aturon/crossbeam/issues/16 crossbeam::scope(|scope| { - let machine = self.machine; let vm_factory = self.state.vm_factory(); let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer, static_call); scope.spawn(move || { - executor(machine, &vm_factory, ¶ms).exec(params, &mut ext).finalize(ext) + let mut vm = vm_factory.create(¶ms, &schedule); + vm.exec(params, &mut ext).finalize(ext) }) }).join() } @@ -473,7 +466,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("scope is conditional on params.code.is_some(); qed")); let res = { - self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer, &mut subvmtracer) + self.exec_vm(schedule, params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer, &mut subvmtracer) }; vm_tracer.done_subtrace(subvmtracer); @@ -564,9 +557,14 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed")); - let res = { - self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(output.as_mut().or(trace_output.as_mut())), &mut subtracer, &mut subvmtracer) - }; + let res = self.exec_vm( + schedule, + params, + &mut unconfirmed_substate, + OutputPolicy::InitContract(output.as_mut().or(trace_output.as_mut())), + &mut subtracer, + &mut subvmtracer + ); vm_tracer.done_subtrace(subvmtracer); @@ -1485,8 +1483,6 @@ mod tests { params.gas = U256::from(20025); params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::zero()); - let mut state = get_temp_state_with_factory(factory); - state.add_balance(&sender, &U256::from_str("152d02c7e14af68000000").unwrap(), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); let machine = ::ethereum::new_byzantium_test_machine(); let mut substate = Substate::new(); @@ -1501,4 +1497,60 @@ mod tests { assert_eq!(output[..], returns[..]); assert_eq!(state.storage_at(&contract_address, &H256::from(&U256::zero())).unwrap(), H256::from(&U256::from(0))); } + + fn wasm_sample_code() -> Arc> { + Arc::new( + "0061736d01000000010d0360027f7f0060017f0060000002270303656e7603726574000003656e760673656e646572000103656e76066d656d6f727902010110030201020404017000000501000708010463616c6c00020901000ac10101be0102057f017e4100410028020441c0006b22043602042004412c6a41106a220041003602002004412c6a41086a22014200370200200441186a41106a22024100360200200441186a41086a220342003703002004420037022c2004410036021c20044100360218200441186a1001200020022802002202360200200120032903002205370200200441106a2002360200200441086a200537030020042004290318220537022c200420053703002004411410004100200441c0006a3602040b0b0a010041040b0410c00000" + .from_hex() + .unwrap() + ) + } + + #[test] + fn wasm_activated_test() { + let contract_address = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + + let mut state = get_temp_state(); + state.add_balance(&sender, &U256::from(10000000000u64), CleanupMode::NoEmpty).unwrap(); + state.commit().unwrap(); + + let mut params = ActionParams::default(); + params.origin = sender.clone(); + params.sender = sender.clone(); + params.address = contract_address.clone(); + params.gas = U256::from(20025); + params.code = Some(wasm_sample_code()); + + let mut info = EnvInfo::default(); + + // 100 > 10 + info.number = 100; + + // Network with wasm activated at block 10 + let machine = ::ethereum::new_kovan_wasm_test_machine(); + + let mut output = [0u8; 20]; + let FinalizationResult { gas_left: result, .. } = { + let mut ex = Executive::new(&mut state, &info, &machine); + ex.call(params.clone(), &mut Substate::new(), BytesRef::Fixed(&mut output), &mut NoopTracer, &mut NoopVMTracer).unwrap() + }; + + assert_eq!(result, U256::from(18433)); + // Transaction successfully returned sender + assert_eq!(output[..], sender[..]); + + // 1 < 10 + info.number = 1; + + let mut output = [0u8; 20]; + let FinalizationResult { gas_left: result, .. } = { + let mut ex = Executive::new(&mut state, &info, &machine); + ex.call(params, &mut Substate::new(), BytesRef::Fixed(&mut output), &mut NoopTracer, &mut NoopVMTracer).unwrap() + }; + + assert_eq!(result, U256::from(20025)); + // Since transaction errored due to wasm was not activated, result is just empty + assert_eq!(output[..], [0u8; 20][..]); + } } diff --git a/ethcore/src/factory.rs b/ethcore/src/factory.rs index e9edfa3bd..68a15f164 100644 --- a/ethcore/src/factory.rs +++ b/ethcore/src/factory.rs @@ -15,14 +15,44 @@ // along with Parity. If not, see . use trie::TrieFactory; -use evm::Factory as EvmFactory; use account_db::Factory as AccountFactory; +use evm::{Factory as EvmFactory, VMType}; +use vm::{Vm, ActionParams, Schedule}; +use wasm::WasmInterpreter; + +const WASM_MAGIC_NUMBER: &'static [u8; 4] = b"\0asm"; + +/// Virtual machine factory +#[derive(Default, Clone)] +pub struct VmFactory { + evm: EvmFactory, +} + +impl VmFactory { + pub fn create(&self, params: &ActionParams, schedule: &Schedule) -> Box { + if schedule.wasm.is_some() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) { + Box::new(WasmInterpreter) + } else { + self.evm.create(¶ms.gas) + } + } + + pub fn new(evm: VMType, cache_size: usize) -> Self { + VmFactory { evm: EvmFactory::new(evm, cache_size) } + } +} + +impl From for VmFactory { + fn from(evm: EvmFactory) -> Self { + VmFactory { evm: evm } + } +} /// Collection of factories. #[derive(Default, Clone)] pub struct Factories { /// factory for evm. - pub vm: EvmFactory, + pub vm: VmFactory, /// factory for tries. pub trie: TrieFactory, /// factory for account databases. diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index a962ca04f..4a6309cac 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -259,7 +259,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { &mut tracer, &mut vm_tracer, )); - let mut evm = vm_factory.create(params.gas); + let mut evm = vm_factory.create(¶ms, &machine.schedule(0u64.into())); let res = evm.exec(params, &mut ex); // a return in finalize will not alter callcreates let callcreates = ex.callcreates.clone(); diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index e583cd592..2b37e1baa 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -377,11 +377,6 @@ impl EthereumMachine { Ok(()) } - /// If this machine supports wasm. - pub fn supports_wasm(&self) -> bool { - self.params().wasm - } - /// Additional params. pub fn additional_params(&self) -> HashMap { hash_map![ diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 6f94713ad..6aa9d4891 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -110,8 +110,8 @@ pub struct CommonParams { pub nonce_cap_increment: u64, /// Enable dust cleanup for contracts. pub remove_dust_contracts: bool, - /// Wasm support - pub wasm: bool, + /// Wasm activation blocknumber, if any disabled initially. + pub wasm_activation_transition: BlockNumber, /// Gas limit bound divisor (how much gas limit can change per block) pub gas_limit_bound_divisor: U256, /// Registrar contract address. @@ -147,6 +147,9 @@ impl CommonParams { false => ::vm::CleanDustMode::BasicOnly, }; } + if block_number >= self.wasm_activation_transition { + schedule.wasm = Some(Default::default()); + } } /// Whether these params contain any bug-fix hard forks. @@ -221,12 +224,15 @@ impl From for CommonParams { ), nonce_cap_increment: p.nonce_cap_increment.map_or(64, Into::into), remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false), - wasm: p.wasm.unwrap_or(false), gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), registrar: p.registrar.map_or_else(Address::new, Into::into), node_permission_contract: p.node_permission_contract.map(Into::into), max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into), transaction_permission_contract: p.transaction_permission_contract.map(Into::into), + wasm_activation_transition: p.wasm_activation_transition.map_or( + BlockNumber::max_value(), + Into::into + ), } } } diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index c355f61e3..e94a3c197 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -40,7 +40,7 @@ use executed::{Executed, ExecutionError}; use types::state_diff::StateDiff; use transaction::SignedTransaction; use state_db::StateDB; -use evm::{Factory as EvmFactory}; +use factory::VmFactory; use bigint::prelude::U256; use bigint::hash::H256; @@ -376,7 +376,7 @@ impl State { } /// Get a VM factory that can execute on this state. - pub fn vm_factory(&self) -> EvmFactory { + pub fn vm_factory(&self) -> VmFactory { self.factories.vm.clone() } diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index ead7755e0..5de0c1ff6 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -275,7 +275,7 @@ pub fn get_temp_state() -> State<::state_db::StateDB> { pub fn get_temp_state_with_factory(factory: EvmFactory) -> State<::state_db::StateDB> { let journal_db = get_temp_state_db(); let mut factories = Factories::default(); - factories.vm = factory; + factories.vm = factory.into(); State::new(journal_db, U256::from(0), factories) } diff --git a/ethcore/vm/src/lib.rs b/ethcore/vm/src/lib.rs index 08f0aa639..de54bea7c 100644 --- a/ethcore/vm/src/lib.rs +++ b/ethcore/vm/src/lib.rs @@ -38,7 +38,7 @@ pub mod tests; pub use action_params::{ActionParams, ActionValue, ParamsType}; pub use call_type::CallType; pub use env_info::{EnvInfo, LastHashes}; -pub use schedule::{Schedule, CleanDustMode}; +pub use schedule::{Schedule, CleanDustMode, WasmCosts}; pub use ext::{Ext, MessageCallResult, ContractCreateResult, CreateContractAddress}; pub use return_data::{ReturnData, GasLeft}; pub use error::{Error, Result}; diff --git a/ethcore/vm/src/schedule.rs b/ethcore/vm/src/schedule.rs index 11a049dfc..cc184825c 100644 --- a/ethcore/vm/src/schedule.rs +++ b/ethcore/vm/src/schedule.rs @@ -113,8 +113,8 @@ pub struct Schedule { pub kill_dust: CleanDustMode, /// Enable EIP-86 rules pub eip86: bool, - /// Wasm extra schedule settings - pub wasm: WasmCosts, + /// Wasm extra schedule settings, if wasm activated + pub wasm: Option, } /// Wasm cost table @@ -231,7 +231,7 @@ impl Schedule { have_static_call: false, kill_dust: CleanDustMode::Off, eip86: false, - wasm: Default::default(), + wasm: None, } } @@ -294,9 +294,17 @@ impl Schedule { have_static_call: false, kill_dust: CleanDustMode::Off, eip86: false, - wasm: Default::default(), + wasm: None, } } + + /// Returns wasm schedule + /// + /// May panic if there is no wasm schedule + pub fn wasm(&self) -> &WasmCosts { + // *** Prefer PANIC here instead of silently breaking consensus! *** + self.wasm.as_ref().expect("Wasm schedule expected to exist while checking wasm contract. Misconfigured client?") + } } impl Default for Schedule { diff --git a/ethcore/vm/src/tests.rs b/ethcore/vm/src/tests.rs index 222578afd..e2d9f3050 100644 --- a/ethcore/vm/src/tests.rs +++ b/ethcore/vm/src/tests.rs @@ -78,15 +78,23 @@ pub fn test_finalize(res: Result) -> Result { } impl FakeExt { + /// New fake externalities pub fn new() -> Self { FakeExt::default() } + /// New fake externalities with byzantium schedule rules pub fn new_byzantium() -> Self { let mut ext = FakeExt::default(); ext.schedule = Schedule::new_byzantium(); ext } + + /// Alter fake externalities to allow wasm + pub fn with_wasm(mut self) -> Self { + self.schedule.wasm = Some(Default::default()); + self + } } impl Ext for FakeExt { diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index 0228dbdf2..fcc2a6696 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -69,7 +69,7 @@ impl From for vm::Error { impl vm::Vm for WasmInterpreter { fn exec(&mut self, params: ActionParams, ext: &mut vm::Ext) -> vm::Result { - let (module, data) = parser::payload(¶ms, ext.schedule())?; + let (module, data) = parser::payload(¶ms, ext.schedule().wasm())?; let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error)?; @@ -80,8 +80,8 @@ impl vm::Vm for WasmInterpreter { &wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolover) ).map_err(Error)?; - let adjusted_gas = params.gas * U256::from(ext.schedule().wasm.opcodes_div) / - U256::from(ext.schedule().wasm.opcodes_mul); + let adjusted_gas = params.gas * U256::from(ext.schedule().wasm().opcodes_div) / + U256::from(ext.schedule().wasm().opcodes_mul); if adjusted_gas > ::std::u64::MAX.into() { @@ -112,8 +112,8 @@ impl vm::Vm for WasmInterpreter { // total_charge <- static_region * 2^32 * 2^16 // total_charge ∈ [0..2^64) if static_region ∈ [0..2^16) // qed - assert!(runtime.schedule().wasm.initial_mem < 1 << 16); - runtime.charge(|s| initial_memory as u64 * s.wasm.initial_mem as u64)?; + assert!(runtime.schedule().wasm().initial_mem < 1 << 16); + runtime.charge(|s| initial_memory as u64 * s.wasm().initial_mem as u64)?; let module_instance = module_instance.run_start(&mut runtime).map_err(Error)?; @@ -149,8 +149,8 @@ impl vm::Vm for WasmInterpreter { }; let gas_left = - U256::from(gas_left) * U256::from(ext.schedule().wasm.opcodes_mul) - / U256::from(ext.schedule().wasm.opcodes_div); + U256::from(gas_left) * U256::from(ext.schedule().wasm().opcodes_mul) + / U256::from(ext.schedule().wasm().opcodes_div); if result.is_empty() { trace!(target: "wasm", "Contract execution result is empty."); diff --git a/ethcore/wasm/src/parser.rs b/ethcore/wasm/src/parser.rs index 13e055529..8ba7a7098 100644 --- a/ethcore/wasm/src/parser.rs +++ b/ethcore/wasm/src/parser.rs @@ -21,21 +21,21 @@ use wasm_utils::{self, rules}; use parity_wasm::elements::{self, Deserialize}; use parity_wasm::peek_size; -fn gas_rules(schedule: &vm::Schedule) -> rules::Set { +fn gas_rules(wasm_costs: &vm::WasmCosts) -> rules::Set { rules::Set::new({ let mut vals = ::std::collections::HashMap::with_capacity(4); - vals.insert(rules::InstructionType::Load, schedule.wasm.mem as u32); - vals.insert(rules::InstructionType::Store, schedule.wasm.mem as u32); - vals.insert(rules::InstructionType::Div, schedule.wasm.div as u32); - vals.insert(rules::InstructionType::Mul, schedule.wasm.mul as u32); + vals.insert(rules::InstructionType::Load, wasm_costs.mem as u32); + vals.insert(rules::InstructionType::Store, wasm_costs.mem as u32); + vals.insert(rules::InstructionType::Div, wasm_costs.div as u32); + vals.insert(rules::InstructionType::Mul, wasm_costs.mul as u32); vals - }).with_grow_cost(schedule.wasm.grow_mem) + }).with_grow_cost(wasm_costs.grow_mem) } /// Splits payload to code and data according to params.params_type, also /// loads the module instance from payload and injects gas counter according /// to schedule. -pub fn payload<'a>(params: &'a vm::ActionParams, schedule: &vm::Schedule) +pub fn payload<'a>(params: &'a vm::ActionParams, wasm_costs: &vm::WasmCosts) -> Result<(elements::Module, &'a [u8]), vm::Error> { let code = match params.code { @@ -70,7 +70,7 @@ pub fn payload<'a>(params: &'a vm::ActionParams, schedule: &vm::Schedule) let contract_module = wasm_utils::inject_gas_counter( deserialized_module, - &gas_rules(schedule), + &gas_rules(wasm_costs), ); let data = match params.params_type { diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index e405d0f86..e4071c4d5 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -186,7 +186,7 @@ impl<'a> Runtime<'a> { pub fn adjusted_charge(&mut self, f: F) -> Result<()> where F: FnOnce(&vm::Schedule) -> u64 { - self.charge(|schedule| f(schedule) * schedule.wasm.opcodes_div as u64 / schedule.wasm.opcodes_mul as u64) + self.charge(|schedule| f(schedule) * schedule.wasm().opcodes_div as u64 / schedule.wasm().opcodes_mul as u64) } /// Charge gas provided by the closure, and closure also can return overflowing @@ -212,8 +212,8 @@ impl<'a> Runtime<'a> { { self.overflow_charge(|schedule| f(schedule) - .and_then(|x| x.checked_mul(schedule.wasm.opcodes_div as u64)) - .map(|x| x / schedule.wasm.opcodes_mul as u64) + .and_then(|x| x.checked_mul(schedule.wasm().opcodes_div as u64)) + .map(|x| x / schedule.wasm().opcodes_mul as u64) ) } @@ -385,8 +385,8 @@ impl<'a> Runtime<'a> { // todo: optimize to use memory views once it's in let payload = self.memory.get(input_ptr, input_len as usize)?; - let adjusted_gas = match gas.checked_mul(self.ext.schedule().wasm.opcodes_div as u64) - .map(|x| x / self.ext.schedule().wasm.opcodes_mul as u64) + let adjusted_gas = match gas.checked_mul(self.ext.schedule().wasm().opcodes_div as u64) + .map(|x| x / self.ext.schedule().wasm().opcodes_mul as u64) { Some(x) => x, None => { @@ -412,8 +412,8 @@ impl<'a> Runtime<'a> { vm::MessageCallResult::Success(gas_left, _) => { // cannot overflow, before making call gas_counter was incremented with gas, and gas_left < gas self.gas_counter = self.gas_counter - - gas_left.low_u64() * self.ext.schedule().wasm.opcodes_div as u64 - / self.ext.schedule().wasm.opcodes_mul as u64; + gas_left.low_u64() * self.ext.schedule().wasm().opcodes_div as u64 + / self.ext.schedule().wasm().opcodes_mul as u64; self.memory.set(result_ptr, &result)?; Ok(0i32.into()) @@ -421,8 +421,8 @@ impl<'a> Runtime<'a> { vm::MessageCallResult::Reverted(gas_left, _) => { // cannot overflow, before making call gas_counter was incremented with gas, and gas_left < gas self.gas_counter = self.gas_counter - - gas_left.low_u64() * self.ext.schedule().wasm.opcodes_div as u64 - / self.ext.schedule().wasm.opcodes_mul as u64; + gas_left.low_u64() * self.ext.schedule().wasm().opcodes_div as u64 + / self.ext.schedule().wasm().opcodes_mul as u64; self.memory.set(result_ptr, &result)?; Ok((-1i32).into()) @@ -450,14 +450,14 @@ impl<'a> Runtime<'a> { fn return_address_ptr(&mut self, ptr: u32, val: Address) -> Result<()> { - self.charge(|schedule| schedule.wasm.static_address as u64)?; + self.charge(|schedule| schedule.wasm().static_address as u64)?; self.memory.set(ptr, &*val)?; Ok(()) } fn return_u256_ptr(&mut self, ptr: u32, val: U256) -> Result<()> { let value: H256 = val.into(); - self.charge(|schedule| schedule.wasm.static_u256 as u64)?; + self.charge(|schedule| schedule.wasm().static_u256 as u64)?; self.memory.set(ptr, &*value)?; Ok(()) } @@ -489,8 +489,8 @@ impl<'a> Runtime<'a> { self.adjusted_charge(|schedule| schedule.create_data_gas as u64 * code.len() as u64)?; let gas_left: U256 = U256::from(self.gas_left()?) - * U256::from(self.ext.schedule().wasm.opcodes_mul) - / U256::from(self.ext.schedule().wasm.opcodes_div); + * U256::from(self.ext.schedule().wasm().opcodes_mul) + / U256::from(self.ext.schedule().wasm().opcodes_div); match self.ext.create(&gas_left, &endowment, &code, vm::CreateContractAddress::FromSenderAndCodeHash) { vm::ContractCreateResult::Created(address, gas_left) => { @@ -498,8 +498,8 @@ impl<'a> Runtime<'a> { self.gas_counter = self.gas_limit - // this cannot overflow, since initial gas is in [0..u64::max) range, // and gas_left cannot be bigger - gas_left.low_u64() * self.ext.schedule().wasm.opcodes_div as u64 - / self.ext.schedule().wasm.opcodes_mul as u64; + gas_left.low_u64() * self.ext.schedule().wasm().opcodes_div as u64 + / self.ext.schedule().wasm().opcodes_mul as u64; trace!(target: "wasm", "runtime: create contract success (@{:?})", address); Ok(0i32.into()) }, @@ -512,8 +512,8 @@ impl<'a> Runtime<'a> { self.gas_counter = self.gas_limit - // this cannot overflow, since initial gas is in [0..u64::max) range, // and gas_left cannot be bigger - gas_left.low_u64() * self.ext.schedule().wasm.opcodes_div as u64 - / self.ext.schedule().wasm.opcodes_mul as u64; + gas_left.low_u64() * self.ext.schedule().wasm().opcodes_div as u64 + / self.ext.schedule().wasm().opcodes_mul as u64; Ok((-1i32).into()) }, diff --git a/ethcore/wasm/src/tests.rs b/ethcore/wasm/src/tests.rs index 58267aa13..0083877c0 100644 --- a/ethcore/wasm/src/tests.rs +++ b/ethcore/wasm/src/tests.rs @@ -45,7 +45,7 @@ macro_rules! reqrep_test { params.code = Some(Arc::new(code)); params.data = Some($input); - let mut fake_ext = FakeExt::new(); + let mut fake_ext = FakeExt::new().with_wasm(); fake_ext.info = $info; fake_ext.blockhashes = $block_hashes; @@ -81,7 +81,7 @@ fn empty() { params.address = address.clone(); params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let gas_left = { let mut interpreter = wasm_interpreter(); @@ -110,7 +110,7 @@ fn logger() { params.gas = U256::from(100_000); params.value = ActionValue::transfer(1_000_000_000); params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let gas_left = { let mut interpreter = wasm_interpreter(); @@ -159,7 +159,7 @@ fn identity() { params.sender = sender.clone(); params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { let mut interpreter = wasm_interpreter(); @@ -194,7 +194,7 @@ fn dispersion() { params.data = Some(vec![ 0u8, 125, 197, 255, 19 ]); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { let mut interpreter = wasm_interpreter(); @@ -222,7 +222,7 @@ fn suicide_not() { params.data = Some(vec![ 0u8 ]); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { let mut interpreter = wasm_interpreter(); @@ -255,7 +255,7 @@ fn suicide() { args.extend(refund.to_vec()); params.data = Some(args); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let gas_left = { let mut interpreter = wasm_interpreter(); @@ -282,7 +282,7 @@ fn create() { params.data = Some(vec![0u8, 2, 4, 8, 16, 32, 64, 128]); params.value = ActionValue::transfer(1_000_000_000); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let gas_left = { let mut interpreter = wasm_interpreter(); @@ -326,7 +326,7 @@ fn call_msg() { params.code = Some(Arc::new(load_sample!("call.wasm"))); params.data = Some(Vec::new()); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); ext.balances.insert(receiver.clone(), U256::from(10000000000u64)); let gas_left = { @@ -369,7 +369,7 @@ fn call_code() { params.data = Some(Vec::new()); params.value = ActionValue::transfer(1_000_000_000); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { let mut interpreter = wasm_interpreter(); @@ -416,7 +416,7 @@ fn call_static() { params.value = ActionValue::transfer(1_000_000_000); params.code_address = contract_address.clone(); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { let mut interpreter = wasm_interpreter(); @@ -456,7 +456,7 @@ fn realloc() { params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); params.data = Some(vec![0u8]); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { let mut interpreter = wasm_interpreter(); @@ -478,7 +478,7 @@ fn alloc() { params.gas = U256::from(10_000_000); params.code = Some(Arc::new(code)); params.data = Some(vec![0u8]); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { let mut interpreter = wasm_interpreter(); @@ -504,7 +504,7 @@ fn storage_read() { let mut params = ActionParams::default(); params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); ext.store.insert("0100000000000000000000000000000000000000000000000000000000000000".into(), address.into()); let (gas_left, result) = { @@ -531,7 +531,7 @@ fn keccak() { params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); params.data = Some(b"something".to_vec()); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { let mut interpreter = wasm_interpreter(); @@ -666,7 +666,7 @@ fn storage_metering() { ::ethcore_logger::init_log(); // #1 - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let code = Arc::new(load_sample!("setter.wasm")); let address: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); @@ -807,7 +807,7 @@ fn embedded_keccak() { params.code = Some(Arc::new(code)); params.params_type = vm::ParamsType::Embedded; - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { let mut interpreter = wasm_interpreter(); @@ -835,7 +835,7 @@ fn events() { params.code = Some(Arc::new(code)); params.data = Some(b"something".to_vec()); - let mut ext = FakeExt::new(); + let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { let mut interpreter = wasm_interpreter(); diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index 283e24ba0..8d5494f56 100644 --- a/json/src/spec/ethash.rs +++ b/json/src/spec/ethash.rs @@ -16,7 +16,7 @@ //! Ethash params deserialization. -use uint::Uint; +use uint::{self, Uint}; use hash::Address; /// Deserializable doppelganger of EthashParams. @@ -27,12 +27,15 @@ pub struct EthashParams { pub minimum_difficulty: Uint, /// See main EthashParams docs. #[serde(rename="difficultyBoundDivisor")] + #[serde(deserialize_with="uint::validate_non_zero")] pub difficulty_bound_divisor: Uint, /// See main EthashParams docs. #[serde(rename="difficultyIncrementDivisor")] + #[serde(default, deserialize_with="uint::validate_optional_non_zero")] pub difficulty_increment_divisor: Option, /// See main EthashParams docs. #[serde(rename="metropolisDifficultyIncrementDivisor")] + #[serde(default, deserialize_with="uint::validate_optional_non_zero")] pub metropolis_difficulty_increment_divisor: Option, /// See main EthashParams docs. #[serde(rename="durationLimit")] @@ -60,6 +63,7 @@ pub struct EthashParams { pub difficulty_hardfork_transition: Option, /// See main EthashParams docs. #[serde(rename="difficultyHardforkBoundDivisor")] + #[serde(default, deserialize_with="uint::validate_optional_non_zero")] pub difficulty_hardfork_bound_divisor: Option, /// See main EthashParams docs. #[serde(rename="bombDefuseTransition")] @@ -302,4 +306,17 @@ mod tests { } }); } + + #[test] + #[should_panic(expected = "a non-zero value")] + fn test_zero_value_divisor() { + let s = r#"{ + "params": { + "difficultyBoundDivisor": "0x0", + "minimumDifficulty": "0x020000" + } + }"#; + + let _deserialized: Ethash = serde_json::from_str(s).unwrap(); + } } diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index f62626899..1f4b248ca 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -16,7 +16,7 @@ //! Spec params deserialization. -use uint::Uint; +use uint::{self, Uint}; use hash::{H256, Address}; use bytes::Bytes; @@ -98,10 +98,9 @@ pub struct Params { pub nonce_cap_increment: Option, /// See `CommonParams` docs. pub remove_dust_contracts : Option, - /// Wasm support flag - pub wasm: Option, /// See `CommonParams` docs. #[serde(rename="gasLimitBoundDivisor")] + #[serde(deserialize_with="uint::validate_non_zero")] pub gas_limit_bound_divisor: Uint, /// See `CommonParams` docs. pub registrar: Option
, @@ -117,6 +116,9 @@ pub struct Params { /// Transaction permission contract address. #[serde(rename="transactionPermissionContract")] pub transaction_permission_contract: Option
, + /// Wasm activation block height, if not activated from start + #[serde(rename="wasmActivationTransition")] + pub wasm_activation_transition: Option, } #[cfg(test)] @@ -136,7 +138,8 @@ mod tests { "minGasLimit": "0x1388", "accountStartNonce": "0x01", "gasLimitBoundDivisor": "0x20", - "maxCodeSize": "0x1000" + "maxCodeSize": "0x1000", + "wasmActivationTransition": "0x1010" }"#; let deserialized: Params = serde_json::from_str(s).unwrap(); @@ -148,5 +151,23 @@ mod tests { assert_eq!(deserialized.account_start_nonce, Some(Uint(U256::from(0x01)))); assert_eq!(deserialized.gas_limit_bound_divisor, Uint(U256::from(0x20))); assert_eq!(deserialized.max_code_size, Some(Uint(U256::from(0x1000)))); + assert_eq!(deserialized.wasm_activation_transition, Some(Uint(U256::from(0x1010)))); + } + + #[test] + #[should_panic(expected = "a non-zero value")] + fn test_zero_value_divisor() { + let s = r#"{ + "maximumExtraDataSize": "0x20", + "networkID" : "0x1", + "chainID" : "0x15", + "subprotocolName" : "exp", + "minGasLimit": "0x1388", + "accountStartNonce": "0x01", + "gasLimitBoundDivisor": "0x0", + "maxCodeSize": "0x1000" + }"#; + + let _deserialized: Params = serde_json::from_str(s).unwrap(); } } diff --git a/json/src/uint.rs b/json/src/uint.rs index b6962c762..4fc8d2abc 100644 --- a/json/src/uint.rs +++ b/json/src/uint.rs @@ -19,7 +19,7 @@ use std::fmt; use std::str::FromStr; use serde::{Deserialize, Deserializer}; -use serde::de::{Error, Visitor}; +use serde::de::{Error, Visitor, Unexpected}; use bigint::prelude::U256; /// Lenient uint json deserialization for test json files. @@ -90,6 +90,28 @@ impl<'a> Visitor<'a> for UintVisitor { } } +pub fn validate_non_zero<'de, D>(d: D) -> Result where D: Deserializer<'de> { + let value = Uint::deserialize(d)?; + + if value == Uint(U256::from(0)) { + return Err(Error::invalid_value(Unexpected::Unsigned(value.into()), &"a non-zero value")) + } + + Ok(value) +} + +pub fn validate_optional_non_zero<'de, D>(d: D) -> Result, D::Error> where D: Deserializer<'de> { + let value: Option = Option::deserialize(d)?; + + if let Some(value) = value { + if value == Uint(U256::from(0)) { + return Err(Error::invalid_value(Unexpected::Unsigned(value.into()), &"a non-zero value")) + } + } + + Ok(value) +} + #[cfg(test)] mod test { use serde_json;