#![deny(warnings)]
#![forbid(unsafe_code, unused_variables, unused_imports)]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
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};
use alloc::vec::Vec;
use alloc::rc::Rc;
macro_rules! step {
( $self:expr, $handler:expr, $return:tt $($err:path)?; $($ok:path)? ) => ({
if let Some((opcode, stack)) = $self.machine.inspect() {
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()))
},
}
match $self.machine.step() {
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))
},
}
},
}
});
}
pub struct Runtime<'config> {
machine: Machine,
status: Result<(), ExitReason>,
return_data_buffer: Vec<u8>,
context: Context,
_config: &'config Config,
}
impl<'config> Runtime<'config> {
pub fn new(
code: Rc<Vec<u8>>,
data: Rc<Vec<u8>>,
context: Context,
config: &'config Config,
) -> Self {
Self {
machine: Machine::new(code, data, config.stack_limit, config.memory_limit),
status: Ok(()),
return_data_buffer: Vec::new(),
context,
_config: config,
}
}
pub fn machine(&self) -> &Machine {
&self.machine
}
pub fn step<'a, H: Handler>(
&'a mut self,
handler: &mut H,
) -> Result<(), Capture<ExitReason, Resolve<'a, 'config, H>>> {
step!(self, handler, return Err; Ok)
}
pub fn run<'a, H: Handler>(
&'a mut self,
handler: &mut H,
) -> Capture<ExitReason, Resolve<'a, 'config, H>> {
loop {
step!(self, handler, return;)
}
}
}
#[derive(Clone, Debug)]
pub struct Config {
pub gas_ext_code: u64,
pub gas_ext_code_hash: u64,
pub gas_sstore_set: u64,
pub gas_sstore_reset: u64,
pub refund_sstore_clears: i64,
pub gas_balance: u64,
pub gas_sload: u64,
pub gas_suicide: u64,
pub gas_suicide_new_account: u64,
pub gas_call: u64,
pub gas_expbyte: u64,
pub gas_transaction_create: u64,
pub gas_transaction_call: u64,
pub gas_transaction_zero_data: u64,
pub gas_transaction_non_zero_data: u64,
pub sstore_gas_metering: bool,
pub sstore_revert_under_stipend: bool,
pub err_on_call_with_more_gas: bool,
pub call_l64_after_gas: bool,
pub empty_considered_exists: bool,
pub create_increase_nonce: bool,
pub stack_limit: usize,
pub memory_limit: usize,
pub call_stack_limit: usize,
pub create_contract_limit: Option<usize>,
pub call_stipend: u64,
pub has_delegate_call: bool,
pub has_create2: bool,
pub has_revert: bool,
pub has_return_data: bool,
pub has_bitwise_shifting: bool,
pub has_chain_id: bool,
pub has_self_balance: bool,
pub has_ext_code_hash: bool,
pub estimate: bool,
}
impl Config {
pub const fn frontier() -> Config {
Config {
gas_ext_code: 20,
gas_ext_code_hash: 20,
gas_balance: 20,
gas_sload: 50,
gas_sstore_set: 20000,
gas_sstore_reset: 5000,
refund_sstore_clears: 15000,
gas_suicide: 0,
gas_suicide_new_account: 0,
gas_call: 40,
gas_expbyte: 10,
gas_transaction_create: 21000,
gas_transaction_call: 21000,
gas_transaction_zero_data: 4,
gas_transaction_non_zero_data: 68,
sstore_gas_metering: false,
sstore_revert_under_stipend: false,
err_on_call_with_more_gas: true,
empty_considered_exists: true,
create_increase_nonce: false,
call_l64_after_gas: false,
stack_limit: 1024,
memory_limit: usize::max_value(),
call_stack_limit: 1024,
create_contract_limit: None,
call_stipend: 2300,
has_delegate_call: false,
has_create2: false,
has_revert: false,
has_return_data: false,
has_bitwise_shifting: false,
has_chain_id: false,
has_self_balance: false,
has_ext_code_hash: false,
estimate: false,
}
}
pub const fn istanbul() -> Config {
Config {
gas_ext_code: 700,
gas_ext_code_hash: 700,
gas_balance: 700,
gas_sload: 800,
gas_sstore_set: 20000,
gas_sstore_reset: 5000,
refund_sstore_clears: 15000,
gas_suicide: 5000,
gas_suicide_new_account: 25000,
gas_call: 700,
gas_expbyte: 50,
gas_transaction_create: 53000,
gas_transaction_call: 21000,
gas_transaction_zero_data: 4,
gas_transaction_non_zero_data: 16,
sstore_gas_metering: true,
sstore_revert_under_stipend: true,
err_on_call_with_more_gas: false,
empty_considered_exists: false,
create_increase_nonce: true,
call_l64_after_gas: true,
stack_limit: 1024,
memory_limit: usize::max_value(),
call_stack_limit: 1024,
create_contract_limit: Some(0x6000),
call_stipend: 2300,
has_delegate_call: true,
has_create2: true,
has_revert: true,
has_return_data: true,
has_bitwise_shifting: true,
has_chain_id: true,
has_self_balance: true,
has_ext_code_hash: true,
estimate: false,
}
}
}