Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
e35d741
rename main Evm structs, introduce wip external type
rakita Nov 8, 2023
fc03f9b
tests
rakita Nov 8, 2023
90a81f0
Split evm and external context
rakita Nov 16, 2023
fda7f8f
continue previous commit
rakita Nov 16, 2023
99ec673
wip inspector handle register
rakita Nov 21, 2023
ee65aa5
add few more handlers for frame and host
rakita Nov 21, 2023
006a9c2
Add instruction handle
rakita Nov 23, 2023
73f5ebd
add instruction handler registration and Inspector wrap
rakita Nov 24, 2023
42dfcf4
Rm Spec generic, more handlers, start factory
rakita Nov 25, 2023
6252606
move towards the builder, allow EVM modify
rakita Nov 27, 2023
a8eba54
wip on EvmBuilder and modify functionality
rakita Nov 28, 2023
ed188d7
EvmBuilder with stages wip
rakita Nov 29, 2023
85be2c5
Merge remote-tracking branch 'origin/main' into handlers
rakita Nov 29, 2023
64a1bd6
Add wip stages for builer
rakita Nov 29, 2023
45d7209
make handle register simple function, add raw instruction table, spli…
rakita Nov 30, 2023
5ca9580
wip on simple builder functions and handler registry
rakita Dec 1, 2023
aef8257
Examples and cleanup
rakita Dec 1, 2023
bebc79d
fix lifetime and fmt
rakita Dec 1, 2023
580ddf8
Add more handlers, deduct caller, validate tx agains state
rakita Dec 5, 2023
6352f90
All handlers counted, started on docs, some cleanup
rakita Dec 6, 2023
900ff89
renaming and docs
rakita Dec 7, 2023
55f8924
Support all Inspector functionality with Handler
rakita Dec 8, 2023
c930aeb
Handler restructured. Documentation added
rakita Dec 9, 2023
5dc55bd
more docs on registers
rakita Dec 9, 2023
238ea1b
integrate builder, fmt, move optimism l1block
rakita Dec 15, 2023
66b8642
add utility builder stage functions
rakita Dec 15, 2023
697a3e3
add precompiles, fix bugs with journal spec
rakita Dec 18, 2023
9375f35
spec to generic, optimism build
rakita Dec 18, 2023
65997b7
fix optimism test
rakita Dec 18, 2023
bfc5d8b
fuck macros
rakita Dec 26, 2023
8b23136
Merge remote-tracking branch 'origin/main' into handlers
rakita Dec 26, 2023
be3af16
clippy and fmt
rakita Dec 26, 2023
74ae8b0
fix trace block example
rakita Dec 26, 2023
55fc1f7
ci fixes
rakita Dec 26, 2023
b74f7e3
Flatten builder stages to generic and handler stage
rakita Dec 27, 2023
b24c19c
EvmBuilder doc and refactor fn access
rakita Dec 30, 2023
a6feb1a
Merge remote-tracking branch 'origin/main' into handlers
rakita Dec 30, 2023
7024cad
ignore rust code in book
rakita Dec 30, 2023
a704c70
make revme compile, will refactor this in future
rakita Jan 3, 2024
6fdf87a
Rename handles to Pre/Post Execution and ExecutionLoop
rakita Jan 4, 2024
62e9eaa
fix optimism clippy
rakita Jan 4, 2024
c6385be
small rename
rakita Jan 8, 2024
648ba93
FrameData and docs
rakita Jan 9, 2024
055f0f6
check links mdbook
rakita Jan 9, 2024
0b3be0e
comments and cleanup
rakita Jan 10, 2024
8fdcea9
comment
rakita Jan 10, 2024
6cd1414
Merge remote-tracking branch 'origin/main' into handlers
rakita Jan 11, 2024
fa4c712
Add initialize interepreter to first frame
rakita Jan 12, 2024
2133f7f
clippy
rakita Jan 12, 2024
d95dae5
clippy2
rakita Jan 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add more handlers, deduct caller, validate tx agains state
  • Loading branch information
