diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 6a613fa7da9..7b463ef565d 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -352,7 +352,7 @@ pub fn get_temp_state_db() -> GuardedTempResult { impl MiningBlockChainClient for TestBlockChainClient { fn latest_schedule(&self) -> Schedule { - Schedule::new_post_eip150(24576, true, true, true) + Schedule::new_post_eip150(24576, true, true, true, true) } fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 03b5d785fa9..94091be0122 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -230,8 +230,9 @@ impl Engine for AuthorityRound { ] } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { - Schedule::new_post_eip150(usize::max_value(), true, true, true) + fn schedule(&self, env_info: &EnvInfo) -> Schedule { + let eip214 = env_info.number >= self.params.eip214_transition; + Schedule::new_post_eip150(usize::max_value(), true, true, true, eip214) } fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index 45bede9f451..d298a8bfd46 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -58,8 +58,9 @@ impl Engine for InstantSeal { &self.builtins } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { - Schedule::new_post_eip150(usize::max_value(), true, true, true) + fn schedule(&self, env_info: &EnvInfo) -> Schedule { + let eip214 = env_info.number >= self.params.eip214_transition; + Schedule::new_post_eip150(usize::max_value(), true, true, true, eip214) } fn seals_internally(&self) -> Option { Some(true) } diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index aac10144758..4f54bffeddf 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -401,8 +401,9 @@ impl Engine for Tendermint { ] } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { - Schedule::new_post_eip150(usize::max_value(), true, true, true) + fn schedule(&self, env_info: &EnvInfo) -> Schedule { + let eip214 = env_info.number >= self.params.eip214_transition; + Schedule::new_post_eip150(usize::max_value(), true, true, true, eip214) } fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index fbb9c8a5ca4..4c9123d1799 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -173,7 +173,8 @@ impl Engine for Ethash { self.ethash_params.max_code_size as usize, env_info.number >= self.ethash_params.eip160_transition, env_info.number >= self.ethash_params.eip161abc_transition, - env_info.number >= self.ethash_params.eip161d_transition + env_info.number >= self.ethash_params.eip161d_transition, + env_info.number >= self.params.eip214_transition, ) } } diff --git a/ethcore/src/evm/evm.rs b/ethcore/src/evm/evm.rs index 09a93f087d1..ab8fbe3d506 100644 --- a/ethcore/src/evm/evm.rs +++ b/ethcore/src/evm/evm.rs @@ -59,6 +59,8 @@ pub enum Error { /// What was the stack limit limit: usize }, + /// When execution tries to modify the state in static context + MutableCallInStaticContext, /// Returned on evm internal error. Should never be ignored during development. /// Likely to cause consensus issues. Internal(String), @@ -79,6 +81,7 @@ impl fmt::Display for Error { BadInstruction { .. } => "Bad instruction", StackUnderflow { .. } => "Stack underflow", OutOfStack { .. } => "Out of stack", + MutableCallInStaticContext => "Mutable call in static context", Internal(ref msg) => msg, }; message.fmt(f) diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs index 352ffb7d9b8..490050073a2 100644 --- a/ethcore/src/evm/ext.rs +++ b/ethcore/src/evm/ext.rs @@ -48,7 +48,7 @@ pub trait Ext { fn storage_at(&self, key: &H256) -> trie::Result; /// Stores a value for given key. - fn set_storage(&mut self, key: H256, value: H256) -> trie::Result<()>; + fn set_storage(&mut self, key: H256, value: H256) -> evm::Result<()>; /// Determine whether an account exists. fn exists(&self, address: &Address) -> trie::Result; @@ -94,7 +94,7 @@ pub trait Ext { fn extcodesize(&self, address: &Address) -> trie::Result; /// Creates log entry with given topics and data - fn log(&mut self, topics: Vec, data: &[u8]); + fn log(&mut self, topics: Vec, data: &[u8]) -> evm::Result<()>; /// Should be called when transaction calls `RETURN` opcode. /// Returns gas_left if cost of returning the data is not too high. @@ -102,7 +102,7 @@ pub trait Ext { /// Should be called when contract commits suicide. /// Address to which funds should be refunded. - fn suicide(&mut self, refund_address: &Address) -> trie::Result<()> ; + fn suicide(&mut self, refund_address: &Address) -> evm::Result<()> ; /// Returns schedule. fn schedule(&self) -> &Schedule; diff --git a/ethcore/src/evm/instructions.rs b/ethcore/src/evm/instructions.rs index d93ddc437b7..a30199ca5fc 100644 --- a/ethcore/src/evm/instructions.rs +++ b/ethcore/src/evm/instructions.rs @@ -277,6 +277,7 @@ lazy_static! { arr[CALLCODE as usize] = InstructionInfo::new("CALLCODE", 0, 7, 1, true, GasPriceTier::Special); arr[RETURN as usize] = InstructionInfo::new("RETURN", 0, 2, 0, true, GasPriceTier::Zero); arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special); + arr[STATICCALL as usize] = InstructionInfo::new("STATICCALL", 0, 6, 1, true, GasPriceTier::Special); arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Special); arr }; @@ -553,6 +554,8 @@ pub const CALLCODE: Instruction = 0xf2; pub const RETURN: Instruction = 0xf3; /// like CALLCODE but keeps caller's value and sender pub const DELEGATECALL: Instruction = 0xf4; +/// like CALL but it does not take value, nor modify the state +pub const STATICCALL: Instruction = 0xfa; /// halt execution and register account for later deletion pub const SUICIDE: Instruction = 0xff; diff --git a/ethcore/src/evm/interpreter/mod.rs b/ethcore/src/evm/interpreter/mod.rs index 79304793eae..785a940ff86 100644 --- a/ethcore/src/evm/interpreter/mod.rs +++ b/ethcore/src/evm/interpreter/mod.rs @@ -292,14 +292,21 @@ impl Interpreter { } }; }, - instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => { + instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => { assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible"); + if instruction == instructions::STATICCALL && !ext.schedule().static_call { + // STATICCALL is not enabled, yet called + return Err(evm::Error::BadInstruction { + instruction: instruction + }); + } + stack.pop_back(); let call_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is one of `CALL`/`CALLCODE`/`DELEGATECALL`; qed"); let code_address = stack.pop_back(); let code_address = u256_to_address(&code_address); - let value = if instruction == instructions::DELEGATECALL { + let value = if instruction == instructions::DELEGATECALL || instruction == instructions::STATICCALL { None } else { Some(stack.pop_back()) @@ -327,6 +334,7 @@ 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), _ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction)) }; @@ -378,7 +386,7 @@ impl Interpreter { .iter() .map(H256::from) .collect(); - ext.log(topics, self.mem.read_slice(offset, size)); + ext.log(topics, self.mem.read_slice(offset, size))?; }, instructions::PUSH1...instructions::PUSH32 => { let bytes = instructions::get_push_bytes(instruction); diff --git a/ethcore/src/evm/schedule.rs b/ethcore/src/evm/schedule.rs index 70801983dc4..6a168c3e6a5 100644 --- a/ethcore/src/evm/schedule.rs +++ b/ethcore/src/evm/schedule.rs @@ -99,6 +99,8 @@ pub struct Schedule { pub no_empty: bool, /// Kill empty accounts if touched. pub kill_empty: bool, + /// Static Call opcode enabled. + pub static_call: bool, } impl Schedule { @@ -113,7 +115,7 @@ impl Schedule { } /// Schedule for the post-EIP-150-era of the Ethereum main net. - pub fn new_post_eip150(max_code_size: usize, fix_exp: bool, no_empty: bool, kill_empty: bool) -> Schedule { + pub fn new_post_eip150(max_code_size: usize, fix_exp: bool, no_empty: bool, kill_empty: bool, static_call: bool) -> Schedule { Schedule { exceptional_failed_code_deposit: true, have_delegate_call: true, @@ -155,6 +157,7 @@ impl Schedule { sub_gas_cap_divisor: Some(64), no_empty: no_empty, kill_empty: kill_empty, + static_call: static_call, } } @@ -200,6 +203,7 @@ impl Schedule { sub_gas_cap_divisor: None, no_empty: false, kill_empty: false, + static_call: false, } } } diff --git a/ethcore/src/evm/tests.rs b/ethcore/src/evm/tests.rs index 3002c170c4f..1bc46131cd4 100644 --- a/ethcore/src/evm/tests.rs +++ b/ethcore/src/evm/tests.rs @@ -86,7 +86,7 @@ impl Ext for FakeExt { Ok(self.store.get(key).unwrap_or(&H256::new()).clone()) } - fn set_storage(&mut self, key: H256, value: H256) -> trie::Result<()> { + fn set_storage(&mut self, key: H256, value: H256) -> evm::Result<()> { self.store.insert(key, value); Ok(()) } @@ -155,18 +155,19 @@ impl Ext for FakeExt { Ok(self.codes.get(address).map_or(0, |c| c.len())) } - fn log(&mut self, topics: Vec, data: &[u8]) { + fn log(&mut self, topics: Vec, data: &[u8]) -> evm::Result<()> { self.logs.push(FakeLogEntry { topics: topics, data: data.to_vec() }); + Ok(()) } fn ret(self, _gas: &U256, _data: &[u8]) -> evm::Result { unimplemented!(); } - fn suicide(&mut self, _refund_address: &Address) -> trie::Result<()> { + fn suicide(&mut self, _refund_address: &Address) -> evm::Result<()> { unimplemented!(); } diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index d9f1b74132e..3c95efaf417 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -62,6 +62,7 @@ pub struct Executive<'a, B: 'a + StateBackend> { engine: &'a Engine, vm_factory: &'a Factory, depth: usize, + static_flag: bool, } impl<'a, B: 'a + StateBackend> Executive<'a, B> { @@ -73,17 +74,19 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { engine: engine, vm_factory: vm_factory, depth: 0, + static_flag: false, } } /// Populates executive from parent properties. Increments executive depth. - pub fn from_parent(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, vm_factory: &'a Factory, parent_depth: usize) -> Self { + pub fn from_parent(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, vm_factory: &'a Factory, parent_depth: usize, static_flag: bool) -> Self { Executive { state: state, info: info, engine: engine, vm_factory: vm_factory, depth: parent_depth + 1, + static_flag: static_flag, } } @@ -94,9 +97,11 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { substate: &'any mut Substate, output: OutputPolicy<'any, 'any>, tracer: &'any mut T, - vm_tracer: &'any mut V + vm_tracer: &'any mut V, + static_call: bool, ) -> Externalities<'any, T, V, B> where T: Tracer, V: VMTracer { - Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer, vm_tracer) + let is_static = self.static_flag || static_call; + Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer, vm_tracer, is_static) } /// This function should be used to execute transaction. @@ -216,11 +221,12 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { ) -> evm::Result where T: Tracer, V: VMTracer { let depth_threshold = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get() / STACK_SIZE_PER_DEPTH); + let static_call = params.call_type == CallType::StaticCall; // Ordinary execution - keep VM in same thread if (self.depth + 1) % depth_threshold != 0 { let vm_factory = self.vm_factory; - let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer); + let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer, static_call); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); return vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext); } @@ -230,7 +236,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { // https://github.com/aturon/crossbeam/issues/16 crossbeam::scope(|scope| { let vm_factory = self.vm_factory; - let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer); + let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer, static_call); scope.spawn(move || { vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext) @@ -253,13 +259,17 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { // backup used in case of running out of gas self.state.checkpoint(); + trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info); + if (params.call_type == CallType::StaticCall || self.static_flag) && params.value.value() > 0.into() { + return Err(evm::Error::MutableCallInStaticContext); + } + let schedule = self.engine.schedule(self.info); // at first, transfer value to destination if let ActionValue::Transfer(val) = params.value { self.state.transfer_balance(¶ms.sender, ¶ms.address, &val, substate.to_cleanup_mode(&schedule))?; } - trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info); if self.engine.is_builtin(¶ms.code_address) { // if destination is builtin, try to execute it @@ -359,6 +369,10 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { // backup used in case of running out of gas self.state.checkpoint(); + if params.call_type == CallType::StaticCall || self.static_flag { + return Err(evm::Error::MutableCallInStaticContext); + } + // part of substate that may be reverted let mut unconfirmed_substate = Substate::new(); @@ -492,7 +506,8 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { | Err(evm::Error::BadJumpDestination {..}) | Err(evm::Error::BadInstruction {.. }) | Err(evm::Error::StackUnderflow {..}) - | Err(evm::Error::OutOfStack {..}) => { + | Err(evm::Error::OutOfStack {..}) + | Err(evm::Error::MutableCallInStaticContext) => { self.state.revert_to_checkpoint(); }, Ok(_) | Err(evm::Error::Internal(_)) => { diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 893ba03bece..231490b9528 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -39,7 +39,7 @@ pub struct OriginInfo { address: Address, origin: Address, gas_price: U256, - value: U256 + value: U256, } impl OriginInfo { @@ -51,7 +51,7 @@ impl OriginInfo { gas_price: params.gas_price, value: match params.value { ActionValue::Transfer(val) | ActionValue::Apparent(val) => val - } + }, } } } @@ -71,6 +71,7 @@ pub struct Externalities<'a, T: 'a, V: 'a, B: 'a> output: OutputPolicy<'a, 'a>, tracer: &'a mut T, vm_tracer: &'a mut V, + static_flag: bool, } impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B> @@ -88,6 +89,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B> output: OutputPolicy<'a, 'a>, tracer: &'a mut T, vm_tracer: &'a mut V, + static_flag: bool, ) -> Self { Externalities { state: state, @@ -101,6 +103,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B> output: output, tracer: tracer, vm_tracer: vm_tracer, + static_flag: static_flag, } } } @@ -112,8 +115,12 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> self.state.storage_at(&self.origin_info.address, key) } - fn set_storage(&mut self, key: H256, value: H256) -> trie::Result<()> { - self.state.set_storage(&self.origin_info.address, key, value) + fn set_storage(&mut self, key: H256, value: H256) -> evm::Result<()> { + if self.static_flag { + Err(evm::Error::MutableCallInStaticContext) + } else { + self.state.set_storage(&self.origin_info.address, key, value).map_err(Into::into) + } } fn exists(&self, address: &Address) -> trie::Result { @@ -176,7 +183,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> debug!(target: "ext", "Database corruption encountered: {:?}", e); return ContractCreateResult::Failed } - let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth); + let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth, self.static_flag); // TODO: handle internal error separately match ex.create(params, self.substate, self.tracer, self.vm_tracer) { @@ -226,7 +233,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> params.value = ActionValue::Transfer(value); } - let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth); + let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth, self.static_flag); match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) { Ok(gas_left) => MessageCallResult::Success(gas_left), @@ -280,18 +287,28 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> } } - fn log(&mut self, topics: Vec, data: &[u8]) { + fn log(&mut self, topics: Vec, data: &[u8]) -> evm::Result<()> { use log_entry::LogEntry; + if self.static_flag { + return Err(evm::Error::MutableCallInStaticContext); + } + let address = self.origin_info.address.clone(); self.substate.logs.push(LogEntry { address: address, topics: topics, data: data.to_vec() }); + + Ok(()) } - fn suicide(&mut self, refund_address: &Address) -> trie::Result<()> { + fn suicide(&mut self, refund_address: &Address) -> evm::Result<()> { + if self.static_flag { + return Err(evm::Error::MutableCallInStaticContext); + } + let address = self.origin_info.address.clone(); let balance = self.balance(&address)?; if &address == refund_address { @@ -404,7 +421,7 @@ mod tests { let mut vm_tracer = NoopVMTracer; let vm_factory = Default::default(); - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); assert_eq!(ext.env_info().number, 100); } @@ -417,7 +434,7 @@ mod tests { let mut vm_tracer = NoopVMTracer; let vm_factory = Default::default(); - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); @@ -442,7 +459,7 @@ mod tests { let mut vm_tracer = NoopVMTracer; let vm_factory = Default::default(); - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); @@ -458,7 +475,7 @@ mod tests { let mut vm_tracer = NoopVMTracer; let vm_factory = Default::default(); - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); let mut output = vec![]; @@ -487,8 +504,8 @@ mod tests { { let vm_factory = Default::default(); - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); - ext.log(log_topics, &log_data); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); + ext.log(log_topics, &log_data).unwrap(); } assert_eq!(setup.sub_state.logs.len(), 1); @@ -505,7 +522,7 @@ mod tests { { let vm_factory = Default::default(); - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false); ext.suicide(refund_account).unwrap(); } diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 844fa08f597..b177db79446 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -75,9 +75,10 @@ impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B> tracer: &'a mut T, vm_tracer: &'a mut V, ) -> trie::Result { + let static_call = false; Ok(TestExt { contract_address: contract_address(&address, &state.nonce(&address)?), - ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer, vm_tracer), + ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer, vm_tracer, static_call), callcreates: vec![] }) } @@ -90,7 +91,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> self.ext.storage_at(key) } - fn set_storage(&mut self, key: H256, value: H256) -> trie::Result<()> { + fn set_storage(&mut self, key: H256, value: H256) -> evm::Result<()> { self.ext.set_storage(key, value) } @@ -151,7 +152,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> self.ext.extcodesize(address) } - fn log(&mut self, topics: Vec, data: &[u8]) { + fn log(&mut self, topics: Vec, data: &[u8]) -> evm::Result<()> { self.ext.log(topics, data) } @@ -159,7 +160,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> self.ext.ret(gas, data) } - fn suicide(&mut self, refund_address: &Address) -> trie::Result<()> { + fn suicide(&mut self, refund_address: &Address) -> evm::Result<()> { self.ext.suicide(refund_address) } diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 078908db4f1..7e8442c5da4 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -55,6 +55,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-214 rules begin. + pub eip214_transition: BlockNumber, } impl From for CommonParams { @@ -68,6 +70,7 @@ impl From for CommonParams { min_gas_limit: p.min_gas_limit.into(), fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { Some((n.into(), h.into())) } else { None }, eip98_transition: p.eip98_transition.map_or(0, Into::into), + eip214_transition: p.eip214_transition.map_or(0, Into::into), } } } diff --git a/ethcore/src/types/executed.rs b/ethcore/src/types/executed.rs index 4301044ce0f..4ebb4f9a963 100644 --- a/ethcore/src/types/executed.rs +++ b/ethcore/src/types/executed.rs @@ -36,6 +36,8 @@ pub enum CallType { CallCode, /// DELEGATECALL. DelegateCall, + /// STATICCALL + StaticCall, } impl Encodable for CallType { @@ -45,6 +47,7 @@ impl Encodable for CallType { CallType::Call => 1, CallType::CallCode => 2, CallType::DelegateCall => 3, + CallType::StaticCall => 4, }; Encodable::rlp_append(&v, s); } @@ -57,6 +60,7 @@ impl Decodable for CallType { 1 => CallType::Call, 2 => CallType::CallCode, 3 => CallType::DelegateCall, + 4 => CallType::StaticCall, _ => return Err(DecoderError::Custom("Invalid value of CallType item")), })) } @@ -145,6 +149,8 @@ pub enum ExecutionError { /// Actual balance. got: U512 }, + /// When execution tries to modify the state in static context + MutableCallInStaticContext, /// Returned when internal evm error occurs. Internal(String), /// Returned when generic transaction occurs @@ -172,6 +178,7 @@ impl fmt::Display for ExecutionError { NotEnoughCash { ref required, ref got } => format!("Cost of transaction exceeds sender balance. {} is required \ but the sender only has {}", required, got), + MutableCallInStaticContext => "Mutable Call in static context".to_owned(), Internal(ref msg) => msg.clone(), TransactionMalformed(ref err) => format!("Malformed transaction: {}", err), }; diff --git a/ethcore/src/types/trace_types/error.rs b/ethcore/src/types/trace_types/error.rs index ea3d326799a..5f7cf17d97b 100644 --- a/ethcore/src/types/trace_types/error.rs +++ b/ethcore/src/types/trace_types/error.rs @@ -38,6 +38,8 @@ pub enum Error { /// Returned on evm internal error. Should never be ignored during development. /// Likely to cause consensus issues. Internal, + /// When execution tries to modify the state in static context + MutableCallInStaticContext, } impl<'a> From<&'a EvmError> for Error { @@ -49,6 +51,7 @@ impl<'a> From<&'a EvmError> for Error { EvmError::StackUnderflow { .. } => Error::StackUnderflow, EvmError::OutOfStack { .. } => Error::OutOfStack, EvmError::Internal(_) => Error::Internal, + EvmError::MutableCallInStaticContext => Error::MutableCallInStaticContext, } } } @@ -69,6 +72,7 @@ impl fmt::Display for Error { StackUnderflow => "Stack underflow", OutOfStack => "Out of stack", Internal => "Internal error", + MutableCallInStaticContext => "Mutable Call In Static Context", }; message.fmt(f) } @@ -84,6 +88,7 @@ impl Encodable for Error { StackUnderflow => 3, OutOfStack => 4, Internal => 5, + MutableCallInStaticContext => 6, }; RlpEncodable::rlp_append(&value, s); } @@ -100,6 +105,7 @@ impl Decodable for Error { 3 => Ok(StackUnderflow), 4 => Ok(OutOfStack), 5 => Ok(Internal), + 6 => Ok(MutableCallInStaticContext), _ => Err(DecoderError::Custom("Invalid error type")), } } diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index 26cedcf4a16..afd9d52a411 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -53,6 +53,10 @@ pub struct Params { /// See `CommonParams` docs. #[serde(rename="eip98Transition")] pub eip98_transition: Option, + + /// See `CommonParams` docs. + #[serde(rename="eip214Transition")] + pub eip214_transition: Option, } #[cfg(test)]