diff --git a/Cargo.lock b/Cargo.lock index 50a118413..bd35e0345 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,6 +106,17 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bigint" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -175,7 +186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bn" version = "0.4.4" -source = "git+https://github.com/paritytech/bn#b97e95a45f4484a41a515338c4f0e093bf6675e0" +source = "git+https://github.com/paritytech/bn#c9831a8d10d55045692394cbc10efe0321ddb16f" dependencies = [ "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -311,6 +322,11 @@ name = "crossbeam" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "crunchy" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "crypt32-sys" version = "0.2.0" @@ -492,7 +508,7 @@ dependencies = [ name = "ethcore-bigint" version = "0.1.3" dependencies = [ - "bigint 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bigint 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -876,6 +892,7 @@ dependencies = [ "docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore 1.7.0", "ethcore-util 1.7.2", + "ethjson 0.1.0", "evm 0.1.0", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3106,6 +3123,7 @@ dependencies = [ "checksum base-x 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f59103b47307f76e03bef1633aec7fa9e29bfb5aa6daf5a334f94233c71f6c1" "checksum base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9605ba46d61df0410d8ac686b0007add8172eba90e8e909c347856fe794d8c" "checksum bigint 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0673c930652d3d4d6dcd5c45b5db4fa5f8f33994d7323618c43c083b223e8c" +"checksum bigint 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5442186ef6560f30f1ee4b9c1e4c87a35a6879d3644550cc248ec2b955eb5fcd" "checksum bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e103c8b299b28a9c6990458b7013dc4a8356a9b854c51b9883241f5866fac36e" "checksum bit-set 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6e1e6fb1c9e3d6fcdec57216a74eaa03e41f52a22f13a16438251d8e88b89da" "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" @@ -3131,6 +3149,7 @@ dependencies = [ "checksum core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "20a6d0448d3a99d977ae4a2aa5a98d886a923e863e81ad9ff814645b6feb3bbd" "checksum core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "05eed248dc504a5391c63794fe4fb64f46f071280afaa1b73308f3c0ce4574c5" "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" +"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" "checksum crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34988f7e069e0b2f3bfc064295161e489b2d4e04a2e4248fb94360cdf00b4ec" "checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "" "checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" diff --git a/ethcore/benches/evm.rs b/ethcore/benches/evm.rs new file mode 100644 index 000000000..384aa60a6 --- /dev/null +++ b/ethcore/benches/evm.rs @@ -0,0 +1,97 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +#![feature(test)] + +extern crate test; +extern crate ethcore_util as util; +extern crate rand; +extern crate bn; +extern crate crypto; +extern crate rustc_serialize; +extern crate ethkey; + +use self::test::{Bencher}; +use rand::{StdRng}; + + +#[bench] +fn bn_128_pairing(b: &mut Bencher) { + use bn::{pairing, G1, G2, Fr, Group}; + + let rng = &mut ::rand::thread_rng(); + + let sk0 = Fr::random(rng); + let sk1 = Fr::random(rng); + + let pk0 = G1::one() * sk0; + let pk1 = G2::one() * sk1; + + b.iter(|| { + let _ = pairing(pk0, pk1); + }); +} + +#[bench] +fn bn_128_mul(b: &mut Bencher) { + use bn::{AffineG1, G1, Fr, Group}; + + let mut rng = StdRng::new().unwrap(); + let p: G1 = G1::random(&mut rng); + let fr = Fr::random(&mut rng); + + b.iter(|| { + let _ = AffineG1::from_jacobian(p * fr); + }); +} + +#[bench] +fn sha256(b: &mut Bencher) { + use crypto::sha2::Sha256; + use crypto::digest::Digest; + + let mut input: [u8; 256] = [0; 256]; + let mut out = [0; 32]; + + b.iter(|| { + let mut sha = Sha256::new(); + sha.input(&input); + sha.result(&mut input[0..32]); + }); +} + +#[bench] +fn ecrecover(b: &mut Bencher) { + use rustc_serialize::hex::FromHex; + use ethkey::{Signature, recover as ec_recover}; + use util::H256; + let input = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap(); + let hash = H256::from_slice(&input[0..32]); + let v = H256::from_slice(&input[32..64]); + let r = H256::from_slice(&input[64..96]); + let s = H256::from_slice(&input[96..128]); + + let bit = match v[31] { + 27 | 28 if &v.0[..31] == &[0; 31] => v[31] - 27, + _ => { return; }, + }; + + let s = Signature::from_rsv(&r, &s, bit); + b.iter(|| { + let _ = ec_recover(&s, &hash); + }); +} + diff --git a/ethcore/evm/src/evm.rs b/ethcore/evm/src/evm.rs index c27caf8b3..c3fc7f905 100644 --- a/ethcore/evm/src/evm.rs +++ b/ethcore/evm/src/evm.rs @@ -69,6 +69,10 @@ pub enum Error { Internal(String), /// Wasm runtime error Wasm(String), + /// Out of bounds access in RETURNDATACOPY. + OutOfBounds, + /// Execution has been reverted with REVERT. + Reverted, } impl From> for Error { @@ -96,6 +100,8 @@ impl fmt::Display for Error { Internal(ref msg) => write!(f, "Internal error: {}", msg), MutableCallInStaticContext => write!(f, "Mutable call in static context"), Wasm(ref msg) => write!(f, "Internal error: {}", msg), + OutOfBounds => write!(f, "Out of bounds"), + Reverted => write!(f, "Reverted"), } } } @@ -179,7 +185,7 @@ impl Finalize for Result { fn finalize(self, ext: E) -> Result { match self { Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult { gas_left: gas_left, apply_state: true, return_data: ReturnData::empty() }), - Ok(GasLeft::NeedsReturn {gas_left, data, apply_state}) => ext.ret(&gas_left, &data).map(|gas_left| FinalizationResult { + Ok(GasLeft::NeedsReturn {gas_left, data, apply_state}) => ext.ret(&gas_left, &data, apply_state).map(|gas_left| FinalizationResult { gas_left: gas_left, apply_state: apply_state, return_data: data, diff --git a/ethcore/evm/src/ext.rs b/ethcore/evm/src/ext.rs index 1c3ddb317..477312481 100644 --- a/ethcore/evm/src/ext.rs +++ b/ethcore/evm/src/ext.rs @@ -29,7 +29,12 @@ pub enum ContractCreateResult { Created(Address, U256), /// Returned when contract creation failed. /// VM doesn't have to know the reason. - Failed + Failed, + /// Returned when contract creation failed. + /// VM doesn't have to know the reason. + FailedInStaticCall, + /// Reverted with REVERT. + Reverted(U256, ReturnData), } /// Result of externalities call function. @@ -39,7 +44,10 @@ pub enum MessageCallResult { Success(U256, ReturnData), /// Returned when message call failed. /// VM doesn't have to know the reason. - Failed + Failed, + /// Returned when message call was reverted. + /// Contains gas left and output data. + Reverted(U256, ReturnData), } /// Specifies how an address is calculated for a new contract. @@ -109,7 +117,7 @@ pub trait Ext { /// Should be called when transaction calls `RETURN` opcode. /// Returns gas_left if cost of returning the data is not too high. - fn ret(self, gas: &U256, data: &ReturnData) -> evm::Result; + fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> evm::Result; /// Should be called when contract commits suicide. /// Address to which funds should be refunded. @@ -138,4 +146,7 @@ pub trait Ext { /// Trace the finalised execution of a single instruction. fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {} + + /// Check if running in static context. + fn is_static(&self) -> bool; } diff --git a/ethcore/evm/src/interpreter/mod.rs b/ethcore/evm/src/interpreter/mod.rs index 633fe70d4..f413fa3fc 100644 --- a/ethcore/evm/src/interpreter/mod.rs +++ b/ethcore/evm/src/interpreter/mod.rs @@ -319,6 +319,9 @@ impl Interpreter { let contract_code = self.mem.read_slice(init_off, init_size); let can_create = ext.balance(¶ms.address)? >= endowment && ext.depth() < ext.schedule().max_depth; + // clear return data buffer before creating new call frame. + self.return_data = ReturnData::empty(); + if !can_create { stack.push(U256::zero()); return Ok(InstructionResult::UnusedGas(create_gas)); @@ -330,10 +333,18 @@ impl Interpreter { stack.push(address_to_u256(address)); Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))) }, + ContractCreateResult::Reverted(gas_left, return_data) => { + stack.push(U256::zero()); + self.return_data = return_data; + Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))) + }, ContractCreateResult::Failed => { stack.push(U256::zero()); Ok(InstructionResult::Ok) - } + }, + ContractCreateResult::FailedInStaticCall => { + Err(evm::Error::MutableCallInStaticContext) + }, }; }, instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => { @@ -344,8 +355,10 @@ impl Interpreter { let code_address = stack.pop_back(); let code_address = u256_to_address(&code_address); - let value = if instruction == instructions::DELEGATECALL || instruction == instructions::STATICCALL { + let value = if instruction == instructions::DELEGATECALL { None + } else if instruction == instructions::STATICCALL { + Some(U256::zero()) } else { Some(stack.pop_back()) }; @@ -364,6 +377,9 @@ impl Interpreter { // Get sender & receive addresses, check if we have balance let (sender_address, receive_address, has_balance, call_type) = match instruction { instructions::CALL => { + if ext.is_static() && value.map_or(false, |v| !v.is_zero()) { + return Err(evm::Error::MutableCallInStaticContext); + } let has_balance = ext.balance(¶ms.address)? >= value.expect("value set for all but delegate call; qed"); (¶ms.address, &code_address, has_balance, CallType::Call) }, @@ -372,10 +388,13 @@ impl Interpreter { (¶ms.address, ¶ms.address, has_balance, CallType::CallCode) }, instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true, CallType::DelegateCall), - instructions::STATICCALL => (¶ms.sender, ¶ms.address, true, CallType::StaticCall), + instructions::STATICCALL => (¶ms.address, &code_address, true, CallType::StaticCall), _ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction)) }; + // clear return data buffer before creating new call frame. + self.return_data = ReturnData::empty(); + let can_call = has_balance && ext.depth() < ext.schedule().max_depth; if !can_call { stack.push(U256::zero()); @@ -394,12 +413,17 @@ impl Interpreter { MessageCallResult::Success(gas_left, data) => { stack.push(U256::one()); self.return_data = data; - Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater then current one"))) + Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one"))) + }, + MessageCallResult::Reverted(gas_left, data) => { + stack.push(U256::zero()); + self.return_data = data; + Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one"))) }, MessageCallResult::Failed => { stack.push(U256::zero()); Ok(InstructionResult::Ok) - } + }, }; }, instructions::RETURN => { @@ -537,6 +561,14 @@ impl Interpreter { Self::copy_data_to_memory(&mut self.mem, stack, params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8])); }, instructions::RETURNDATACOPY => { + { + let source_offset = stack.peek(1); + let size = stack.peek(2); + let return_data_len = U256::from(self.return_data.len()); + if source_offset.overflow_add(*size).0 > return_data_len { + return Err(evm::Error::OutOfBounds); + } + } Self::copy_data_to_memory(&mut self.mem, stack, &*self.return_data); }, instructions::CODECOPY => { diff --git a/ethcore/evm/src/schedule.rs b/ethcore/evm/src/schedule.rs index e721dc803..6982c48ff 100644 --- a/ethcore/evm/src/schedule.rs +++ b/ethcore/evm/src/schedule.rs @@ -111,6 +111,8 @@ pub struct Schedule { pub have_return_data: bool, /// Kill basic accounts below this balance if touched. pub kill_dust: CleanDustMode, + /// Enable EIP-86 rules + pub eip86: bool, } /// Dust accounts cleanup mode. @@ -184,17 +186,17 @@ impl Schedule { blockhash_gas: 20, have_static_call: false, kill_dust: CleanDustMode::Off, + eip86: false, } } - /// Schedule for the Metropolis of the Ethereum main net. - pub fn new_metropolis() -> Schedule { + /// Schedule for the Byzantium fork of the Ethereum main net. + pub fn new_byzantium() -> Schedule { let mut schedule = Self::new_post_eip150(24576, true, true, true); schedule.have_create2 = true; schedule.have_revert = true; schedule.have_static_call = true; schedule.have_return_data = true; - schedule.blockhash_gas = 350; schedule } @@ -246,6 +248,7 @@ impl Schedule { blockhash_gas: 20, have_static_call: false, kill_dust: CleanDustMode::Off, + eip86: false, } } } diff --git a/ethcore/evm/src/wasm/runtime.rs b/ethcore/evm/src/wasm/runtime.rs index 7beb4c599..46841392b 100644 --- a/ethcore/evm/src/wasm/runtime.rs +++ b/ethcore/evm/src/wasm/runtime.rs @@ -163,7 +163,16 @@ impl<'a> Runtime<'a> { ext::ContractCreateResult::Failed => { trace!(target: "wasm", "runtime: create contract fail"); Ok(Some((-1i32).into())) - } + }, + ext::ContractCreateResult::Reverted(gas_left, _) => { + trace!(target: "wasm", "runtime: create contract reverted"); + self.gas_counter = self.gas_limit - gas_left.low_u64(); + Ok(Some((-1i32).into())) + }, + ext::ContractCreateResult::FailedInStaticCall => { + trace!(target: "wasm", "runtime: create contract called in static context"); + Err(interpreter::Error::Trap("CREATE in static context".to_owned())) + }, } } diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index 9587dd8ed..a2b9ab3ac 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -927,6 +927,7 @@ mod tests { fn check_receipts() { let receipts = (0..5).map(|_| Receipt { state_root: Some(H256::random()), + status_code: None, gas_used: 21_000u64.into(), log_bloom: Default::default(), logs: Vec::new(), diff --git a/ethcore/res/ethereum/metropolis_test.json b/ethcore/res/ethereum/byzantium_test.json similarity index 61% rename from ethcore/res/ethereum/metropolis_test.json rename to ethcore/res/ethereum/byzantium_test.json index 76189e731..2e97cb15e 100644 --- a/ethcore/res/ethereum/metropolis_test.json +++ b/ethcore/res/ethereum/byzantium_test.json @@ -1,5 +1,5 @@ { - "name": "Metropolis (Test)", + "name": "Byzantium (Test)", "engine": { "Ethash": { "params": { @@ -7,27 +7,31 @@ "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", - "blockReward": "0x4563918244F40000", - "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "homesteadTransition": "0x0", "eip150Transition": "0x0", "eip155Transition": "0x0", "eip160Transition": "0x0", "eip161abcTransition": "0x0", "eip161dTransition": "0x0", - "maxCodeSize": 24576 + "maxCodeSize": 24576, + "eip649Reward": "0x29A2241AF62C0000", + "blockReward": "0x4563918244F40000", + "eip100bTransition": "0x0", + "eip649Transition": "0x0" } } }, "params": { + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", - "eip98Transition": "0x0", - "eip86Transition": "0x0", + "eip98Transition": "0xffffffffffffffff", "eip140Transition": "0x0", - "eip210Transition": "0x0" + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0" }, "genesis": { "seal": { @@ -47,6 +51,10 @@ "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } } }, + "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x00", "pricing": { "linear": { "base": 500, "word": 0 } } } }, + "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x00", "pricing": { "linear": { "base": 40000, "word": 0 } } } }, + "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x00", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } } } } diff --git a/ethcore/res/ethereum/constantinople_test.json b/ethcore/res/ethereum/constantinople_test.json new file mode 100644 index 000000000..b9d927cf3 --- /dev/null +++ b/ethcore/res/ethereum/constantinople_test.json @@ -0,0 +1,61 @@ +{ + "name": "Byzantium (Test)", + "engine": { + "Ethash": { + "params": { + "gasLimitBoundDivisor": "0x0400", + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "homesteadTransition": "0x0", + "eip150Transition": "0x0", + "eip155Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "maxCodeSize": 24576, + "eip649Reward": "0x29A2241AF62C0000", + "blockReward": "0x4563918244F40000", + "eip100bTransition": "0x0", + "eip649Transition": "0x0" + } + } + }, + "params": { + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x1", + "eip98Transition": "0xffffffffffffffff", + "eip140Transition": "0x0", + "eip210Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x400000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x1388" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 100 } } } }, + "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x00", "pricing": { "linear": { "base": 500, "word": 0 } } } }, + "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x00", "pricing": { "linear": { "base": 2000, "word": 0 } } } }, + "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x00", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } } + } +} diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index a77b39934..3f9eb8d7c 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -190,10 +190,10 @@ "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 } } } }, - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x7fffffffffffff", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "builtin": { "name": "bn128_add", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 999999, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "builtin": { "name": "bn128_mul", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 999999, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "builtin": { "name": "bn128_pairing", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 999999, "word": 0 } } } }, + "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x7fffffffffffff", "pricing": { "modexp": { "divisor": 100 } } } }, + "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 500, "word": 0 } } } }, + "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 2000, "word": 0 } } } }, + "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x7fffffffffffff", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, "3282791d6fd713f1e94f4bfd565eaa78b3a0599d": { "balance": "1337000000000000000000" }, diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index ef191fdc6..9b722a014 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit ef191fdc61cf76cdb9cdc147465fb447304b0ed2 +Subproject commit 9b722a014a2b2c9ea6eac456fe01a5c3dd1042a8 diff --git a/ethcore/res/ethereum/transition_test.json b/ethcore/res/ethereum/transition_test.json index 7709ba015..67f3062b3 100644 --- a/ethcore/res/ethereum/transition_test.json +++ b/ethcore/res/ethereum/transition_test.json @@ -1,5 +1,5 @@ { - "name": "EIP150.1b hard-fork consensus test", + "name": "Transition consensus test spec template", "engine": { "Ethash": { "params": { @@ -9,132 +9,16 @@ "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", - "homesteadTransition": "0x5", - "daoHardforkTransition": "0x8", - "daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754", - "daoHardforkAccounts": [ - "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", - "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425", - "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", - "0xecd135fa4f61a655311e86238c92adcd779555d2", - "0x1975bd06d486162d5dc297798dfc41edd5d160a7", - "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6", - "0x319f70bab6845585f412ec7724b744fec6095c85", - "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936", - "0x5c8536898fbb74fc7445814902fd08422eac56d0", - "0x6966ab0d485353095148a2155858910e0965b6f9", - "0x779543a0491a837ca36ce8c635d6154e3c4911a6", - "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c", - "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", - "0x9c50426be05db97f5d64fc54bf89eff947f0a321", - "0x200450f06520bdd6c527622a273333384d870efb", - "0xbe8539bfe837b67d1282b2b1d61c3f723966f049", - "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", - "0xf1385fb24aad0cd7432824085e42aff90886fef5", - "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", - "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd", - "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", - "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7", - "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", - "0x24c4d950dfd4dd1902bbed3508144a54542bba94", - "0x9f27daea7aca0aa0446220b98d028715e3bc803d", - "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", - "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", - "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf", - "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", - "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", - "0x492ea3bb0f3315521c31f273e565b868fc090f17", - "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00", - "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", - "0xceaeb481747ca6c540a000c1f3641f8cef161fa7", - "0xcc34673c6c40e791051898567a1222daf90be287", - "0x579a80d909f346fbfb1189493f521d7f48d52238", - "0xe308bd1ac5fda103967359b2712dd89deffb7973", - "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c", - "0xac1ecab32727358dba8962a0f3b261731aad9723", - "0x4fd6ace747f06ece9c49699c7cabc62d02211f75", - "0x440c59b325d2997a134c2c7c60a8c61611212bad", - "0x4486a3d68fac6967006d7a517b889fd3f98c102b", - "0x9c15b54878ba618f494b38f0ae7443db6af648ba", - "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f", - "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", - "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50", - "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", - "0xb9637156d330c0d605a791f1c31ba5890582fe1c", - "0x6131c42fa982e56929107413a9d526fd99405560", - "0x1591fc0f688c81fbeb17f5426a162a7024d430c2", - "0x542a9515200d14b68e934e9830d91645a980dd7a", - "0xc4bbd073882dd2add2424cf47d35213405b01324", - "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", - "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb", - "0x3ba4d81db016dc2890c81f3acec2454bff5aada5", - "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab", - "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", - "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", - "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", - "0x97f43a37f595ab5dd318fb46e7a155eae057317a", - "0x52c5317c848ba20c7504cb2c8052abd1fde29d03", - "0x4863226780fe7c0356454236d3b1c8792785748d", - "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", - "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c", - "0x057b56736d32b86616a10f619859c6cd6f59092a", - "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192", - "0x304a554a310c7e546dfe434669c62820b7d83490", - "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", - "0x4deb0033bb26bc534b197e61d19e0733e5679784", - "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", - "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", - "0x4fa802324e929786dbda3b8820dc7834e9134a2a", - "0x9da397b9e80755301a3b32173283a91c0ef6c87e", - "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6", - "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", - "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f", - "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", - "0x12e626b0eebfe86a56d633b9864e389b45dcb260", - "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", - "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", - "0xd164b088bd9108b60d0ca3751da4bceb207b0782", - "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b", - "0x1cba23d343a983e9b5cfd19496b9a9701ada385f", - "0xa82f360a8d3455c5c41366975bde739c37bfeb8a", - "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", - "0x005f5cee7a43331d5a3d3eec71305925a62f34b6", - "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", - "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e", - "0xbc07118b9ac290e4622f5e77a0853539789effbe", - "0x47e7aa56d6bdf3f36be34619660de61275420af8", - "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", - "0xadf80daec7ba8dcf15392f1ac611fff65d94f880", - "0x5524c55fb03cf21f549444ccbecb664d0acad706", - "0x40b803a9abce16f50f36a77ba41180eb90023925", - "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", - "0x17802f43a0137c506ba92291391a8a8f207f487d", - "0x253488078a4edf4d6f42f113d1e62836a942cf1a", - "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915", - "0xb136707642a4ea12fb4bae820f03d2562ebff487", - "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", - "0xf14c14075d6c4ed84b86798af0956deef67365b5", - "0xca544e5c4687d109611d0f8f928b53a25af72448", - "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", - "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", - "0x6d87578288b6cb5549d5076a207456a1f6a63dc0", - "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", - "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", - "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", - "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", - "0xd343b217de44030afaa275f54d31a9317c7f441e", - "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", - "0xda2fef9e4a3230988ff17df2165440f37e8b1708", - "0xf4c64518ea10f995918a454158c6b61407ea345c", - "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", - "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", - "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" - ], - "eip150Transition": "0xa", - "eip155Transition": "0x7fffffffffffffff", - "eip160Transition": "0x7fffffffffffffff", - "eip161abcTransition": "0x7fffffffffffffff", - "eip161dTransition": "0x7fffffffffffffff" + "eip155Transition": "5", + "homesteadTransition": "0", + "eip150Transition": "0", + "eip160Transition": "0", + "eip161abcTransition": "0", + "eip161dTransition": "0", + "maxCodeSize": 24576, + "eip649Reward": "0x29A2241AF62C0000", + "eip100bTransition": "5", + "eip649Transition": "5" } } }, @@ -143,8 +27,12 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", - "eip98Transition": "0x7fffffffffffff", - "eip86Transition": "0x7fffffffffffff" + "eip98Transition": "0xffffffffffffffff", + "eip140Transition": "5", + "eip211Transition": "5", + "eip214Transition": "5", + "eip155Transition": "5", + "eip658Transition": "5" }, "genesis": { "seal": { @@ -161,9 +49,13 @@ "gasLimit": "0x1388" }, "accounts": { - "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 } } } } + "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "5", "pricing": { "modexp": { "divisor": 100 } } } }, + "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "5", "pricing": { "linear": { "base": 500, "word": 0 } } } }, + "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "5", "pricing": { "linear": { "base": 2000, "word": 0 } } } }, + "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "5", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } } } } diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index b7d7dd188..69b6b7255 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -2039,6 +2039,7 @@ mod tests { let bc = new_chain(&genesis, db.clone()); insert_block(&db, &bc, &b1, vec![Receipt { state_root: Some(H256::default()), + status_code: None, gas_used: 10_000.into(), log_bloom: Default::default(), logs: vec![ @@ -2048,6 +2049,7 @@ mod tests { }, Receipt { state_root: Some(H256::default()), + status_code: None, gas_used: 10_000.into(), log_bloom: Default::default(), logs: vec![ @@ -2057,6 +2059,7 @@ mod tests { insert_block(&db, &bc, &b2, vec![ Receipt { state_root: Some(H256::default()), + status_code: None, gas_used: 10_000.into(), log_bloom: Default::default(), logs: vec![ diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index d5dc459b2..be73c913b 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -61,7 +61,7 @@ struct Linear { } /// A special pricing model for modular exponentiation. -struct Modexp { +struct ModexpPricer { divisor: usize, } @@ -71,7 +71,20 @@ impl Pricer for Linear { } } -impl Pricer for Modexp { +/// A alt_bn128_parinig pricing model. This computes a price using a base cost and a cost per pair. +struct AltBn128PairingPricer { + base: usize, + pair: usize, +} + +impl Pricer for AltBn128PairingPricer { + fn cost(&self, input: &[u8]) -> U256 { + let cost = U256::from(self.base) + U256::from(self.pair) * U256::from(input.len() / 192); + cost + } +} + +impl Pricer for ModexpPricer { fn cost(&self, input: &[u8]) -> U256 { let mut reader = input.chain(io::repeat(0)); let mut buf = [0; 32]; @@ -85,17 +98,49 @@ impl Pricer for Modexp { let exp_len = read_len(); let mod_len = read_len(); - // floor(max(length_of_MODULUS, length_of_BASE) ** 2 * max(length_of_EXPONENT, 1) / GQUADDIVISOR) - // TODO: is saturating the best behavior here? + let max_len = U256::from(u32::max_value() / 2); + if base_len > max_len || mod_len > max_len { + return U256::max_value(); + } + + let base_len = base_len.low_u64(); + let exp_len = exp_len.low_u64(); + let mod_len = mod_len.low_u64(); let m = max(mod_len, base_len); - match m.overflowing_mul(m) { - (_, true) => U256::max_value(), - (val, _) => { - match val.overflowing_mul(max(exp_len, U256::one())) { - (_, true) => U256::max_value(), - (val, _) => val / (self.divisor as u64).into() - } - } + if m == 0 { + return U256::zero(); + } + // read fist 32-byte word of the exponent. + let exp_low = if base_len + 96 >= input.len() as u64 { U256::zero() } else { + let mut buf = [0; 32]; + let mut reader = input[(96 + base_len as usize)..].chain(io::repeat(0)); + let len = min(exp_len, 32) as usize; + reader.read_exact(&mut buf[(32 - len)..]).expect("reading from zero-extended memory cannot fail; qed"); + U256::from(H256::from_slice(&buf[..])) + }; + + let adjusted_exp_len = Self::adjusted_exp_len(exp_len, exp_low); + + (Self::mult_complexity(m) * max(adjusted_exp_len, 1) / self.divisor as u64).into() + } +} + +impl ModexpPricer { + fn adjusted_exp_len(len: u64, exp_low: U256) -> u64 { + let bit_index = if exp_low.is_zero() { 0 } else { (255 - exp_low.leading_zeros()) as u64 }; + if len <= 32 { + bit_index + } + else { + 8 * (len - 32) + bit_index + } + } + + fn mult_complexity(x: u64) -> u64 { + match x { + x if x <= 64 => x * x, + x if x <= 1024 => (x * x) / 4 + 96 * x - 3072, + x => (x * x) / 16 + 480 * x - 199680, } } } @@ -135,7 +180,7 @@ impl From for Builtin { }) } ethjson::spec::Pricing::Modexp(exp) => { - Box::new(Modexp { + Box::new(ModexpPricer { divisor: if exp.divisor == 0 { warn!("Zero modexp divisor specified. Falling back to default."); 10 @@ -144,6 +189,12 @@ impl From for Builtin { } }) } + ethjson::spec::Pricing::AltBn128Pairing(pricer) => { + Box::new(AltBn128PairingPricer { + base: pricer.base, + pair: pricer.pair, + }) + } }; Builtin { @@ -162,9 +213,9 @@ fn ethereum_builtin(name: &str) -> Box { "sha256" => Box::new(Sha256) as Box, "ripemd160" => Box::new(Ripemd160) as Box, "modexp" => Box::new(ModexpImpl) as Box, - "bn128_add" => Box::new(Bn128AddImpl) as Box, - "bn128_mul" => Box::new(Bn128MulImpl) as Box, - "bn128_pairing" => Box::new(Bn128PairingImpl) as Box, + "alt_bn128_add" => Box::new(Bn128AddImpl) as Box, + "alt_bn128_mul" => Box::new(Bn128MulImpl) as Box, + "alt_bn128_pairing" => Box::new(Bn128PairingImpl) as Box, _ => panic!("invalid builtin name: {}", name), } } @@ -266,6 +317,38 @@ impl Impl for Ripemd160 { } } +// calculate modexp: exponentiation by squaring. the `num` crate has pow, but not modular. +fn modexp(mut base: BigUint, mut exp: BigUint, modulus: BigUint) -> BigUint { + use num::Integer; + + if modulus <= BigUint::one() { // n^m % 0 || n^m % 1 + return BigUint::zero(); + } + + if exp.is_zero() { // n^0 % m + return BigUint::one(); + } + + if base.is_zero() { // 0^n % m, n>0 + return BigUint::zero(); + } + + let mut result = BigUint::one(); + base = base % &modulus; + + // fast path for base divisible by modulus. + if base.is_zero() { return BigUint::zero() } + while !exp.is_zero() { + if exp.is_odd() { + result = (result * &base) % &modulus; + } + + exp = exp >> 1; + base = (base.clone() * base) % &modulus; + } + result +} + impl Impl for ModexpImpl { fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { let mut reader = input.chain(io::repeat(0)); @@ -283,47 +366,25 @@ impl Impl for ModexpImpl { let exp_len = read_len(&mut reader); let mod_len = read_len(&mut reader); - // read the numbers themselves. - let mut buf = vec![0; max(mod_len, max(base_len, exp_len))]; - let mut read_num = |len| { - reader.read_exact(&mut buf[..len]).expect("reading from zero-extended memory cannot fail; qed"); - BigUint::from_bytes_be(&buf[..len]) + // Gas formula allows arbitrary large exp_len when base and modulus are empty, so we need to handle empty base first. + let r = if base_len == 0 && mod_len == 0 { + BigUint::zero() + } else { + // read the numbers themselves. + let mut buf = vec![0; max(mod_len, max(base_len, exp_len))]; + let mut read_num = |len| { + reader.read_exact(&mut buf[..len]).expect("reading from zero-extended memory cannot fail; qed"); + BigUint::from_bytes_be(&buf[..len]) + }; + + let base = read_num(base_len); + let exp = read_num(exp_len); + let modulus = read_num(mod_len); + modexp(base, exp, modulus) }; - let base = read_num(base_len); - let exp = read_num(exp_len); - let modulus = read_num(mod_len); - - // calculate modexp: exponentiation by squaring. the `num` crate has pow, but not modular. - fn modexp(mut base: BigUint, mut exp: BigUint, modulus: BigUint) -> BigUint { - use num::Integer; - - match (base.is_zero(), exp.is_zero()) { - (_, true) => return BigUint::one(), // n^0 % m - (true, false) => return BigUint::zero(), // 0^n % m, n>0 - (false, false) if modulus <= BigUint::one() => return BigUint::zero(), // a^b % 1 = 0. - _ => {} - } - - let mut result = BigUint::one(); - base = base % &modulus; - - // fast path for base divisible by modulus. - if base.is_zero() { return result } - while !exp.is_zero() { - if exp.is_odd() { - result = (result * &base) % &modulus; - } - - exp = exp >> 1; - base = (base.clone() * base) % &modulus; - } - - result - } - // write output to given memory, left padded and same length as the modulus. - let bytes = modexp(base, exp, modulus).to_bytes_be(); + let bytes = r.to_bytes_be(); // always true except in the case of zero-length modulus, which leads to // output of length and value 1. @@ -352,8 +413,8 @@ fn read_point(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<::bn::G1, Err let px = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point x coordinate"))?; reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); - let py = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point x coordinate"))?; + let py = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point y coordinate"))?; Ok( if px == Fq::zero() && py == Fq::zero() { G1::zero() @@ -404,50 +465,29 @@ impl Impl for Bn128MulImpl { } } -mod bn128_gen { - use bn::{AffineG1, AffineG2, Fq, Fq2, G1, G2, Gt, pairing}; - - lazy_static! { - pub static ref P1: G1 = G1::from(AffineG1::new( - Fq::from_str("1").expect("1 is a valid field element"), - Fq::from_str("2").expect("2 is a valid field element"), - ).expect("Generator P1(1, 2) is a valid curve point")); - } - - lazy_static! { - pub static ref P2: G2 = G2::from(AffineG2::new( - Fq2::new( - Fq::from_str("10857046999023057135944570762232829481370756359578518086990519993285655852781") - .expect("a valid field element"), - Fq::from_str("11559732032986387107991004021392285783925812861821192530917403151452391805634") - .expect("a valid field element"), - ), - Fq2::new( - Fq::from_str("8495653923123431417604973247489272438418190587263600148770280649306958101930") - .expect("a valid field element"), - Fq::from_str("4082367875863433681332203403145435568316851327593401208105741076214120093531") - .expect("a valid field element"), - ), - ).expect("the generator P2(10857046999023057135944570762232829481370756359578518086990519993285655852781 + 11559732032986387107991004021392285783925812861821192530917403151452391805634i, 8495653923123431417604973247489272438418190587263600148770280649306958101930 + 4082367875863433681332203403145435568316851327593401208105741076214120093531i) is a valid curve point")); - } - - lazy_static! { - pub static ref P1_P2_PAIRING: Gt = pairing(P1.clone(), P2.clone()); - } -} - impl Impl for Bn128PairingImpl { /// Can fail if: /// - input length is not a multiple of 192 /// - any of odd points does not belong to bn128 curve /// - any of even points does not belong to the twisted bn128 curve over the field F_p^2 = F_p[i] / (i^2 + 1) fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - use bn::{AffineG1, AffineG2, Fq, Fq2, pairing, G1, G2, Gt}; - - let elements = input.len() / 192; // (a, b_a, b_b - each 64-byte affine coordinates) if input.len() % 192 != 0 { return Err("Invalid input length, must be multiple of 192 (3 * (32*2))".into()) } + + if let Err(err) = self.execute_with_error(input, output) { + trace!("Pairining error: {:?}", err); + return Err(err) + } + Ok(()) + } +} + +impl Bn128PairingImpl { + fn execute_with_error(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { + use bn::{AffineG1, AffineG2, Fq, Fq2, pairing, G1, G2, Gt, Group}; + + let elements = input.len() / 192; // (a, b_a, b_b - each 64-byte affine coordinates) let ret_val = if input.len() == 0 { U256::one() } else { @@ -459,34 +499,36 @@ impl Impl for Bn128PairingImpl { let a_y = Fq::from_slice(&input[idx*192+32..idx*192+64]) .map_err(|_| Error::from("Invalid a argument y coordinate"))?; - let b_b_x = Fq::from_slice(&input[idx*192+64..idx*192+96]) + let b_a_y = Fq::from_slice(&input[idx*192+64..idx*192+96]) .map_err(|_| Error::from("Invalid b argument imaginary coeff x coordinate"))?; - let b_b_y = Fq::from_slice(&input[idx*192+96..idx*192+128]) + let b_a_x = Fq::from_slice(&input[idx*192+96..idx*192+128]) .map_err(|_| Error::from("Invalid b argument imaginary coeff y coordinate"))?; - let b_a_x = Fq::from_slice(&input[idx*192+128..idx*192+160]) + let b_b_y = Fq::from_slice(&input[idx*192+128..idx*192+160]) .map_err(|_| Error::from("Invalid b argument real coeff x coordinate"))?; - let b_a_y = Fq::from_slice(&input[idx*192+160..idx*192+192]) + let b_b_x = Fq::from_slice(&input[idx*192+160..idx*192+192]) .map_err(|_| Error::from("Invalid b argument real coeff y coordinate"))?; - vals.push(( - G1::from( - AffineG1::new(a_x, a_y).map_err(|_| Error::from("Invalid a argument - not on curve"))? - ), - G2::from( - AffineG2::new( - Fq2::new(b_a_x, b_a_y), - Fq2::new(b_b_x, b_b_y), - ).map_err(|_| Error::from("Invalid b argument - not on curve"))? - ), - )); + let b_a = Fq2::new(b_a_x, b_a_y); + let b_b = Fq2::new(b_b_x, b_b_y); + let b = if b_a.is_zero() && b_b.is_zero() { + G2::zero() + } else { + G2::from(AffineG2::new(b_a, b_b).map_err(|_| Error::from("Invalid b argument - not on curve"))?) + }; + let a = if a_x.is_zero() && a_y.is_zero() { + G1::zero() + } else { + G1::from(AffineG1::new(a_x, a_y).map_err(|_| Error::from("Invalid a argument - not on curve"))?) + }; + vals.push((a, b)); }; let mul = vals.into_iter().fold(Gt::one(), |s, (a, b)| s * pairing(a, b)); - if mul == *bn128_gen::P1_P2_PAIRING { + if mul == Gt::one() { U256::one() } else { U256::zero() @@ -503,10 +545,44 @@ impl Impl for Bn128PairingImpl { #[cfg(test)] mod tests { - use super::{Builtin, Linear, ethereum_builtin, Pricer, Modexp}; + use super::{Builtin, Linear, ethereum_builtin, Pricer, ModexpPricer, modexp as me}; use ethjson; use util::{U256, BytesRef}; use rustc_hex::FromHex; + use num::{BigUint, Zero, One}; + + #[test] + fn modexp_func() { + // n^0 % m == 1 + let mut base = BigUint::parse_bytes(b"12345", 10).unwrap(); + let mut exp = BigUint::zero(); + let mut modulus = BigUint::parse_bytes(b"789", 10).unwrap(); + assert_eq!(me(base, exp, modulus), BigUint::one()); + + // 0^n % m == 0 + base = BigUint::zero(); + exp = BigUint::parse_bytes(b"12345", 10).unwrap(); + modulus = BigUint::parse_bytes(b"789", 10).unwrap(); + assert_eq!(me(base, exp, modulus), BigUint::zero()); + + // n^m % 1 == 0 + base = BigUint::parse_bytes(b"12345", 10).unwrap(); + exp = BigUint::parse_bytes(b"789", 10).unwrap(); + modulus = BigUint::one(); + assert_eq!(me(base, exp, modulus), BigUint::zero()); + + // if n % d == 0, then n^m % d == 0 + base = BigUint::parse_bytes(b"12345", 10).unwrap(); + exp = BigUint::parse_bytes(b"789", 10).unwrap(); + modulus = BigUint::parse_bytes(b"15", 10).unwrap(); + assert_eq!(me(base, exp, modulus), BigUint::zero()); + + // others + base = BigUint::parse_bytes(b"12345", 10).unwrap(); + exp = BigUint::parse_bytes(b"789", 10).unwrap(); + modulus = BigUint::parse_bytes(b"97", 10).unwrap(); + assert_eq!(me(base, exp, modulus), BigUint::parse_bytes(b"55", 10).unwrap()); + } #[test] fn identity() { @@ -632,7 +708,7 @@ mod tests { fn modexp() { let f = Builtin { - pricer: Box::new(Modexp { divisor: 20 }), + pricer: Box::new(ModexpPricer { divisor: 20 }), native: ethereum_builtin("modexp"), activate_at: 0, }; @@ -649,7 +725,7 @@ mod tests { let mut output = vec![0u8; 32]; let expected = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000001").unwrap(); - let expected_cost = 1638; + let expected_cost = 13056; f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); assert_eq!(output, expected); @@ -660,15 +736,15 @@ mod tests { { let input = FromHex::from_hex("\ 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 0000000000000000000000000000000000000000000000000000000000000020\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" + 0000000000000000000000000000000000000000000000000000000000000020\ + 0000000000000000000000000000000000000000000000000000000000000020\ + fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e\ + fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" ).unwrap(); let mut output = vec![0u8; 32]; let expected = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); - let expected_cost = 1638; + let expected_cost = 13056; f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); assert_eq!(output, expected); @@ -688,7 +764,7 @@ mod tests { let mut output = vec![0u8; 32]; let expected = FromHex::from_hex("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab").unwrap(); - let expected_cost = 102; + let expected_cost = 768; f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); assert_eq!(output, expected); @@ -719,7 +795,7 @@ mod tests { let f = Builtin { pricer: Box::new(Linear { base: 0, word: 0 }), - native: ethereum_builtin("bn128_add"), + native: ethereum_builtin("alt_bn128_add"), activate_at: 0, }; @@ -780,7 +856,7 @@ mod tests { let f = Builtin { pricer: Box::new(Linear { base: 0, word: 0 }), - native: ethereum_builtin("bn128_mul"), + native: ethereum_builtin("alt_bn128_mul"), activate_at: 0, }; @@ -820,7 +896,7 @@ mod tests { fn builtin_pairing() -> Builtin { Builtin { pricer: Box::new(Linear { base: 0, word: 0 }), - native: ethereum_builtin("bn128_pairing"), + native: ethereum_builtin("alt_bn128_pairing"), activate_at: 0, } } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index b0ffe8bef..a738fbfa2 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1120,6 +1120,41 @@ impl Client { BlockId::Latest | BlockId::Pending => Some(self.chain.read().best_block_number()), } } + + fn do_virtual_call(&self, env_info: &EnvInfo, state: &mut State, t: &SignedTransaction, analytics: CallAnalytics) -> Result { + fn call( + state: &mut State, + env_info: &EnvInfo, + engine: &E, + state_diff: bool, + transaction: &SignedTransaction, + options: TransactOptions, + ) -> Result where + E: Engine + ?Sized, + T: trace::Tracer, + V: trace::VMTracer, + { + let options = options.dont_check_nonce(); + let original_state = if state_diff { Some(state.clone()) } else { None }; + + let mut ret = Executive::new(state, env_info, engine).transact_virtual(transaction, options)?; + + if let Some(original) = original_state { + ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?); + } + Ok(ret) + } + + let state_diff = analytics.state_diffing; + let engine = &*self.engine; + + match (analytics.transaction_tracing, analytics.vm_tracing) { + (true, true) => call(state, env_info, engine, state_diff, t, TransactOptions::with_tracing_and_vm_tracing()), + (true, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_tracing()), + (false, true) => call(state, env_info, engine, state_diff, t, TransactOptions::with_vm_tracing()), + (false, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_no_tracing()), + } + } } impl snapshot::DatabaseRestore for Client { @@ -1150,19 +1185,8 @@ impl BlockChainClient for Client { // that's just a copy of the state. let mut state = self.state_at(block).ok_or(CallError::StatePruned)?; - let original_state = if analytics.state_diffing { Some(state.clone()) } else { None }; - let options = TransactOptions::new(analytics.transaction_tracing, analytics.vm_tracing) - .dont_check_nonce() - .save_output_from_contract(); - let mut ret = Executive::new(&mut state, &env_info, &*self.engine).transact_virtual(t, options)?; - - // TODO gav move this into Executive. - if let Some(original) = original_state { - ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?); - } - - Ok(ret) + self.do_virtual_call(&env_info, &mut state, t, analytics) } fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result { @@ -1177,7 +1201,7 @@ impl BlockChainClient for Client { // that's just a copy of the state. let original_state = self.state_at(block).ok_or(CallError::StatePruned)?; let sender = t.sender(); - let options = TransactOptions::with_tracing().dont_check_nonce(); + let options = || TransactOptions::with_tracing(); let cond = |gas| { let mut tx = t.as_unsigned().clone(); @@ -1186,7 +1210,7 @@ impl BlockChainClient for Client { let mut state = original_state.clone(); Ok(Executive::new(&mut state, &env_info, &*self.engine) - .transact_virtual(&tx, options.clone()) + .transact_virtual(&tx, options()) .map(|r| r.exception.is_none()) .unwrap_or(false)) }; @@ -1242,24 +1266,17 @@ impl BlockChainClient for Client { return Err(CallError::TransactionNotFound); } - let options = TransactOptions::new(analytics.transaction_tracing, analytics.vm_tracing) - .dont_check_nonce() - .save_output_from_contract(); const PROOF: &'static str = "Transactions fetched from blockchain; blockchain transactions are valid; qed"; let rest = txs.split_off(address.index); for t in txs { let t = SignedTransaction::new(t).expect(PROOF); - let x = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, Default::default())?; + let x = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, TransactOptions::with_no_tracing())?; env_info.gas_used = env_info.gas_used + x.gas_used; } let first = rest.into_iter().next().expect("We split off < `address.index`; Length is checked earlier; qed"); let t = SignedTransaction::new(first).expect(PROOF); - let original_state = if analytics.state_diffing { Some(state.clone()) } else { None }; - let mut ret = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, options)?; - if let Some(original) = original_state { - ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?) - } - Ok(ret) + + self.do_virtual_call(&env_info, &mut state, &t, analytics) } fn mode(&self) -> IpcMode { @@ -2047,11 +2064,13 @@ mod tests { }]; let receipts = vec![Receipt { state_root: state_root, + status_code: None, gas_used: 5.into(), log_bloom: Default::default(), logs: vec![logs[0].clone()], }, Receipt { state_root: state_root, + status_code: None, gas_used: gas_used, log_bloom: Default::default(), logs: logs.clone(), diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/client/evm_test_client.rs index 5f23f449d..1bb5b1c12 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/client/evm_test_client.rs @@ -18,11 +18,11 @@ use std::fmt; use std::sync::Arc; -use util::{self, U256, journaldb, trie}; +use util::{self, U256, H256, journaldb, trie}; use util::kvdb::{self, KeyValueDB}; -use {state, state_db, client, executive, trace, db, spec}; +use {state, state_db, client, executive, trace, transaction, db, spec, pod_state}; use factory::Factories; -use evm::{self, VMType}; +use evm::{self, VMType, FinalizationResult}; use evm::action_params::ActionParams; /// EVM test Error. @@ -33,9 +33,17 @@ pub enum EvmTestError { /// EVM error. Evm(evm::Error), /// Initialization error. - Initialization(::error::Error), + ClientError(::error::Error), /// Low-level database error. Database(String), + /// Post-condition failure, + PostCondition(String), +} + +impl> From for EvmTestError { + fn from(err: E) -> Self { + EvmTestError::ClientError(err.into()) + } } impl fmt::Display for EvmTestError { @@ -45,52 +53,118 @@ impl fmt::Display for EvmTestError { match *self { Trie(ref err) => write!(fmt, "Trie: {}", err), Evm(ref err) => write!(fmt, "EVM: {}", err), - Initialization(ref err) => write!(fmt, "Initialization: {}", err), + ClientError(ref err) => write!(fmt, "{}", err), Database(ref err) => write!(fmt, "DB: {}", err), + PostCondition(ref err) => write!(fmt, "{}", err), } } } -/// Simplified, single-block EVM test client. -pub struct EvmTestClient { - state_db: state_db::StateDB, - factories: Factories, - spec: spec::Spec, +use ethereum; +use ethjson::state::test::ForkSpec; + +lazy_static! { + pub static ref FRONTIER: spec::Spec = ethereum::new_frontier_test(); + pub static ref HOMESTEAD: spec::Spec = ethereum::new_homestead_test(); + pub static ref EIP150: spec::Spec = ethereum::new_eip150_test(); + pub static ref EIP161: spec::Spec = ethereum::new_eip161_test(); + pub static ref BYZANTIUM: spec::Spec = ethereum::new_byzantium_test(); + pub static ref BYZANTIUM_TRANSITION: spec::Spec = ethereum::new_transition_test(); } -impl EvmTestClient { - /// Creates new EVM test client with in-memory DB initialized with genesis of given Spec. - pub fn new(spec: spec::Spec) -> Result { - let factories = Factories { - vm: evm::Factory::new(VMType::Interpreter, 5 * 1024), - trie: trie::TrieFactory::new(trie::TrieSpec::Secure), - accountdb: Default::default(), - }; - let db = Arc::new(kvdb::in_memory(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); - let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); - let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); - state_db = spec.ensure_db_good(state_db, &factories).map_err(EvmTestError::Initialization)?; - // Write DB - { - let mut batch = kvdb::DBTransaction::new(); - state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash()).map_err(|e| EvmTestError::Initialization(e.into()))?; - db.write(batch).map_err(EvmTestError::Database)?; +/// Simplified, single-block EVM test client. +pub struct EvmTestClient<'a> { + state: state::State, + spec: &'a spec::Spec, +} + +impl<'a> EvmTestClient<'a> { + /// Converts a json spec definition into spec. + pub fn spec_from_json(spec: &ForkSpec) -> Option<&'static spec::Spec> { + match *spec { + ForkSpec::Frontier => Some(&*FRONTIER), + ForkSpec::Homestead => Some(&*HOMESTEAD), + ForkSpec::EIP150 => Some(&*EIP150), + ForkSpec::EIP158 => Some(&*EIP161), + ForkSpec::Byzantium => Some(&*BYZANTIUM), + ForkSpec::EIP158ToByzantiumAt5 => Some(&BYZANTIUM_TRANSITION), + ForkSpec::FrontierToHomesteadAt5 | ForkSpec::HomesteadToDaoAt5 | ForkSpec::HomesteadToEIP150At5 => None, + _ => None, } + } + + /// Creates new EVM test client with in-memory DB initialized with genesis of given Spec. + pub fn new(spec: &'a spec::Spec) -> Result { + let factories = Self::factories(); + let state = Self::state_from_spec(spec, &factories)?; Ok(EvmTestClient { - state_db, - factories, + state, spec, }) } - /// Call given contract. + /// Creates new EVM test client with in-memory DB initialized with given PodState. + pub fn from_pod_state(spec: &'a spec::Spec, pod_state: pod_state::PodState) -> Result { + let factories = Self::factories(); + let state = Self::state_from_pod(spec, &factories, pod_state)?; + + Ok(EvmTestClient { + state, + spec, + }) + } + + fn factories() -> Factories { + Factories { + vm: evm::Factory::new(VMType::Interpreter, 5 * 1024), + trie: trie::TrieFactory::new(trie::TrieSpec::Secure), + accountdb: Default::default(), + } + } + + fn state_from_spec(spec: &'a spec::Spec, factories: &Factories) -> Result, EvmTestError> { + let db = Arc::new(kvdb::in_memory(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); + let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); + let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); + state_db = spec.ensure_db_good(state_db, factories)?; + + let genesis = spec.genesis_header(); + // Write DB + { + let mut batch = kvdb::DBTransaction::new(); + state_db.journal_under(&mut batch, 0, &genesis.hash())?; + db.write(batch).map_err(EvmTestError::Database)?; + } + + state::State::from_existing( + state_db, + *genesis.state_root(), + spec.engine.account_start_nonce(0), + factories.clone() + ).map_err(EvmTestError::Trie) + } + + fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: pod_state::PodState) -> Result, EvmTestError> { + let db = Arc::new(kvdb::in_memory(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); + let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); + let state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); + let mut state = state::State::new( + state_db, + spec.engine.account_start_nonce(0), + factories.clone(), + ); + state.populate_from(pod_state); + state.commit()?; + Ok(state) + } + + /// Execute the VM given ActionParams and tracer. + /// Returns amount of gas left and the output. pub fn call(&mut self, params: ActionParams, vm_tracer: &mut T) - -> Result<(U256, Vec), EvmTestError> + -> Result { let genesis = self.spec.genesis_header(); - let mut state = state::State::from_existing(self.state_db.boxed_clone(), *genesis.state_root(), self.spec.engine.account_start_nonce(0), self.factories.clone()) - .map_err(EvmTestError::Trie)?; let info = client::EnvInfo { number: genesis.number(), author: *genesis.author(), @@ -103,15 +177,71 @@ impl EvmTestClient { let mut substate = state::Substate::new(); let mut tracer = trace::NoopTracer; let mut output = vec![]; - let mut executive = executive::Executive::new(&mut state, &info, &*self.spec.engine); - let (gas_left, _) = executive.call( + let mut executive = executive::Executive::new(&mut self.state, &info, &*self.spec.engine); + executive.call( params, &mut substate, util::BytesRef::Flexible(&mut output), &mut tracer, vm_tracer, - ).map_err(EvmTestError::Evm)?; + ).map_err(EvmTestError::Evm) + } - Ok((gas_left, output)) + /// Executes a SignedTransaction within context of the provided state and `EnvInfo`. + /// Returns the state root, gas left and the output. + pub fn transact( + &mut self, + env_info: &client::EnvInfo, + transaction: transaction::SignedTransaction, + vm_tracer: T, + ) -> TransactResult { + let initial_gas = transaction.gas; + // Verify transaction + let is_ok = transaction.verify_basic(true, None, env_info.number >= self.spec.engine.params().eip86_transition); + if let Err(error) = is_ok { + return TransactResult::Err { + state_root: *self.state.root(), + error, + }; + } + + // Apply transaction + let tracer = trace::NoopTracer; + let result = self.state.apply_with_tracing(&env_info, &*self.spec.engine, &transaction, tracer, vm_tracer); + + match result { + Ok(result) => { + self.state.commit().ok(); + TransactResult::Ok { + state_root: *self.state.root(), + gas_left: initial_gas - result.receipt.gas_used, + output: result.output + } + }, + Err(error) => TransactResult::Err { + state_root: *self.state.root(), + error, + }, + } } } + +/// A result of applying transaction to the state. +pub enum TransactResult { + /// Successful execution + Ok { + /// State root + state_root: H256, + /// Amount of gas left + gas_left: U256, + /// Output + output: Vec, + }, + /// Transaction failed to run + Err { + /// State root + state_root: H256, + /// Execution error + error: ::error::Error, + }, +} diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index b2d1ce991..2591187a1 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -27,7 +27,7 @@ mod client; pub use self::client::*; pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType}; pub use self::error::Error; -pub use self::evm_test_client::{EvmTestClient, EvmTestError}; +pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactResult}; pub use self::test_client::{TestBlockChainClient, EachBlockWith}; pub use self::chain_notify::ChainNotify; pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient}; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index b0d2301ac..9b5bd754f 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -595,6 +595,7 @@ impl BlockChainClient for TestBlockChainClient { if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") { let receipt = BlockReceipts::new(vec![Receipt::new( Some(H256::zero()), + None, U256::zero(), vec![])]); let mut rlp = RlpStream::new(); diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index c5fa4818e..412766200 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -57,8 +57,8 @@ use ethkey::Signature; use util::*; /// Default EIP-210 contrat code. -/// As defined in https://github.com/ethereum/EIPs/pull/210/commits/9df24a3714af42e3bf350265bdc75b486c909d7f#diff-e02a92c2fb96c1a1bfb05e4c6e2ef5daR49 -pub const DEFAULT_BLOCKHASH_CONTRACT: &'static str = "73fffffffffffffffffffffffffffffffffffffffe33141561007a57600143036020526000356101006020510755600061010060205107141561005057600035610100610100602051050761010001555b6000620100006020510714156100755760003561010062010000602051050761020001555b610161565b436000351215801561008c5780610095565b623567e0600035125b9050156100a757600060605260206060f35b610100600035430312156100ca57610100600035075460805260206080f3610160565b62010000600035430312156100e857600061010060003507146100eb565b60005b1561010d576101006101006000350507610100015460a052602060a0f361015f565b63010000006000354303121561012d576000620100006000350714610130565b60005b1561015357610100620100006000350507610200015460c052602060c0f361015e565b600060e052602060e0f35b5b5b5b5b"; +/// As defined in https://github.com/ethereum/EIPs/pull/210 +pub const DEFAULT_BLOCKHASH_CONTRACT: &'static str = "73fffffffffffffffffffffffffffffffffffffffe33141561006a5760014303600035610100820755610100810715156100455760003561010061010083050761010001555b6201000081071515610064576000356101006201000083050761020001555b5061013e565b4360003512151561008457600060405260206040f361013d565b61010060003543031315156100a857610100600035075460605260206060f361013c565b6101006000350715156100c55762010000600035430313156100c8565b60005b156100ea576101006101006000350507610100015460805260206080f361013b565b620100006000350715156101095763010000006000354303131561010c565b60005b1561012f57610100620100006000350507610200015460a052602060a0f361013a565b600060c052602060c0f35b5b5b5b5b"; /// Voting errors. #[derive(Debug)] diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 48117b832..68c769c7c 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -40,6 +40,7 @@ const SNAPSHOT_BLOCKS: u64 = 5000; /// Maximum number of blocks allowed in an ethash snapshot. const MAX_SNAPSHOT_BLOCKS: u64 = 30000; +const DEFAULT_EIP649_DELAY: u64 = 3_000_000; /// Ethash params. #[derive(Debug, PartialEq)] @@ -102,6 +103,12 @@ pub struct EthashParams { pub min_gas_price_transition: u64, /// Do not alow transactions with lower gas price. pub min_gas_price: U256, + /// EIP-649 transition block. + pub eip649_transition: u64, + /// EIP-649 bomb delay. + pub eip649_delay: u64, + /// EIP-649 base reward. + pub eip649_reward: Option, } impl From for EthashParams { @@ -136,6 +143,9 @@ impl From for EthashParams { max_gas_limit: p.max_gas_limit.map_or(U256::max_value(), Into::into), min_gas_price_transition: p.min_gas_price_transition.map_or(u64::max_value(), Into::into), min_gas_price: p.min_gas_price.map_or(U256::zero(), Into::into), + eip649_transition: p.eip649_transition.map_or(u64::max_value(), Into::into), + eip649_delay: p.eip649_delay.map_or(DEFAULT_EIP649_DELAY, Into::into), + eip649_reward: p.eip649_reward.map(Into::into), } } } @@ -207,8 +217,10 @@ impl Engine for Arc { } else if block_number < self.ethash_params.eip150_transition { Schedule::new_homestead() } else { + /// There's no max_code_size transition so we tie it to eip161abc + let max_code_size = if block_number >= self.ethash_params.eip161abc_transition { self.ethash_params.max_code_size as usize } else { usize::max_value() }; let mut schedule = Schedule::new_post_eip150( - self.ethash_params.max_code_size as usize, + max_code_size, block_number >= self.ethash_params.eip160_transition, block_number >= self.ethash_params.eip161abc_transition, block_number >= self.ethash_params.eip161d_transition); @@ -287,8 +299,13 @@ impl Engine for Arc { /// Apply the block reward on finalisation of the block. /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current). fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { - let reward = self.ethash_params.block_reward; let fields = block.fields_mut(); + let reward = if fields.header.number() >= self.ethash_params.eip649_transition { + self.ethash_params.eip649_reward.unwrap_or(self.ethash_params.block_reward) + } else { + self.ethash_params.block_reward + }; + let eras_rounds = self.ethash_params.ecip1017_era_rounds; let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, fields.header.number()); @@ -455,7 +472,7 @@ fn ecip1017_eras_block_reward(era_rounds: u64, mut reward: U256, block_number:u6 #[cfg_attr(feature="dev", allow(wrong_self_convention))] impl Ethash { fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 { - const EXP_DIFF_PERIOD: u64 = 100000; + const EXP_DIFF_PERIOD: u64 = 100_000; if header.number() == 0 { panic!("Can't calculate genesis block difficulty"); } @@ -505,7 +522,11 @@ impl Ethash { target = max(min_difficulty, target); if header.number() < self.ethash_params.bomb_defuse_transition { if header.number() < self.ethash_params.ecip1010_pause_transition { - let period = ((parent.number() + 1) / EXP_DIFF_PERIOD) as usize; + let mut number = header.number(); + if number >= self.ethash_params.eip649_transition { + number = number.saturating_sub(self.ethash_params.eip649_delay); + } + let period = (number / EXP_DIFF_PERIOD) as usize; if period > 1 { target = max(min_difficulty, target + (U256::from(1) << (period - 2))); } diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index e731ef7db..bdb8ab6ca 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -87,8 +87,11 @@ pub fn new_transition_test() -> Spec { load(None, include_bytes!("../../res/ethe /// Create a new Foundation Mainnet chain spec without genesis accounts. pub fn new_mainnet_like() -> Spec { load(None, include_bytes!("../../res/ethereum/frontier_like_test.json")) } -/// Create a new Foundation Metropolis era spec. -pub fn new_metropolis_test() -> Spec { load(None, include_bytes!("../../res/ethereum/metropolis_test.json")) } +/// Create a new Foundation Byzantium era spec. +pub fn new_byzantium_test() -> Spec { load(None, include_bytes!("../../res/ethereum/byzantium_test.json")) } + +/// Create a new Foundation Constantinople era spec. +pub fn new_constantinople_test() -> Spec { load(None, include_bytes!("../../res/ethereum/constantinople_test.json")) } #[cfg(test)] mod tests { @@ -151,6 +154,6 @@ mod tests { new_eip161_test(); new_transition_test(); new_mainnet_like(); - new_metropolis_test(); + new_byzantium_test(); } } diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 4f63db9b9..60db246f7 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -24,7 +24,7 @@ use evm::env_info::EnvInfo; use error::ExecutionError; use evm::{self, wasm, Factory, Ext, Finalize, CreateContractAddress, FinalizationResult, ReturnData, CleanDustMode}; use externalities::*; -use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; +use trace::{self, FlatTrace, VMTrace, Tracer, VMTracer}; use transaction::{Action, SignedTransaction}; use crossbeam; pub use executed::{Executed, ExecutionResult}; @@ -64,24 +64,24 @@ pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address, } /// Transaction execution options. -#[derive(Default, Copy, Clone, PartialEq)] -pub struct TransactOptions { +#[derive(Copy, Clone, PartialEq)] +pub struct TransactOptions { /// Enable call tracing. - pub tracing: bool, + pub tracer: T, /// Enable VM tracing. - pub vm_tracing: bool, + pub vm_tracer: V, /// Check transaction nonce before execution. pub check_nonce: bool, /// Records the output from init contract calls. pub output_from_init_contract: bool, } -impl TransactOptions { +impl TransactOptions { /// Create new `TransactOptions` with given tracer and VM tracer. - pub fn new(tracing: bool, vm_tracing: bool) -> Self { + pub fn new(tracer: T, vm_tracer: V) -> Self { TransactOptions { - tracing, - vm_tracing, + tracer, + vm_tracer, check_nonce: true, output_from_init_contract: false, } @@ -98,42 +98,50 @@ impl TransactOptions { self.output_from_init_contract = true; self } +} +impl TransactOptions { /// Creates new `TransactOptions` with default tracing and VM tracing. pub fn with_tracing_and_vm_tracing() -> Self { TransactOptions { - tracing: true, - vm_tracing: true, + tracer: trace::ExecutiveTracer::default(), + vm_tracer: trace::ExecutiveVMTracer::toplevel(), check_nonce: true, output_from_init_contract: false, } } +} +impl TransactOptions { /// Creates new `TransactOptions` with default tracing and no VM tracing. pub fn with_tracing() -> Self { TransactOptions { - tracing: true, - vm_tracing: false, + tracer: trace::ExecutiveTracer::default(), + vm_tracer: trace::NoopVMTracer, check_nonce: true, output_from_init_contract: false, } } +} +impl TransactOptions { /// Creates new `TransactOptions` with no tracing and default VM tracing. pub fn with_vm_tracing() -> Self { TransactOptions { - tracing: false, - vm_tracing: true, + tracer: trace::NoopTracer, + vm_tracer: trace::ExecutiveVMTracer::toplevel(), check_nonce: true, output_from_init_contract: false, } } +} +impl TransactOptions { /// Creates new `TransactOptions` without any tracing. pub fn with_no_tracing() -> Self { TransactOptions { - tracing: false, - vm_tracing: false, + tracer: trace::NoopTracer, + vm_tracer: trace::NoopVMTracer, check_nonce: true, output_from_init_contract: false, } @@ -201,25 +209,18 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { } /// This function should be used to execute transaction. - pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result { - let check = options.check_nonce; - let output = options.output_from_init_contract; - match options.tracing { - true => match options.vm_tracing { - true => self.transact_with_tracer(t, check, output, ExecutiveTracer::default(), ExecutiveVMTracer::toplevel()), - false => self.transact_with_tracer(t, check, output, ExecutiveTracer::default(), NoopVMTracer), - }, - false => match options.vm_tracing { - true => self.transact_with_tracer(t, check, output, NoopTracer, ExecutiveVMTracer::toplevel()), - false => self.transact_with_tracer(t, check, output, NoopTracer, NoopVMTracer), - }, - } + pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) + -> Result where T: Tracer, V: VMTracer, + { + self.transact_with_tracer(t, options.check_nonce, options.output_from_init_contract, options.tracer, options.vm_tracer) } /// Execute a transaction in a "virtual" context. /// This will ensure the caller has enough balance to execute the desired transaction. /// Used for extra-block executions for things like consensus contracts and RPCs - pub fn transact_virtual(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result { + pub fn transact_virtual(&'a mut self, t: &SignedTransaction, options: TransactOptions) + -> Result where T: Tracer, V: VMTracer, + { let sender = t.sender(); let balance = self.state.balance(&sender)?; let needed_balance = t.value.saturating_add(t.gas.saturating_mul(t.gas_price)); @@ -232,7 +233,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { } /// Execute transaction/call with tracing enabled - pub fn transact_with_tracer( + fn transact_with_tracer( &'a mut self, t: &SignedTransaction, check_nonce: bool, @@ -284,7 +285,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { let mut substate = Substate::new(); // NOTE: there can be no invalid transactions from this point. - if !t.is_unsigned() { + if !schedule.eip86 || !t.is_unsigned() { self.state.inc_nonce(&sender)?; } self.state.sub_balance(&sender, &U256::from(gas_cost), &mut substate.to_cleanup_mode(&schedule))?; @@ -328,7 +329,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { }; // finalize here! - Ok(self.finalize(t, substate, result, output, tracer.traces(), vm_tracer.drain())?) + Ok(self.finalize(t, substate, result, output, tracer.drain(), vm_tracer.drain())?) } fn exec_vm( @@ -376,12 +377,12 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { mut output: BytesRef, tracer: &mut T, vm_tracer: &mut V - ) -> evm::Result<(U256, ReturnData)> where T: Tracer, V: VMTracer { + ) -> evm::Result where T: Tracer, V: VMTracer { - trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info); + trace!("Executive::call(params={:?}) self.env_info={:?}, static={}", params, self.info, self.static_flag); if (params.call_type == CallType::StaticCall || - ((params.call_type == CallType::Call || params.call_type == CallType::DelegateCall) && - self.static_flag)) + ((params.call_type == CallType::Call) && + self.static_flag)) && params.value.value() > 0.into() { return Err(evm::Error::MutableCallInStaticContext); } @@ -434,7 +435,11 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { ); } - Ok((params.gas - cost, ReturnData::empty())) + Ok(FinalizationResult { + gas_left: params.gas - cost, + return_data: ReturnData::new(output.to_owned(), 0, output.len()), + apply_state: true, + }) } } else { // just drain the whole gas @@ -466,7 +471,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { trace!(target: "executive", "res={:?}", res); - let traces = subtracer.traces(); + let traces = subtracer.drain(); match res { Ok(ref res) => tracer.trace_call( trace_info, @@ -481,13 +486,17 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { self.enact_result(&res, substate, unconfirmed_substate); trace!(target: "executive", "enacted: substate={:?}\n", substate); - res.map(|r| (r.gas_left, r.return_data)) + res } else { // otherwise it's just a basic transaction, only do tracing, if necessary. self.state.discard_checkpoint(); tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]); - Ok((params.gas, ReturnData::empty())) + Ok(FinalizationResult { + gas_left: params.gas, + return_data: ReturnData::empty(), + apply_state: true, + }) } } } @@ -502,13 +511,18 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { output: &mut Option, tracer: &mut T, vm_tracer: &mut V, - ) -> evm::Result<(U256, ReturnData)> where T: Tracer, V: VMTracer { + ) -> evm::Result where T: Tracer, V: VMTracer { - let scheme = self.engine.create_address_scheme(self.info.number); - if scheme != CreateContractAddress::FromSenderAndNonce && self.state.exists_and_has_code(¶ms.address)? { + // EIP-684: If a contract creation is attempted, due to either a creation transaction or the + // CREATE (or future CREATE2) opcode, and the destination address already has either + // nonzero nonce, or nonempty code, then the creation throws immediately, with exactly + // the same behavior as would arise if the first byte in the init code were an invalid + // opcode. This applies retroactively starting from genesis. + if self.state.exists_and_has_code_or_nonce(¶ms.address)? { return Err(evm::Error::OutOfGas); } + trace!("Executive::create(params={:?}) self.env_info={:?}, static={}", params, self.info, self.static_flag); if params.call_type == CallType::StaticCall || self.static_flag { let trace_info = tracer.prepare_trace_create(¶ms); tracer.trace_failed_create(trace_info, vec![], evm::Error::MutableCallInStaticContext.into()); @@ -552,13 +566,13 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { gas - res.gas_left, trace_output.map(|data| output.as_ref().map(|out| out.to_vec()).unwrap_or(data)), created, - subtracer.traces() + subtracer.drain() ), - Err(ref e) => tracer.trace_failed_create(trace_info, subtracer.traces(), e.into()) + Err(ref e) => tracer.trace_failed_create(trace_info, subtracer.drain(), e.into()) }; self.enact_result(&res, substate, unconfirmed_substate); - res.map(|r| (r.gas_left, r.return_data)) + res } /// Finalizes the transaction (does refunds and suicides). @@ -566,7 +580,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { &mut self, t: &SignedTransaction, mut substate: Substate, - result: evm::Result<(U256, ReturnData)>, + result: evm::Result, output: Bytes, trace: Vec, vm_trace: Option @@ -580,7 +594,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { let refunds_bound = sstore_refunds + suicide_refunds; // real ammount to refund - let gas_left_prerefund = match result { Ok((x, _)) => x, _ => 0.into() }; + let gas_left_prerefund = match result { Ok(FinalizationResult{ gas_left, .. }) => gas_left, _ => 0.into() }; let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) >> 1); let gas_left = gas_left_prerefund + refunded; @@ -624,9 +638,9 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { state_diff: None, }) }, - _ => { + Ok(r) => { Ok(Executed { - exception: None, + exception: if r.apply_state { None } else { Some(evm::Error::Reverted) }, gas: t.gas, gas_used: gas_used, refunded: refunded, @@ -652,6 +666,8 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { | Err(evm::Error::Wasm {..}) | Err(evm::Error::OutOfStack {..}) | Err(evm::Error::MutableCallInStaticContext) + | Err(evm::Error::OutOfBounds) + | Err(evm::Error::Reverted) | Ok(FinalizationResult { apply_state: false, .. }) => { self.state.revert_to_checkpoint(); }, @@ -709,7 +725,7 @@ mod tests { let engine = TestEngine::new(0); let mut substate = Substate::new(); - let (gas_left, _) = { + let FinalizationResult { gas_left, .. } = { let mut ex = Executive::new(&mut state, &info, &engine); ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; @@ -767,7 +783,7 @@ mod tests { let engine = TestEngine::new(0); let mut substate = Substate::new(); - let (gas_left, _) = { + let FinalizationResult { gas_left, .. } = { let mut ex = Executive::new(&mut state, &info, &engine); ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; @@ -825,7 +841,7 @@ mod tests { let mut tracer = ExecutiveTracer::default(); let mut vm_tracer = ExecutiveVMTracer::toplevel(); - let (gas_left, _) = { + let FinalizationResult { gas_left, .. } = { let mut ex = Executive::new(&mut state, &info, &engine); let output = BytesRef::Fixed(&mut[0u8;0]); ex.call(params, &mut substate, output, &mut tracer, &mut vm_tracer).unwrap() @@ -864,7 +880,7 @@ mod tests { }), }]; - assert_eq!(tracer.traces(), expected_trace); + assert_eq!(tracer.drain(), expected_trace); let expected_vm_trace = VMTrace { parent_step: 0, @@ -934,7 +950,7 @@ mod tests { let mut tracer = ExecutiveTracer::default(); let mut vm_tracer = ExecutiveVMTracer::toplevel(); - let (gas_left, _) = { + let FinalizationResult { gas_left, .. } = { let mut ex = Executive::new(&mut state, &info, &engine); ex.create(params.clone(), &mut substate, &mut None, &mut tracer, &mut vm_tracer).unwrap() }; @@ -957,7 +973,7 @@ mod tests { }), }]; - assert_eq!(tracer.traces(), expected_trace); + assert_eq!(tracer.drain(), expected_trace); let expected_vm_trace = VMTrace { parent_step: 0, @@ -1019,7 +1035,7 @@ mod tests { let engine = TestEngine::new(0); let mut substate = Substate::new(); - let (gas_left, _) = { + let FinalizationResult { gas_left, .. } = { let mut ex = Executive::new(&mut state, &info, &engine); ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; @@ -1130,7 +1146,7 @@ mod tests { let engine = TestEngine::new(0); let mut substate = Substate::new(); - let (gas_left, _) = { + let FinalizationResult { gas_left, .. } = { let mut ex = Executive::new(&mut state, &info, &engine); ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap() }; @@ -1174,7 +1190,7 @@ mod tests { let engine = TestEngine::new(0); let mut substate = Substate::new(); - let (gas_left, _) = { + let FinalizationResult { gas_left, .. } = { let mut ex = Executive::new(&mut state, &info, &engine); ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap() }; @@ -1208,7 +1224,7 @@ mod tests { let executed = { let mut ex = Executive::new(&mut state, &info, &engine); - let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false, output_from_init_contract: false }; + let opts = TransactOptions::with_no_tracing(); ex.transact(&t, opts).unwrap() }; @@ -1245,7 +1261,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine); - let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false, output_from_init_contract: false }; + let opts = TransactOptions::with_no_tracing(); ex.transact(&t, opts) }; @@ -1278,7 +1294,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine); - let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false, output_from_init_contract: false }; + let opts = TransactOptions::with_no_tracing(); ex.transact(&t, opts) }; @@ -1311,7 +1327,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine); - let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false, output_from_init_contract: false }; + let opts = TransactOptions::with_no_tracing(); ex.transact(&t, opts) }; @@ -1375,11 +1391,11 @@ mod tests { 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 engine = TestEngine::new_metropolis(); + let engine = TestEngine::new_byzantium(); let mut substate = Substate::new(); let mut output = [0u8; 14]; - let (result, _) = { + let FinalizationResult { gas_left: result, .. } = { let mut ex = Executive::new(&mut state, &info, &engine); ex.call(params, &mut substate, BytesRef::Fixed(&mut output), &mut NoopTracer, &mut NoopVMTracer).unwrap() }; diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 5841e1700..e071de1bc 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -23,6 +23,7 @@ use evm::env_info::EnvInfo; use executive::*; use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, CreateContractAddress, ReturnData}; use evm::CallType; +use evm::FinalizationResult; use transaction::UNSIGNED_SENDER; use trace::{Tracer, VMTracer}; @@ -121,6 +122,10 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> } } + fn is_static(&self) -> bool { + return self.static_flag + } + fn exists(&self, address: &Address) -> evm::Result { self.state.exists(address).map_err(Into::into) } @@ -210,21 +215,27 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> call_type: CallType::None, }; - if params.sender != UNSIGNED_SENDER { - if let Err(e) = self.state.inc_nonce(&self.origin_info.address) { - debug!(target: "ext", "Database corruption encountered: {:?}", e); - return ContractCreateResult::Failed + if !self.static_flag { + if !self.schedule.eip86 || params.sender != UNSIGNED_SENDER { + if let Err(e) = self.state.inc_nonce(&self.origin_info.address) { + debug!(target: "ext", "Database corruption encountered: {:?}", e); + return ContractCreateResult::Failed + } } } let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth, self.static_flag); // TODO: handle internal error separately match ex.create(params, self.substate, &mut None, self.tracer, self.vm_tracer) { - Ok((gas_left, _)) => { + Ok(FinalizationResult{ gas_left, apply_state: true, .. }) => { self.substate.contracts_created.push(address.clone()); ContractCreateResult::Created(address, gas_left) }, - _ => ContractCreateResult::Failed + Ok(FinalizationResult{ gas_left, apply_state: false, return_data }) => { + ContractCreateResult::Reverted(gas_left, return_data) + }, + Err(evm::Error::MutableCallInStaticContext) => ContractCreateResult::FailedInStaticCall, + _ => ContractCreateResult::Failed, } } @@ -269,7 +280,8 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth, self.static_flag); match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) { - Ok((gas_left, return_data)) => MessageCallResult::Success(gas_left, return_data), + Ok(FinalizationResult{ gas_left, return_data, apply_state: true }) => MessageCallResult::Success(gas_left, return_data), + Ok(FinalizationResult{ gas_left, return_data, apply_state: false }) => MessageCallResult::Reverted(gas_left, return_data), _ => MessageCallResult::Failed } } @@ -283,7 +295,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> } #[cfg_attr(feature="dev", allow(match_ref_pats))] - fn ret(mut self, gas: &U256, data: &ReturnData) -> evm::Result + fn ret(mut self, gas: &U256, data: &ReturnData, apply_state: bool) -> evm::Result where Self: Sized { let handle_copy = |to: &mut Option<&mut Bytes>| { to.as_mut().map(|b| **b = data.to_vec()); @@ -303,7 +315,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> vec.extend_from_slice(&*data); Ok(*gas) }, - OutputPolicy::InitContract(ref mut copy) => { + OutputPolicy::InitContract(ref mut copy) if apply_state => { let return_cost = U256::from(data.len()) * U256::from(self.schedule.create_data_gas); if return_cost > *gas || data.len() > self.schedule.create_data_limit { return match self.schedule.exceptional_failed_code_deposit { @@ -311,12 +323,13 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> false => Ok(*gas) } } - handle_copy(copy); - self.state.init_code(&self.origin_info.address, data.to_vec())?; Ok(*gas - return_cost) - } + }, + OutputPolicy::InitContract(_) => { + Ok(*gas) + }, } } diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs index ccdd7d499..9ae55f73d 100644 --- a/ethcore/src/json_tests/chain.rs +++ b/ethcore/src/json_tests/chain.rs @@ -14,17 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use super::test_common::*; -use client::{BlockChainClient, Client, ClientConfig}; +use std::sync::Arc; +use client::{EvmTestClient, BlockChainClient, Client, ClientConfig}; use block::Block; -use ethereum; -use tests::helpers::*; use spec::Genesis; use ethjson; use miner::Miner; use io::IoChannel; -pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { +pub fn json_chain_test(json_data: &[u8]) -> Vec { ::ethcore_logger::init_log(); let tests = ethjson::blockchain::Test::load(json_data).unwrap(); let mut failed = Vec::new(); @@ -42,15 +40,16 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { flush!(" - {}...", name); let spec = { + let mut spec = match EvmTestClient::spec_from_json(&blockchain.network) { + Some(spec) => (*spec).clone(), + None => { + println!(" - {} | {:?} Ignoring tests because of missing spec", name, blockchain.network); + continue; + } + }; + let genesis = Genesis::from(blockchain.genesis()); let state = From::from(blockchain.pre_state.clone()); - let mut spec = match era { - ChainEra::Frontier => ethereum::new_frontier_test(), - ChainEra::Homestead => ethereum::new_homestead_test(), - ChainEra::Eip150 => ethereum::new_eip150_test(), - ChainEra::_Eip161 => ethereum::new_eip161_test(), - ChainEra::TransitionTest => ethereum::new_transition_test(), - }; spec.set_genesis_state(state).expect("Failed to overwrite genesis state"); spec.overwrite_genesis_params(genesis); assert!(spec.is_state_root_valid()); @@ -86,67 +85,69 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { failed } -mod frontier_era_tests { - use tests::helpers::*; +mod block_tests { use super::json_chain_test; fn do_json_test(json_data: &[u8]) -> Vec { - json_chain_test(json_data, ChainEra::Frontier) + json_chain_test(json_data) } declare_test!{BlockchainTests_bcBlockGasLimitTest, "BlockchainTests/bcBlockGasLimitTest"} - declare_test!{BlockchainTests_bcForkBlockTest, "BlockchainTests/bcForkBlockTest"} + declare_test!{BlockchainTests_bcExploitTest, "BlockchainTests/bcExploitTest"} + declare_test!{BlockchainTests_bcForgedTest, "BlockchainTests/bcForgedTest"} declare_test!{BlockchainTests_bcForkStressTest, "BlockchainTests/bcForkStressTest"} - declare_test!{BlockchainTests_bcForkUncle, "BlockchainTests/bcForkUncle"} declare_test!{BlockchainTests_bcGasPricerTest, "BlockchainTests/bcGasPricerTest"} declare_test!{BlockchainTests_bcInvalidHeaderTest, "BlockchainTests/bcInvalidHeaderTest"} - // TODO [ToDr] Ignored because of incorrect JSON (https://github.com/ethereum/tests/pull/113) - declare_test!{ignore => BlockchainTests_bcInvalidRLPTest, "BlockchainTests/bcInvalidRLPTest"} declare_test!{BlockchainTests_bcMultiChainTest, "BlockchainTests/bcMultiChainTest"} - declare_test!{BlockchainTests_bcRPC_API_Test, "BlockchainTests/bcRPC_API_Test"} - declare_test!{BlockchainTests_bcStateTest, "BlockchainTests/bcStateTest"} + declare_test!{BlockchainTests_bcRandomBlockhashTest, "BlockchainTests/bcRandomBlockhashTest"} declare_test!{BlockchainTests_bcTotalDifficultyTest, "BlockchainTests/bcTotalDifficultyTest"} declare_test!{BlockchainTests_bcUncleHeaderValiditiy, "BlockchainTests/bcUncleHeaderValiditiy"} declare_test!{BlockchainTests_bcUncleTest, "BlockchainTests/bcUncleTest"} declare_test!{BlockchainTests_bcValidBlockTest, "BlockchainTests/bcValidBlockTest"} declare_test!{BlockchainTests_bcWalletTest, "BlockchainTests/bcWalletTest"} - declare_test!{BlockchainTests_RandomTests_bl10251623GO, "BlockchainTests/RandomTests/bl10251623GO"} - declare_test!{BlockchainTests_RandomTests_bl201507071825GO, "BlockchainTests/RandomTests/bl201507071825GO"} + declare_test!{BlockchainTests_GeneralStateTest_stAttackTest, "BlockchainTests/GeneralStateTests/stAttackTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stBadOpcodeTest, "BlockchainTests/GeneralStateTests/stBadOpcode/"} + declare_test!{BlockchainTests_GeneralStateTest_stCallCodes, "BlockchainTests/GeneralStateTests/stCallCodes/"} + declare_test!{BlockchainTests_GeneralStateTest_stCallDelegateCodesCallCodeHomestead, "BlockchainTests/GeneralStateTests/stCallDelegateCodesCallCodeHomestead/"} + declare_test!{BlockchainTests_GeneralStateTest_stCallDelegateCodesHomestead, "BlockchainTests/GeneralStateTests/stCallDelegateCodesHomestead/"} + declare_test!{BlockchainTests_GeneralStateTest_stChangedEIP150, "BlockchainTests/GeneralStateTests/stChangedEIP150/"} + declare_test!{BlockchainTests_GeneralStateTest_stCodeSizeLimit, "BlockchainTests/GeneralStateTests/stCodeSizeLimit/"} + declare_test!{BlockchainTests_GeneralStateTest_stCreateTest, "BlockchainTests/GeneralStateTests/stCreateTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stDelegatecallTestHomestead, "BlockchainTests/GeneralStateTests/stDelegatecallTestHomestead/"} + declare_test!{BlockchainTests_GeneralStateTest_stEIP150singleCodeGasPrices, "BlockchainTests/GeneralStateTests/stEIP150singleCodeGasPrices/"} + declare_test!{BlockchainTests_GeneralStateTest_stEIP150Specific, "BlockchainTests/GeneralStateTests/stEIP150Specific/"} + declare_test!{BlockchainTests_GeneralStateTest_stEIP158Specific, "BlockchainTests/GeneralStateTests/stEIP158Specific/"} + declare_test!{BlockchainTests_GeneralStateTest_stExample, "BlockchainTests/GeneralStateTests/stExample/"} + declare_test!{BlockchainTests_GeneralStateTest_stHomesteadSpecific, "BlockchainTests/GeneralStateTests/stHomesteadSpecific/"} + declare_test!{BlockchainTests_GeneralStateTest_stInitCodeTest, "BlockchainTests/GeneralStateTests/stInitCodeTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stLogTests, "BlockchainTests/GeneralStateTests/stLogTests/"} + declare_test!{BlockchainTests_GeneralStateTest_stMemExpandingEIP150Calls, "BlockchainTests/GeneralStateTests/stMemExpandingEIP150Calls/"} + declare_test!{heavy => BlockchainTests_GeneralStateTest_stMemoryStressTest, "BlockchainTests/GeneralStateTests/stMemoryStressTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stMemoryTest, "BlockchainTests/GeneralStateTests/stMemoryTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stNonZeroCallsTest, "BlockchainTests/GeneralStateTests/stNonZeroCallsTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stPreCompiledContracts, "BlockchainTests/GeneralStateTests/stPreCompiledContracts/"} + declare_test!{heavy => BlockchainTests_GeneralStateTest_stQuadraticComplexityTest, "BlockchainTests/GeneralStateTests/stQuadraticComplexityTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stRandom, "BlockchainTests/GeneralStateTests/stRandom/"} + declare_test!{BlockchainTests_GeneralStateTest_stRecursiveCreate, "BlockchainTests/GeneralStateTests/stRecursiveCreate/"} + declare_test!{BlockchainTests_GeneralStateTest_stRefundTest, "BlockchainTests/GeneralStateTests/stRefundTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stReturnDataTest, "BlockchainTests/GeneralStateTests/stReturnDataTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stRevertTest, "BlockchainTests/GeneralStateTests/stRevertTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stSolidityTest, "BlockchainTests/GeneralStateTests/stSolidityTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stSpecialTest, "BlockchainTests/GeneralStateTests/stSpecialTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stStackTests, "BlockchainTests/GeneralStateTests/stStackTests/"} + declare_test!{BlockchainTests_GeneralStateTest_stStaticCall, "BlockchainTests/GeneralStateTests/stStaticCall/"} + declare_test!{BlockchainTests_GeneralStateTest_stSystemOperationsTest, "BlockchainTests/GeneralStateTests/stSystemOperationsTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stTransactionTest, "BlockchainTests/GeneralStateTests/stTransactionTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stTransitionTest, "BlockchainTests/GeneralStateTests/stTransitionTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stWalletTest, "BlockchainTests/GeneralStateTests/stWalletTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stZeroCallsRevert, "BlockchainTests/GeneralStateTests/stZeroCallsRevert/"} + declare_test!{BlockchainTests_GeneralStateTest_stZeroCallsTest, "BlockchainTests/GeneralStateTests/stZeroCallsTest/"} + declare_test!{BlockchainTests_GeneralStateTest_stZeroKnowledge, "BlockchainTests/GeneralStateTests/stZeroKnowledge/"} + + declare_test!{BlockchainTests_TransitionTests_bcEIP158ToByzantium, "BlockchainTests/TransitionTests/bcEIP158ToByzantium/"} + declare_test!{BlockchainTests_TransitionTests_bcFrontierToHomestead, "BlockchainTests/TransitionTests/bcFrontierToHomestead/"} + declare_test!{BlockchainTests_TransitionTests_bcHomesteadToDao, "BlockchainTests/TransitionTests/bcHomesteadToDao/"} + declare_test!{BlockchainTests_TransitionTests_bcHomesteadToEIP150, "BlockchainTests/TransitionTests/bcHomesteadToEIP150/"} } -mod transition_tests { - use tests::helpers::*; - use super::json_chain_test; - - fn do_json_test(json_data: &[u8]) -> Vec { - json_chain_test(json_data, ChainEra::TransitionTest) - } - - declare_test!{BlockchainTests_TestNetwork_bcSimpleTransitionTest, "BlockchainTests/TestNetwork/bcSimpleTransitionTest"} - declare_test!{BlockchainTests_TestNetwork_bcTheDaoTest, "BlockchainTests/TestNetwork/bcTheDaoTest"} - declare_test!{BlockchainTests_TestNetwork_bcEIP150Test, "BlockchainTests/TestNetwork/bcEIP150Test"} -} - -mod eip150_blockchain_tests { - use tests::helpers::*; - use super::json_chain_test; - - fn do_json_test(json_data: &[u8]) -> Vec { - json_chain_test(json_data, ChainEra::Eip150) - } - - declare_test!{BlockchainTests_EIP150_bcBlockGasLimitTest, "BlockchainTests/EIP150/bcBlockGasLimitTest"} - declare_test!{BlockchainTests_EIP150_bcForkStressTest, "BlockchainTests/EIP150/bcForkStressTest"} - declare_test!{BlockchainTests_EIP150_bcGasPricerTest, "BlockchainTests/EIP150/bcGasPricerTest"} - declare_test!{BlockchainTests_EIP150_bcInvalidHeaderTest, "BlockchainTests/EIP150/bcInvalidHeaderTest"} - declare_test!{BlockchainTests_EIP150_bcInvalidRLPTest, "BlockchainTests/EIP150/bcInvalidRLPTest"} - declare_test!{BlockchainTests_EIP150_bcMultiChainTest, "BlockchainTests/EIP150/bcMultiChainTest"} - declare_test!{BlockchainTests_EIP150_bcRPC_API_Test, "BlockchainTests/EIP150/bcRPC_API_Test"} - declare_test!{BlockchainTests_EIP150_bcStateTest, "BlockchainTests/EIP150/bcStateTest"} - declare_test!{BlockchainTests_EIP150_bcTotalDifficultyTest, "BlockchainTests/EIP150/bcTotalDifficultyTest"} - declare_test!{BlockchainTests_EIP150_bcUncleHeaderValiditiy, "BlockchainTests/EIP150/bcUncleHeaderValiditiy"} - declare_test!{BlockchainTests_EIP150_bcUncleTest, "BlockchainTests/EIP150/bcUncleTest"} - declare_test!{BlockchainTests_EIP150_bcValidBlockTest, "BlockchainTests/EIP150/bcValidBlockTest"} - declare_test!{BlockchainTests_EIP150_bcWalletTest, "BlockchainTests/EIP150/bcWalletTest"} -} diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 0c5a6a90d..761ed266f 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -28,6 +28,7 @@ use tests::helpers::*; use ethjson; use trace::{Tracer, NoopTracer}; use trace::{VMTracer, NoopVMTracer}; +use rlp::RlpStream; #[derive(Debug, PartialEq, Clone)] struct CallCreate { @@ -158,8 +159,8 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for TestExt<'a, T, V, B, E> self.ext.log(topics, data) } - fn ret(self, gas: &U256, data: &ReturnData) -> Result { - self.ext.ret(gas, data) + fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> Result { + self.ext.ret(gas, data, apply_state) } fn suicide(&mut self, refund_address: &Address) -> evm::Result<()> { @@ -178,6 +179,10 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for TestExt<'a, T, V, B, E> 0 } + fn is_static(&self) -> bool { + false + } + fn inc_sstore_clears(&mut self) { self.ext.inc_sstore_clears() } @@ -251,6 +256,14 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { (res.finalize(ex), callcreates) }; + let log_hash = { + let mut rlp = RlpStream::new_list(substate.logs.len()); + for l in &substate.logs { + rlp.append(l); + } + &rlp.drain().sha3() + }; + match res { Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."), Ok(res) => { @@ -258,6 +271,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { fail_unless(Some(res.gas_left) == vm.gas_left.map(Into::into), "gas_left is incorrect"); let vm_output: Option> = vm.output.map(Into::into); fail_unless(Some(output) == vm_output, "output is incorrect"); + fail_unless(Some(*log_hash) == vm.logs.map(|h| h.0), "logs are incorrect"); for (address, account) in vm.post_state.unwrap().into_iter() { let address = address.into(); @@ -291,15 +305,15 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { } declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"} -declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"} +declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperation"} declare_test!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"} // TODO [todr] Fails with Signal 11 when using JIT -declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"} -declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"} -declare_test!{heavy => ExecutiveTests_vmInputLimits, "VMTests/vmInputLimits"} +declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfo"} +declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperations"} declare_test!{ExecutiveTests_vmLogTest, "VMTests/vmLogTest"} -declare_test!{ExecutiveTests_vmPerformanceTest, "VMTests/vmPerformanceTest"} +declare_test!{heavy => ExecutiveTests_vmPerformance, "VMTests/vmPerformance"} declare_test!{ExecutiveTests_vmPushDupSwapTest, "VMTests/vmPushDupSwapTest"} +declare_test!{ExecutiveTests_vmRandomTest, "VMTests/vmRandomTest"} declare_test!{ExecutiveTests_vmSha3Test, "VMTests/vmSha3Test"} -declare_test!{ExecutiveTests_vmSystemOperationsTest, "VMTests/vmSystemOperationsTest"} -declare_test!{ExecutiveTests_vmtests, "VMTests/vmtests"} +declare_test!{ExecutiveTests_vmSystemOperationsTest, "VMTests/vmSystemOperations"} +declare_test!{ExecutiveTests_vmTests, "VMTests/vmTests"} diff --git a/ethcore/src/json_tests/homestead_chain.rs b/ethcore/src/json_tests/homestead_chain.rs deleted file mode 100644 index 5f0fe5769..000000000 --- a/ethcore/src/json_tests/homestead_chain.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use super::chain::json_chain_test; -use tests::helpers::*; - -fn do_json_test(json_data: &[u8]) -> Vec { - json_chain_test(json_data, ChainEra::Homestead) -} - -declare_test!{BlockchainTests_Homestead_bcBlockGasLimitTest, "BlockchainTests/Homestead/bcBlockGasLimitTest"} -declare_test!{BlockchainTests_Homestead_bcForkStressTest, "BlockchainTests/Homestead/bcForkStressTest"} -declare_test!{BlockchainTests_Homestead_bcGasPricerTest, "BlockchainTests/Homestead/bcGasPricerTest"} -declare_test!{BlockchainTests_Homestead_bcInvalidHeaderTest, "BlockchainTests/Homestead/bcInvalidHeaderTest"} -declare_test!{BlockchainTests_Homestead_bcInvalidRLPTest, "BlockchainTests/Homestead/bcInvalidRLPTest"} -declare_test!{BlockchainTests_Homestead_bcMultiChainTest, "BlockchainTests/Homestead/bcMultiChainTest"} -declare_test!{BlockchainTests_Homestead_bcRPC_API_Test, "BlockchainTests/Homestead/bcRPC_API_Test"} -declare_test!{BlockchainTests_Homestead_bcStateTest, "BlockchainTests/Homestead/bcStateTest"} -declare_test!{BlockchainTests_Homestead_bcTotalDifficultyTest, "BlockchainTests/Homestead/bcTotalDifficultyTest"} -declare_test!{BlockchainTests_Homestead_bcUncleHeaderValiditiy, "BlockchainTests/Homestead/bcUncleHeaderValiditiy"} -declare_test!{BlockchainTests_Homestead_bcUncleTest, "BlockchainTests/Homestead/bcUncleTest"} -declare_test!{BlockchainTests_Homestead_bcValidBlockTest, "BlockchainTests/Homestead/bcValidBlockTest"} -declare_test!{BlockchainTests_Homestead_bcWalletTest, "BlockchainTests/Homestead/bcWalletTest"} -declare_test!{BlockchainTests_Homestead_bcShanghaiLove, "BlockchainTests/Homestead/bcShanghaiLove"} -declare_test!{BlockchainTests_Homestead_bcSuicideIssue, "BlockchainTests/Homestead/bcSuicideIssue"} -declare_test!{BlockchainTests_Homestead_bcExploitTest, "BlockchainTests/Homestead/bcExploitTest"} diff --git a/ethcore/src/json_tests/mod.rs b/ethcore/src/json_tests/mod.rs index 406782072..ab3205d18 100644 --- a/ethcore/src/json_tests/mod.rs +++ b/ethcore/src/json_tests/mod.rs @@ -21,5 +21,4 @@ mod transaction; mod executive; mod state; mod chain; -mod homestead_chain; mod trie; diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index 74bbfb266..323d95462 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -15,23 +15,13 @@ // along with Parity. If not, see . use super::test_common::*; -use tests::helpers::*; use pod_state::PodState; -use ethereum; -use spec::Spec; +use trace; +use client::{EvmTestClient, EvmTestError, TransactResult}; use ethjson; -use ethjson::state::test::ForkSpec; use transaction::SignedTransaction; use evm::env_info::EnvInfo; -lazy_static! { - pub static ref FRONTIER: Spec = ethereum::new_frontier_test(); - pub static ref HOMESTEAD: Spec = ethereum::new_homestead_test(); - pub static ref EIP150: Spec = ethereum::new_eip150_test(); - pub static ref EIP161: Spec = ethereum::new_eip161_test(); - pub static ref _METROPOLIS: Spec = ethereum::new_metropolis_test(); -} - pub fn json_chain_test(json_data: &[u8]) -> Vec { ::ethcore_logger::init_log(); let tests = ethjson::state::test::Test::load(json_data).unwrap(); @@ -43,35 +33,49 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec { let env: EnvInfo = test.env.into(); let pre: PodState = test.pre_state.into(); - for (spec, states) in test.post_states { + for (spec_name, states) in test.post_states { let total = states.len(); - let engine = match spec { - ForkSpec::Frontier => &FRONTIER.engine, - ForkSpec::Homestead => &HOMESTEAD.engine, - ForkSpec::EIP150 => &EIP150.engine, - ForkSpec::EIP158 => &EIP161.engine, - ForkSpec::Metropolis => continue, + let spec = match EvmTestClient::spec_from_json(&spec_name) { + Some(spec) => spec, + None => { + println!(" - {} | {:?} Ignoring tests because of missing spec", name, spec_name); + continue; + } }; for (i, state) in states.into_iter().enumerate() { - let info = format!(" - {} | {:?} ({}/{}) ...", name, spec, i + 1, total); + let info = format!(" - {} | {:?} ({}/{}) ...", name, spec_name, i + 1, total); let post_root: H256 = state.hash.into(); let transaction: SignedTransaction = multitransaction.select(&state.indexes).into(); - let mut state = get_temp_state(); - state.populate_from(pre.clone()); - if transaction.verify_basic(true, None, env.number >= engine.params().eip86_transition).is_ok() { - state.commit().expect(&format!("State test {} failed due to internal error.", name)); - let _res = state.apply(&env, &**engine, &transaction, false); - } else { - let _rest = state.commit(); - } - if state.root() != &post_root { - println!("{} !!! State mismatch (got: {}, expect: {}", info, state.root(), post_root); - flushln!("{} fail", info); - failed.push(name.clone()); - } else { - flushln!("{} ok", info); + + let result = || -> Result<_, EvmTestError> { + Ok(EvmTestClient::from_pod_state(spec, pre.clone())? + .transact(&env, transaction, trace::NoopVMTracer)) + }; + match result() { + Err(err) => { + println!("{} !!! Unexpected internal error: {:?}", info, err); + flushln!("{} fail", info); + failed.push(name.clone()); + }, + Ok(TransactResult::Ok { state_root, .. }) if state_root != post_root => { + println!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root); + flushln!("{} fail", info); + failed.push(name.clone()); + }, + Ok(TransactResult::Err { state_root, ref error }) if state_root != post_root => { + println!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root); + println!("{} !!! Execution error: {:?}", info, error); + flushln!("{} fail", info); + failed.push(name.clone()); + }, + Ok(TransactResult::Err { error, .. }) => { + flushln!("{} ok ({:?})", info, error); + }, + Ok(_) => { + flushln!("{} ok", info); + }, } } } @@ -93,9 +97,8 @@ mod state_tests { } declare_test!{GeneralStateTest_stAttackTest, "GeneralStateTests/stAttackTest/"} - declare_test!{GeneralStateTest_stBoundsTest, "GeneralStateTests/stBoundsTest/"} + declare_test!{GeneralStateTest_stBadOpcodeTest, "GeneralStateTests/stBadOpcode/"} declare_test!{GeneralStateTest_stCallCodes, "GeneralStateTests/stCallCodes/"} - declare_test!{skip => [ "createJS_ExampleContract" ], GeneralStateTest_stCallCreateCallCodeTest, "GeneralStateTests/stCallCreateCallCodeTest/"} declare_test!{GeneralStateTest_stCallDelegateCodesCallCodeHomestead, "GeneralStateTests/stCallDelegateCodesCallCodeHomestead/"} declare_test!{GeneralStateTest_stCallDelegateCodesHomestead, "GeneralStateTests/stCallDelegateCodesHomestead/"} declare_test!{GeneralStateTest_stChangedEIP150, "GeneralStateTests/stChangedEIP150/"} @@ -104,6 +107,7 @@ mod state_tests { declare_test!{GeneralStateTest_stDelegatecallTestHomestead, "GeneralStateTests/stDelegatecallTestHomestead/"} declare_test!{GeneralStateTest_stEIP150singleCodeGasPrices, "GeneralStateTests/stEIP150singleCodeGasPrices/"} declare_test!{GeneralStateTest_stEIP150Specific, "GeneralStateTests/stEIP150Specific/"} + declare_test!{GeneralStateTest_stEIP158Specific, "GeneralStateTests/stEIP158Specific/"} declare_test!{GeneralStateTest_stExample, "GeneralStateTests/stExample/"} declare_test!{GeneralStateTest_stHomesteadSpecific, "GeneralStateTests/stHomesteadSpecific/"} declare_test!{GeneralStateTest_stInitCodeTest, "GeneralStateTests/stInitCodeTest/"} @@ -117,15 +121,18 @@ mod state_tests { declare_test!{GeneralStateTest_stRandom, "GeneralStateTests/stRandom/"} declare_test!{GeneralStateTest_stRecursiveCreate, "GeneralStateTests/stRecursiveCreate/"} declare_test!{GeneralStateTest_stRefundTest, "GeneralStateTests/stRefundTest/"} - declare_test!{skip => [ "RevertDepthCreateAddressCollision" ], GeneralStateTest_stRevertTest, "GeneralStateTests/stRevertTest/"} + declare_test!{GeneralStateTest_stReturnDataTest, "GeneralStateTests/stReturnDataTest/"} + declare_test!{GeneralStateTest_stRevertTest, "GeneralStateTests/stRevertTest/"} declare_test!{GeneralStateTest_stSolidityTest, "GeneralStateTests/stSolidityTest/"} declare_test!{GeneralStateTest_stSpecialTest, "GeneralStateTests/stSpecialTest/"} declare_test!{GeneralStateTest_stStackTests, "GeneralStateTests/stStackTests/"} + declare_test!{GeneralStateTest_stStaticCall, "GeneralStateTests/stStaticCall/"} declare_test!{GeneralStateTest_stSystemOperationsTest, "GeneralStateTests/stSystemOperationsTest/"} declare_test!{GeneralStateTest_stTransactionTest, "GeneralStateTests/stTransactionTest/"} declare_test!{GeneralStateTest_stTransitionTest, "GeneralStateTests/stTransitionTest/"} declare_test!{GeneralStateTest_stWalletTest, "GeneralStateTests/stWalletTest/"} declare_test!{GeneralStateTest_stZeroCallsRevert, "GeneralStateTests/stZeroCallsRevert/"} declare_test!{GeneralStateTest_stZeroCallsTest, "GeneralStateTests/stZeroCallsTest/"} + declare_test!{GeneralStateTest_stZeroKnowledge, "GeneralStateTests/stZeroKnowledge/"} } diff --git a/ethcore/src/json_tests/transaction.rs b/ethcore/src/json_tests/transaction.rs index a3c3c889d..4bee9313e 100644 --- a/ethcore/src/json_tests/transaction.rs +++ b/ethcore/src/json_tests/transaction.rs @@ -25,7 +25,7 @@ fn do_json_test(json_data: &[u8]) -> Vec { let mut failed = Vec::new(); let frontier_schedule = evm::Schedule::new_frontier(); let homestead_schedule = evm::Schedule::new_homestead(); - let metropolis_schedule = evm::Schedule::new_metropolis(); + let byzantium_schedule = evm::Schedule::new_byzantium(); for (name, test) in tests.into_iter() { let mut fail_unless = |cond: bool, title: &str| if !cond { failed.push(name.clone()); println!("Transaction failed: {:?}: {:?}", name, title); }; @@ -34,7 +34,7 @@ fn do_json_test(json_data: &[u8]) -> Vec { None => &frontier_schedule, Some(x) if x < 1_150_000 => &frontier_schedule, Some(x) if x < 3_000_000 => &homestead_schedule, - Some(_) => &metropolis_schedule + Some(_) => &byzantium_schedule }; let allow_network_id_of_one = number.map_or(false, |n| n >= 2_675_000); let allow_unsigned = number.map_or(false, |n| n >= 3_000_000); @@ -75,18 +75,13 @@ fn do_json_test(json_data: &[u8]) -> Vec { failed } -declare_test!{TransactionTests_ttTransactionTest, "TransactionTests/ttTransactionTest"} -declare_test!{heavy => TransactionTests_tt10mbDataField, "TransactionTests/tt10mbDataField"} -declare_test!{TransactionTests_ttWrongRLPTransaction, "TransactionTests/ttWrongRLPTransaction"} -declare_test!{TransactionTests_Homestead_ttTransactionTest, "TransactionTests/Homestead/ttTransactionTest"} -declare_test!{heavy => TransactionTests_Homestead_tt10mbDataField, "TransactionTests/Homestead/tt10mbDataField"} -declare_test!{TransactionTests_Homestead_ttWrongRLPTransaction, "TransactionTests/Homestead/ttWrongRLPTransaction"} -declare_test!{TransactionTests_RandomTests_tr201506052141PYTHON, "TransactionTests/RandomTests/tr201506052141PYTHON"} -declare_test!{TransactionTests_Homestead_ttTransactionTestEip155VitaliksTests, "TransactionTests/Homestead/ttTransactionTestEip155VitaliksTests"} -declare_test!{TransactionTests_EIP155_ttTransactionTest, "TransactionTests/EIP155/ttTransactionTest"} -declare_test!{TransactionTests_EIP155_ttTransactionTestEip155VitaliksTests, "TransactionTests/EIP155/ttTransactionTestEip155VitaliksTests"} -declare_test!{TransactionTests_EIP155_ttTransactionTestVRule, "TransactionTests/EIP155/ttTransactionTestVRule"} - -declare_test!{TransactionTests_Metropolis_ttMetropolisTest, "TransactionTests/Metropolis/ttMetropolisTest"} -declare_test!{TransactionTests_Metropolis_ttTransactionTest, "TransactionTests/Metropolis/ttTransactionTest"} -declare_test!{TransactionTests_Metropolis_ttTransactionTestZeroSig, "TransactionTests/Metropolis/ttTransactionTestZeroSig"} +declare_test!{TransactionTests_ttEip155VitaliksHomesead, "TransactionTests/ttEip155VitaliksHomesead"} +declare_test!{TransactionTests_ttEip155VitaliksEip158, "TransactionTests/ttEip155VitaliksEip158"} +declare_test!{TransactionTests_ttEip158, "TransactionTests/ttEip158"} +declare_test!{TransactionTests_ttFrontier, "TransactionTests/ttFrontier"} +declare_test!{TransactionTests_ttHomestead, "TransactionTests/ttHomestead"} +declare_test!{TransactionTests_ttVRuleEip158, "TransactionTests/ttVRuleEip158"} +declare_test!{TransactionTests_ttWrongRLPFrontier, "TransactionTests/ttWrongRLPFrontier"} +declare_test!{TransactionTests_ttWrongRLPHomestead, "TransactionTests/ttWrongRLPHomestead"} +declare_test!{TransactionTests_ttConstantinople, "TransactionTests/ttConstantinople"} +declare_test!{TransactionTests_ttSpecConstantinople, "TransactionTests/ttSpecConstantinople"} diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index fbdab0ea5..a294792a5 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -742,8 +742,12 @@ impl MinerService for Miner { state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty) .map_err(ExecutionError::from)?; } - let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false, output_from_init_contract: true }; - let mut ret = Executive::new(&mut state, &env_info, &*self.engine).transact(t, options)?; + let mut ret = match (analytics.transaction_tracing, analytics.vm_tracing) { + (true, true) => Executive::new(&mut state, &env_info, &*self.engine).transact(t, TransactOptions::with_tracing_and_vm_tracing())?, + (true, false) => Executive::new(&mut state, &env_info, &*self.engine).transact(t, TransactOptions::with_tracing())?, + (false, true) => Executive::new(&mut state, &env_info, &*self.engine).transact(t, TransactOptions::with_vm_tracing())?, + (false, false) => Executive::new(&mut state, &env_info, &*self.engine).transact(t, TransactOptions::with_no_tracing())?, + }; // TODO gav move this into Executive. if let Some(original) = original_state { diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 8b6476436..d09f22605 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -59,6 +59,8 @@ pub struct CommonParams { pub fork_block: Option<(BlockNumber, H256)>, /// Number of first block where EIP-98 rules begin. pub eip98_transition: BlockNumber, + /// Number of first block where EIP-658 rules begin. + pub eip658_transition: BlockNumber, /// Validate block receipts root. pub validate_receipts_transition: u64, /// Number of first block where EIP-86 (Metropolis) rules begin. @@ -102,7 +104,7 @@ impl CommonParams { schedule.have_static_call = block_number >= self.eip214_transition; schedule.have_return_data = block_number >= self.eip211_transition; if block_number >= self.eip210_transition { - schedule.blockhash_gas = 350; + schedule.blockhash_gas = 800; } if block_number >= self.dust_protection_transition { schedule.kill_dust = match self.remove_dust_contracts { @@ -135,6 +137,7 @@ impl From for CommonParams { eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into), eip211_transition: p.eip211_transition.map_or(BlockNumber::max_value(), Into::into), eip214_transition: p.eip214_transition.map_or(BlockNumber::max_value(), Into::into), + eip658_transition: p.eip658_transition.map_or(BlockNumber::max_value(), Into::into), dust_protection_transition: p.dust_protection_transition.map_or(BlockNumber::max_value(), Into::into), nonce_cap_increment: p.nonce_cap_increment.map_or(64, Into::into), remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false), @@ -187,7 +190,33 @@ pub struct Spec { genesis_state: PodState, } -fn load_from>(cache_dir: T, s: ethjson::spec::Spec) -> Result { +#[cfg(test)] +impl Clone for Spec { + fn clone(&self) -> Spec { + Spec { + name: self.name.clone(), + engine: self.engine.clone(), + data_dir: self.data_dir.clone(), + nodes: self.nodes.clone(), + parent_hash: self.parent_hash.clone(), + transactions_root: self.transactions_root.clone(), + receipts_root: self.receipts_root.clone(), + author: self.author.clone(), + difficulty: self.difficulty.clone(), + gas_limit: self.gas_limit.clone(), + gas_used: self.gas_used.clone(), + timestamp: self.timestamp.clone(), + extra_data: self.extra_data.clone(), + seal_rlp: self.seal_rlp.clone(), + constructors: self.constructors.clone(), + state_root_memo: RwLock::new(*self.state_root_memo.read()), + genesis_state: self.genesis_state.clone(), + } + } +} + +/// Load from JSON object. +pub fn load_from>(cache_dir: T, s: ethjson::spec::Spec) -> Result { let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect(); let g = Genesis::from(s.genesis); let GenericSeal(seal_rlp) = g.seal.into(); diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index d1a1cb583..eda9de6da 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -28,7 +28,7 @@ use evm::env_info::EnvInfo; use error::Error; use executive::{Executive, TransactOptions}; use factory::Factories; -use trace::FlatTrace; +use trace::{self, FlatTrace, VMTrace}; use pod_account::*; use pod_state::{self, PodState}; use types::basic_account::BasicAccount; @@ -56,8 +56,12 @@ pub use self::substate::Substate; pub struct ApplyOutcome { /// The receipt for the applied transaction. pub receipt: Receipt, - /// The trace for the applied transaction, if None if tracing is disabled. + /// The output of the applied transaction. + pub output: Bytes, + /// The trace for the applied transaction, empty if tracing was not produced. pub trace: Vec, + /// The VM trace for the applied transaction, None if tracing was not produced. + pub vm_trace: Option } /// Result type for the execution ("application") of a transaction. @@ -202,7 +206,7 @@ pub fn check_proof( Err(_) => return ProvedExecution::BadProof, }; - match state.execute(env_info, engine, transaction, false, true) { + match state.execute(env_info, engine, transaction, TransactOptions::with_no_tracing(), true) { Ok(executed) => ProvedExecution::Complete(executed), Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, Err(e) => ProvedExecution::Failed(e), @@ -287,7 +291,7 @@ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with v impl State { /// Creates new state with empty state root - #[cfg(test)] + /// Used for tests. pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State { let mut root = H256::new(); { @@ -451,9 +455,10 @@ impl State { self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null())) } - /// Determine whether an account exists and has code. - pub fn exists_and_has_code(&self, a: &Address) -> trie::Result { - self.ensure_cached(a, RequireCache::CodeSize, false, |a| a.map_or(false, |a| a.code_size().map_or(false, |size| size != 0))) + /// Determine whether an account exists and has code or non-zero nonce. + pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> trie::Result { + self.ensure_cached(a, RequireCache::CodeSize, false, + |a| a.map_or(false, |a| a.code_hash() != SHA3_EMPTY || *a.nonce() != self.account_start_nonce)) } /// Get the balance of account `a`. @@ -620,29 +625,68 @@ impl State { /// Execute a given transaction, producing a receipt and an optional trace. /// This will change the state accordingly. pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult { -// let old = self.to_pod(); + if tracing { + let options = TransactOptions::with_tracing(); + self.apply_with_tracing(env_info, engine, t, options.tracer, options.vm_tracer) + } else { + let options = TransactOptions::with_no_tracing(); + self.apply_with_tracing(env_info, engine, t, options.tracer, options.vm_tracer) + } + } - let e = self.execute(env_info, engine, t, tracing, false)?; -// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod())); - let state_root = if env_info.number < engine.params().eip98_transition || env_info.number < engine.params().validate_receipts_transition { + /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. + /// This will change the state accordingly. + pub fn apply_with_tracing( + &mut self, + env_info: &EnvInfo, + engine: &Engine, + t: &SignedTransaction, + tracer: T, + vm_tracer: V, + ) -> ApplyResult where + T: trace::Tracer, + V: trace::VMTracer, + { + let options = TransactOptions::new(tracer, vm_tracer); + let e = self.execute(env_info, engine, t, options, false)?; + + let eip658 = env_info.number >= engine.params().eip658_transition; + let no_intermediate_commits = + eip658 || + (env_info.number >= engine.params().eip98_transition && env_info.number >= engine.params().validate_receipts_transition); + + let state_root = if no_intermediate_commits { + None + } else { self.commit()?; Some(self.root().clone()) + }; + + let status_byte = if eip658 { + Some(if e.exception.is_some() { 0 } else { 1 }) } else { None }; - let receipt = Receipt::new(state_root, e.cumulative_gas_used, e.logs); + + let output = e.output; + let receipt = Receipt::new(state_root, status_byte, e.cumulative_gas_used, e.logs); trace!(target: "state", "Transaction receipt: {:?}", receipt); - Ok(ApplyOutcome{receipt: receipt, trace: e.trace}) + + Ok(ApplyOutcome { + receipt, + output, + trace: e.trace, + vm_trace: e.vm_trace, + }) } // Execute a given transaction without committing changes. // // `virt` signals that we are executing outside of a block set and restrictions like // gas limits and gas costs should be lifted. - fn execute(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool, virt: bool) - -> Result + fn execute(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, options: TransactOptions, virt: bool) + -> Result where T: trace::Tracer, V: trace::VMTracer, { - let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true, output_from_init_contract: true }; let mut e = Executive::new(self, env_info, engine); match virt { @@ -727,9 +771,8 @@ impl State { Ok(()) } - #[cfg(test)] - #[cfg(feature = "json-tests")] /// Populate the state from `accounts`. + /// Used for tests. pub fn populate_from(&mut self, accounts: PodState) { assert!(self.checkpoints.borrow().is_empty()); for (add, acc) in accounts.drain().into_iter() { diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 3b32f9094..b94ae1fe3 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -17,7 +17,7 @@ use io::IoChannel; use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockId}; use state::{self, State, CleanupMode}; -use executive::Executive; +use executive::{Executive, TransactOptions}; use ethereum; use block::IsBlock; use tests::helpers::*; @@ -359,7 +359,7 @@ fn transaction_proof() { let mut state = State::from_existing(backend, root, 0.into(), factories.clone()).unwrap(); Executive::new(&mut state, &client.latest_env_info(), &*test_spec.engine) - .transact(&transaction, Default::default()).unwrap(); + .transact(&transaction, TransactOptions::with_no_tracing().dont_check_nonce()).unwrap(); assert_eq!(state.balance(&Address::default()).unwrap(), 5.into()); assert_eq!(state.balance(&address).unwrap(), 95.into()); diff --git a/ethcore/src/tests/evm.rs b/ethcore/src/tests/evm.rs index c97fd4ac0..13087201b 100644 --- a/ethcore/src/tests/evm.rs +++ b/ethcore/src/tests/evm.rs @@ -22,7 +22,7 @@ fn test_blockhash_eip210(factory: Factory) { let test_blockhash_contract = "73fffffffffffffffffffffffffffffffffffffffe33141561007a57600143036020526000356101006020510755600061010060205107141561005057600035610100610100602051050761010001555b6000620100006020510714156100755760003561010062010000602051050761020001555b61014a565b4360003512151561009057600060405260206040f35b610100600035430312156100b357610100600035075460605260206060f3610149565b62010000600035430312156100d157600061010060003507146100d4565b60005b156100f6576101006101006000350507610100015460805260206080f3610148565b630100000060003543031215610116576000620100006000350714610119565b60005b1561013c57610100620100006000350507610200015460a052602060a0f3610147565b600060c052602060c0f35b5b5b5b5b"; let blockhash_contract_code = Arc::new(test_blockhash_contract.from_hex().unwrap()); let blockhash_contract_code_hash = blockhash_contract_code.sha3(); - let engine = TestEngine::new_metropolis(); + let engine = TestEngine::new_constantinople(); let mut env_info = EnvInfo::default(); // populate state with 256 last hashes diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index f8e412073..1acda5142 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -36,15 +36,6 @@ use transaction::{Action, Transaction, SignedTransaction}; use rlp::{self, RlpStream}; use views::BlockView; -#[cfg(feature = "json-tests")] -pub enum ChainEra { - Frontier, - Homestead, - Eip150, - _Eip161, - TransitionTest, -} - pub struct TestEngine { engine: Arc, max_depth: usize, @@ -58,9 +49,16 @@ impl TestEngine { } } - pub fn new_metropolis() -> TestEngine { + pub fn new_byzantium() -> TestEngine { TestEngine { - engine: ethereum::new_metropolis_test().engine, + engine: ethereum::new_byzantium_test().engine, + max_depth: 0, + } + } + + pub fn new_constantinople() -> TestEngine { + TestEngine { + engine: ethereum::new_constantinople_test().engine, max_depth: 0, } } @@ -425,5 +423,8 @@ pub fn get_default_ethash_params() -> EthashParams{ max_gas_limit: U256::max_value(), min_gas_price_transition: u64::max_value(), min_gas_price: U256::zero(), + eip649_transition: u64::max_value(), + eip649_delay: 3_000_000, + eip649_reward: None, } } diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index cdfe1e004..744dbc609 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -167,7 +167,7 @@ impl Tracer for ExecutiveTracer { ExecutiveTracer::default() } - fn traces(self) -> Vec { + fn drain(self) -> Vec { self.traces } } diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 39af8a08a..c84c2e9cf 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -85,7 +85,7 @@ pub trait Tracer: Send { fn subtracer(&self) -> Self where Self: Sized; /// Consumes self and returns all traces. - fn traces(self) -> Vec; + fn drain(self) -> Vec; } /// Used by executive to build VM traces. diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index 5fb8a7c55..b2917c271 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -62,7 +62,7 @@ impl Tracer for NoopTracer { NoopTracer } - fn traces(self) -> Vec { + fn drain(self) -> Vec { vec![] } } diff --git a/ethcore/src/trace/types/error.rs b/ethcore/src/trace/types/error.rs index 4242bfad4..667742293 100644 --- a/ethcore/src/trace/types/error.rs +++ b/ethcore/src/trace/types/error.rs @@ -43,6 +43,10 @@ pub enum Error { MutableCallInStaticContext, /// Wasm error Wasm, + /// Contract tried to access past the return data buffer. + OutOfBounds, + /// Execution has been reverted with REVERT instruction. + Reverted, } impl<'a> From<&'a EvmError> for Error { @@ -57,6 +61,8 @@ impl<'a> From<&'a EvmError> for Error { EvmError::Wasm { .. } => Error::Wasm, EvmError::Internal(_) => Error::Internal, EvmError::MutableCallInStaticContext => Error::MutableCallInStaticContext, + EvmError::OutOfBounds => Error::OutOfBounds, + EvmError::Reverted => Error::Reverted, } } } @@ -80,6 +86,8 @@ impl fmt::Display for Error { Wasm => "Wasm runtime error", Internal => "Internal error", MutableCallInStaticContext => "Mutable Call In Static Context", + OutOfBounds => "Out of bounds", + Reverted => "Reverted", }; message.fmt(f) } @@ -98,6 +106,8 @@ impl Encodable for Error { BuiltIn => 6, MutableCallInStaticContext => 7, Wasm => 8, + OutOfBounds => 9, + Reverted => 10, }; s.append_internal(&value); @@ -118,6 +128,8 @@ impl Decodable for Error { 6 => Ok(BuiltIn), 7 => Ok(MutableCallInStaticContext), 8 => Ok(Wasm), + 9 => Ok(OutOfBounds), + 10 => Ok(Reverted), _ => Err(DecoderError::Custom("Invalid error type")), } } diff --git a/ethcore/types/src/receipt.rs b/ethcore/types/src/receipt.rs index 81439ecdf..2489958cb 100644 --- a/ethcore/types/src/receipt.rs +++ b/ethcore/types/src/receipt.rs @@ -34,23 +34,29 @@ pub struct Receipt { pub log_bloom: LogBloom, /// The logs stemming from this transaction. pub logs: Vec, + /// Status byte. Optional before EIP-658. + pub status_code: Option, } impl Receipt { /// Create a new receipt. - pub fn new(state_root: Option, gas_used: U256, logs: Vec) -> Receipt { + pub fn new(state_root: Option, status_code: Option, gas_used: U256, logs: Vec) -> Receipt { Receipt { state_root: state_root, gas_used: gas_used, log_bloom: logs.iter().fold(LogBloom::default(), |mut b, l| { b = &b | &l.bloom(); b }), //TODO: use |= operator logs: logs, + status_code: status_code, } } } impl Encodable for Receipt { fn rlp_append(&self, s: &mut RlpStream) { - if let Some(ref root) = self.state_root { + if let Some(ref status) = self.status_code { + s.begin_list(4); + s.append(status); + } else if let Some(ref root) = self.state_root { s.begin_list(4); s.append(root); } else { @@ -67,17 +73,27 @@ impl Decodable for Receipt { if rlp.item_count()? == 3 { Ok(Receipt { state_root: None, + status_code: None, gas_used: rlp.val_at(0)?, log_bloom: rlp.val_at(1)?, logs: rlp.list_at(2)?, }) } else { - Ok(Receipt { - state_root: Some(rlp.val_at(0)?), + let mut receipt = Receipt { gas_used: rlp.val_at(1)?, log_bloom: rlp.val_at(2)?, logs: rlp.list_at(3)?, - }) + state_root: None, + status_code: None, + }; + + let first = rlp.at(0)?; + if first.is_data() && first.data()?.len() == 1 { + receipt.status_code = Some(first.as_val()?); + } else { + receipt.state_root = Some(first.as_val()?); + } + Ok(receipt) } } } @@ -143,6 +159,7 @@ mod tests { fn test_no_state_root() { let expected = ::rustc_hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let r = Receipt::new( + None, None, 0x40cae.into(), vec![LogEntry { @@ -159,6 +176,7 @@ mod tests { let expected = ::rustc_hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let r = Receipt::new( Some("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into()), + None, 0x40cae.into(), vec![LogEntry { address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index e3de99f2b..55bec69bc 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -14,6 +14,7 @@ docopt = "0.8" serde = "1.0" serde_derive = "1.0" ethcore = { path = "../ethcore" } +ethjson = { path = "../json" } ethcore-util = { path = "../util" } evm = { path = "../ethcore/evm" } diff --git a/evmbin/src/display/json.rs b/evmbin/src/display/json.rs index f12657579..a27cbb68f 100644 --- a/evmbin/src/display/json.rs +++ b/evmbin/src/display/json.rs @@ -30,10 +30,8 @@ pub struct Informant { depth: usize, pc: usize, instruction: u8, - name: &'static str, gas_cost: U256, gas_used: U256, - stack_pop: usize, stack: Vec, memory: Vec, storage: HashMap, @@ -58,11 +56,19 @@ impl Informant { } impl vm::Informant for Informant { + fn before_test(&self, name: &str, action: &str) { + println!( + "{{\"test\":\"{name}\",\"action\":\"{action}\"}}", + name = name, + action = action, + ); + } + fn set_gas(&mut self, gas: U256) { self.gas_used = gas; } - fn finish(&mut self, result: Result) { + fn finish(result: Result) { match result { Ok(success) => println!( "{{\"output\":\"0x{output}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}", @@ -112,7 +118,7 @@ impl trace::VMTracer for Informant { self.gas_used = gas_used; let len = self.stack.len(); - self.stack.truncate(len - info.args); + self.stack.truncate(if len > info.args { len - info.args } else { 0 }); self.stack.extend_from_slice(stack_push); if let Some((pos, data)) = mem_diff { diff --git a/evmbin/src/display/simple.rs b/evmbin/src/display/simple.rs index 9f8f7ee14..1ff1b3135 100644 --- a/evmbin/src/display/simple.rs +++ b/evmbin/src/display/simple.rs @@ -27,7 +27,11 @@ use vm; pub struct Informant; impl vm::Informant for Informant { - fn finish(&mut self, result: Result) { + fn before_test(&self, name: &str, action: &str) { + println!("Test: {} ({})", name, action); + } + + fn finish(result: Result) { match result { Ok(success) => { println!("Output: 0x{}", success.output.to_hex()); diff --git a/evmbin/src/main.rs b/evmbin/src/main.rs index df9b088d0..ad9bc12ac 100644 --- a/evmbin/src/main.rs +++ b/evmbin/src/main.rs @@ -17,8 +17,9 @@ //! Parity EVM interpreter binary. #![warn(missing_docs)] -#![allow(dead_code)] + extern crate ethcore; +extern crate ethjson; extern crate rustc_hex; extern crate serde; #[macro_use] @@ -29,6 +30,7 @@ extern crate evm; use std::sync::Arc; use std::{fmt, fs}; +use std::path::PathBuf; use docopt::Docopt; use rustc_hex::FromHex; use util::{U256, Bytes, Address}; @@ -48,6 +50,7 @@ Usage: evmbin stats [options] evmbin [options] evmbin [-h | --help] + evmbin state-test [--json --only NAME --chain CHAIN] Transaction options: --code CODE Contract code as hex (without 0x). @@ -55,6 +58,10 @@ Transaction options: --input DATA Input data as hex (without 0x). --gas GAS Supplied gas as hex (without 0x). +State test options: + --only NAME Runs only a single test matching the name. + --chain CHAIN Run only tests from specific chain. + General options: --json Display verbose results in JSON. --chain CHAIN Chain spec file path. @@ -65,14 +72,61 @@ General options: fn main() { let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit()); - if args.flag_json { - run(args, display::json::Informant::default()) + if args.cmd_state_test { + run_state_test(args) + } else if args.flag_json { + run_call(args, display::json::Informant::default()) } else { - run(args, display::simple::Informant::default()) + run_call(args, display::simple::Informant::default()) } } -fn run(args: Args, mut informant: T) { +fn run_state_test(args: Args) { + use ethjson::state::test::Test; + + let file = args.arg_file.expect("FILE is required"); + let mut file = match fs::File::open(&file) { + Err(err) => die(format!("Unable to open: {:?}: {}", file, err)), + Ok(file) => file, + }; + let state_test = match Test::load(&mut file) { + Err(err) => die(format!("Unable to load the test file: {}", err)), + Ok(test) => test, + }; + let only_test = args.flag_only.map(|s| s.to_lowercase()); + let only_chain = args.flag_chain.map(|s| s.to_lowercase()); + + for (name, test) in state_test { + if let Some(false) = only_test.as_ref().map(|only_test| &name.to_lowercase() == only_test) { + continue; + } + + let multitransaction = test.transaction; + let env_info = test.env.into(); + let pre = test.pre_state.into(); + + for (spec, states) in test.post_states { + if let Some(false) = only_chain.as_ref().map(|only_chain| &format!("{:?}", spec).to_lowercase() == only_chain) { + continue; + } + + for (idx, state) in states.into_iter().enumerate() { + let post_root = state.hash.into(); + let transaction = multitransaction.select(&state.indexes).into(); + + if args.flag_json { + let i = display::json::Informant::default(); + vm::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, i) + } else { + let i = display::simple::Informant::default(); + vm::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, i) + } + } + } + } +} + +fn run_call(args: Args, mut informant: T) { let from = arg(args.from(), "--from"); let code = arg(args.code(), "--code"); let spec = arg(args.spec(), "--chain"); @@ -87,18 +141,24 @@ fn run(args: Args, mut informant: T) { params.data = data; informant.set_gas(gas); - let result = vm::run(&mut informant, spec, params); - informant.finish(result); + let result = vm::run(&spec, gas, None, |mut client| { + client.call(params, &mut informant).map(|r| (r.gas_left, r.return_data.to_vec())) + }); + T::finish(result); } #[derive(Debug, Deserialize)] struct Args { cmd_stats: bool, + cmd_state_test: bool, + arg_file: Option, + flag_only: Option, flag_from: Option, flag_code: Option, flag_gas: Option, flag_input: Option, flag_spec: Option, + flag_chain: Option, flag_json: bool, } @@ -156,3 +216,55 @@ fn die(msg: T) -> ! { println!("{}", msg); ::std::process::exit(-1) } + +#[cfg(test)] +mod tests { + use docopt::Docopt; + use super::{Args, USAGE}; + + fn run>(args: &[T]) -> Args { + Docopt::new(USAGE).and_then(|d| d.argv(args.into_iter()).deserialize()).unwrap() + } + + #[test] + fn should_parse_all_the_options() { + let args = run(&[ + "parity-evm", + "--json", + "--gas", "1", + "--gas-price", "2", + "--from", "0000000000000000000000000000000000000003", + "--to", "0000000000000000000000000000000000000004", + "--code", "05", + "--input", "06", + "--chain", "./testfile", + ]); + + assert_eq!(args.flag_json, true); + assert_eq!(args.gas(), Ok(1.into())); + assert_eq!(args.gas_price(), Ok(2.into())); + assert_eq!(args.from(), Ok(3.into())); + assert_eq!(args.to(), Ok(4.into())); + assert_eq!(args.code(), Ok(Some(vec![05]))); + assert_eq!(args.data(), Ok(Some(vec![06]))); + assert_eq!(args.flag_chain, Some("./testfile".to_owned())); + } + + #[test] + fn should_parse_state_test_command() { + let args = run(&[ + "parity-evm", + "state-test", + "./file.json", + "--chain", "homestead", + "--only=add11", + "--json", + ]); + + assert_eq!(args.cmd_state_test, true); + assert!(args.arg_file.is_some()); + assert_eq!(args.flag_json, true); + assert_eq!(args.flag_chain, Some("homestead".to_owned())); + assert_eq!(args.flag_only, Some("add11".to_owned())); + } +} diff --git a/evmbin/src/vm.rs b/evmbin/src/vm.rs index c4530bb9e..3392cb441 100644 --- a/evmbin/src/vm.rs +++ b/evmbin/src/vm.rs @@ -17,17 +17,19 @@ //! VM runner. use std::time::{Instant, Duration}; -use util::U256; -use ethcore::{trace, spec}; -use ethcore::client::{EvmTestClient, EvmTestError}; -use evm::action_params::ActionParams; +use util::{U256, H256}; +use ethcore::{trace, spec, transaction, pod_state}; +use ethcore::client::{self, EvmTestClient, EvmTestError, TransactResult}; +use ethjson; /// VM execution informant pub trait Informant: trace::VMTracer { + /// Display a single run init message + fn before_test(&self, test: &str, action: &str); /// Set initial gas. fn set_gas(&mut self, _gas: U256) {} /// Display final result. - fn finish(&mut self, result: Result); + fn finish(result: Result); } /// Execution finished correctly @@ -50,17 +52,71 @@ pub struct Failure { pub time: Duration, } +/// Execute given Transaction and verify resulting state root. +pub fn run_transaction( + name: &str, + idx: usize, + spec: ðjson::state::test::ForkSpec, + pre_state: &pod_state::PodState, + post_root: H256, + env_info: &client::EnvInfo, + transaction: transaction::SignedTransaction, + mut informant: T, +) { + let spec_name = format!("{:?}", spec).to_lowercase(); + let spec = match EvmTestClient::spec_from_json(spec) { + Some(spec) => { + informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "starting"); + spec + }, + None => { + informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "skipping because of missing spec"); + return; + }, + }; + + informant.set_gas(env_info.gas_limit); + + let result = run(spec, env_info.gas_limit, pre_state, |mut client| { + let result = client.transact(env_info, transaction, informant); + match result { + TransactResult::Ok { state_root, .. } if state_root != post_root => { + Err(EvmTestError::PostCondition(format!( + "State root mismatch (got: {}, expected: {})", + state_root, + post_root, + ))) + }, + TransactResult::Ok { gas_left, output, .. } => { + Ok((gas_left, output)) + }, + TransactResult::Err { error, .. } => { + Err(EvmTestError::PostCondition(format!( + "Unexpected execution error: {:?}", error + ))) + }, + } + }); + + T::finish(result) +} + /// Execute VM with given `ActionParams` -pub fn run(vm_tracer: &mut T, spec: spec::Spec, params: ActionParams) -> Result { - let mut test_client = EvmTestClient::new(spec).map_err(|error| Failure { +pub fn run<'a, F, T>(spec: &'a spec::Spec, initial_gas: U256, pre_state: T, run: F) -> Result where + F: FnOnce(EvmTestClient) -> Result<(U256, Vec), EvmTestError>, + T: Into>, +{ + let test_client = match pre_state.into() { + Some(pre_state) => EvmTestClient::from_pod_state(spec, pre_state.clone()), + None => EvmTestClient::new(spec), + }.map_err(|error| Failure { gas_used: 0.into(), error, time: Duration::from_secs(0) })?; - let initial_gas = params.gas; let start = Instant::now(); - let result = test_client.call(params, vm_tracer); + let result = run(test_client); let duration = start.elapsed(); match result { diff --git a/json/src/blockchain/blockchain.rs b/json/src/blockchain/blockchain.rs index 07014db07..9edd75313 100644 --- a/json/src/blockchain/blockchain.rs +++ b/json/src/blockchain/blockchain.rs @@ -21,6 +21,7 @@ use hash::H256; use blockchain::state::State; use blockchain::header::Header; use blockchain::block::Block; +use state::test::ForkSpec; use spec::{Genesis, Seal, Ethereum}; /// Blockchain deserialization. @@ -42,7 +43,9 @@ pub struct BlockChain { pub pre_state: State, /// Hash of best block. #[serde(rename="lastblockhash")] - pub best_block: H256 + pub best_block: H256, + /// Network. + pub network: ForkSpec, } impl BlockChain { @@ -102,7 +105,7 @@ mod tests { "rlp" : "0xf90285f90219a0f052d217bd5275a5177a3c3b7debdfe2670f1c8394b2965ccd5c1883cc1a524da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0bac6177a79e910c98d86ec31a09ae37ac2de15b754fd7bed1ba52362c49416bfa0498785da562aa0c5dd5937cf15f22139b0b1bcf3b4fc48986e1bb1dae9292796a0c7778a7376099ee2e5c455791c1885b5c361b95713fddcbe32d97fd01334d296b90100000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000400000000000000000000000000000000000000000000000000000008302000001832fefba82560b8456850c2ca00102030405060708091011121314151617181920212223242526272829303132a05266ca43e81d25925a9ba573c3e4f9180bc076d316d90e63c6f8708b272f5ce28859ba4daed1898e21f866f864800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d8785012a05f200801ca0ee0b9ec878fbd4258a9473199d8ecc32996a20c323c004e79e0cda20e0418ce3a04e6bc63927d1510bab54f37e46fa036faf4b2c465d271920d9afea1fadf7bd21c0", "transactions" : [ { - "data" : "0x", + "data" : "0x00", "gasLimit" : "0xc350", "gasPrice" : "0x0a", "nonce" : "0x00", @@ -116,6 +119,7 @@ mod tests { "uncleHeaders" : [ ] }], + "network" : "Frontier", "genesisBlockHeader" : { "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1", diff --git a/json/src/spec/builtin.rs b/json/src/spec/builtin.rs index 892bf532e..34e9a2df1 100644 --- a/json/src/spec/builtin.rs +++ b/json/src/spec/builtin.rs @@ -34,6 +34,15 @@ pub struct Modexp { pub divisor: usize, } +/// Pricing for alt_bn128_pairing. +#[derive(Debug, PartialEq, Deserialize, Clone)] +pub struct AltBn128Pairing { + /// Base price. + pub base: usize, + /// Price per point pair. + pub pair: usize, +} + /// Pricing variants. #[derive(Debug, PartialEq, Deserialize, Clone)] pub enum Pricing { @@ -43,6 +52,9 @@ pub enum Pricing { /// Pricing for modular exponentiation. #[serde(rename="modexp")] Modexp(Modexp), + /// Pricing for alt_bn128_pairing exponentiation. + #[serde(rename="alt_bn128_pairing")] + AltBn128Pairing(AltBn128Pairing), } /// Spec builtin. diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index f3391e067..1a9d28e0b 100644 --- a/json/src/spec/ethash.rs +++ b/json/src/spec/ethash.rs @@ -123,6 +123,18 @@ pub struct EthashParams { /// See main EthashParams docs. #[serde(rename="minGasPrice")] pub min_gas_price: Option, + + /// EIP-649 transition block. + #[serde(rename="eip649Transition")] + pub eip649_transition: Option, + + /// EIP-649 bomb delay. + #[serde(rename="eip649Delay")] + pub eip649_delay: Option, + + /// EIP-649 base reward. + #[serde(rename="eip649Reward")] + pub eip649_reward: Option, } /// Ethash engine deserialization. @@ -242,6 +254,9 @@ mod tests { max_gas_limit: None, min_gas_price_transition: None, min_gas_price: None, + eip649_transition: None, + eip649_delay: None, + eip649_reward: None, } }); } @@ -289,6 +304,9 @@ mod tests { max_gas_limit: None, min_gas_price_transition: None, min_gas_price: None, + eip649_transition: None, + eip649_delay: None, + eip649_reward: None, } }); } diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index 0c0b1b01e..25d6540dc 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -82,6 +82,9 @@ pub struct Params { #[serde(rename="eip214Transition")] pub eip214_transition: Option, /// See `CommonParams` docs. + #[serde(rename="eip658Transition")] + pub eip658_transition: Option, + /// See `CommonParams` docs. #[serde(rename="dustProtectionTransition")] pub dust_protection_transition: Option, /// See `CommonParams` docs. diff --git a/json/src/state/test.rs b/json/src/state/test.rs index ceaccfd17..3a25c007d 100644 --- a/json/src/state/test.rs +++ b/json/src/state/test.rs @@ -104,7 +104,12 @@ pub enum ForkSpec { EIP158, Frontier, Homestead, - Metropolis, + Byzantium, + Constantinople, + EIP158ToByzantiumAt5, + FrontierToHomesteadAt5, + HomesteadToDaoAt5, + HomesteadToEIP150At5, } /// State test indexes deserialization. @@ -161,7 +166,7 @@ mod tests { "EIP150" : [ { "hash" : "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", - "indexes" : { "data" : 0, "gas" : 0, "value" : 0 } + "indexes" : { "data" : 0, "gas" : 0, "value" : 0 } }, { "hash" : "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", diff --git a/json/src/vm/log.rs b/json/src/vm/log.rs deleted file mode 100644 index c406253bc..000000000 --- a/json/src/vm/log.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -//! Vm log deserialization. - -use hash::{Address, H256, Bloom}; -use bytes::Bytes; - -/// Vm log deserialization. -#[derive(Debug, PartialEq, Deserialize)] -pub struct Log { - /// Log address. - pub address: Address, - /// Log bloom. - pub bloom: Bloom, - /// Data. - pub data: Bytes, - /// Topics. - pub topics: Vec, -} - -#[cfg(test)] -mod tests { - use serde_json; - use vm::Log; - - #[test] - fn log_deserialization() { - let s = r#"{ - "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", - "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000800000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000", - "data" : "0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd", - "topics" : [ - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ] - }"#; - let _deserialized: Log = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} - diff --git a/json/src/vm/mod.rs b/json/src/vm/mod.rs index 8917d89db..a2588e37c 100644 --- a/json/src/vm/mod.rs +++ b/json/src/vm/mod.rs @@ -19,13 +19,11 @@ pub mod env; pub mod transaction; pub mod vm; -pub mod log; pub mod call; pub mod test; pub use self::env::Env; pub use self::transaction::Transaction; pub use self::vm::Vm; -pub use self::log::Log; pub use self::call::Call; pub use self::test::Test; diff --git a/json/src/vm/vm.rs b/json/src/vm/vm.rs index 36a19cdad..c7e33b609 100644 --- a/json/src/vm/vm.rs +++ b/json/src/vm/vm.rs @@ -18,8 +18,9 @@ use bytes::Bytes; use uint::Uint; +use hash::H256; use blockchain::State; -use vm::{Transaction, Log, Call, Env}; +use vm::{Transaction, Call, Env}; /// Reporesents vm execution environment before and after exeuction of transaction. #[derive(Debug, PartialEq, Deserialize)] @@ -35,8 +36,8 @@ pub struct Vm { /// Gas left after transaction execution. #[serde(rename="gas")] pub gas_left: Option, - /// Logs created during execution of transaction. - pub logs: Option>, + /// Hash of logs created during execution of transaction. + pub logs: Option, /// Transaction output. #[serde(rename="out")] pub output: Option, @@ -83,9 +84,9 @@ mod tests { "value" : "0x0de0b6b3a7640000" }, "gas" : "0x013874", - "logs" : [ - ], + "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "out" : "0x", + "network" : "Frontier", "post" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "0x0de0b6b3a7640000", diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 7cd146082..63e0e719f 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -28,6 +28,7 @@ use ethcore::ethereum; use ethcore::miner::{MinerOptions, Banning, GasPricer, MinerService, ExternalMiner, Miner, PendingSet, PrioritizationStrategy, GasLimit}; use ethcore::account_provider::AccountProvider; use ethjson::blockchain::BlockChain; +use ethjson::state::test::ForkSpec; use io::IoChannel; use util::{U256, H256, Address, Hashable}; @@ -166,13 +167,13 @@ impl EthTester { #[test] fn harness_works() { - let chain: BlockChain = extract_chain!("BlockchainTests/bcUncleTest"); + let chain: BlockChain = extract_chain!("BlockchainTests/bcWalletTest/wallet2outOf3txs"); let _ = EthTester::from_chain(&chain); } #[test] fn eth_get_balance() { - let chain = extract_chain!("BlockchainTests/bcWalletTest", "wallet2outOf3txs"); + let chain = extract_chain!("BlockchainTests/bcWalletTest/wallet2outOf3txs"); let tester = EthTester::from_chain(&chain); // final account state let req_latest = r#"{ @@ -198,7 +199,7 @@ fn eth_get_balance() { #[test] fn eth_block_number() { - let chain = extract_chain!("BlockchainTests/bcRPC_API_Test"); + let chain = extract_chain!("BlockchainTests/bcGasPricerTest/RPC_API_Test"); let tester = EthTester::from_chain(&chain); let req_number = r#"{ "jsonrpc": "2.0", @@ -213,11 +214,11 @@ fn eth_block_number() { #[test] fn eth_get_block() { - let chain = extract_chain!("BlockchainTests/bcRPC_API_Test"); + let chain = extract_chain!("BlockchainTests/bcGasPricerTest/RPC_API_Test"); let tester = EthTester::from_chain(&chain); let req_block = r#"{"method":"eth_getBlockByNumber","params":["0x0",false],"id":1,"jsonrpc":"2.0"}"#; - let res_block = r#"{"jsonrpc":"2.0","result":{"author":"0x8888f1f195afa192cfee860698584c030f4c9db1","difficulty":"0x20000","extraData":"0x42","gasLimit":"0x2fefd8","gasUsed":"0x0","hash":"0x5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x8888f1f195afa192cfee860698584c030f4c9db1","mixHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","nonce":"0x0102030405060708","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":["0xa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","0x880102030405060708"],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1ff","stateRoot":"0x7dba07d6b448a186e9612e5f737d1c909dce473e53199901a302c00646d523c1","timestamp":"0x54c98c81","totalDifficulty":"0x20000","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]},"id":1}"#.to_owned(); + let res_block = r#"{"jsonrpc":"2.0","result":{"author":"0x8888f1f195afa192cfee860698584c030f4c9db1","difficulty":"0x20000","extraData":"0x42","gasLimit":"0x1df5d44","gasUsed":"0x0","hash":"0xcded1bc807465a72e2d54697076ab858f28b15d4beaae8faa47339c8eee386a3","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x8888f1f195afa192cfee860698584c030f4c9db1","mixHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","nonce":"0x0102030405060708","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":["0xa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","0x880102030405060708"],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x200","stateRoot":"0x7dba07d6b448a186e9612e5f737d1c909dce473e53199901a302c00646d523c1","timestamp":"0x54c98c81","totalDifficulty":"0x20000","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]},"id":1}"#; assert_eq!(tester.handler.handle_request_sync(req_block).unwrap(), res_block); } @@ -463,6 +464,6 @@ fn starting_nonce_test() { assert_eq!(r#"{"jsonrpc":"2.0","result":"0x100","id":15}"#, &sample); } -register_test!(eth_transaction_count_1, verify_transaction_counts, "BlockchainTests/bcWalletTest"); -register_test!(eth_transaction_count_2, verify_transaction_counts, "BlockchainTests/bcTotalDifficultyTest"); -register_test!(eth_transaction_count_3, verify_transaction_counts, "BlockchainTests/bcGasPricerTest"); +register_test!(eth_transaction_count_1, verify_transaction_counts, "BlockchainTests/bcWalletTest/wallet2outOf3txs"); +register_test!(eth_transaction_count_2, verify_transaction_counts, "BlockchainTests/bcTotalDifficultyTest/sideChainWithMoreTransactions"); +register_test!(eth_transaction_count_3, verify_transaction_counts, "BlockchainTests/bcGasPricerTest/RPC_API_Test"); diff --git a/rpc/src/v1/tests/mod.rs b/rpc/src/v1/tests/mod.rs index a48727475..31ac1c541 100644 --- a/rpc/src/v1/tests/mod.rs +++ b/rpc/src/v1/tests/mod.rs @@ -19,19 +19,8 @@ macro_rules! extract_chain { ::ethjson::blockchain::Test::load(RAW_DATA).unwrap().into_iter() }}; - ($file:expr, $name:expr) => {{ - let mut chain = None; - for (name, c) in extract_chain!(iter $file) { - if name == $name { - chain = Some(c); - break; - } - } - chain.unwrap() - }}; - ($file:expr) => {{ - extract_chain!(iter $file).next().unwrap().1 + extract_chain!(iter $file).filter(|&(_, ref t)| t.network == ForkSpec::Frontier).next().unwrap().1 }}; } @@ -39,27 +28,7 @@ macro_rules! register_test { ($name:ident, $cb:expr, $file:expr) => { #[test] fn $name() { - for (name, chain) in extract_chain!(iter $file) { - $cb(name, chain); - } - } - }; - - (heavy $name:ident, $cb:expr, $file:expr) => { - #[test] - #[cfg(feature = "test-heavy")] - fn $name() { - for (name, chain) in extract_chain!(iter $file) { - $cb(name, chain); - } - } - }; - - (ignore $name:ident, $cb:expr, $file:expr) => { - #[test] - #[ignore] - fn $name() { - for (name, chain) in extract_chain!(iter $file) { + for (name, chain) in extract_chain!(iter $file).filter(|&(_, ref t)| t.network == ForkSpec::Frontier) { $cb(name, chain); } } diff --git a/util/bigint/Cargo.toml b/util/bigint/Cargo.toml index 477925334..36a272f8c 100644 --- a/util/bigint/Cargo.toml +++ b/util/bigint/Cargo.toml @@ -8,7 +8,7 @@ version = "0.1.3" authors = ["Parity Technologies "] [dependencies] -bigint = "3.0" +bigint = "4.0" rustc-hex = "1.0" rand = "0.3.12" libc = "0.2"