diff --git a/Cargo.toml b/Cargo.toml index 217ff22fd..d545b17c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ default = ["std"] with-codec = ["codec", "evm-core/with-codec", "evm-runtime/with-codec"] with-serde = ["serde", "serde_bytes", "evm-core/with-serde", "evm-runtime/with-serde"] std = ["evm-core/std", "evm-runtime/std", "sha3/std", "serde/std", "codec/std", "log/std"] -tracing = ["evm-runtime/tracing"] +tracing = ["evm-runtime/tracing", "evm-core/tracing"] #[workspace] #members = [ # "core", diff --git a/core/Cargo.toml b/core/Cargo.toml index 1aaf5ac1e..506557757 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -28,3 +28,4 @@ default = ["std"] with-codec = ["codec"] with-serde = ["serde", "serde_bytes", "impl-serde"] std = ["log/std", "codec/std", "serde/std"] +tracing = [] diff --git a/runtime/src/context.rs b/core/src/context.rs similarity index 73% rename from runtime/src/context.rs rename to core/src/context.rs index 523f85b2c..0080c3458 100644 --- a/runtime/src/context.rs +++ b/core/src/context.rs @@ -47,3 +47,17 @@ pub struct Context { /// Apparent value of the EVM. pub apparent_value: U256, } + + +/// Transfer from source to target, with given value. +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "with-codec", derive(codec::Encode, codec::Decode))] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Transfer { + /// Source address. + pub source: H160, + /// Target address. + pub target: H160, + /// Transfer value. + pub value: U256, +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 4c25f6ed7..5d40ea221 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -22,6 +22,9 @@ mod error; mod eval; mod utils; mod primitive_types; +mod context; +#[cfg(feature = "tracing")] +mod tracing; pub use crate::memory::Memory; pub use crate::stack::Stack; @@ -29,10 +32,35 @@ pub use crate::valids::Valids; pub use crate::opcode::Opcode; pub use crate::error::{Trap, Capture, ExitReason, ExitSucceed, ExitError, ExitRevert, ExitFatal}; pub use crate::primitive_types::{H160, H256, U256, U512}; +pub use crate::context::{Context, CreateScheme, CallScheme, Transfer}; use alloc::vec::Vec; use crate::eval::{eval, Control}; +#[cfg(feature = "tracing")] +pub use crate::tracing::*; + +#[cfg(feature = "tracing")] +extern "C" {fn sol_send_trace_message(val: *const u8) -> u64;} + + +#[macro_export] +#[cfg(feature = "tracing")] +macro_rules! event { + ($x:expr) => { + let ptr = &$x as *const _ as *const u8; + unsafe { + sol_send_trace_message(ptr); + } + }; +} + +#[macro_export] +#[cfg(not(feature = "tracing"))] +macro_rules! event { + ($x:expr) => {} +} + /// Core execution layer for EVM. #[cfg_attr(feature = "with-codec", derive(codec::Encode, codec::Decode))] #[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] @@ -126,7 +154,11 @@ impl Machine { } /// Loop stepping the machine, until it stops. - pub fn run(&mut self, max_steps: u64, mut pre_validate: F) -> (u64, Capture) + pub fn run(&mut self, + max_steps: u64, + mut pre_validate: F, + _context : &Context + ) -> (u64, Capture) where F: FnMut(Opcode, &Stack) -> Result<(), ExitError> { for step in 0..max_steps { @@ -143,61 +175,54 @@ impl Machine { } }; + event!(Event::Step( + StepTrace { + context: _context, + opcode, + position: &self.position, + stack: &self.stack, + memory: &self.memory, + } + )); + if let Err(error) = pre_validate(opcode, &self.stack()) { let reason = ExitReason::from(error); self.exit(reason); return (step, Capture::Exit(reason)); } - match eval(self, opcode, position) { + let result = match eval(self, opcode, position) { Control::Continue(p) => { self.position = Ok(position + p); + Ok(()) }, Control::Exit(reason) => { self.exit(reason); - return (step, Capture::Exit(reason)) + Err(Capture::Exit(reason)) }, Control::Jump(p) => { self.position = Ok(p); + Ok(()) }, Control::Trap(opcode) => { self.position = Ok(position + 1); - return (step, Capture::Trap(opcode)); + Err(Capture::Trap(opcode)) }, + }; + + event!(Event::StepResult (StepResultTrace{ + result: &result, + return_value: &self.return_value(), + stack: &self.stack, + memory: &self.memory + })); + + if let Err(capture) = result { + return (step, capture) } } (max_steps, Capture::Exit(ExitReason::StepLimitReached)) } - /// Step the machine, executing one opcode. It then returns. - pub fn step(&mut self) -> Result<(), Capture> { - let position = *self.position.as_ref().map_err(|reason| Capture::Exit(reason.clone()))?; - - let opcode = if let Some(opcode) = self.code.get(position).map(|v| Opcode(*v)) { - opcode - } else { - self.position = Err(ExitSucceed::Stopped.into()); - return Err(Capture::Exit(ExitSucceed::Stopped.into())) - }; - - match eval(self, opcode, position) { - Control::Continue(p) => { - self.position = Ok(position + p); - Ok(()) - }, - Control::Exit(e) => { - self.position = Err(e.clone()); - Err(Capture::Exit(e)) - }, - Control::Jump(p) => { - self.position = Ok(p); - Ok(()) - }, - Control::Trap(opcode) => { - self.position = Ok(position + 1); - Err(Capture::Trap(opcode)) - }, - } - } } diff --git a/core/src/memory.rs b/core/src/memory.rs index 56c0c8076..264301a5e 100644 --- a/core/src/memory.rs +++ b/core/src/memory.rs @@ -26,6 +26,13 @@ impl Memory { } } + pub fn from(data: &[u8], len: usize, limit: usize) -> Self { + Self { + data: Vec::from(data), + effective_len: len, + limit, + } + } /// Memory limit. #[must_use] pub const fn limit(&self) -> usize { @@ -50,9 +57,9 @@ impl Memory { self.len() == 0 } - pub fn data(&self) -> &[u8] { - &self.data - } + pub fn data(&self) -> &[u8] { &self.data } + + pub fn data_vec(&self) -> &Vec { &self.data } /// Resize the memory, making it cover the memory region of `offset..(offset /// + len)`, with 32 bytes as the step. If the length is zero, this function diff --git a/core/src/stack.rs b/core/src/stack.rs index 0af33a1f2..ea2d03082 100644 --- a/core/src/stack.rs +++ b/core/src/stack.rs @@ -69,6 +69,12 @@ impl Stack { } } + pub fn from(data: &[U256], limit: usize) -> Self { + Self { + data: Vec::from(data), + limit, + } + } /// Stack limit. #[must_use] pub const fn limit(&self) -> usize { @@ -168,4 +174,9 @@ impl Stack { Ok(()) } + + pub fn data_vec(&self) -> &Vec { + &self.data + } + } diff --git a/core/src/tracing.rs b/core/src/tracing.rs new file mode 100644 index 000000000..0087fa21b --- /dev/null +++ b/core/src/tracing.rs @@ -0,0 +1,144 @@ +use crate::{H160, H256, U256, Context, Opcode, Stack, Memory, Capture, ExitReason, Trap, CreateScheme, Transfer}; +use alloc::vec::Vec; + + +#[derive(Debug, Clone)] +pub struct CallTrace<'a>{ + /// Called code address + pub code_address: H160, + /// Transfer parameters + pub transfer: &'a Option, + /// Input data provided to the call + pub input: &'a Vec, + /// Target gas + pub target_gas: Option, + /// Static call flag + pub is_static: bool, + /// Runtime context + pub context: &'a Context, +} + +#[derive(Debug, Clone)] +pub struct CreateTrace<'a>{ + /// Creator address + pub caller: H160, + /// Address of the created account + pub address: H160, + /// Scheme + pub scheme: CreateScheme, + /// Value the created account is endowed with + pub value: U256, + /// Init code + pub init_code: &'a Vec, + /// Target Gas + pub target_gas: Option, +} + +#[derive(Debug, Clone)] +pub struct SuicideTrace{ + /// Suicided address + pub address: H160, + /// Suicided contract heir + pub target: H160, + /// Balance before suicide + pub balance: U256, +} + +#[derive(Debug, Clone)] +pub struct ExitTrace<'a>{ + pub reason: &'a ExitReason, + pub return_value: &'a Vec, +} + + +#[derive(Debug, Clone)] +pub struct TransactCallTrace<'a>{ + /// Caller account address + pub caller: H160, + /// Destination account address + pub address: H160, + /// Value transferred to the destination account + pub value: U256, + /// Input data provided to the call + pub data: &'a Vec, + /// Gas Limit + pub gas_limit: U256, +} + +#[derive(Debug, Clone)] +pub struct TransactCreateTrace<'a>{ + /// Creator address + pub caller: H160, + /// Value the created account is endowed with + pub value: U256, + /// Init code + pub init_code: &'a Vec, + /// Gas limit + pub gas_limit: U256, + /// Address of the created account + pub address: H160, +} + +#[derive(Debug, Clone)] +pub struct TransactCreate2Trace<'a>{ + /// Creator address + pub caller: H160, + /// Value the created account is endowed with + pub value: U256, + /// Init code + pub init_code: &'a Vec, + /// Salt + pub salt: H256, + /// Gas limit + pub gas_limit: U256, + /// Address of the created account + pub address: H160, +} + +#[derive(Debug, Clone)] +pub struct StepTrace<'a>{ + pub context: &'a Context, + pub opcode: Opcode, + pub position: &'a Result, + pub stack: &'a Stack, + pub memory: &'a Memory, +} + +#[derive(Debug, Clone)] +pub struct StepResultTrace<'a>{ + pub result: &'a Result<(), Capture>, + pub return_value: &'a Vec, + pub stack: &'a Stack, + pub memory: &'a Memory, +} + +#[derive(Debug, Clone)] +pub struct SLoadTrace{ + pub address: H160, + pub index: U256, + pub value: U256 +} + +#[derive(Debug, Clone)] +pub struct SStoreTrace { + pub address: H160, + pub index: U256, + pub value: U256 +} + +/// Trace event +#[derive(Debug, Clone)] +// #[allow(dead_code)] +pub enum Event<'a>{ + Call(CallTrace<'a>) , + Create(CreateTrace<'a>) , + Suicide(SuicideTrace) , + Exit(ExitTrace<'a>) , + TransactCall(TransactCallTrace<'a>) , + TransactCreate(TransactCreateTrace<'a>) , + TransactCreate2(TransactCreate2Trace<'a>) , + Step(StepTrace<'a>) , + StepResult(StepResultTrace<'a>), + SLoad(SLoadTrace), + SStore(SStoreTrace), +} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index b1408e778..73349d7f0 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -14,14 +14,11 @@ sha3 = { version = "0.8", default-features = false } codec = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive", "full"], optional = true } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } serde_bytes = { version = "0.11.5", optional = true } -environmental = { version = "1.1.2", default-features = false, optional = true} borsh = { version = "0.9" } [features] default = ["std"] with-codec = ["codec"] with-serde = ["serde", "serde_bytes"] -std = ["evm-core/std", "sha3/std", "environmental/std"] -tracing = [ - "environmental" -] +std = ["evm-core/std", "sha3/std"] +tracing = ["evm-core/tracing"] diff --git a/runtime/src/eval/system.rs b/runtime/src/eval/system.rs index 299869bd9..08d345497 100644 --- a/runtime/src/eval/system.rs +++ b/runtime/src/eval/system.rs @@ -1,9 +1,15 @@ use core::cmp::min; use alloc::vec::Vec; -use crate::{Runtime, ExitError, Handler, Capture, Transfer, ExitReason, - CreateScheme, CallScheme, Context, ExitSucceed, ExitFatal, - H160, H256, U256}; +use crate::{Runtime, ExitError, Handler, Capture, Transfer, ExitReason, CreateScheme, CallScheme, Context, ExitSucceed, ExitFatal, H160, H256, U256}; use super::Control; +use evm_core::event; + +#[cfg(feature = "tracing")] +use evm_core::{Event, SStoreTrace, SLoadTrace}; + +#[cfg(feature = "tracing")] +extern "C" {fn sol_send_trace_message(val: *const u8) -> u64;} + pub fn sha3(runtime: &mut Runtime, handler: &H) -> Control { pop_u256!(runtime, from, len); @@ -180,11 +186,13 @@ pub fn sload(runtime: &mut Runtime, handler: &H) -> Control { let value = handler.storage(runtime.context.address, index); push_u256!(runtime, value); - event!(SLoad { - address: runtime.context.address, - index, - value - }); + event!(Event::SLoad( + SLoadTrace{ + address: runtime.context.address, + index, + value + } + )); Control::Continue } @@ -192,11 +200,12 @@ pub fn sload(runtime: &mut Runtime, handler: &H) -> Control { pub fn sstore(runtime: &mut Runtime, handler: &mut H) -> Control { pop_u256!(runtime, index, value); - event!(SStore { + event!(Event::SStore( SStoreTrace{ address: runtime.context.address, index, value - }); + } + )); match handler.set_storage(runtime.context.address, index, value) { Ok(()) => Control::Continue, diff --git a/runtime/src/handler.rs b/runtime/src/handler.rs index c3337e22c..3f5366d5d 100644 --- a/runtime/src/handler.rs +++ b/runtime/src/handler.rs @@ -1,20 +1,8 @@ use alloc::vec::Vec; use crate::{Capture, Stack, ExitError, Opcode, - CreateScheme, Context, Machine, ExitReason, + Machine, ExitReason, H160, H256, U256}; - -/// Transfer from source to target, with given value. -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "with-codec", derive(codec::Encode, codec::Decode))] -#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Transfer { - /// Source address. - pub source: H160, - /// Target address. - pub target: H160, - /// Transfer value. - pub value: U256, -} +use evm_core::{Context, CreateScheme, Transfer}; /// EVM context handler. pub trait Handler { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 1797a6543..e57ed30c9 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -13,109 +13,19 @@ extern crate alloc; -#[cfg(feature = "tracing")] -pub mod tracing; - -#[cfg(feature = "tracing")] -macro_rules! event { - ($x:expr) => { - use crate::tracing::Event::*; - crate::tracing::with(|listener| listener.event($x)); - } -} - -#[cfg(not(feature = "tracing"))] -macro_rules! event { - ($x:expr) => {} -} mod eval; -mod context; mod interrupt; mod handler; pub use evm_core::*; -pub use crate::context::{CreateScheme, CallScheme, Context}; pub use crate::interrupt::{Resolve, ResolveCall, ResolveCreate}; -pub use crate::handler::{Transfer, Handler}; +pub use crate::handler::Handler; pub use crate::eval::{save_return_value, save_created_address, Control}; use alloc::vec::Vec; -macro_rules! step { - ( $self:expr, $handler:expr, $return:tt $($err:path)?; $($ok:path)? ) => ({ - let mut skip_step_result_event = true; - if let Some((opcode, stack)) = $self.machine.inspect() { - event!(Step { - context: &$self.context, - opcode, - position: $self.machine.position(), - stack, - memory: $self.machine.memory() - }); - skip_step_result_event = false; - - match $handler.pre_validate(&$self.context, opcode, stack) { - Ok(()) => (), - Err(e) => { - $self.machine.exit(e.clone().into()); - $self.status = Err(e.into()); - }, - } - } - - match &$self.status { - Ok(()) => (), - Err(e) => { - #[allow(unused_parens)] - $return $($err)*(Capture::Exit(e.clone())) - }, - } - - let result = $self.machine.step(); - - if !skip_step_result_event { - event!(StepResult { - result: &result, - return_value: &$self.machine.return_value(), - stack: $self.machine.stack(), - memory: $self.machine.memory(), - }); - } - - match result { - Ok(()) => $($ok)?(()), - Err(Capture::Exit(e)) => { - $self.status = Err(e.clone()); - #[allow(unused_parens)] - $return $($err)*(Capture::Exit(e)) - }, - Err(Capture::Trap(opcode)) => { - match eval::eval($self, opcode, $handler) { - eval::Control::Continue => $($ok)?(()), - eval::Control::CallInterrupt(interrupt) => { - let resolve = ResolveCall::new($self); - #[allow(unused_parens)] - $return $($err)*(Capture::Trap(Resolve::Call(interrupt, resolve))) - }, - eval::Control::CreateInterrupt(interrupt) => { - let resolve = ResolveCreate::new($self); - #[allow(unused_parens)] - $return $($err)*(Capture::Trap(Resolve::Create(interrupt, resolve))) - }, - eval::Control::Exit(exit) => { - $self.machine.exit(exit.clone().into()); - $self.status = Err(exit.clone()); - #[allow(unused_parens)] - $return $($err)*(Capture::Exit(exit)) - }, - } - }, - } - }); -} - /// EVM runtime. /// /// The runtime wraps an EVM `Machine` with support of return data and context. @@ -161,14 +71,6 @@ impl Runtime { &self.machine } - /// Step the runtime. - pub fn step<'a, H: Handler>( - &'a mut self, - handler: &mut H, - ) -> Result<(), Capture>> { - step!(self, handler, return Err; Ok) - } - /// Loop stepping the runtime until it stops. pub fn run<'a, H: Handler>( &'a mut self, @@ -185,7 +87,7 @@ impl Runtime { let (steps_executed, capture) = { let context = &self.context; let pre_validate = |opcode, stack: &Stack| { handler.pre_validate(context, opcode, stack) }; - self.machine.run(max_steps - steps, pre_validate) + self.machine.run(max_steps - steps, pre_validate, &self.context) }; steps += steps_executed; diff --git a/runtime/src/tracing.rs b/runtime/src/tracing.rs deleted file mode 100644 index a0f136266..000000000 --- a/runtime/src/tracing.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! Allows to listen to runtime events. - -use crate::{Context, Opcode, Stack, Memory, Capture, ExitReason, Trap}; -use crate::{H160, U256}; - -environmental::environmental!(listener: dyn EventListener + 'static); - -pub trait EventListener { - fn event( - &mut self, - event: Event - ); -} - -#[derive(Debug, Copy, Clone)] -pub enum Event<'a> { - Step { - context: &'a Context, - opcode: Opcode, - position: &'a Result, - stack: &'a Stack, - memory: &'a Memory - }, - StepResult { - result: &'a Result<(), Capture>, - return_value: &'a [u8], - stack: &'a Stack, - memory: &'a Memory - }, - SLoad { - address: H160, - index: U256, - value: U256 - }, - SStore { - address: H160, - index: U256, - value: U256 - }, -} - -/// Run closure with provided listener. -pub fn using R>( - new: &mut (dyn EventListener + 'static), - f: F -) -> R { - listener::using(new, f) -} - -pub(crate) fn with(f: F) { - listener::with(f); -} - diff --git a/src/backend/mod.rs b/src/backend/mod.rs index cce6047f7..9e1fe77ad 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -6,8 +6,8 @@ extern crate alloc; use alloc::vec::Vec; use core::convert::Infallible; -use evm_runtime::CreateScheme; -use crate::{Capture, Transfer, ExitReason, H160, H256, U256}; +use evm_core::{CreateScheme, Transfer}; +use crate::{Capture, ExitReason, H160, H256, U256}; /// Basic account information. #[derive(Clone, Eq, PartialEq, Debug, Default)]