rakita committed Dec 5, 2023
commit 580ddf8db1f3ac8beea0006eda9d906957f6830d
2 changes: 1 addition & 1 deletion crates/revm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> {
let mut handler = Handler::mainnet::<SPEC>();
// apply all registers to default handeler and raw mainnet instruction table.
for register in self.handle_registers.iter() {
register(&mut handler);
register.register(&mut handler);
if handler.instruction_table.is_none() {
panic!("Handler must have instruction table")
}
Expand Down
148 changes: 45 additions & 103 deletions crates/revm/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ use crate::{
journaled_state::JournaledState,
precompile::Precompiles,
primitives::{
specification::{self, SpecId},
Address, Bytecode, Bytes, EVMError, EVMResult, Env, HashSet, InvalidTransaction, Output,
SpecId::*,
TransactTo, B256, U256,
specification::SpecId, Address, Bytecode, Bytes, EVMError, EVMResult, Env, HashSet, Output,
SpecId::*, TransactTo, B256, U256,
},
CallStackFrame, Context, EvmContext, FrameOrResult,
};
Expand Down Expand Up @@ -198,44 +196,9 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> {
}
}

/// Pre verify transaction. Returns initial gas spend
pub fn preverify_transaction_inner(&mut self) -> Result<u64, EVMError<DB::Error>> {
self.handler.validate_env(&self.context.evm.env)?;
let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env);

let env = self.env();

// Additional check to see if limit is big enough to cover initial gas.
if initial_gas_spend > env.tx.gas_limit {
return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into());
}

// load acc
let tx_caller = env.tx.caller;
let (caller_account, _) = self
.context
.evm
.journaled_state
.load_account(tx_caller, &mut self.context.evm.db)
.map_err(EVMError::Database)?;

self.context
.evm
.env
.validate_tx_against_state(caller_account)
.map_err(Into::into)?;

Ok(initial_gas_spend)
}

