diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 1ddda5b18..6f04d06ce 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -1,18 +1,17 @@ use crate::backend::Backend; +use crate::executor::stack::precompile::{ + PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileSet, +}; 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, Stack, Transfer, -}; -use alloc::{ - collections::{BTreeMap, BTreeSet}, - rc::Rc, - vec::Vec, + Capture, Config, Context, CreateScheme, ExitError, ExitReason, Handler, Opcode, Runtime, Stack, + Transfer, }; +use alloc::{collections::BTreeSet, rc::Rc, vec::Vec}; use core::{cmp::min, convert::Infallible}; -use evm_core::{ExitFatal, ExitRevert}; +use evm_core::ExitFatal; use evm_runtime::Resolve; use primitive_types::{H160, H256, U256}; use sha3::{Digest, Keccak256}; @@ -232,138 +231,6 @@ pub trait StackState<'config>: Backend { } } -/// Data returned by a precompile on success. -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct PrecompileOutput { - pub exit_status: ExitSucceed, - pub output: Vec, -} - -/// Data returned by a precompile in case of failure. -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum PrecompileFailure { - /// Reverts the state changes and consume all the gas. - Error { exit_status: ExitError }, - /// Reverts the state changes. - /// Returns the provided error message. - Revert { - exit_status: ExitRevert, - output: Vec, - }, - /// Mark this failure as fatal, and all EVM execution stacks must be exited. - Fatal { exit_status: ExitFatal }, -} - -impl From for PrecompileFailure { - fn from(error: ExitError) -> PrecompileFailure { - PrecompileFailure::Error { exit_status: error } - } -} - -/// Handle provided to a precompile to interact with the EVM. -pub trait PrecompileHandle { - /// Perform subcall in provided context. - /// Precompile specifies in which context the subcall is executed. - fn call( - &mut self, - to: H160, - transfer: Option, - input: Vec, - gas_limit: Option, - is_static: bool, - context: &Context, - ) -> (ExitReason, Vec); - - /// Record cost to the Runtime gasometer. - fn record_cost(&mut self, cost: u64) -> Result<(), ExitError>; - - /// Retreive the remaining gas. - fn remaining_gas(&self) -> u64; - - /// Record a log. - fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError>; - - /// Retreive the code address (what is the address of the precompile being called). - fn code_address(&self) -> H160; - - /// Retreive the input data the precompile is called with. - fn input(&self) -> &[u8]; - - /// Retreive the context in which the precompile is executed. - fn context(&self) -> &Context; - - /// Is the precompile call is done statically. - fn is_static(&self) -> bool; - - /// Retreive the gas limit of this call. - fn gas_limit(&self) -> Option; -} - -/// A precompile result. -pub type PrecompileResult = Result; - -/// A set of precompiles. -/// Checks of the provided address being in the precompile set should be -/// as cheap as possible since it may be called often. -pub trait PrecompileSet { - /// Tries to execute a precompile in the precompile set. - /// If the provided address is not a precompile, returns None. - fn execute(&self, handle: &mut impl PrecompileHandle) -> Option; - - /// Check if the given address is a precompile. Should only be called to - /// perform the check while not executing the precompile afterward, since - /// `execute` already performs a check internally. - fn is_precompile(&self, address: H160) -> bool; -} - -impl PrecompileSet for () { - fn execute(&self, _: &mut impl PrecompileHandle) -> Option { - None - } - - fn is_precompile(&self, _: H160) -> bool { - false - } -} - -/// Precompiles function signature. Expected input arguments are: -/// * Input -/// * Gas limit -/// * Context -/// * Is static -/// -/// In case of success returns the output and the cost. -pub type PrecompileFn = - fn(&[u8], Option, &Context, bool) -> Result<(PrecompileOutput, u64), PrecompileFailure>; - -impl PrecompileSet for BTreeMap { - fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { - let address = handle.code_address(); - - self.get(&address).map(|precompile| { - let input = handle.input(); - let gas_limit = handle.gas_limit(); - let context = handle.context(); - let is_static = handle.is_static(); - - match (*precompile)(input, gas_limit, context, is_static) { - Ok((output, cost)) => { - handle.record_cost(cost)?; - Ok(output) - } - Err(err) => Err(err), - } - }) - } - - /// Check if the given address is a precompile. Should only be called to - /// perform the check while not executing the precompile afterward, since - /// `execute` already performs a check internally. - fn is_precompile(&self, address: H160) -> bool { - self.contains_key(&address) - } -} - /// Stack-based executor. pub struct StackExecutor<'config, 'precompiles, S, P> { config: &'config Config, diff --git a/src/executor/stack/mod.rs b/src/executor/stack/mod.rs index 1d64fbe03..6cca96dca 100644 --- a/src/executor/stack/mod.rs +++ b/src/executor/stack/mod.rs @@ -4,13 +4,14 @@ mod executor; mod memory; +mod precompile; mod tagged_runtime; pub use self::executor::{ - Accessed, PrecompileFailure, PrecompileFn, PrecompileHandle, PrecompileOutput, PrecompileSet, - StackExecutor, StackExitKind, StackState, StackSubstateMetadata, + Accessed, StackExecutor, StackExitKind, StackState, StackSubstateMetadata, }; - pub use self::memory::{MemoryStackAccount, MemoryStackState, MemoryStackSubstate}; - +pub use self::precompile::{ + PrecompileFailure, PrecompileFn, PrecompileHandle, PrecompileOutput, PrecompileSet, +}; pub use ethereum::Log; diff --git a/src/executor/stack/precompile.rs b/src/executor/stack/precompile.rs new file mode 100644 index 000000000..bda25b33c --- /dev/null +++ b/src/executor/stack/precompile.rs @@ -0,0 +1,135 @@ +use crate::{Context, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed, Transfer}; +use alloc::collections::BTreeMap; +use primitive_types::{H160, H256}; + +/// A precompile result. +pub type PrecompileResult = Result; + +/// Data returned by a precompile on success. +#[derive(Debug, Eq, PartialEq, Clone)] +pub struct PrecompileOutput { + pub exit_status: ExitSucceed, + pub output: Vec, +} + +/// Data returned by a precompile in case of failure. +#[derive(Debug, Eq, PartialEq, Clone)] +pub enum PrecompileFailure { + /// Reverts the state changes and consume all the gas. + Error { exit_status: ExitError }, + /// Reverts the state changes. + /// Returns the provided error message. + Revert { + exit_status: ExitRevert, + output: Vec, + }, + /// Mark this failure as fatal, and all EVM execution stacks must be exited. + Fatal { exit_status: ExitFatal }, +} + +impl From for PrecompileFailure { + fn from(error: ExitError) -> PrecompileFailure { + PrecompileFailure::Error { exit_status: error } + } +} + +/// Handle provided to a precompile to interact with the EVM. +pub trait PrecompileHandle { + /// Perform subcall in provided context. + /// Precompile specifies in which context the subcall is executed. + fn call( + &mut self, + to: H160, + transfer: Option, + input: Vec, + gas_limit: Option, + is_static: bool, + context: &Context, + ) -> (ExitReason, Vec); + + /// Record cost to the Runtime gasometer. + fn record_cost(&mut self, cost: u64) -> Result<(), ExitError>; + + /// Retreive the remaining gas. + fn remaining_gas(&self) -> u64; + + /// Record a log. + fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError>; + + /// Retreive the code address (what is the address of the precompile being called). + fn code_address(&self) -> H160; + + /// Retreive the input data the precompile is called with. + fn input(&self) -> &[u8]; + + /// Retreive the context in which the precompile is executed. + fn context(&self) -> &Context; + + /// Is the precompile call is done statically. + fn is_static(&self) -> bool; + + /// Retreive the gas limit of this call. + fn gas_limit(&self) -> Option; +} + +/// A set of precompiles. +/// Checks of the provided address being in the precompile set should be +/// as cheap as possible since it may be called often. +pub trait PrecompileSet { + /// Tries to execute a precompile in the precompile set. + /// If the provided address is not a precompile, returns None. + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option; + + /// Check if the given address is a precompile. Should only be called to + /// perform the check while not executing the precompile afterward, since + /// `execute` already performs a check internally. + fn is_precompile(&self, address: H160) -> bool; +} + +impl PrecompileSet for () { + fn execute(&self, _: &mut impl PrecompileHandle) -> Option { + None + } + + fn is_precompile(&self, _: H160) -> bool { + false + } +} + +/// Precompiles function signature. Expected input arguments are: +/// * Input +/// * Gas limit +/// * Context +/// * Is static +/// +/// In case of success returns the output and the cost. +pub type PrecompileFn = + fn(&[u8], Option, &Context, bool) -> Result<(PrecompileOutput, u64), PrecompileFailure>; + +impl PrecompileSet for BTreeMap { + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { + let address = handle.code_address(); + + self.get(&address).map(|precompile| { + let input = handle.input(); + let gas_limit = handle.gas_limit(); + let context = handle.context(); + let is_static = handle.is_static(); + + match (*precompile)(input, gas_limit, context, is_static) { + Ok((output, cost)) => { + handle.record_cost(cost)?; + Ok(output) + } + Err(err) => Err(err), + } + }) + } + + /// Check if the given address is a precompile. Should only be called to + /// perform the check while not executing the precompile afterward, since + /// `execute` already performs a check internally. + fn is_precompile(&self, address: H160) -> bool { + self.contains_key(&address) + } +}