From cc2843ff23afe15fcc0f16996038a36ee20d2072 Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Tue, 25 Oct 2022 21:24:02 +0200 Subject: [PATCH] Fix: explicit EVM call stack --- runtime/src/eval/mod.rs | 88 ++++++++ runtime/src/eval/system.rs | 82 +------ runtime/src/interrupt.rs | 28 +-- runtime/src/lib.rs | 28 +++ src/executor/stack/executor.rs | 316 +++++++++++++++++++-------- src/executor/stack/mod.rs | 1 + src/executor/stack/tagged_runtime.rs | 19 ++ src/lib.rs | 1 + src/maybe_borrowed.rs | 34 +++ 9 files changed, 412 insertions(+), 185 deletions(-) create mode 100644 src/executor/stack/tagged_runtime.rs create mode 100644 src/maybe_borrowed.rs diff --git a/runtime/src/eval/mod.rs b/runtime/src/eval/mod.rs index 1af2322df..72d644996 100644 --- a/runtime/src/eval/mod.rs +++ b/runtime/src/eval/mod.rs @@ -3,6 +3,9 @@ mod macros; mod system; use crate::{CallScheme, ExitReason, Handler, Opcode, Runtime}; +use alloc::vec::Vec; +use core::cmp::min; +use primitive_types::{H160, H256, U256}; pub enum Control { Continue, @@ -59,3 +62,88 @@ pub fn eval(state: &mut Runtime, opcode: Opcode, handler: &mut H) -> _ => handle_other(state, opcode, handler), } } + +pub fn finish_create( + runtime: &mut Runtime, + reason: ExitReason, + address: Option, + return_data: Vec, +) -> Result<(), ExitReason> { + runtime.return_data_buffer = return_data; + let create_address: H256 = address.map(|a| a.into()).unwrap_or_default(); + + match reason { + ExitReason::Succeed(_) => { + runtime + .machine + .stack_mut() + .push(U256::from_big_endian(&create_address[..]))?; + Ok(()) + } + ExitReason::Revert(_) => { + runtime.machine.stack_mut().push(U256::zero())?; + Ok(()) + } + ExitReason::Error(_) => { + runtime.machine.stack_mut().push(U256::zero())?; + Ok(()) + } + ExitReason::Fatal(e) => { + runtime.machine.stack_mut().push(U256::zero())?; + Err(e.into()) + } + } +} + +pub fn finish_call( + runtime: &mut Runtime, + out_len: U256, + out_offset: U256, + reason: ExitReason, + return_data: Vec, +) -> Result<(), ExitReason> { + runtime.return_data_buffer = return_data; + let target_len = min(out_len, U256::from(runtime.return_data_buffer.len())); + + match reason { + ExitReason::Succeed(_) => { + match runtime.machine.memory_mut().copy_large( + out_offset, + U256::zero(), + target_len, + &runtime.return_data_buffer[..], + ) { + Ok(()) => { + runtime.machine.stack_mut().push(U256::one())?; + Ok(()) + } + Err(_) => { + runtime.machine.stack_mut().push(U256::zero())?; + Ok(()) + } + } + } + ExitReason::Revert(_) => { + runtime.machine.stack_mut().push(U256::zero())?; + + let _ = runtime.machine.memory_mut().copy_large( + out_offset, + U256::zero(), + target_len, + &runtime.return_data_buffer[..], + ); + + Ok(()) + } + ExitReason::Error(_) => { + runtime.machine.stack_mut().push(U256::zero())?; + + Ok(()) + } + ExitReason::Fatal(e) => { + runtime.machine.stack_mut().push(U256::zero())?; + + Err(e.into()) + } + } +} diff --git a/runtime/src/eval/system.rs b/runtime/src/eval/system.rs index fe300d046..1c287a0e9 100644 --- a/runtime/src/eval/system.rs +++ b/runtime/src/eval/system.rs @@ -1,10 +1,9 @@ use super::Control; use crate::{ - CallScheme, Capture, Context, CreateScheme, ExitError, ExitFatal, ExitReason, ExitSucceed, - Handler, Runtime, Transfer, + CallScheme, Capture, Context, CreateScheme, ExitError, ExitFatal, ExitSucceed, Handler, + Runtime, Transfer, }; use alloc::vec::Vec; -use core::cmp::min; use primitive_types::{H256, U256}; use sha3::{Digest, Keccak256}; @@ -297,32 +296,12 @@ pub fn create(runtime: &mut Runtime, is_create2: bool, handler: &mut match handler.create(runtime.context.address, scheme, value, code, None) { Capture::Exit((reason, address, return_data)) => { - runtime.return_data_buffer = return_data; - let create_address: H256 = address.map(|a| a.into()).unwrap_or_default(); - - match reason { - ExitReason::Succeed(_) => { - push_h256!(runtime, create_address); - Control::Continue - } - ExitReason::Revert(_) => { - push_h256!(runtime, H256::default()); - Control::Continue - } - ExitReason::Error(_) => { - push_h256!(runtime, H256::default()); - Control::Continue - } - ExitReason::Fatal(e) => { - push_h256!(runtime, H256::default()); - Control::Exit(e.into()) - } + match super::finish_create(runtime, reason, address, return_data) { + Ok(()) => Control::Continue, + Err(e) => Control::Exit(e), } } - Capture::Trap(interrupt) => { - push_h256!(runtime, H256::default()); - Control::CreateInterrupt(interrupt) - } + Capture::Trap(interrupt) => Control::CreateInterrupt(interrupt), } } @@ -408,53 +387,14 @@ pub fn call(runtime: &mut Runtime, scheme: CallScheme, handler: &mut context, ) { Capture::Exit((reason, return_data)) => { - runtime.return_data_buffer = return_data; - let target_len = min(out_len, U256::from(runtime.return_data_buffer.len())); - - match reason { - ExitReason::Succeed(_) => { - match runtime.machine.memory_mut().copy_large( - out_offset, - U256::zero(), - target_len, - &runtime.return_data_buffer[..], - ) { - Ok(()) => { - push_u256!(runtime, U256::one()); - Control::Continue - } - Err(_) => { - push_u256!(runtime, U256::zero()); - Control::Continue - } - } - } - ExitReason::Revert(_) => { - push_u256!(runtime, U256::zero()); - - let _ = runtime.machine.memory_mut().copy_large( - out_offset, - U256::zero(), - target_len, - &runtime.return_data_buffer[..], - ); - - Control::Continue - } - ExitReason::Error(_) => { - push_u256!(runtime, U256::zero()); - - Control::Continue - } - ExitReason::Fatal(e) => { - push_u256!(runtime, U256::zero()); - - Control::Exit(e.into()) - } + match super::finish_call(runtime, out_len, out_offset, reason, return_data) { + Ok(()) => Control::Continue, + Err(e) => Control::Exit(e), } } Capture::Trap(interrupt) => { - push_h256!(runtime, H256::default()); + runtime.return_data_len = out_len; + runtime.return_dat_offset = out_offset; Control::CallInterrupt(interrupt) } } diff --git a/runtime/src/interrupt.rs b/runtime/src/interrupt.rs index 1742fd86d..136cf9a6a 100644 --- a/runtime/src/interrupt.rs +++ b/runtime/src/interrupt.rs @@ -1,4 +1,4 @@ -use crate::{ExitFatal, Handler, Runtime}; +use crate::{Handler, Runtime}; /// Interrupt resolution. pub enum Resolve<'a, 'config, H: Handler> { @@ -10,40 +10,22 @@ pub enum Resolve<'a, 'config, H: Handler> { /// Create interrupt resolution. pub struct ResolveCreate<'a, 'config> { - runtime: &'a mut Runtime<'config>, + _runtime: &'a mut Runtime<'config>, } impl<'a, 'config> ResolveCreate<'a, 'config> { pub(crate) fn new(runtime: &'a mut Runtime<'config>) -> Self { - Self { runtime } - } -} - -impl<'a, 'config> Drop for ResolveCreate<'a, 'config> { - fn drop(&mut self) { - self.runtime.status = Err(ExitFatal::UnhandledInterrupt.into()); - self.runtime - .machine - .exit(ExitFatal::UnhandledInterrupt.into()); + Self { _runtime: runtime } } } /// Call interrupt resolution. pub struct ResolveCall<'a, 'config> { - runtime: &'a mut Runtime<'config>, + _runtime: &'a mut Runtime<'config>, } impl<'a, 'config> ResolveCall<'a, 'config> { pub(crate) fn new(runtime: &'a mut Runtime<'config>) -> Self { - Self { runtime } - } -} - -impl<'a, 'config> Drop for ResolveCall<'a, 'config> { - fn drop(&mut self) { - self.runtime.status = Err(ExitFatal::UnhandledInterrupt.into()); - self.runtime - .machine - .exit(ExitFatal::UnhandledInterrupt.into()); + Self { _runtime: runtime } } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 74318456d..f433abb3b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -35,6 +35,7 @@ pub use crate::interrupt::{Resolve, ResolveCall, ResolveCreate}; use alloc::rc::Rc; use alloc::vec::Vec; +use primitive_types::{H160, U256}; macro_rules! step { ( $self:expr, $handler:expr, $return:tt $($err:path)?; $($ok:path)? ) => ({ @@ -78,6 +79,8 @@ pub struct Runtime<'config> { machine: Machine, status: Result<(), ExitReason>, return_data_buffer: Vec, + return_data_len: U256, + return_dat_offset: U256, context: Context, _config: &'config Config, } @@ -94,6 +97,8 @@ impl<'config> Runtime<'config> { machine: Machine::new(code, data, config.stack_limit, config.memory_limit), status: Ok(()), return_data_buffer: Vec::new(), + return_data_len: U256::zero(), + return_dat_offset: U256::zero(), context, _config: config, } @@ -126,6 +131,29 @@ impl<'config> Runtime<'config> { step!(self, handler, return;) } } + + pub fn finish_create( + &mut self, + reason: ExitReason, + address: Option, + return_data: Vec, + ) -> Result<(), ExitReason> { + eval::finish_create(self, reason, address, return_data) + } + + pub fn finish_call( + &mut self, + reason: ExitReason, + return_data: Vec, + ) -> Result<(), ExitReason> { + eval::finish_call( + self, + self.return_data_len, + self.return_dat_offset, + reason, + return_data, + ) + } } /// Runtime configuration. diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 1d4650d41..445cdc632 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -1,5 +1,7 @@ use crate::backend::Backend; +use crate::executor::stack::tagged_runtime::{RuntimeKind, TaggedRuntime}; use crate::gasometer::{self, Gasometer, StorageTarget}; +use crate::maybe_borrowed::MaybeBorrowed; use crate::{ Capture, Config, Context, CreateScheme, ExitError, ExitReason, ExitSucceed, Handler, Opcode, Runtime, Transfer, @@ -11,6 +13,7 @@ use alloc::{ }; use core::{cmp::min, convert::Infallible}; use evm_core::{ExitFatal, ExitRevert, InterpreterHandler, Machine, Trap}; +use evm_runtime::Resolve; use primitive_types::{H160, H256, U256}; use sha3::{Digest, Keccak256}; @@ -34,6 +37,8 @@ macro_rules! emit_exit { }}; } +const DEFAULT_CALL_STACK_CAPACITY: usize = 4; + pub enum StackExitKind { Succeeded, Reverted, @@ -403,10 +408,95 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> } /// Execute the runtime until it returns. - pub fn execute(&mut self, runtime: &mut Runtime) -> ExitReason { - match runtime.run(self) { - Capture::Exit(s) => s, - Capture::Trap(_) => unreachable!("Trap is Infallible"), + pub fn execute(&mut self, runtime: &mut Runtime<'config>) -> ExitReason { + let mut call_stack = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY); + call_stack.push(TaggedRuntime { + kind: RuntimeKind::Execute, + inner: MaybeBorrowed::Borrowed(runtime), + }); + let (reason, _, _) = self.execute_with_call_stack(&mut call_stack); + reason + } + + /// Execute using Runtimes on the call_stack until it returns. + fn execute_with_call_stack<'borrow>( + &mut self, + call_stack: &mut Vec>, + ) -> (ExitReason, Option, Vec) { + // This `interrupt_runtime` is used to pass the runtime obtained from the + // `Capture::Trap` branch in the match below back to the top of the call stack. + // The reason we can't simply `push` the runtime directly onto the stack in the + // `Capture::Trap` branch is because the borrow-checker complains that the stack + // is already borrowed as long as we hold a pointer on the last element + // (i.e. the currently executing runtime). + let mut interrupt_runtime = None; + loop { + if let Some(rt) = interrupt_runtime.take() { + call_stack.push(rt); + } + let runtime = match call_stack.last_mut() { + Some(runtime) => runtime, + None => { + return ( + ExitReason::Fatal(ExitFatal::UnhandledInterrupt), + None, + Vec::new(), + ); + } + }; + let reason = { + let inner_runtime = &mut runtime.inner; + match inner_runtime.run(self) { + Capture::Exit(reason) => reason, + Capture::Trap(Resolve::Call(rt, _)) => { + interrupt_runtime = Some(rt.0); + continue; + } + Capture::Trap(Resolve::Create(rt, _)) => { + interrupt_runtime = Some(rt.0); + continue; + } + } + }; + let runtime_kind = runtime.kind; + let (reason, maybe_address, return_data) = match runtime_kind { + RuntimeKind::Create(created_address) => { + let (reason, maybe_address, return_data) = self.cleanup_for_create( + created_address, + reason, + runtime.inner.machine().return_value(), + ); + (reason, maybe_address, return_data) + } + RuntimeKind::Call(code_address) => { + let return_data = self.cleanup_for_call( + code_address, + &reason, + runtime.inner.machine().return_value(), + ); + (reason, None, return_data) + } + RuntimeKind::Execute => (reason, None, runtime.inner.machine().return_value()), + }; + // We're done with that runtime now, so can pop it off the call stack + call_stack.pop(); + // Now pass the results from that runtime on to the next one in the stack + let runtime = match call_stack.last_mut() { + Some(r) => r, + None => return (reason, None, return_data), + }; + let inner_runtime = &mut runtime.inner; + let maybe_error = match runtime_kind { + RuntimeKind::Create(_) => { + inner_runtime.finish_create(reason, maybe_address, return_data) + } + RuntimeKind::Call(_) => inner_runtime.finish_call(reason, return_data), + RuntimeKind::Execute => inner_runtime.finish_call(reason, return_data), + }; + // Early exit if passing on the result caused an error + if let Err(e) = maybe_error { + return (e, None, Vec::new()); + } } } @@ -456,7 +546,12 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> false, ) { Capture::Exit((s, _, v)) => emit_exit!(s, v), - Capture::Trap(_) => unreachable!(), + Capture::Trap(rt) => { + let mut cs = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY); + cs.push(rt.0); + let (s, _, v) = self.execute_with_call_stack(&mut cs); + emit_exit!(s, v) + } } } @@ -502,7 +597,12 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> false, ) { Capture::Exit((s, _, v)) => emit_exit!(s, v), - Capture::Trap(_) => unreachable!(), + Capture::Trap(rt) => { + let mut cs = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY); + cs.push(rt.0); + let (s, _, v) = self.execute_with_call_stack(&mut cs); + emit_exit!(s, v) + } } } @@ -567,7 +667,12 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> context, ) { Capture::Exit((s, v)) => emit_exit!(s, v), - Capture::Trap(_) => unreachable!(), + Capture::Trap(rt) => { + let mut cs = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY); + cs.push(rt.0); + let (s, _, v) = self.execute_with_call_stack(&mut cs); + emit_exit!(s, v) + } } } @@ -635,7 +740,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> init_code: Vec, target_gas: Option, take_l64: bool, - ) -> Capture<(ExitReason, Option, Vec), Infallible> { + ) -> Capture<(ExitReason, Option, Vec), StackExecutorCreateInterrupt<'config>> { macro_rules! try_or_fail { ( $e:expr ) => { match $e { @@ -645,13 +750,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> }; } - fn check_first_byte(config: &Config, code: &[u8]) -> Result<(), ExitError> { - if config.disallow_executable_format && Some(&Opcode::EOFMAGIC.as_u8()) == code.get(0) { - return Err(ExitError::InvalidCode(Opcode::EOFMAGIC)); - } - Ok(()) - } - fn l64(gas: u64) -> u64 { gas - gas / 64 } @@ -738,76 +836,17 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> self.state.inc_nonce(address); } - let mut runtime = Runtime::new( + let runtime = Runtime::new( Rc::new(init_code), Rc::new(Vec::new()), context, self.config, ); - let reason = self.execute(&mut runtime); - log::debug!(target: "evm", "Create execution using address {}: {:?}", address, reason); - - match reason { - ExitReason::Succeed(s) => { - let out = runtime.machine().return_value(); - - // As of EIP-3541 code starting with 0xef cannot be deployed - if let Err(e) = check_first_byte(self.config, &out) { - self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); - return Capture::Exit((e.into(), None, Vec::new())); - } - - if let Some(limit) = self.config.create_contract_limit { - if out.len() > limit { - self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); - return Capture::Exit(( - ExitError::CreateContractLimit.into(), - None, - Vec::new(), - )); - } - } - - match self - .state - .metadata_mut() - .gasometer - .record_deposit(out.len()) - { - Ok(()) => { - let e = self.exit_substate(StackExitKind::Succeeded); - self.state.set_code(address, out); - try_or_fail!(e); - Capture::Exit((ExitReason::Succeed(s), Some(address), Vec::new())) - } - Err(e) => { - let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Error(e), None, Vec::new())) - } - } - } - ExitReason::Error(e) => { - self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Error(e), None, Vec::new())) - } - ExitReason::Revert(e) => { - let _ = self.exit_substate(StackExitKind::Reverted); - Capture::Exit(( - ExitReason::Revert(e), - None, - runtime.machine().return_value(), - )) - } - ExitReason::Fatal(e) => { - self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Fatal(e), None, Vec::new())) - } - } + Capture::Trap(StackExecutorCreateInterrupt(TaggedRuntime { + kind: RuntimeKind::Create(address), + inner: MaybeBorrowed::Owned(runtime), + })) } #[allow(clippy::too_many_arguments)] @@ -821,7 +860,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> take_l64: bool, take_stipend: bool, context: Context, - ) -> Capture<(ExitReason, Vec), Infallible> { + ) -> Capture<(ExitReason, Vec), StackExecutorCallInterrupt<'config>> { macro_rules! try_or_fail { ( $e:expr ) => { match $e { @@ -925,28 +964,109 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> }; } - let mut runtime = Runtime::new(Rc::new(code), Rc::new(input), context, self.config); + let runtime = Runtime::new(Rc::new(code), Rc::new(input), context, self.config); - let reason = self.execute(&mut runtime); - log::debug!(target: "evm", "Call execution using address {}: {:?}", code_address, reason); + Capture::Trap(StackExecutorCallInterrupt(TaggedRuntime { + kind: RuntimeKind::Call(code_address), + inner: MaybeBorrowed::Owned(runtime), + })) + } + + fn cleanup_for_create( + &mut self, + created_address: H160, + reason: ExitReason, + return_data: Vec, + ) -> (ExitReason, Option, Vec) { + fn check_first_byte(config: &Config, code: &[u8]) -> Result<(), ExitError> { + if config.disallow_executable_format && Some(&Opcode::EOFMAGIC.as_u8()) == code.get(0) { + return Err(ExitError::InvalidCode(Opcode::EOFMAGIC)); + } + Ok(()) + } + + log::debug!(target: "evm", "Create execution using address {}: {:?}", created_address, reason); match reason { ExitReason::Succeed(s) => { - let _ = self.exit_substate(StackExitKind::Succeeded); - Capture::Exit((ExitReason::Succeed(s), runtime.machine().return_value())) + let out = return_data; + let address = created_address; + // As of EIP-3541 code starting with 0xef cannot be deployed + if let Err(e) = check_first_byte(self.config, &out) { + self.state.metadata_mut().gasometer.fail(); + let _ = self.exit_substate(StackExitKind::Failed); + return (e.into(), None, Vec::new()); + } + + if let Some(limit) = self.config.create_contract_limit { + if out.len() > limit { + self.state.metadata_mut().gasometer.fail(); + let _ = self.exit_substate(StackExitKind::Failed); + return (ExitError::CreateContractLimit.into(), None, Vec::new()); + } + } + + match self + .state + .metadata_mut() + .gasometer + .record_deposit(out.len()) + { + Ok(()) => { + let exit_result = self.exit_substate(StackExitKind::Succeeded); + self.state.set_code(address, out); + if let Err(e) = exit_result { + return (e.into(), None, Vec::new()); + } + (ExitReason::Succeed(s), Some(address), Vec::new()) + } + Err(e) => { + let _ = self.exit_substate(StackExitKind::Failed); + (ExitReason::Error(e), None, Vec::new()) + } + } } ExitReason::Error(e) => { + self.state.metadata_mut().gasometer.fail(); let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Error(e), Vec::new())) + (ExitReason::Error(e), None, Vec::new()) } ExitReason::Revert(e) => { let _ = self.exit_substate(StackExitKind::Reverted); - Capture::Exit((ExitReason::Revert(e), runtime.machine().return_value())) + (ExitReason::Revert(e), None, return_data) } ExitReason::Fatal(e) => { self.state.metadata_mut().gasometer.fail(); let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Fatal(e), Vec::new())) + (ExitReason::Fatal(e), None, Vec::new()) + } + } + } + + fn cleanup_for_call( + &mut self, + code_address: H160, + reason: &ExitReason, + return_data: Vec, + ) -> Vec { + log::debug!(target: "evm", "Call execution using address {}: {:?}", code_address, reason); + match reason { + ExitReason::Succeed(_) => { + let _ = self.exit_substate(StackExitKind::Succeeded); + return_data + } + ExitReason::Error(_) => { + let _ = self.exit_substate(StackExitKind::Failed); + Vec::new() + } + ExitReason::Revert(_) => { + let _ = self.exit_substate(StackExitKind::Reverted); + return_data + } + ExitReason::Fatal(_) => { + self.state.metadata_mut().gasometer.fail(); + let _ = self.exit_substate(StackExitKind::Failed); + Vec::new() } } } @@ -1035,12 +1155,15 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Interprete } } +pub struct StackExecutorCallInterrupt<'config>(TaggedRuntime<'config, 'config>); +pub struct StackExecutorCreateInterrupt<'config>(TaggedRuntime<'config, 'config>); + impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler for StackExecutor<'config, 'precompiles, S, P> { - type CreateInterrupt = Infallible; + type CreateInterrupt = StackExecutorCreateInterrupt<'config>; type CreateFeedback = Infallible; - type CallInterrupt = Infallible; + type CallInterrupt = StackExecutorCallInterrupt<'config>; type CallFeedback = Infallible; fn balance(&self, address: H160) -> U256 { @@ -1308,7 +1431,18 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr context.clone(), ) { Capture::Exit((s, v)) => (s, v), - Capture::Trap(_) => unreachable!("Trap is infaillible since StackExecutor is sync"), + Capture::Trap(rt) => { + // Ideally this would pass the interrupt back to the executor so it could be + // handled like any other call, however the type signature of this function does + // not allow it. For now we'll make a recursive call instead of making a breaking + // change to the precompile API. But this means a custom precompile could still + // potentially cause a stack overflow if you're not careful. + let mut call_stack = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY); + call_stack.push(rt.0); + let (reason, _, return_data) = + self.executor.execute_with_call_stack(&mut call_stack); + (reason, return_data) + } } } diff --git a/src/executor/stack/mod.rs b/src/executor/stack/mod.rs index 5bb448a7a..1d64fbe03 100644 --- a/src/executor/stack/mod.rs +++ b/src/executor/stack/mod.rs @@ -4,6 +4,7 @@ mod executor; mod memory; +mod tagged_runtime; pub use self::executor::{ Accessed, PrecompileFailure, PrecompileFn, PrecompileHandle, PrecompileOutput, PrecompileSet, diff --git a/src/executor/stack/tagged_runtime.rs b/src/executor/stack/tagged_runtime.rs new file mode 100644 index 000000000..9af9fa827 --- /dev/null +++ b/src/executor/stack/tagged_runtime.rs @@ -0,0 +1,19 @@ +//! A module containing data types for keeping track of the kinds of calls +//! (CALL vs CREATE) in the EVM call stack. + +use crate::maybe_borrowed::MaybeBorrowed; +use crate::Runtime; +use primitive_types::H160; + +pub struct TaggedRuntime<'config, 'borrow> { + pub kind: RuntimeKind, + pub inner: MaybeBorrowed<'borrow, Runtime<'config>>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RuntimeKind { + Create(H160), + Call(H160), + /// Special variant used only in `StackExecutor::execute` + Execute, +} diff --git a/src/lib.rs b/src/lib.rs index 6b1e85819..44d3c23c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,3 +28,4 @@ macro_rules! event { pub mod backend; pub mod executor; +pub mod maybe_borrowed; diff --git a/src/maybe_borrowed.rs b/src/maybe_borrowed.rs new file mode 100644 index 000000000..a7483bb76 --- /dev/null +++ b/src/maybe_borrowed.rs @@ -0,0 +1,34 @@ +//! A module containing the `MaybeBorrowed` enum. See its documentation for details. + +/// Similar to `Cow` from the standard library, but without requiring `T: Clone`. +/// Instead of "copy on write", this data structure represents a type that can create +/// `&mut T`, either because it is `&mut T`, or because it is an owned `T`. +/// This is also distinct from the `BorrowMut` trait in the standard library because +/// you can have a single collection mix both borrowed and owned data (e.g. +/// `let xs: Vec> = vec![&mut t1, t2]` would be possible whereas +/// `Vec where B: BorrowMut` would need to consist of all owned or all borrowed data). +#[derive(Debug)] +pub enum MaybeBorrowed<'a, T> { + Borrowed(&'a mut T), + Owned(T), +} + +impl<'a, T> core::ops::Deref for MaybeBorrowed<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match self { + Self::Borrowed(x) => x, + Self::Owned(x) => x, + } + } +} + +impl<'a, T> core::ops::DerefMut for MaybeBorrowed<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + Self::Borrowed(x) => x, + Self::Owned(x) => x, + } + } +}