/// Transact preverified transaction.
pub fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult<DB::Error> {
let env = &self.context.evm.env;
let tx_caller = env.tx.caller;
let tx_value = env.tx.value;
let tx_data = env.tx.data.clone();
let tx_gas_limit = env.tx.gas_limit;

let tx_caller = self.context.evm.env.tx.caller;
// the L1-cost fee is only computed for Optimism non-deposit transactions.
#[cfg(feature = "optimism")]
let tx_l1_cost = if env.cfg.optimism && env.tx.optimism.source_hash.is_none() {
Expand Down Expand Up @@ -272,11 +235,11 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> {

self.context.evm.load_access_list()?;

// load acc
let journal = &mut self.context.evm.journaled_state;

#[cfg(feature = "optimism")]
if self.context.evm.env.cfg.optimism {
// load acc
let journal = &mut self.context.evm.journaled_state;

crate::optimism::commit_mint_value(
tx_caller,
self.context.evm.env.tx.optimism.mint,
Expand All @@ -294,68 +257,42 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> {
)?;
}

let (caller_account, _) = journal
.load_account(tx_caller, &mut self.context.evm.db)
.map_err(EVMError::Database)?;
// deduce caller balance with its limit.
self.handler.deduct_caller(&mut self.context)?;

// Subtract gas costs from the caller's account.
// We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled.
let mut gas_cost =
U256::from(tx_gas_limit).saturating_mul(self.context.evm.env.effective_gas_price());

// EIP-4844
if self.handler.spec_id.is_enabled_in(CANCUN) {
let data_fee = self
.context
.evm
.env
.calc_data_fee()
.expect("already checked");
gas_cost = gas_cost.saturating_add(data_fee);
}

caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost);

// touch account so we know it is changed.
caller_account.mark_touch();

let transact_gas_limit = tx_gas_limit - initial_gas_spend;
// gas limit used in calls.
let transact_gas_limit = self.context.evm.env.tx.gas_limit - initial_gas_spend;

// call inner handling of call/create
let first_stack_frame = match self.context.evm.env.tx.transact_to {
TransactTo::Call(address) => {
// Nonce is already checked
caller_account.info.nonce = caller_account.info.nonce.saturating_add(1);

self.context.evm.make_call_frame(
&CallInputs {
contract: address,
transfer: Transfer {
source: tx_caller,
target: address,
value: tx_value,
},
input: tx_data,
gas_limit: transact_gas_limit,
context: CallContext {
caller: tx_caller,
address,
code_address: address,
apparent_value: tx_value,
scheme: CallScheme::Call,
},
is_static: false,
TransactTo::Call(address) => self.context.evm.make_call_frame(
&CallInputs {
contract: address,
transfer: Transfer {
source: tx_caller,
target: address,
value: self.context.evm.env.tx.value,
},
0..0,
)
}
input: self.context.evm.env.tx.data.clone(),
gas_limit: transact_gas_limit,
context: CallContext {
caller: tx_caller,
address,
code_address: address,
apparent_value: self.context.evm.env.tx.value,
scheme: CallScheme::Call,
},
is_static: false,
},
0..0,
),
TransactTo::Create(scheme) => self.context.evm.make_create_frame(
self.spec_id(),
&CreateInputs {
caller: tx_caller,
scheme,
value: tx_value,
init_code: tx_data,
value: self.context.evm.env.tx.value,
init_code: self.context.evm.env.tx.data.clone(),
gas_limit: transact_gas_limit,
},
),
Expand Down Expand Up @@ -419,22 +356,27 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> {

impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> {
#[inline]
fn preverify_transaction(&mut self) -> Result<(), EVMError<DB::Error>> {
self.preverify_transaction_inner().map(|_| ())
pub fn preverify_transaction(&mut self) -> Result<(), EVMError<DB::Error>> {
self.handler.validate_env(&self.context.evm.env)?;
self.handler.initial_tx_gas(&self.context.evm.env)?;
self.handler.validate_tx_against_state(&mut self.context)?;
Ok(())
}

#[inline]
fn transact_preverified(&mut self) -> EVMResult<DB::Error> {
let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env);
pub fn transact_preverified(&mut self) -> EVMResult<DB::Error> {
let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env)?;
let output = self.transact_preverified_inner(initial_gas_spend);
self.handler.end(&mut self.context, output)
}

#[inline]
fn transact(&mut self) -> EVMResult<DB::Error> {
let output = self
.preverify_transaction_inner()
.and_then(|initial_gas_spend| self.transact_preverified_inner(initial_gas_spend));
pub fn transact(&mut self) -> EVMResult<DB::Error> {
self.handler.validate_env(&self.context.evm.env)?;
let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env)?;
self.handler.validate_tx_against_state(&mut self.context)?;

let output = self.transact_preverified_inner(initial_gas_spend);
self.handler.end(&mut self.context, output)
}
}
Expand Down
47 changes: 38 additions & 9 deletions crates/revm/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,20 @@ use crate::{
},
precompile::{Address, Bytes, B256},
primitives::{
db::Database, specification::*, EVMError, EVMResultGeneric, Env, Output, ResultAndState,
Spec, SpecId,
db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec, SpecId,
},
CallStackFrame, Context,
};
use alloc::sync::Arc;
use core::ops::Range;
use once_cell::race::OnceBox;

/// Handle call return and return final gas value.
pub type CallReturnHandle<'a> = Arc<dyn Fn(&Env, InstructionResult, Gas) -> Gas + 'a>;

/// Deduct the caller to its limit.
type DeductCallerHandle<'a, EXT, DB> =
Arc<dyn Fn(&mut Context<EXT, DB>) -> EVMResultGeneric<(), <DB as Database>::Error> + 'a>;

/// Reimburse the caller with ethereum it didn't spent.
type ReimburseCallerHandle<'a, EXT, DB> =
Arc<dyn Fn(&mut Context<EXT, DB>, &Gas) -> EVMResultGeneric<(), <DB as Database>::Error> + 'a>;
Expand Down Expand Up @@ -121,8 +123,14 @@ pub type FrameSubCreateHandle<'a, EXT, DB> = Arc<
pub type ValidateEnvHandle<'a, DB> =
Arc<dyn Fn(&Env) -> Result<(), EVMError<<DB as Database>::Error>> + 'a>;

/// Handle that validates transaction environment against the state.
/// Second parametar is initial gas.
pub type ValidateTxEnvAgainstState<'a, EXT, DB> =
Arc<dyn Fn(&mut Context<EXT, DB>) -> Result<(), EVMError<<DB as Database>::Error>> + 'a>;

/// Initial gas calculation handle
pub type InitialTxGasHandle<'a> = Arc<dyn Fn(&Env) -> u64 + 'a>;
pub type InitialTxGasHandle<'a, DB> =
Arc<dyn Fn(&Env) -> Result<u64, EVMError<<DB as Database>::Error>> + 'a>;

/// Handler acts as a proxy and allow to define different behavior for different
/// sections of the code. This allows nice integration of different chains or
Expand All @@ -133,13 +141,17 @@ pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> {
/// Instruction table type.
pub instruction_table: Option<InstructionTables<'a, H>>,
/// Initial tx gas.
pub initial_tx_gas: InitialTxGasHandle<'a>,
pub initial_tx_gas: InitialTxGasHandle<'a, DB>,
/// Validate transactions agains state data.
pub validate_tx_against_state: ValidateTxEnvAgainstState<'a, EXT, DB>,
/// Validate Env
pub validate_env: ValidateEnvHandle<'a, DB>,
/// Validate Transaction against the state.
// Uses env, call result and returned gas from the call to determine the gas
// that is returned from transaction execution..
/// Uses env, call result and returned gas from the call to determine the gas
/// that is returned from transaction execution..
pub call_return: CallReturnHandle<'a>,
/// Deduct max value from the caller.
pub deduct_caller: DeductCallerHandle<'a, EXT, DB>,
/// Reimburse the caller with ethereum it didn't spent.
pub reimburse_caller: ReimburseCallerHandle<'a, EXT, DB>,
/// Reward the beneficiary with caller fee.
Expand Down Expand Up @@ -169,10 +181,14 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> {
Self {
spec_id: SPEC::SPEC_ID,
instruction_table: Some(InstructionTables::Plain(make_instruction_table::<H, SPEC>())),
initial_tx_gas: Arc::new(mainnet::preexecution::initial_tx_gas::<SPEC>),
initial_tx_gas: Arc::new(mainnet::preexecution::initial_tx_gas::<SPEC, DB>),
validate_env: Arc::new(mainnet::preexecution::validate_env::<SPEC, DB>),
validate_tx_against_state: Arc::new(
mainnet::preexecution::validate_tx_against_state::<SPEC, EXT, DB>,
),
call_return: Arc::new(mainnet::handle_call_return::<SPEC>),
calculate_gas_refund: Arc::new(mainnet::calculate_gas_refund::<SPEC>),
deduct_caller: Arc::new(mainnet::deduct_caller::<SPEC, EXT, DB>),
reimburse_caller: Arc::new(mainnet::handle_reimburse_caller::<SPEC, EXT, DB>),
reward_beneficiary: Arc::new(mainnet::reward_beneficiary::<SPEC, EXT, DB>),
main_return: Arc::new(mainnet::main::main_return::<EXT, DB>),
Expand All @@ -199,6 +215,11 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> {
(self.reimburse_caller)(context, gas)
}

/// Deduct caller to its limit.
pub fn deduct_caller(&self, context: &mut Context<EXT, DB>) -> Result<(), EVMError<DB::Error>> {
(self.deduct_caller)(context)
}

/// Calculate gas refund for transaction. Some chains have it disabled.
pub fn calculate_gas_refund(&self, env: &Env, gas: &Gas) -> u64 {
(self.calculate_gas_refund)(env, gas)
Expand Down Expand Up @@ -305,7 +326,15 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> {
}

/// Initial gas
pub fn initial_tx_gas(&self, env: &Env) -> u64 {
pub fn initial_tx_gas(&self, env: &Env) -> Result<u64, EVMError<DB::Error>> {
(self.initial_tx_gas)(env)
}

/// Validate ttansaction against the state.
pub fn validate_tx_against_state(
&self,
context: &mut Context<EXT, DB>,
) -> Result<(), EVMError<DB::Error>> {
(self.validate_tx_against_state)(context)
}
}
44 changes: 43 additions & 1 deletion crates/revm/src/handler/mainnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ pub mod preexecution;

use crate::{
interpreter::{return_ok, return_revert, Gas, InstructionResult},
primitives::{db::Database, EVMError, Env, Spec, SpecId::LONDON, U256},
primitives::{
db::Database,
EVMError, Env, Spec,
SpecId::{CANCUN, LONDON},
TransactTo, U256,
},
Context,
};

Expand Down Expand Up @@ -36,6 +41,43 @@ pub fn handle_call_return<SPEC: Spec>(
gas
}

#[inline]
pub fn deduct_caller<SPEC: Spec, EXT, DB: Database>(
context: &mut Context<EXT, DB>,
) -> Result<(), EVMError<DB::Error>> {
// load caller's account.
let (caller_account, _) = context
.evm
.journaled_state
.load_account(context.evm.env.tx.caller, &mut context.evm.db)
.map_err(EVMError::Database)?;

// Subtract gas costs from the caller's account.
// We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled.
let mut gas_cost = U256::from(context.evm.env.tx.gas_limit)
.saturating_mul(context.evm.env.effective_gas_price());

// EIP-4844
if SPEC::enabled(CANCUN) {
let data_fee = context.evm.env.calc_data_fee().expect("already checked");
gas_cost = gas_cost.saturating_add(data_fee);
}

// set new caller account balance.
caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost);

// touch account so we know it is changed.
caller_account.mark_touch();

// bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`.
if matches!(context.evm.env.tx.transact_to, TransactTo::Call(_)) {
// Nonce is already checked
caller_account.info.nonce = caller_account.info.nonce.saturating_add(1);
}

Ok(())
}

#[inline]
pub fn handle_reimburse_caller<SPEC: Spec, EXT, DB: Database>(
context: &mut Context<EXT, DB>,
Expand Down
Loading