Skip to content

Commit

Permalink
fix: Save evm context after errors
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielSchiavini committed May 3, 2024
1 parent bfd1451 commit 827a013
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 20 deletions.
6 changes: 3 additions & 3 deletions src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,9 @@ impl EVM {
let evm_context: EvmContext<DB> =
replace(&mut self.context, EvmContext::new(DB::new_memory()));
let (result, evm_context) =
call_evm(evm_context, self.handler_cfg, self.tracing, is_static)?;
call_evm(evm_context, self.handler_cfg, self.tracing, is_static);
self.context = evm_context;
self.result = Some(result.clone());
Ok(result)
self.result = result.as_ref().map(|r| r.clone()).ok();
result
}
}
36 changes: 19 additions & 17 deletions src/executor.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
use crate::database::DB;
use crate::utils::pyerr;
use std::mem::replace;

use pyo3::exceptions::PyRuntimeError;
use pyo3::PyResult;
use revm::{
Context, ContextWithHandlerCfg, Evm, EvmContext, FrameOrResult, FrameResult,
inspector_handle_register,
};
use revm::inspectors::TracerEip3155;
use revm::precompile::Log;
use revm::primitives::TransactTo;
use revm::primitives::{ExecutionResult, ShanghaiSpec};
use revm::{
inspector_handle_register, Context, ContextWithHandlerCfg, Evm, EvmContext, FrameOrResult,
FrameResult,
};
use revm::primitives::TransactTo;
use revm_interpreter::{CallInputs, CreateInputs, gas, SuccessOrHalt};
use revm_interpreter::primitives::HandlerCfg;
use revm_interpreter::{gas, CallInputs, CreateInputs, SuccessOrHalt};
use std::mem::replace;

use crate::database::DB;
use crate::utils::pyerr;

/// Calls the EVM with the given context and handler configuration.
pub(crate) fn call_evm(
evm_context: EvmContext<DB>,
handler_cfg: HandlerCfg,
tracing: bool,
is_static: bool,
) -> PyResult<(ExecutionResult, EvmContext<DB>)> {
) -> (PyResult<ExecutionResult>, EvmContext<DB>) {
if tracing {
let tracer = TracerEip3155::new(Box::new(crate::pystdout::PySysStdout {}));
let evm = Evm::builder()
let mut evm = Evm::builder()
.with_context_with_handler_cfg(ContextWithHandlerCfg {
cfg: handler_cfg,
context: Context {
Expand All @@ -33,9 +35,9 @@ pub(crate) fn call_evm(
})
.append_handler_register(inspector_handle_register)
.build();
run_evm(evm, is_static)
(run_evm(&mut evm, is_static), evm.context.evm)
} else {
let evm = Evm::builder()
let mut evm = Evm::builder()
.with_context_with_handler_cfg(ContextWithHandlerCfg {
cfg: handler_cfg,
context: Context {
Expand All @@ -44,15 +46,15 @@ pub(crate) fn call_evm(
},
})
.build();
run_evm(evm, is_static)
(run_evm(&mut evm, is_static), evm.context.evm)
}
}

/// Calls the given evm. This is originally a copy of revm::Evm::transact, but it calls our own output function
fn run_evm<EXT>(
mut evm: Evm<'_, EXT, DB>,
evm: &mut Evm<'_, EXT, DB>,
is_static: bool,
) -> PyResult<(ExecutionResult, EvmContext<DB>)> {
) -> PyResult<ExecutionResult> {
let logs_i = evm.context.evm.journaled_state.logs.len();

evm.handler
Expand Down Expand Up @@ -137,7 +139,7 @@ fn run_evm<EXT>(
let logs = ctx.evm.journaled_state.logs[logs_i..].to_vec();

// Returns output of transaction.
Ok((output(ctx, result, logs)?, evm.context.evm))
Ok(output(ctx, result, logs)?)
}

fn call_inputs<EXT>(
Expand Down
6 changes: 6 additions & 0 deletions tests/fixtures/min.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
61003e61000f60003961003e6000f35f3560e01c637ae2b5c781186100365760443610341761003a5760043560243580820382811161003a579050905060405260206040f35b5f5ffd5b5f80fd84183e8000a16576797065728300030b0012

@external
@view
def min(x: uint256, y: uint256) -> uint256:
return x - y
17 changes: 17 additions & 0 deletions tests/test_evm.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,20 @@ def test_get_blobhashes(blob_hashes):

# the contract logs 6 blob hashes, so pad with 0s
assert logged == blob_hashes + [b"\0" * 32] * (6 - len(blob_hashes))


@pytest.mark.parametrize("kwargs", KWARG_CASES)
def test_call_reverting(kwargs):
evm = EVM()
code = load_contract_bin("min.bin")
deploy_address = evm.deploy(address, code)

with pytest.raises(RuntimeError) as err:
evm.message_call(
caller=address,
to=deploy_address,
value=10,
)

assert evm.get_code(deploy_address), "The code should still be deployed after revert"
assert str(err.value).startswith("Transaction(LackOfFundForMaxFee")

0 comments on commit 827a013

Please sign in to comment.