Skip to content

Commit

Permalink
feat: custom evm with generic handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
royvardhan committed Dec 13, 2024
1 parent e5c9f34 commit 181ea9a
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 86 deletions.
6 changes: 0 additions & 6 deletions examples/erc20_gas/src/error.rs

This file was deleted.

30 changes: 27 additions & 3 deletions examples/erc20_gas/src/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
pub mod pre_execution;
pub mod post_execution;
pub mod pre_execution;
pub mod validation;

pub use pre_execution::Erc20PreExecution;
use revm::{
context::{block::BlockEnv, tx::TxEnv, CfgEnv, Context},
context_interface::result::{EVMError, InvalidTransaction},
database_interface::Database,
handler::{EthExecution, EthHandler},
Evm,
};

pub use post_execution::Erc20PostExecution;
pub use validation::Erc20Validation;
pub use pre_execution::Erc20PreExecution;
pub use validation::Erc20Validation;

pub type Erc20Error<DB> = EVMError<<DB as Database>::Error, InvalidTransaction>;

pub type Erc20Context<DB> = Context<BlockEnv, TxEnv, CfgEnv, DB>;

pub type Erc20Handler<
CTX,
ERROR,
VAL = Erc20Validation<CTX, ERROR>,
PREEXEC = Erc20PreExecution<CTX, ERROR>,
EXEC = EthExecution<CTX, ERROR>,
POSTEXEC = Erc20PostExecution<CTX, ERROR>,
> = EthHandler<CTX, ERROR, VAL, PREEXEC, EXEC, POSTEXEC>;

