Skip to content

Commit

Permalink
Take precompile part out (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
boundless-forest committed Mar 8, 2023
1 parent 842e03d commit 8ac1908
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 144 deletions.
147 changes: 7 additions & 140 deletions src/executor/stack/executor.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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<u8>,
}

/// 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<u8>,
},
/// Mark this failure as fatal, and all EVM execution stacks must be exited.
Fatal { exit_status: ExitFatal },
}

impl From<ExitError> 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<Transfer>,
input: Vec<u8>,
gas_limit: Option<u64>,
is_static: bool,
context: &Context,
) -> (ExitReason, Vec<u8>);

/// 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<H256>, data: Vec<u8>) -> 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<u64>;
}

/// A precompile result.
pub type PrecompileResult = Result<PrecompileOutput, PrecompileFailure>;

/// 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<PrecompileResult>;

/// 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<PrecompileResult> {
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<u64>, &Context, bool) -> Result<(PrecompileOutput, u64), PrecompileFailure>;

impl PrecompileSet for BTreeMap<H160, PrecompileFn> {
fn execute(&self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
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,
Expand Down
9 changes: 5 additions & 4 deletions src/executor/stack/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
135 changes: 135 additions & 0 deletions src/executor/stack/precompile.rs
Original file line number Diff line number Diff line change
@@ -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<PrecompileOutput, PrecompileFailure>;

/// Data returned by a precompile on success.
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct PrecompileOutput {
pub exit_status: ExitSucceed,
pub output: Vec<u8>,
}

/// 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<u8>,
},
/// Mark this failure as fatal, and all EVM execution stacks must be exited.
Fatal { exit_status: ExitFatal },
}

impl From<ExitError> 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<Transfer>,
input: Vec<u8>,
gas_limit: Option<u64>,
is_static: bool,
context: &Context,
) -> (ExitReason, Vec<u8>);

/// 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<H256>, data: Vec<u8>) -> 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<u64>;
}

/// 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<PrecompileResult>;

/// 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<PrecompileResult> {
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<u64>, &Context, bool) -> Result<(PrecompileOutput, u64), PrecompileFailure>;

impl PrecompileSet for BTreeMap<H160, PrecompileFn> {
fn execute(&self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
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)
}
}

0 comments on commit 8ac1908

Please sign in to comment.