From 675195e18319dd9252f21a325d0acc5b6ecbee33 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 6 Dec 2023 02:03:02 +0100 Subject: [PATCH 1/3] Unify interpreter::call_create and interpreter::trap --- interpreter/src/call_create.rs | 483 -------------------------------- interpreter/src/etable.rs | 2 +- interpreter/src/eval/mod.rs | 2 +- interpreter/src/lib.rs | 3 +- interpreter/src/trap.rs | 484 +++++++++++++++++++++++++++++++++ interpreter/tests/usability.rs | 2 +- src/standard/invoker/mod.rs | 4 +- src/standard/mod.rs | 4 +- 8 files changed, 491 insertions(+), 493 deletions(-) delete mode 100644 interpreter/src/call_create.rs diff --git a/interpreter/src/call_create.rs b/interpreter/src/call_create.rs deleted file mode 100644 index 4ca951160..000000000 --- a/interpreter/src/call_create.rs +++ /dev/null @@ -1,483 +0,0 @@ -//! Call and create trap handler. - -use crate::utils::{h256_to_u256, u256_to_usize}; -use crate::{ - Context, ExitError, ExitException, ExitResult, Machine, Memory, RuntimeBackend, RuntimeState, - Transfer, TrapConstruct, TrapConsume, -}; -use alloc::vec::Vec; -use core::cmp::{max, min}; -use core::convert::Infallible; -use primitive_types::{H160, H256, U256}; -use sha3::{Digest, Keccak256}; - -/// Create scheme. -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -pub enum CreateScheme { - /// Legacy create scheme of `CREATE`. - Legacy { - /// Caller of the create. - caller: H160, - }, - /// Create scheme of `CREATE2`. - Create2 { - /// Caller of the create. - caller: H160, - /// Code hash. - code_hash: H256, - /// Salt. - salt: H256, - }, -} - -impl CreateScheme { - pub fn address(&self, handler: &H) -> H160 { - match self { - Self::Create2 { - caller, - code_hash, - salt, - } => { - let mut hasher = Keccak256::new(); - hasher.update([0xff]); - hasher.update(&caller[..]); - hasher.update(&salt[..]); - hasher.update(&code_hash[..]); - H256::from_slice(hasher.finalize().as_slice()).into() - } - Self::Legacy { caller } => { - let nonce = handler.nonce(*caller); - let mut stream = rlp::RlpStream::new_list(2); - stream.append(caller); - stream.append(&nonce); - H256::from_slice(Keccak256::digest(&stream.out()).as_slice()).into() - } - } - } - - pub const fn caller(&self) -> H160 { - match self { - Self::Create2 { caller, .. } => *caller, - Self::Legacy { caller } => *caller, - } - } -} - -/// Call scheme. -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -pub enum CallScheme { - /// `CALL` - Call, - /// `CALLCODE` - CallCode, - /// `DELEGATECALL` - DelegateCall, - /// `STATICCALL` - StaticCall, -} - -pub enum CallCreateTrap { - Create, - Create2, - Call, - CallCode, - DelegateCall, - StaticCall, -} - -impl TrapConstruct for CallCreateTrap { - fn construct(v: CallCreateTrap) -> Self { - v - } -} - -impl TrapConsume for CallCreateTrap { - type Rest = Infallible; - - fn consume(self) -> Result { - Ok(self) - } -} - -/// Combined call create trap data. -pub enum CallCreateTrapData { - /// A call trap data. - Call(CallTrapData), - /// A create trap data. - Create(CreateTrapData), -} - -impl CallCreateTrapData { - pub const fn target_gas(&self) -> Option { - match self { - Self::Call(CallTrapData { gas, .. }) => Some(*gas), - Self::Create(_) => None, - } - } - - pub fn new_from + AsMut>( - opcode: CallCreateTrap, - machine: &mut Machine, - ) -> Result { - match opcode { - CallCreateTrap::Create => Ok(Self::Create(CreateTrapData::new_create_from(machine)?)), - CallCreateTrap::Create2 => Ok(Self::Create(CreateTrapData::new_create2_from(machine)?)), - CallCreateTrap::Call => Ok(Self::Call(CallTrapData::new_from( - CallScheme::Call, - machine, - )?)), - CallCreateTrap::CallCode => Ok(Self::Call(CallTrapData::new_from( - CallScheme::CallCode, - machine, - )?)), - CallCreateTrap::DelegateCall => Ok(Self::Call(CallTrapData::new_from( - CallScheme::DelegateCall, - machine, - )?)), - CallCreateTrap::StaticCall => Ok(Self::Call(CallTrapData::new_from( - CallScheme::StaticCall, - machine, - )?)), - } - } - - pub fn code(&self, handler: &H) -> Vec { - match self { - Self::Call(trap) => handler.code(trap.target), - Self::Create(trap) => trap.code.clone(), - } - } -} - -pub struct CallTrapData { - pub target: H160, - pub transfer: Option, - pub input: Vec, - pub gas: U256, - pub is_static: bool, - pub out_offset: U256, - pub out_len: U256, - pub context: Context, -} - -impl CallTrapData { - #[allow(clippy::too_many_arguments)] - fn new_from_params + AsMut>( - scheme: CallScheme, - memory: &mut Memory, - state: &mut S, - gas: &H256, - to: &H256, - value: Option<&H256>, - in_offset: &H256, - in_len: &H256, - out_offset: &H256, - out_len: &H256, - ) -> Result<((), Self), ExitError> { - let gas = h256_to_u256(*gas); - let value = value.map(|v| h256_to_u256(*v)).unwrap_or(U256::zero()); - let in_offset = h256_to_u256(*in_offset); - let in_len = h256_to_u256(*in_len); - let out_offset = h256_to_u256(*out_offset); - let out_len = h256_to_u256(*out_len); - - let in_end = in_offset - .checked_add(in_len) - .ok_or(ExitException::InvalidRange)?; - let out_end = out_offset - .checked_add(out_len) - .ok_or(ExitException::InvalidRange)?; - - let in_offset_len = if in_len == U256::zero() { - None - } else { - Some((u256_to_usize(in_offset)?, u256_to_usize(in_len)?)) - }; - - memory.resize_end(max(in_end, out_end))?; - - let input = in_offset_len - .map(|(in_offset, in_len)| memory.get(in_offset, in_len)) - .unwrap_or(Vec::new()); - - let context = match scheme { - CallScheme::Call | CallScheme::StaticCall => Context { - address: (*to).into(), - caller: state.as_ref().context.address, - apparent_value: value, - }, - CallScheme::CallCode => Context { - address: state.as_ref().context.address, - caller: state.as_ref().context.address, - apparent_value: value, - }, - CallScheme::DelegateCall => Context { - address: state.as_ref().context.address, - caller: state.as_ref().context.caller, - apparent_value: state.as_ref().context.apparent_value, - }, - }; - - let transfer = if scheme == CallScheme::Call { - Some(Transfer { - source: state.as_ref().context.address, - target: (*to).into(), - value, - }) - } else if scheme == CallScheme::CallCode { - Some(Transfer { - source: state.as_ref().context.address, - target: state.as_ref().context.address, - value, - }) - } else { - None - }; - - state.as_mut().retbuf = Vec::new(); - - Ok(( - (), - Self { - target: (*to).into(), - transfer, - input, - gas, - is_static: scheme == CallScheme::StaticCall, - context, - out_offset, - out_len, - }, - )) - } - - pub fn new_from + AsMut>( - scheme: CallScheme, - machine: &mut Machine, - ) -> Result { - let stack = &mut machine.stack; - let memory = &mut machine.memory; - let state = &mut machine.state; - - match scheme { - CallScheme::Call | CallScheme::CallCode => stack.perform_pop7_push0( - |gas, to, value, in_offset, in_len, out_offset, out_len| { - Self::new_from_params( - scheme, - memory, - state, - gas, - to, - Some(value), - in_offset, - in_len, - out_offset, - out_len, - ) - }, - ), - CallScheme::DelegateCall | CallScheme::StaticCall => { - stack.perform_pop6_push0(|gas, to, in_offset, in_len, out_offset, out_len| { - Self::new_from_params( - scheme, memory, state, gas, to, None, in_offset, in_len, out_offset, - out_len, - ) - }) - } - } - } - - pub fn feedback + AsMut>( - self, - reason: ExitResult, - retbuf: Vec, - machine: &mut Machine, - ) -> Result<(), ExitError> { - let target_len = min(self.out_len, U256::from(retbuf.len())); - let out_offset = self.out_offset; - - let ret = match reason { - Ok(_) => { - match machine - .memory - .copy_large(out_offset, U256::zero(), target_len, &retbuf[..]) - { - Ok(()) => { - let mut value = H256::default(); - U256::one().to_big_endian(&mut value[..]); - machine.stack.push(value)?; - - Ok(()) - } - Err(_) => { - machine.stack.push(H256::default())?; - - Ok(()) - } - } - } - Err(ExitError::Reverted) => { - machine.stack.push(H256::default())?; - - let _ = - machine - .memory - .copy_large(out_offset, U256::zero(), target_len, &retbuf[..]); - - Ok(()) - } - Err(ExitError::Exception(_)) => { - machine.stack.push(H256::default())?; - - Ok(()) - } - Err(ExitError::Fatal(e)) => { - machine.stack.push(H256::default())?; - - Err(e.into()) - } - }; - - match ret { - Ok(()) => { - machine.state.as_mut().retbuf = retbuf; - - Ok(()) - } - Err(e) => Err(e), - } - } - - pub fn has_value(&self) -> bool { - self.transfer - .as_ref() - .map(|t| t.value != U256::zero()) - .unwrap_or(false) - } -} - -#[derive(Clone, Debug)] -pub struct CreateTrapData { - pub scheme: CreateScheme, - pub value: U256, - pub code: Vec, -} - -impl CreateTrapData { - pub fn new_create_from + AsMut>( - machine: &mut Machine, - ) -> Result { - let stack = &mut machine.stack; - let memory = &mut machine.memory; - let state = &mut machine.state; - - stack.perform_pop3_push0(|value, code_offset, code_len| { - let value = h256_to_u256(*value); - let code_offset = h256_to_u256(*code_offset); - let code_len = h256_to_u256(*code_len); - - let code_offset_len = if code_len == U256::zero() { - None - } else { - Some((u256_to_usize(code_offset)?, u256_to_usize(code_len)?)) - }; - - let code = code_offset_len - .map(|(code_offset, code_len)| memory.get(code_offset, code_len)) - .unwrap_or(Vec::new()); - - let scheme = CreateScheme::Legacy { - caller: state.as_ref().context.address, - }; - - state.as_mut().retbuf = Vec::new(); - - Ok(( - (), - Self { - scheme, - value, - code, - }, - )) - }) - } - - pub fn new_create2_from + AsMut>( - machine: &mut Machine, - ) -> Result { - let stack = &mut machine.stack; - let memory = &mut machine.memory; - let state = &mut machine.state; - - stack.perform_pop4_push0(|value, code_offset, code_len, salt| { - let value = h256_to_u256(*value); - let code_offset = h256_to_u256(*code_offset); - let code_len = h256_to_u256(*code_len); - - let code_offset_len = if code_len == U256::zero() { - None - } else { - Some((u256_to_usize(code_offset)?, u256_to_usize(code_len)?)) - }; - - let code = code_offset_len - .map(|(code_offset, code_len)| memory.get(code_offset, code_len)) - .unwrap_or(Vec::new()); - - let code_hash = H256::from_slice(Keccak256::digest(&code).as_slice()); - - let scheme = CreateScheme::Create2 { - caller: state.as_ref().context.address, - salt: *salt, - code_hash, - }; - - state.as_mut().retbuf = Vec::new(); - - Ok(( - (), - Self { - scheme, - value, - code, - }, - )) - }) - } - - pub fn feedback + AsMut>( - self, - reason: Result, - retbuf: Vec, - machine: &mut Machine, - ) -> Result<(), ExitError> { - let ret = match reason { - Ok(address) => { - machine.stack.push(address.into())?; - Ok(()) - } - Err(ExitError::Reverted) => { - machine.stack.push(H256::default())?; - Ok(()) - } - Err(ExitError::Exception(_)) => { - machine.stack.push(H256::default())?; - Ok(()) - } - Err(ExitError::Fatal(e)) => { - machine.stack.push(H256::default())?; - Err(e.into()) - } - }; - - match ret { - Ok(()) => { - machine.state.as_mut().retbuf = retbuf; - - Ok(()) - } - Err(e) => Err(e), - } - } -} diff --git a/interpreter/src/etable.rs b/interpreter/src/etable.rs index c813940ea..73d095123 100644 --- a/interpreter/src/etable.rs +++ b/interpreter/src/etable.rs @@ -1,5 +1,5 @@ use crate::{ - call_create::CallCreateTrap, eval::*, ExitResult, GasState, Machine, Opcode, RuntimeBackend, + eval::*, trap::CallCreateTrap, ExitResult, GasState, Machine, Opcode, RuntimeBackend, RuntimeEnvironment, RuntimeState, TrapConstruct, }; use core::marker::PhantomData; diff --git a/interpreter/src/eval/mod.rs b/interpreter/src/eval/mod.rs index 191fa6773..ffd7aa296 100644 --- a/interpreter/src/eval/mod.rs +++ b/interpreter/src/eval/mod.rs @@ -6,7 +6,7 @@ mod misc; mod system; use crate::{ - call_create::CallCreateTrap, Control, ExitException, ExitSucceed, GasState, Machine, Opcode, + trap::CallCreateTrap, Control, ExitException, ExitSucceed, GasState, Machine, Opcode, RuntimeBackend, RuntimeEnvironment, RuntimeState, TrapConstruct, }; use core::ops::{BitAnd, BitOr, BitXor}; diff --git a/interpreter/src/lib.rs b/interpreter/src/lib.rs index d63f4d22f..cc5d97e94 100644 --- a/interpreter/src/lib.rs +++ b/interpreter/src/lib.rs @@ -6,7 +6,6 @@ extern crate alloc; -pub mod call_create; mod error; mod etable; pub mod eval; @@ -15,7 +14,7 @@ mod memory; mod opcode; mod runtime; mod stack; -mod trap; +pub mod trap; pub mod utils; mod valids; diff --git a/interpreter/src/trap.rs b/interpreter/src/trap.rs index deae01a26..4229145c6 100644 --- a/interpreter/src/trap.rs +++ b/interpreter/src/trap.rs @@ -1,3 +1,16 @@ +//! Call and create trap handler. + +use crate::utils::{h256_to_u256, u256_to_usize}; +use crate::{ + Context, ExitError, ExitException, ExitResult, Machine, Memory, RuntimeBackend, RuntimeState, + Transfer, +}; +use alloc::vec::Vec; +use core::cmp::{max, min}; +use core::convert::Infallible; +use primitive_types::{H160, H256, U256}; +use sha3::{Digest, Keccak256}; + pub trait TrapConstruct { fn construct(v: T) -> Self; } @@ -7,3 +20,474 @@ pub trait TrapConsume { fn consume(self) -> Result; } + +/// Create scheme. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub enum CreateScheme { + /// Legacy create scheme of `CREATE`. + Legacy { + /// Caller of the create. + caller: H160, + }, + /// Create scheme of `CREATE2`. + Create2 { + /// Caller of the create. + caller: H160, + /// Code hash. + code_hash: H256, + /// Salt. + salt: H256, + }, +} + +impl CreateScheme { + pub fn address(&self, handler: &H) -> H160 { + match self { + Self::Create2 { + caller, + code_hash, + salt, + } => { + let mut hasher = Keccak256::new(); + hasher.update([0xff]); + hasher.update(&caller[..]); + hasher.update(&salt[..]); + hasher.update(&code_hash[..]); + H256::from_slice(hasher.finalize().as_slice()).into() + } + Self::Legacy { caller } => { + let nonce = handler.nonce(*caller); + let mut stream = rlp::RlpStream::new_list(2); + stream.append(caller); + stream.append(&nonce); + H256::from_slice(Keccak256::digest(&stream.out()).as_slice()).into() + } + } + } + + pub const fn caller(&self) -> H160 { + match self { + Self::Create2 { caller, .. } => *caller, + Self::Legacy { caller } => *caller, + } + } +} + +/// Call scheme. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub enum CallScheme { + /// `CALL` + Call, + /// `CALLCODE` + CallCode, + /// `DELEGATECALL` + DelegateCall, + /// `STATICCALL` + StaticCall, +} + +pub enum CallCreateTrap { + Create, + Create2, + Call, + CallCode, + DelegateCall, + StaticCall, +} + +impl TrapConstruct for CallCreateTrap { + fn construct(v: CallCreateTrap) -> Self { + v + } +} + +impl TrapConsume for CallCreateTrap { + type Rest = Infallible; + + fn consume(self) -> Result { + Ok(self) + } +} + +/// Combined call create trap data. +pub enum CallCreateTrapData { + /// A call trap data. + Call(CallTrapData), + /// A create trap data. + Create(CreateTrapData), +} + +impl CallCreateTrapData { + pub const fn target_gas(&self) -> Option { + match self { + Self::Call(CallTrapData { gas, .. }) => Some(*gas), + Self::Create(_) => None, + } + } + + pub fn new_from + AsMut>( + opcode: CallCreateTrap, + machine: &mut Machine, + ) -> Result { + match opcode { + CallCreateTrap::Create => Ok(Self::Create(CreateTrapData::new_create_from(machine)?)), + CallCreateTrap::Create2 => Ok(Self::Create(CreateTrapData::new_create2_from(machine)?)), + CallCreateTrap::Call => Ok(Self::Call(CallTrapData::new_from( + CallScheme::Call, + machine, + )?)), + CallCreateTrap::CallCode => Ok(Self::Call(CallTrapData::new_from( + CallScheme::CallCode, + machine, + )?)), + CallCreateTrap::DelegateCall => Ok(Self::Call(CallTrapData::new_from( + CallScheme::DelegateCall, + machine, + )?)), + CallCreateTrap::StaticCall => Ok(Self::Call(CallTrapData::new_from( + CallScheme::StaticCall, + machine, + )?)), + } + } + + pub fn code(&self, handler: &H) -> Vec { + match self { + Self::Call(trap) => handler.code(trap.target), + Self::Create(trap) => trap.code.clone(), + } + } +} + +pub struct CallTrapData { + pub target: H160, + pub transfer: Option, + pub input: Vec, + pub gas: U256, + pub is_static: bool, + pub out_offset: U256, + pub out_len: U256, + pub context: Context, +} + +impl CallTrapData { + #[allow(clippy::too_many_arguments)] + fn new_from_params + AsMut>( + scheme: CallScheme, + memory: &mut Memory, + state: &mut S, + gas: &H256, + to: &H256, + value: Option<&H256>, + in_offset: &H256, + in_len: &H256, + out_offset: &H256, + out_len: &H256, + ) -> Result<((), Self), ExitError> { + let gas = h256_to_u256(*gas); + let value = value.map(|v| h256_to_u256(*v)).unwrap_or(U256::zero()); + let in_offset = h256_to_u256(*in_offset); + let in_len = h256_to_u256(*in_len); + let out_offset = h256_to_u256(*out_offset); + let out_len = h256_to_u256(*out_len); + + let in_end = in_offset + .checked_add(in_len) + .ok_or(ExitException::InvalidRange)?; + let out_end = out_offset + .checked_add(out_len) + .ok_or(ExitException::InvalidRange)?; + + let in_offset_len = if in_len == U256::zero() { + None + } else { + Some((u256_to_usize(in_offset)?, u256_to_usize(in_len)?)) + }; + + memory.resize_end(max(in_end, out_end))?; + + let input = in_offset_len + .map(|(in_offset, in_len)| memory.get(in_offset, in_len)) + .unwrap_or(Vec::new()); + + let context = match scheme { + CallScheme::Call | CallScheme::StaticCall => Context { + address: (*to).into(), + caller: state.as_ref().context.address, + apparent_value: value, + }, + CallScheme::CallCode => Context { + address: state.as_ref().context.address, + caller: state.as_ref().context.address, + apparent_value: value, + }, + CallScheme::DelegateCall => Context { + address: state.as_ref().context.address, + caller: state.as_ref().context.caller, + apparent_value: state.as_ref().context.apparent_value, + }, + }; + + let transfer = if scheme == CallScheme::Call { + Some(Transfer { + source: state.as_ref().context.address, + target: (*to).into(), + value, + }) + } else if scheme == CallScheme::CallCode { + Some(Transfer { + source: state.as_ref().context.address, + target: state.as_ref().context.address, + value, + }) + } else { + None + }; + + state.as_mut().retbuf = Vec::new(); + + Ok(( + (), + Self { + target: (*to).into(), + transfer, + input, + gas, + is_static: scheme == CallScheme::StaticCall, + context, + out_offset, + out_len, + }, + )) + } + + pub fn new_from + AsMut>( + scheme: CallScheme, + machine: &mut Machine, + ) -> Result { + let stack = &mut machine.stack; + let memory = &mut machine.memory; + let state = &mut machine.state; + + match scheme { + CallScheme::Call | CallScheme::CallCode => stack.perform_pop7_push0( + |gas, to, value, in_offset, in_len, out_offset, out_len| { + Self::new_from_params( + scheme, + memory, + state, + gas, + to, + Some(value), + in_offset, + in_len, + out_offset, + out_len, + ) + }, + ), + CallScheme::DelegateCall | CallScheme::StaticCall => { + stack.perform_pop6_push0(|gas, to, in_offset, in_len, out_offset, out_len| { + Self::new_from_params( + scheme, memory, state, gas, to, None, in_offset, in_len, out_offset, + out_len, + ) + }) + } + } + } + + pub fn feedback + AsMut>( + self, + reason: ExitResult, + retbuf: Vec, + machine: &mut Machine, + ) -> Result<(), ExitError> { + let target_len = min(self.out_len, U256::from(retbuf.len())); + let out_offset = self.out_offset; + + let ret = match reason { + Ok(_) => { + match machine + .memory + .copy_large(out_offset, U256::zero(), target_len, &retbuf[..]) + { + Ok(()) => { + let mut value = H256::default(); + U256::one().to_big_endian(&mut value[..]); + machine.stack.push(value)?; + + Ok(()) + } + Err(_) => { + machine.stack.push(H256::default())?; + + Ok(()) + } + } + } + Err(ExitError::Reverted) => { + machine.stack.push(H256::default())?; + + let _ = + machine + .memory + .copy_large(out_offset, U256::zero(), target_len, &retbuf[..]); + + Ok(()) + } + Err(ExitError::Exception(_)) => { + machine.stack.push(H256::default())?; + + Ok(()) + } + Err(ExitError::Fatal(e)) => { + machine.stack.push(H256::default())?; + + Err(e.into()) + } + }; + + match ret { + Ok(()) => { + machine.state.as_mut().retbuf = retbuf; + + Ok(()) + } + Err(e) => Err(e), + } + } + + pub fn has_value(&self) -> bool { + self.transfer + .as_ref() + .map(|t| t.value != U256::zero()) + .unwrap_or(false) + } +} + +#[derive(Clone, Debug)] +pub struct CreateTrapData { + pub scheme: CreateScheme, + pub value: U256, + pub code: Vec, +} + +impl CreateTrapData { + pub fn new_create_from + AsMut>( + machine: &mut Machine, + ) -> Result { + let stack = &mut machine.stack; + let memory = &mut machine.memory; + let state = &mut machine.state; + + stack.perform_pop3_push0(|value, code_offset, code_len| { + let value = h256_to_u256(*value); + let code_offset = h256_to_u256(*code_offset); + let code_len = h256_to_u256(*code_len); + + let code_offset_len = if code_len == U256::zero() { + None + } else { + Some((u256_to_usize(code_offset)?, u256_to_usize(code_len)?)) + }; + + let code = code_offset_len + .map(|(code_offset, code_len)| memory.get(code_offset, code_len)) + .unwrap_or(Vec::new()); + + let scheme = CreateScheme::Legacy { + caller: state.as_ref().context.address, + }; + + state.as_mut().retbuf = Vec::new(); + + Ok(( + (), + Self { + scheme, + value, + code, + }, + )) + }) + } + + pub fn new_create2_from + AsMut>( + machine: &mut Machine, + ) -> Result { + let stack = &mut machine.stack; + let memory = &mut machine.memory; + let state = &mut machine.state; + + stack.perform_pop4_push0(|value, code_offset, code_len, salt| { + let value = h256_to_u256(*value); + let code_offset = h256_to_u256(*code_offset); + let code_len = h256_to_u256(*code_len); + + let code_offset_len = if code_len == U256::zero() { + None + } else { + Some((u256_to_usize(code_offset)?, u256_to_usize(code_len)?)) + }; + + let code = code_offset_len + .map(|(code_offset, code_len)| memory.get(code_offset, code_len)) + .unwrap_or(Vec::new()); + + let code_hash = H256::from_slice(Keccak256::digest(&code).as_slice()); + + let scheme = CreateScheme::Create2 { + caller: state.as_ref().context.address, + salt: *salt, + code_hash, + }; + + state.as_mut().retbuf = Vec::new(); + + Ok(( + (), + Self { + scheme, + value, + code, + }, + )) + }) + } + + pub fn feedback + AsMut>( + self, + reason: Result, + retbuf: Vec, + machine: &mut Machine, + ) -> Result<(), ExitError> { + let ret = match reason { + Ok(address) => { + machine.stack.push(address.into())?; + Ok(()) + } + Err(ExitError::Reverted) => { + machine.stack.push(H256::default())?; + Ok(()) + } + Err(ExitError::Exception(_)) => { + machine.stack.push(H256::default())?; + Ok(()) + } + Err(ExitError::Fatal(e)) => { + machine.stack.push(H256::default())?; + Err(e.into()) + } + }; + + match ret { + Ok(()) => { + machine.state.as_mut().retbuf = retbuf; + + Ok(()) + } + Err(e) => Err(e), + } + } +} diff --git a/interpreter/tests/usability.rs b/interpreter/tests/usability.rs index 9d94311c0..45e79dad9 100644 --- a/interpreter/tests/usability.rs +++ b/interpreter/tests/usability.rs @@ -1,5 +1,5 @@ use evm_interpreter::{ - call_create::CallCreateTrap, Capture, Context, Control, Etable, EtableInterpreter, ExitError, + trap::CallCreateTrap, Capture, Context, Control, Etable, EtableInterpreter, ExitError, ExitSucceed, Interpreter, Log, Machine, Opcode, RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment, RuntimeState, TransactionContext, }; diff --git a/src/standard/invoker/mod.rs b/src/standard/invoker/mod.rs index 8d17cdef8..b12d9ee4b 100644 --- a/src/standard/invoker/mod.rs +++ b/src/standard/invoker/mod.rs @@ -6,9 +6,7 @@ pub use self::resolver::{EtableResolver, PrecompileSet, Resolver}; pub use self::state::InvokerState; use super::Config; -use crate::call_create::{ - CallCreateTrap, CallCreateTrapData, CallTrapData, CreateScheme, CreateTrapData, -}; +use crate::trap::{CallCreateTrap, CallCreateTrapData, CallTrapData, CreateScheme, CreateTrapData}; use crate::{ Capture, Context, ExitError, ExitException, ExitResult, ExitSucceed, Interpreter, Invoker as InvokerT, InvokerControl, MergeStrategy, Opcode, RuntimeBackend, RuntimeEnvironment, diff --git a/src/standard/mod.rs b/src/standard/mod.rs index 93afffeeb..446cde139 100644 --- a/src/standard/mod.rs +++ b/src/standard/mod.rs @@ -22,11 +22,11 @@ use primitive_types::{H160, H256, U256}; pub type Machine<'config> = crate::Machine>; /// Standard Etable opcode handle function. -pub type Efn<'config, H> = crate::Efn, H, crate::call_create::CallCreateTrap>; +pub type Efn<'config, H> = crate::Efn, H, crate::trap::CallCreateTrap>; /// Standard Etable. pub type Etable<'config, H, F = Efn<'config, H>> = - crate::Etable, H, crate::call_create::CallCreateTrap, F>; + crate::Etable, H, crate::trap::CallCreateTrap, F>; pub trait GasMutState: GasState { fn record_gas(&mut self, gas: U256) -> Result<(), ExitError>; From 6ba4c6d2ec3784187b734fe0c2cfdf63e052ef9c Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 6 Dec 2023 02:11:06 +0100 Subject: [PATCH 2/3] Move interpreter::advance inside trap --- interpreter/src/trap.rs | 66 +++++++++++++++++++++++-------------- src/standard/invoker/mod.rs | 6 ++-- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/interpreter/src/trap.rs b/interpreter/src/trap.rs index 4229145c6..18b85b974 100644 --- a/interpreter/src/trap.rs +++ b/interpreter/src/trap.rs @@ -2,8 +2,8 @@ use crate::utils::{h256_to_u256, u256_to_usize}; use crate::{ - Context, ExitError, ExitException, ExitResult, Machine, Memory, RuntimeBackend, RuntimeState, - Transfer, + Context, ExitError, ExitException, ExitResult, Interpreter, Machine, Memory, RuntimeBackend, + RuntimeState, Transfer, }; use alloc::vec::Vec; use core::cmp::{max, min}; @@ -297,52 +297,61 @@ impl CallTrapData { } } - pub fn feedback + AsMut>( + pub fn feedback< + S: AsRef + AsMut, + H, + Tr, + I: Interpreter, + >( self, reason: ExitResult, retbuf: Vec, - machine: &mut Machine, + interpreter: &mut I, ) -> Result<(), ExitError> { let target_len = min(self.out_len, U256::from(retbuf.len())); let out_offset = self.out_offset; let ret = match reason { Ok(_) => { - match machine - .memory - .copy_large(out_offset, U256::zero(), target_len, &retbuf[..]) - { + match interpreter.machine_mut().memory.copy_large( + out_offset, + U256::zero(), + target_len, + &retbuf[..], + ) { Ok(()) => { let mut value = H256::default(); U256::one().to_big_endian(&mut value[..]); - machine.stack.push(value)?; + interpreter.machine_mut().stack.push(value)?; Ok(()) } Err(_) => { - machine.stack.push(H256::default())?; + interpreter.machine_mut().stack.push(H256::default())?; Ok(()) } } } Err(ExitError::Reverted) => { - machine.stack.push(H256::default())?; + interpreter.machine_mut().stack.push(H256::default())?; - let _ = - machine - .memory - .copy_large(out_offset, U256::zero(), target_len, &retbuf[..]); + let _ = interpreter.machine_mut().memory.copy_large( + out_offset, + U256::zero(), + target_len, + &retbuf[..], + ); Ok(()) } Err(ExitError::Exception(_)) => { - machine.stack.push(H256::default())?; + interpreter.machine_mut().stack.push(H256::default())?; Ok(()) } Err(ExitError::Fatal(e)) => { - machine.stack.push(H256::default())?; + interpreter.machine_mut().stack.push(H256::default())?; Err(e.into()) } @@ -350,7 +359,8 @@ impl CallTrapData { match ret { Ok(()) => { - machine.state.as_mut().retbuf = retbuf; + interpreter.machine_mut().state.as_mut().retbuf = retbuf; + interpreter.advance(); Ok(()) } @@ -456,34 +466,40 @@ impl CreateTrapData { }) } - pub fn feedback + AsMut>( + pub fn feedback< + S: AsRef + AsMut, + H, + Tr, + I: Interpreter, + >( self, reason: Result, retbuf: Vec, - machine: &mut Machine, + interpreter: &mut I, ) -> Result<(), ExitError> { let ret = match reason { Ok(address) => { - machine.stack.push(address.into())?; + interpreter.machine_mut().stack.push(address.into())?; Ok(()) } Err(ExitError::Reverted) => { - machine.stack.push(H256::default())?; + interpreter.machine_mut().stack.push(H256::default())?; Ok(()) } Err(ExitError::Exception(_)) => { - machine.stack.push(H256::default())?; + interpreter.machine_mut().stack.push(H256::default())?; Ok(()) } Err(ExitError::Fatal(e)) => { - machine.stack.push(H256::default())?; + interpreter.machine_mut().stack.push(H256::default())?; Err(e.into()) } }; match ret { Ok(()) => { - machine.state.as_mut().retbuf = retbuf; + interpreter.machine_mut().state.as_mut().retbuf = retbuf; + interpreter.advance(); Ok(()) } diff --git a/src/standard/invoker/mod.rs b/src/standard/invoker/mod.rs index b12d9ee4b..36c888c39 100644 --- a/src/standard/invoker/mod.rs +++ b/src/standard/invoker/mod.rs @@ -538,8 +538,7 @@ where parent.machine_mut().state.merge(substate, strategy); handler.pop_substate(strategy); - trap.feedback(result, retbuf, parent.machine_mut())?; - parent.advance(); + trap.feedback(result, retbuf, parent)?; Ok(()) } @@ -549,8 +548,7 @@ where parent.machine_mut().state.merge(substate, strategy); handler.pop_substate(strategy); - trap.feedback(result, retbuf, parent.machine_mut())?; - parent.advance(); + trap.feedback(result, retbuf, parent)?; Ok(()) } From 92504d64de9b4d1bf78289ec84bf480e23316f62 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 6 Dec 2023 05:40:33 +0100 Subject: [PATCH 3/3] Move only State to associate type --- interpreter/src/etable.rs | 24 +++-- interpreter/src/interpreter/etable.rs | 48 ++++----- interpreter/src/interpreter/mod.rs | 17 ++-- interpreter/src/lib.rs | 3 +- interpreter/src/trap.rs | 28 +++--- interpreter/tests/performance.rs | 3 +- interpreter/tests/usability.rs | 7 +- jsontests/src/run.rs | 2 +- src/call_stack.rs | 134 ++++++++++++++------------ src/invoker.rs | 15 +-- src/standard/invoker/mod.rs | 39 ++++---- src/standard/invoker/resolver.rs | 35 ++++--- src/standard/invoker/routines.rs | 42 ++++---- 13 files changed, 212 insertions(+), 185 deletions(-) diff --git a/interpreter/src/etable.rs b/interpreter/src/etable.rs index 73d095123..8d38ded60 100644 --- a/interpreter/src/etable.rs +++ b/interpreter/src/etable.rs @@ -5,20 +5,28 @@ use crate::{ use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; -pub trait EtableSet { +pub trait EtableSet { + type State; + type Handle; + type Trap; + fn eval( &self, - machine: &mut Machine, - handle: &mut H, + machine: &mut Machine, + handle: &mut Self::Handle, opcode: Opcode, position: usize, - ) -> Control; + ) -> Control; } -impl EtableSet for Etable +impl EtableSet for Etable where F: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, { + type State = S; + type Handle = H; + type Trap = Tr; + fn eval( &self, machine: &mut Machine, @@ -30,11 +38,15 @@ where } } -impl EtableSet for (Etable, Etable) +impl EtableSet for (Etable, Etable) where F1: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, F2: Fn(&mut Machine, &mut H, Opcode, usize) -> Control, { + type State = S; + type Handle = H; + type Trap = Tr; + fn eval( &self, machine: &mut Machine, diff --git a/interpreter/src/interpreter/etable.rs b/interpreter/src/interpreter/etable.rs index 170cb81ed..6594365bb 100644 --- a/interpreter/src/interpreter/etable.rs +++ b/interpreter/src/interpreter/etable.rs @@ -1,20 +1,19 @@ +use crate::interpreter::{Interpreter, RunInterpreter, StepInterpreter}; use crate::{ Capture, Control, EtableSet, ExitError, ExitException, ExitFatal, ExitResult, ExitSucceed, - Interpreter, Machine, Opcode, Stack, StepInterpreter, Valids, + Machine, Opcode, Stack, Valids, }; use alloc::vec::Vec; -use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; -pub struct EtableInterpreter<'etable, S, H, Tr, ES> { +pub struct EtableInterpreter<'etable, S, ES> { valids: Valids, position: usize, machine: Machine, etable: &'etable ES, - _marker: PhantomData<(H, Tr)>, } -impl<'etable, S, H, Tr, ES> Deref for EtableInterpreter<'etable, S, H, Tr, ES> { +impl<'etable, S, ES> Deref for EtableInterpreter<'etable, S, ES> { type Target = Machine; fn deref(&self) -> &Machine { @@ -22,15 +21,15 @@ impl<'etable, S, H, Tr, ES> Deref for EtableInterpreter<'etable, S, H, Tr, ES> { } } -impl<'etable, S, H, Tr, ES> DerefMut for EtableInterpreter<'etable, S, H, Tr, ES> { +impl<'etable, S, ES> DerefMut for EtableInterpreter<'etable, S, ES> { fn deref_mut(&mut self) -> &mut Machine { &mut self.machine } } -impl<'etable, S, H, Tr, ES> EtableInterpreter<'etable, S, H, Tr, ES> +impl<'etable, S, ES> EtableInterpreter<'etable, S, ES> where - ES: EtableSet, + ES: EtableSet, { /// Return a reference of the program counter. pub const fn position(&self) -> usize { @@ -45,7 +44,6 @@ where valids, position: 0, etable, - _marker: PhantomData, } } @@ -86,10 +84,9 @@ where } } -impl<'etable, S, H, Tr, ES> Interpreter for EtableInterpreter<'etable, S, H, Tr, ES> -where - ES: EtableSet, -{ +impl<'etable, S, ES> Interpreter for EtableInterpreter<'etable, S, ES> { + type State = S; + fn machine(&self) -> &Machine { &self.machine } @@ -102,6 +99,19 @@ where (self.machine.state, self.machine.retval) } + fn advance(&mut self) { + if self.position == self.code.len() { + return; + } + + self.position += 1; + } +} + +impl<'etable, S, H, Tr, ES> RunInterpreter for EtableInterpreter<'etable, S, ES> +where + ES: EtableSet, +{ fn run(&mut self, handle: &mut H) -> Capture { loop { match self.step(handle) { @@ -110,19 +120,11 @@ where } } } - - fn advance(&mut self) { - if self.position == self.code.len() { - return; - } - - self.position += 1; - } } -impl<'etable, S, H, Tr, ES> StepInterpreter for EtableInterpreter<'etable, S, H, Tr, ES> +impl<'etable, S, H, Tr, ES> StepInterpreter for EtableInterpreter<'etable, S, ES> where - ES: EtableSet, + ES: EtableSet, { #[inline] fn step(&mut self, handle: &mut H) -> Result<(), Capture> { diff --git a/interpreter/src/interpreter/mod.rs b/interpreter/src/interpreter/mod.rs index 8de9b7cd6..37f66a83a 100644 --- a/interpreter/src/interpreter/mod.rs +++ b/interpreter/src/interpreter/mod.rs @@ -5,15 +5,20 @@ pub use self::etable::EtableInterpreter; use crate::{Capture, ExitResult, Machine}; use alloc::vec::Vec; -pub trait Interpreter { - fn machine(&self) -> &Machine; - fn machine_mut(&mut self) -> &mut Machine; +pub trait Interpreter { + type State; - fn deconstruct(self) -> (S, Vec); - fn run(&mut self, handle: &mut H) -> Capture; + fn machine(&self) -> &Machine; + fn machine_mut(&mut self) -> &mut Machine; + + fn deconstruct(self) -> (Self::State, Vec); fn advance(&mut self); } -pub trait StepInterpreter: Interpreter { +pub trait RunInterpreter: Interpreter { + fn run(&mut self, handle: &mut H) -> Capture; +} + +pub trait StepInterpreter: Interpreter { fn step(&mut self, handle: &mut H) -> Result<(), Capture>; } diff --git a/interpreter/src/lib.rs b/interpreter/src/lib.rs index cc5d97e94..6cc589ece 100644 --- a/interpreter/src/lib.rs +++ b/interpreter/src/lib.rs @@ -9,7 +9,7 @@ extern crate alloc; mod error; mod etable; pub mod eval; -mod interpreter; +pub mod interpreter; mod memory; mod opcode; mod runtime; @@ -20,7 +20,6 @@ mod valids; pub use crate::error::{Capture, ExitError, ExitException, ExitFatal, ExitResult, ExitSucceed}; pub use crate::etable::{Control, Efn, Etable, EtableSet}; -pub use crate::interpreter::{EtableInterpreter, Interpreter, StepInterpreter}; pub use crate::memory::Memory; pub use crate::opcode::Opcode; pub use crate::runtime::{ diff --git a/interpreter/src/trap.rs b/interpreter/src/trap.rs index 18b85b974..f111e5e80 100644 --- a/interpreter/src/trap.rs +++ b/interpreter/src/trap.rs @@ -2,8 +2,8 @@ use crate::utils::{h256_to_u256, u256_to_usize}; use crate::{ - Context, ExitError, ExitException, ExitResult, Interpreter, Machine, Memory, RuntimeBackend, - RuntimeState, Transfer, + interpreter::Interpreter, Context, ExitError, ExitException, ExitResult, Machine, Memory, + RuntimeBackend, RuntimeState, Transfer, }; use alloc::vec::Vec; use core::cmp::{max, min}; @@ -297,17 +297,15 @@ impl CallTrapData { } } - pub fn feedback< - S: AsRef + AsMut, - H, - Tr, - I: Interpreter, - >( + pub fn feedback( self, reason: ExitResult, retbuf: Vec, interpreter: &mut I, - ) -> Result<(), ExitError> { + ) -> Result<(), ExitError> + where + I::State: AsRef + AsMut, + { let target_len = min(self.out_len, U256::from(retbuf.len())); let out_offset = self.out_offset; @@ -466,17 +464,15 @@ impl CreateTrapData { }) } - pub fn feedback< - S: AsRef + AsMut, - H, - Tr, - I: Interpreter, - >( + pub fn feedback( self, reason: Result, retbuf: Vec, interpreter: &mut I, - ) -> Result<(), ExitError> { + ) -> Result<(), ExitError> + where + I::State: AsRef + AsMut, + { let ret = match reason { Ok(address) => { interpreter.machine_mut().stack.push(address.into())?; diff --git a/interpreter/tests/performance.rs b/interpreter/tests/performance.rs index aa69af9ca..1e9d255f2 100644 --- a/interpreter/tests/performance.rs +++ b/interpreter/tests/performance.rs @@ -1,4 +1,5 @@ -use evm_interpreter::{Capture, Etable, EtableInterpreter, ExitSucceed, Interpreter, Machine}; +use evm_interpreter::interpreter::{EtableInterpreter, RunInterpreter}; +use evm_interpreter::{Capture, Etable, ExitSucceed, Machine}; use std::rc::Rc; static ETABLE: Etable<(), (), ()> = Etable::core(); diff --git a/interpreter/tests/usability.rs b/interpreter/tests/usability.rs index 45e79dad9..02421a987 100644 --- a/interpreter/tests/usability.rs +++ b/interpreter/tests/usability.rs @@ -1,7 +1,8 @@ +use evm_interpreter::interpreter::{EtableInterpreter, RunInterpreter}; use evm_interpreter::{ - trap::CallCreateTrap, Capture, Context, Control, Etable, EtableInterpreter, ExitError, - ExitSucceed, Interpreter, Log, Machine, Opcode, RuntimeBackend, RuntimeBaseBackend, - RuntimeEnvironment, RuntimeState, TransactionContext, + trap::CallCreateTrap, Capture, Context, Control, Etable, ExitError, ExitSucceed, Log, Machine, + Opcode, RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment, RuntimeState, + TransactionContext, }; use primitive_types::{H160, H256, U256}; use std::rc::Rc; diff --git a/jsontests/src/run.rs b/jsontests/src/run.rs index 7ee51edf7..c5306426f 100644 --- a/jsontests/src/run.rs +++ b/jsontests/src/run.rs @@ -4,7 +4,7 @@ use crate::types::*; use evm::standard::{Config, Etable, EtableResolver, Invoker, TransactArgs}; use evm::utils::u256_to_h256; use evm::Capture; -use evm::{GasState, Interpreter}; +use evm::{interpreter::Interpreter, GasState}; use evm_precompile::StandardPrecompileSet; use primitive_types::U256; use std::collections::{BTreeMap, BTreeSet}; diff --git a/src/call_stack.rs b/src/call_stack.rs index 2c0670f23..d0f08adce 100644 --- a/src/call_stack.rs +++ b/src/call_stack.rs @@ -1,7 +1,5 @@ -use crate::{ - Capture, ExitError, ExitFatal, ExitResult, Interpreter, Invoker, InvokerControl, - StepInterpreter, -}; +use crate::interpreter::{Interpreter, RunInterpreter, StepInterpreter}; +use crate::{Capture, ExitError, ExitFatal, ExitResult, Invoker, InvokerControl}; use alloc::vec::Vec; use core::convert::Infallible; @@ -23,7 +21,7 @@ enum LastSubstackStatus { // Note: this should not be exposed to public because it does not implement // Drop. -struct CallStack<'backend, 'invoker, S, H, Tr, I: Invoker> { +struct CallStack<'backend, 'invoker, H, Tr, I: Invoker> { stack: Vec>, last: Option>, initial_depth: usize, @@ -31,9 +29,9 @@ struct CallStack<'backend, 'invoker, S, H, Tr, I: Invoker> { invoker: &'invoker I, } -impl<'backend, 'invoker, S, H, Tr, I> CallStack<'backend, 'invoker, S, H, Tr, I> +impl<'backend, 'invoker, H, Tr, I> CallStack<'backend, 'invoker, H, Tr, I> where - I: Invoker, + I: Invoker, { pub fn new( machine: I::Interpreter, @@ -53,29 +51,6 @@ where } } - #[allow(clippy::type_complexity)] - pub fn run( - &mut self, - ) -> Capture, I::Interrupt> { - loop { - let step_ret = self.step_run(); - - if let Err(step_ret) = step_ret { - return step_ret; - } - } - } - - #[allow(clippy::type_complexity)] - pub fn step_run( - &mut self, - ) -> Result<(), Capture, I::Interrupt>> { - self.step_with(|machine, handler| { - let result = machine.run(handler); - LastSubstackStatus::Exited(result) - }) - } - #[allow(clippy::type_complexity)] fn step_with( &mut self, @@ -206,10 +181,39 @@ where } } -impl<'backend, 'invoker, S, H, Tr, I> CallStack<'backend, 'invoker, S, H, Tr, I> +impl<'backend, 'invoker, H, Tr, I> CallStack<'backend, 'invoker, H, Tr, I> +where + I: Invoker, + I::Interpreter: RunInterpreter, +{ + #[allow(clippy::type_complexity)] + pub fn run( + &mut self, + ) -> Capture, I::Interrupt> { + loop { + let step_ret = self.step_run(); + + if let Err(step_ret) = step_ret { + return step_ret; + } + } + } + + #[allow(clippy::type_complexity)] + pub fn step_run( + &mut self, + ) -> Result<(), Capture, I::Interrupt>> { + self.step_with(|machine, handler| { + let result = machine.run(handler); + LastSubstackStatus::Exited(result) + }) + } +} + +impl<'backend, 'invoker, H, Tr, I> CallStack<'backend, 'invoker, H, Tr, I> where - I: Invoker, - I::Interpreter: StepInterpreter, + I: Invoker, + I::Interpreter: StepInterpreter, { #[allow(clippy::type_complexity)] pub fn step( @@ -225,7 +229,7 @@ where } } -fn execute( +fn execute( mut machine: I::Interpreter, initial_depth: usize, heap_depth: Option, @@ -233,7 +237,8 @@ fn execute( invoker: &I, ) -> Result<(ExitResult, I::Interpreter), ExitFatal> where - I: Invoker, + I: Invoker, + I::Interpreter: RunInterpreter, { let mut result = machine.run(backend); @@ -295,14 +300,14 @@ where } } -enum HeapTransactState<'backend, 'invoker, S, H, Tr, I: Invoker> { +enum HeapTransactState<'backend, 'invoker, H, Tr, I: Invoker> { Created { args: I::TransactArgs, invoker: &'invoker I, backend: &'backend mut H, }, Running { - call_stack: CallStack<'backend, 'invoker, S, H, Tr, I>, + call_stack: CallStack<'backend, 'invoker, H, Tr, I>, transact_invoke: I::TransactInvoke, }, } @@ -310,13 +315,13 @@ enum HeapTransactState<'backend, 'invoker, S, H, Tr, I: Invoker> { /// Heap-based call stack for a transaction. This is suitable for single /// stepping or debugging. The hybrid version [transact] uses a heap-based call /// stack internally after certain depth. -pub struct HeapTransact<'backend, 'invoker, S, H, Tr, I: Invoker>( - Option>, +pub struct HeapTransact<'backend, 'invoker, H, Tr, I: Invoker>( + Option>, ); -impl<'backend, 'invoker, S, H, Tr, I> HeapTransact<'backend, 'invoker, S, H, Tr, I> +impl<'backend, 'invoker, H, Tr, I> HeapTransact<'backend, 'invoker, H, Tr, I> where - I: Invoker, + I: Invoker, { /// Create a new heap-based call stack. pub fn new( @@ -338,7 +343,7 @@ where ) -> Result<(), Capture, I::Interrupt>> where FS: Fn( - &mut CallStack<'backend, 'invoker, S, H, Tr, I>, + &mut CallStack<'backend, 'invoker, H, Tr, I>, ) -> Result< (), Capture, I::Interrupt>, @@ -407,6 +412,24 @@ where ret } + /// The machine of the last item on the call stack. This will be `None` if + /// the heap stack is just created. + pub fn last_interpreter(&self) -> Option<&I::Interpreter> { + match &self.0 { + Some(HeapTransactState::Running { call_stack, .. }) => match &call_stack.last { + Some(last) => Some(&last.machine), + None => None, + }, + _ => None, + } + } +} + +impl<'backend, 'invoker, H, Tr, I> HeapTransact<'backend, 'invoker, H, Tr, I> +where + I: Invoker, + I::Interpreter: RunInterpreter, +{ /// Step the call stack, but run the interpreter inside. #[allow(clippy::type_complexity)] pub fn step_run( @@ -425,24 +448,12 @@ where } } } - - /// The machine of the last item on the call stack. This will be `None` if - /// the heap stack is just created. - pub fn last_interpreter(&self) -> Option<&I::Interpreter> { - match &self.0 { - Some(HeapTransactState::Running { call_stack, .. }) => match &call_stack.last { - Some(last) => Some(&last.machine), - None => None, - }, - _ => None, - } - } } -impl<'backend, 'invoker, S, H, Tr, I> HeapTransact<'backend, 'invoker, S, H, Tr, I> +impl<'backend, 'invoker, H, Tr, I> HeapTransact<'backend, 'invoker, H, Tr, I> where - I: Invoker, - I::Interpreter: StepInterpreter, + I: Invoker, + I::Interpreter: StepInterpreter, { /// Step the call stack, and step the interpreter inside. #[allow(clippy::type_complexity)] @@ -453,9 +464,9 @@ where } } -impl<'backend, 'invoker, S, H, Tr, I> Drop for HeapTransact<'backend, 'invoker, S, H, Tr, I> +impl<'backend, 'invoker, H, Tr, I> Drop for HeapTransact<'backend, 'invoker, H, Tr, I> where - I: Invoker, + I: Invoker, { fn drop(&mut self) { if let Some(HeapTransactState::Running { @@ -506,14 +517,15 @@ where /// /// Because a stack-based call stack cannot handle interrupts, the [Invoker] /// type must have its `Interrupt` type set to [Infallible]. -pub fn transact( +pub fn transact( args: I::TransactArgs, heap_depth: Option, backend: &mut H, invoker: &I, ) -> Result where - I: Invoker, + I: Invoker, + I::Interpreter: RunInterpreter, { let (transact_invoke, control) = invoker.new_transact(args, backend)?; diff --git a/src/invoker.rs b/src/invoker.rs index f4d4ea321..755834763 100644 --- a/src/invoker.rs +++ b/src/invoker.rs @@ -1,4 +1,4 @@ -use crate::{Capture, ExitError, ExitResult, Interpreter}; +use crate::{interpreter::Interpreter, Capture, ExitError, ExitResult}; use alloc::vec::Vec; /// Control for an invoker. @@ -10,8 +10,9 @@ pub enum InvokerControl { } /// An invoker, responsible for pushing/poping values in the call stack. -pub trait Invoker { - type Interpreter: Interpreter; +pub trait Invoker { + type State; + type Interpreter: Interpreter; /// Possible interrupt type that may be returned by the call stack. type Interrupt; @@ -35,7 +36,7 @@ pub trait Invoker { ) -> Result< ( Self::TransactInvoke, - InvokerControl))>, + InvokerControl))>, ), ExitError, >; @@ -45,7 +46,7 @@ pub trait Invoker { &self, invoke: &Self::TransactInvoke, exit: ExitResult, - machine: (S, Vec), + machine: (Self::State, Vec), handler: &mut H, ) -> Result; @@ -61,7 +62,7 @@ pub trait Invoker { Result< ( Self::SubstackInvoke, - InvokerControl))>, + InvokerControl))>, ), ExitError, >, @@ -72,7 +73,7 @@ pub trait Invoker { fn exit_substack( &self, result: ExitResult, - child: (S, Vec), + child: (Self::State, Vec), trap_data: Self::SubstackInvoke, parent: &mut Self::Interpreter, handler: &mut H, diff --git a/src/standard/invoker/mod.rs b/src/standard/invoker/mod.rs index 36c888c39..24b9f8617 100644 --- a/src/standard/invoker/mod.rs +++ b/src/standard/invoker/mod.rs @@ -8,15 +8,15 @@ pub use self::state::InvokerState; use super::Config; use crate::trap::{CallCreateTrap, CallCreateTrapData, CallTrapData, CreateScheme, CreateTrapData}; use crate::{ - Capture, Context, ExitError, ExitException, ExitResult, ExitSucceed, Interpreter, - Invoker as InvokerT, InvokerControl, MergeStrategy, Opcode, RuntimeBackend, RuntimeEnvironment, - RuntimeState, TransactionContext, TransactionalBackend, Transfer, TrapConsume, + interpreter::Interpreter, Capture, Context, ExitError, ExitException, ExitResult, ExitSucceed, + GasState, Invoker as InvokerT, InvokerControl, MergeStrategy, Opcode, RuntimeBackend, + RuntimeEnvironment, RuntimeState, TransactionContext, TransactionalBackend, Transfer, + TrapConsume, }; use alloc::rc::Rc; use alloc::vec::Vec; use core::cmp::min; use core::convert::Infallible; -use core::marker::PhantomData; use primitive_types::{H160, H256, U256}; use sha3::{Digest, Keccak256}; @@ -141,31 +141,26 @@ impl TransactArgs { /// * `R`: Code resolver type, also handle precompiles. Usually /// [EtableResolver] but can be customized. /// * `Tr`: Trap type, usually [crate::Opcode] but can be customized. -pub struct Invoker<'config, 'resolver, S, H, R, Tr> { +pub struct Invoker<'config, 'resolver, R> { config: &'config Config, resolver: &'resolver R, - _marker: PhantomData<(S, H, Tr)>, } -impl<'config, 'resolver, S, H, R, Tr> Invoker<'config, 'resolver, S, H, R, Tr> { +impl<'config, 'resolver, R> Invoker<'config, 'resolver, R> { /// Create a new standard invoker with the given config and resolver. pub fn new(config: &'config Config, resolver: &'resolver R) -> Self { - Self { - config, - resolver, - _marker: PhantomData, - } + Self { config, resolver } } } -impl<'config, 'resolver, S, H, R, Tr> InvokerT - for Invoker<'config, 'resolver, S, H, R, Tr> +impl<'config, 'resolver, H, R, Tr> InvokerT for Invoker<'config, 'resolver, R> where - S: InvokerState<'config> + AsRef + AsMut, + R::State: InvokerState<'config> + AsRef + AsMut, H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, - R: Resolver, + R: Resolver, Tr: TrapConsume, { + type State = R::State; type Interpreter = R::Interpreter; type Interrupt = Tr::Rest; type TransactArgs = TransactArgs; @@ -180,7 +175,7 @@ where ) -> Result< ( Self::TransactInvoke, - InvokerControl))>, + InvokerControl))>, ), ExitError, > { @@ -260,7 +255,7 @@ where } } - let state = S::new_transact_call( + let state = ::new_transact_call( RuntimeState { context, transaction_context: Rc::new(transaction_context), @@ -300,7 +295,7 @@ where access_list, .. } => { - let state = S::new_transact_create( + let state = ::new_transact_create( RuntimeState { context, transaction_context: Rc::new(transaction_context), @@ -340,7 +335,7 @@ where &self, invoke: &Self::TransactInvoke, result: ExitResult, - (mut substate, retval): (S, Vec), + (mut substate, retval): (R::State, Vec), handler: &mut H, ) -> Result { let left_gas = substate.effective_gas(); @@ -397,7 +392,7 @@ where Result< ( Self::SubstackInvoke, - InvokerControl))>, + InvokerControl))>, ), ExitError, >, @@ -508,7 +503,7 @@ where fn exit_substack( &self, result: ExitResult, - (mut substate, retval): (S, Vec), + (mut substate, retval): (R::State, Vec), trap_data: Self::SubstackInvoke, parent: &mut Self::Interpreter, handler: &mut H, diff --git a/src/standard/invoker/resolver.rs b/src/standard/invoker/resolver.rs index 1155bf60c..bd853b3c9 100644 --- a/src/standard/invoker/resolver.rs +++ b/src/standard/invoker/resolver.rs @@ -1,6 +1,7 @@ +use crate::interpreter::{EtableInterpreter, Interpreter}; use crate::{ - standard::Config, EtableInterpreter, EtableSet, ExitError, ExitResult, Interpreter, - InvokerControl, Machine, RuntimeBackend, RuntimeState, + standard::Config, EtableSet, ExitError, ExitResult, InvokerControl, Machine, RuntimeBackend, + RuntimeState, }; use alloc::{rc::Rc, vec::Vec}; use core::marker::PhantomData; @@ -12,8 +13,9 @@ use primitive_types::H160; /// (with the init code) is turned into a colored machine. The resolver can /// construct a machine, pushing the call stack, or directly exit, handling a /// precompile. -pub trait Resolver { - type Interpreter: Interpreter; +pub trait Resolver { + type State; + type Interpreter: Interpreter; /// Resolve a call (with the target code address). #[allow(clippy::type_complexity)] @@ -21,18 +23,18 @@ pub trait Resolver { &self, code_address: H160, input: Vec, - state: S, + state: Self::State, handler: &mut H, - ) -> Result))>, ExitError>; + ) -> Result))>, ExitError>; /// Resolve a create (with the init code). #[allow(clippy::type_complexity)] fn resolve_create( &self, init_code: Vec, - state: S, + state: Self::State, handler: &mut H, - ) -> Result))>, ExitError>; + ) -> Result))>, ExitError>; } /// A set of precompiles. @@ -62,15 +64,15 @@ impl PrecompileSet for () { /// The standard code resolver where the color is an [Etable]. This is usually /// what you need. -pub struct EtableResolver<'config, 'precompile, 'etable, S, H, Pre, Tr, ES> { +pub struct EtableResolver<'config, 'precompile, 'etable, S, Pre, ES> { config: &'config Config, etable: &'etable ES, precompiles: &'precompile Pre, - _marker: PhantomData<(S, H, Tr)>, + _marker: PhantomData, } -impl<'config, 'precompile, 'etable, S, H, Pre, Tr, ES> - EtableResolver<'config, 'precompile, 'etable, S, H, Pre, Tr, ES> +impl<'config, 'precompile, 'etable, S, Pre, ES> + EtableResolver<'config, 'precompile, 'etable, S, Pre, ES> { pub fn new( config: &'config Config, @@ -86,15 +88,16 @@ impl<'config, 'precompile, 'etable, S, H, Pre, Tr, ES> } } -impl<'config, 'precompile, 'etable, S, H, Pre, Tr, ES> Resolver - for EtableResolver<'config, 'precompile, 'etable, S, H, Pre, Tr, ES> +impl<'config, 'precompile, 'etable, S, H, Pre, ES> Resolver + for EtableResolver<'config, 'precompile, 'etable, S, Pre, ES> where S: AsRef + AsMut, H: RuntimeBackend, Pre: PrecompileSet, - ES: EtableSet, + ES: EtableSet, { - type Interpreter = EtableInterpreter<'etable, S, H, Tr, ES>; + type State = S; + type Interpreter = EtableInterpreter<'etable, S, ES>; /// Resolve a call (with the target code address). #[allow(clippy::type_complexity)] diff --git a/src/standard/invoker/routines.rs b/src/standard/invoker/routines.rs index cac2b7e0f..fbc0ff8a2 100644 --- a/src/standard/invoker/routines.rs +++ b/src/standard/invoker/routines.rs @@ -8,19 +8,19 @@ use alloc::vec::Vec; use primitive_types::{H160, U256}; #[allow(clippy::too_many_arguments, clippy::type_complexity)] -pub fn make_enter_call_machine( +pub fn make_enter_call_machine( _config: &Config, resolver: &R, code_address: H160, input: Vec, transfer: Option, - state: S, + state: R::State, handler: &mut H, -) -> Result))>, ExitError> +) -> Result))>, ExitError> where - S: AsRef, + R::State: AsRef, H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, - R: Resolver, + R: Resolver, { handler.mark_hot(state.as_ref().context.address, None); @@ -32,19 +32,19 @@ where } #[allow(clippy::type_complexity, clippy::too_many_arguments)] -pub fn make_enter_create_machine( +pub fn make_enter_create_machine( config: &Config, resolver: &R, caller: H160, init_code: Vec, transfer: Transfer, - state: S, + state: R::State, handler: &mut H, -) -> Result))>, ExitError> +) -> Result))>, ExitError> where - S: AsRef, + R::State: AsRef, H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, - R: Resolver, + R: Resolver, { if let Some(limit) = config.max_initcode_size { if init_code.len() > limit { @@ -73,24 +73,24 @@ where } #[allow(clippy::type_complexity, clippy::too_many_arguments)] -pub fn enter_call_substack( +pub fn enter_call_substack( config: &Config, resolver: &R, trap_data: CallTrapData, code_address: H160, - state: S, + state: R::State, handler: &mut H, ) -> Result< ( SubstackInvoke, - InvokerControl))>, + InvokerControl))>, ), ExitError, > where - S: AsRef, + R::State: AsRef, H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, - R: Resolver, + R: Resolver, { handler.push_substate(); @@ -118,28 +118,28 @@ where } #[allow(clippy::type_complexity, clippy::too_many_arguments)] -pub fn enter_create_substack( +pub fn enter_create_substack( config: &Config, resolver: &R, code: Vec, trap_data: CreateTrapData, - state: S, + state: R::State, handler: &mut H, ) -> Result< ( SubstackInvoke, - InvokerControl))>, + InvokerControl))>, ), ExitError, > where - S: AsRef, + R::State: AsRef, H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend, - R: Resolver, + R: Resolver, { handler.push_substate(); - let work = || -> Result<(SubstackInvoke, InvokerControl))>), ExitError> { + let work = || -> Result<(SubstackInvoke, InvokerControl))>), ExitError> { let CreateTrapData { scheme, value,