pub type Erc20Evm<DB> =
Evm<Erc20Error<DB>, Erc20Context<DB>, Erc20Handler<Erc20Context<DB>, Erc20Error<DB>>>;
41 changes: 26 additions & 15 deletions examples/erc20_gas/src/handlers/post_execution.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::error::Erc20Error;
use crate::{token_operation, TREASURY};
use revm::context_interface::result::{HaltReasonTrait, InvalidHeader, InvalidTransaction};
use revm::context_interface::JournalStateGetterDBError;
use revm::handler::{EthPostExecutionContext, EthPostExecutionError};
use revm::precompile::PrecompileErrors;
use revm::{
context::Cfg,
context_interface::{
Expand All @@ -10,26 +13,34 @@ use revm::{
handler_interface::PostExecutionHandler,
primitives::U256,
specification::hardfork::SpecId,
Context,
};

pub struct Erc20PostExecution {
inner: EthPostExecution<Context, Erc20Error, HaltReason>,
pub struct Erc20PostExecution<CTX, ERROR, HALTREASON = HaltReason> {
inner: EthPostExecution<CTX, ERROR, HALTREASON>,
}

impl Erc20PostExecution {
impl<CTX, ERROR, HALTREASON> Erc20PostExecution<CTX, ERROR, HALTREASON> {
pub fn new() -> Self {
Self {
inner: EthPostExecution::new(),
}
}
}

impl PostExecutionHandler for Erc20PostExecution {
type Context = Context;
type Error = Erc20Error;
impl<CTX, ERROR, HALTREASON> PostExecutionHandler for Erc20PostExecution<CTX, ERROR, HALTREASON>
where
CTX: EthPostExecutionContext<ERROR>,
ERROR: EthPostExecutionError<CTX>
+ From<InvalidTransaction>
+ From<InvalidHeader>
+ From<JournalStateGetterDBError<CTX>>
+ From<PrecompileErrors>,
HALTREASON: HaltReasonTrait,
{
type Context = CTX;
type Error = ERROR;
type ExecResult = FrameResult;
type Output = ResultAndState<HaltReason>;
type Output = ResultAndState<HALTREASON>;

fn refund(
&self,
Expand All @@ -45,14 +56,14 @@ impl PostExecutionHandler for Erc20PostExecution {
context: &mut Self::Context,
exec_result: &mut Self::ExecResult,
) -> Result<(), Self::Error> {
let basefee = context.block.basefee();
let basefee = context.block().basefee();
let caller = context.tx().common_fields().caller();
let effective_gas_price = context.tx().effective_gas_price(*basefee);
let gas = exec_result.gas();

let reimbursement =
effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64);
token_operation(context, TREASURY, caller, reimbursement).unwrap();
token_operation::<CTX, ERROR>(context, TREASURY, caller, reimbursement)?;

Ok(())
}
Expand All @@ -63,19 +74,19 @@ impl PostExecutionHandler for Erc20PostExecution {
exec_result: &mut Self::ExecResult,
) -> Result<(), Self::Error> {
let tx = context.tx();
let beneficiary = context.block.beneficiary();
let basefee = context.block.basefee();
let beneficiary = context.block().beneficiary();
let basefee = context.block().basefee();
let effective_gas_price = tx.effective_gas_price(*basefee);
let gas = exec_result.gas();

let coinbase_gas_price = if context.cfg.spec().is_enabled_in(SpecId::LONDON) {
let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) {
effective_gas_price.saturating_sub(*basefee)
} else {
effective_gas_price
};

let reward = coinbase_gas_price * U256::from(gas.spent() - gas.refunded() as u64);
token_operation(context, TREASURY, *beneficiary, reward).unwrap();
token_operation::<CTX, ERROR>(context, TREASURY, *beneficiary, reward)?;

Ok(())
}
Expand Down
30 changes: 18 additions & 12 deletions examples/erc20_gas/src/handlers/pre_execution.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
use crate::error::Erc20Error;
use crate::{token_operation, TREASURY};
use revm::context_interface::result::InvalidHeader;
use revm::context_interface::transaction::Eip4844Tx;
use revm::context_interface::{Block, Transaction, TransactionGetter};
use revm::handler::{EthPreExecutionContext, EthPreExecutionError};
use revm::precompile::PrecompileErrors;
use revm::{
context_interface::TransactionType, handler::EthPreExecution,
handler_interface::PreExecutionHandler, primitives::U256, Context,
handler_interface::PreExecutionHandler, primitives::U256,
};

pub struct Erc20PreExecution {
inner: EthPreExecution<Context, Erc20Error>,
pub struct Erc20PreExecution<CTX, ERROR> {
inner: EthPreExecution<CTX, ERROR>,
}

impl Erc20PreExecution {
impl<CTX, ERROR> Erc20PreExecution<CTX, ERROR> {
pub fn new() -> Self {
Self {
inner: EthPreExecution::new(),
}
}
}

impl PreExecutionHandler for Erc20PreExecution {
type Context = Context;
type Error = Erc20Error;
impl<CTX, ERROR> PreExecutionHandler for Erc20PreExecution<CTX, ERROR>
where
CTX: EthPreExecutionContext,
ERROR: EthPreExecutionError<CTX> + From<InvalidHeader> + From<PrecompileErrors>,
{
type Context = CTX;
type Error = ERROR;

fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> {
self.inner.load_accounts(context)
Expand All @@ -32,20 +38,20 @@ impl PreExecutionHandler for Erc20PreExecution {
}

fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> {
let basefee = context.block.basefee();
let blob_price = U256::from(context.block.blob_gasprice().unwrap_or_default());
let basefee = context.block().basefee();
let blob_price = U256::from(context.block().blob_gasprice().unwrap_or_default());
let effective_gas_price = context.tx().effective_gas_price(*basefee);

let mut gas_cost = U256::from(context.tx().common_fields().gas_limit())
.saturating_mul(effective_gas_price);

if context.tx().tx_type() == TransactionType::Eip4844 {
if context.tx().tx_type().into() == TransactionType::Eip4844 {
let blob_gas = U256::from(context.tx().eip4844().total_blob_gas());
gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas));
}

let caller = context.tx().common_fields().caller();
token_operation(context, caller, TREASURY, gas_cost)?;
token_operation::<CTX, ERROR>(context, caller, TREASURY, gas_cost)?;

Ok(())
}
Expand Down
55 changes: 25 additions & 30 deletions examples/erc20_gas/src/handlers/validation.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
use crate::error::Erc20Error;
use crate::keccak256;
use crate::TOKEN;
use alloy_sol_types::SolValue;
use revm::context_interface::JournaledState;
use revm::context_interface::{Transaction, TransactionGetter};
use revm::handler::EthValidationContext;
use revm::handler::EthValidationError;
use revm::{
context::Cfg,
context_interface::{
result::{EVMError, InvalidTransaction},
transaction::Eip4844Tx,
JournalStateGetter, TransactionType,
},
context_interface::{result::InvalidTransaction, transaction::Eip4844Tx, TransactionType},
handler::EthValidation,
handler_interface::ValidationHandler,
primitives::U256,
Context,
};
use std::cmp::Ordering;

pub struct Erc20Validation {
inner: EthValidation<Context, Erc20Error>,
pub struct Erc20Validation<CTX, ERROR> {
inner: EthValidation<CTX, ERROR>,
}

impl Erc20Validation {
impl<CTX, ERROR> Erc20Validation<CTX, ERROR> {
pub fn new() -> Self {
Self {
inner: EthValidation::new(),
}
}
}

impl ValidationHandler for Erc20Validation {
type Context = Context;
type Error = Erc20Error;
impl<CTX, ERROR> ValidationHandler for Erc20Validation<CTX, ERROR>
where
CTX: EthValidationContext,
ERROR: EthValidationError<CTX>,
{
type Context = CTX;
type Error = ERROR;

fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> {
self.inner.validate_env(context)
Expand All @@ -42,27 +43,21 @@ impl ValidationHandler for Erc20Validation {
let caller_nonce = context.journal().load_account(caller)?.data.info.nonce;
let token_account = context.journal().load_account(TOKEN)?.data.clone();

if !context.cfg.is_nonce_check_disabled() {
if !context.cfg().is_nonce_check_disabled() {
let tx_nonce = context.tx().common_fields().nonce();
let state_nonce = caller_nonce;
match tx_nonce.cmp(&state_nonce) {
Ordering::Less => {
return Err(EVMError::Transaction(
InvalidTransaction::NonceTooLow {
tx: tx_nonce,
state: state_nonce,
}
.into(),
))
return Err(ERROR::from(InvalidTransaction::NonceTooLow {
tx: tx_nonce,
state: state_nonce,
}))
}
Ordering::Greater => {
return Err(EVMError::Transaction(
InvalidTransaction::NonceTooHigh {
tx: tx_nonce,
state: state_nonce,
}
.into(),
))
return Err(ERROR::from(InvalidTransaction::NonceTooHigh {
tx: tx_nonce,
state: state_nonce,
}))
}
_ => (),
}
Expand All @@ -73,7 +68,7 @@ impl ValidationHandler for Erc20Validation {
.and_then(|gas_cost| gas_cost.checked_add(context.tx().common_fields().value()))
.ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;

if context.tx().tx_type() == TransactionType::Eip4844 {
if context.tx().tx_type().into() == TransactionType::Eip4844 {
let tx = context.tx().eip4844();
let data_fee = tx.calc_max_data_fee();
balance_check = balance_check
Expand All @@ -88,7 +83,7 @@ impl ValidationHandler for Erc20Validation {
.expect("Balance slot not found")
.present_value();

if account_balance < balance_check && !context.cfg.is_balance_check_disabled() {
if account_balance < balance_check && !context.cfg().is_balance_check_disabled() {
return Err(InvalidTransaction::LackOfFundForMaxFee {
fee: Box::new(balance_check),
balance: Box::new(account_balance),
Expand Down
Loading

0 comments on commit 181ea9a

Please sign in to comment.