Skip to content

Commit

Permalink
feat(Handler): Add ClearHandle (bluealloy#1368)
Browse files Browse the repository at this point in the history
* fix(revme): Print one json outcome in statetest

* feat(Handler): Add ClearHandle

* nits

* clippy
  • Loading branch information
rakita authored May 1, 2024
1 parent 67c13f3 commit aceb093
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 30 deletions.
72 changes: 45 additions & 27 deletions crates/revm/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,27 +88,63 @@ impl<EXT, DB: Database> Evm<'_, EXT, DB> {
/// has enough balance to pay for the gas.
#[inline]
pub fn preverify_transaction(&mut self) -> Result<(), EVMError<DB::Error>> {
self.handler.validation().env(&self.context.evm.env)?;
self.handler
.validation()
.initial_tx_gas(&self.context.evm.env)?;
self.handler
.validation()
.tx_against_state(&mut self.context)?;
Ok(())
let output = self.preverify_transaction_inner().map(|_| ());
self.clear();
output
}

/// Calls clear handle of post execution to clear the state for next execution.
fn clear(&mut self) {
self.handler.post_execution().clear(&mut self.context);
}

/// Transact pre-verified transaction
///
/// This function will not validate the transaction.
#[inline]
pub fn transact_preverified(&mut self) -> EVMResult<DB::Error> {
let initial_gas_spend = self
.handler
.validation()
.initial_tx_gas(&self.context.evm.env)
.map_err(|e| {
self.clear();
e
})?;
let output = self.transact_preverified_inner(initial_gas_spend);
let output = self.handler.post_execution().end(&mut self.context, output);
self.clear();
output
}

/// Pre verify transaction inner.
#[inline]
fn preverify_transaction_inner(&mut self) -> Result<u64, EVMError<DB::Error>> {
self.handler.validation().env(&self.context.evm.env)?;
let initial_gas_spend = self
.handler
.validation()
.initial_tx_gas(&self.context.evm.env)?;
self.handler
.validation()
.tx_against_state(&mut self.context)?;
Ok(initial_gas_spend)
}

/// Transact transaction
///
/// This function will validate the transaction.
#[inline]
pub fn transact(&mut self) -> EVMResult<DB::Error> {
let initial_gas_spend = self.preverify_transaction_inner().map_err(|e| {
self.clear();
e
})?;

let output = self.transact_preverified_inner(initial_gas_spend);
self.handler.post_execution().end(&mut self.context, output)
let output = self.handler.post_execution().end(&mut self.context, output);
self.clear();
output
}

/// Returns the reference of handler configuration
Expand Down Expand Up @@ -165,24 +201,6 @@ impl<EXT, DB: Database> Evm<'_, EXT, DB> {
&mut self.context.evm.env.block
}

/// Transact transaction
///
/// This function will validate the transaction.
#[inline]
pub fn transact(&mut self) -> EVMResult<DB::Error> {
self.handler.validation().env(&self.context.evm.env)?;
let initial_gas_spend = self
.handler
.validation()
.initial_tx_gas(&self.context.evm.env)?;
self.handler
.validation()
.tx_against_state(&mut self.context)?;

let output = self.transact_preverified_inner(initial_gas_spend);
self.handler.post_execution().end(&mut self.context, output)
}

/// Modify spec id, this will create new EVM that matches this spec id.
pub fn modify_spec_id(&mut self, spec_id: SpecId) {
self.handler.modify_spec_id(spec_id);
Expand Down
17 changes: 16 additions & 1 deletion crates/revm/src/handler/handle_types/post_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ pub type EndHandle<'a, EXT, DB> = Arc<
+ 'a,
>;

/// Clear handle, doesn't have output, its purpose is to clear the
/// context. It will be always called even on failed validation.
pub type ClearHandle<'a, EXT, DB> = Arc<dyn Fn(&mut Context<EXT, DB>) + 'a>;

/// Handles related to post execution after the stack loop is finished.
pub struct PostExecutionHandler<'a, EXT, DB: Database> {
/// Reimburse the caller with ethereum it didn't spent.
Expand All @@ -43,8 +47,13 @@ pub struct PostExecutionHandler<'a, EXT, DB: Database> {
pub reward_beneficiary: RewardBeneficiaryHandle<'a, EXT, DB>,
/// Main return handle, returns the output of the transact.
pub output: OutputHandle<'a, EXT, DB>,
/// End handle.
/// End handle. Called when execution ends.
/// End in comparison to output will be called every time after execution.
/// Output in case of error will not be called.
pub end: EndHandle<'a, EXT, DB>,
/// Clear handle will be called always. In comparison to end that
/// is called only on execution end, clear handle is called even if validation fails.
pub clear: ClearHandle<'a, EXT, DB>,
}

impl<'a, EXT: 'a, DB: Database + 'a> PostExecutionHandler<'a, EXT, DB> {
Expand All @@ -55,6 +64,7 @@ impl<'a, EXT: 'a, DB: Database + 'a> PostExecutionHandler<'a, EXT, DB> {
reward_beneficiary: Arc::new(mainnet::reward_beneficiary::<SPEC, EXT, DB>),
output: Arc::new(mainnet::output::<EXT, DB>),
end: Arc::new(mainnet::end::<EXT, DB>),
clear: Arc::new(mainnet::clear::<EXT, DB>),
}
}
}
Expand Down Expand Up @@ -94,4 +104,9 @@ impl<'a, EXT, DB: Database> PostExecutionHandler<'a, EXT, DB> {
) -> Result<ResultAndState, EVMError<DB::Error>> {
(self.end)(context, end_output)
}

/// Clean handler.
pub fn clear(&self, context: &mut Context<EXT, DB>) {
(self.clear)(context)
}
}
2 changes: 1 addition & 1 deletion crates/revm/src/handler/mainnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ pub use execution::{
frame_return_with_refund_flag, insert_call_outcome, insert_create_outcome,
insert_eofcreate_outcome, last_frame_return,
};
pub use post_execution::{end, output, reimburse_caller, reward_beneficiary};
pub use post_execution::{clear, end, output, reimburse_caller, reward_beneficiary};
pub use pre_execution::{deduct_caller, deduct_caller_inner, load_accounts, load_precompiles};
pub use validation::{validate_env, validate_initial_tx_gas, validate_tx_against_state};
8 changes: 8 additions & 0 deletions crates/revm/src/handler/mainnet/post_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ pub fn end<EXT, DB: Database>(
evm_output
}

/// Clear handle clears error and journal state.
#[inline]
pub fn clear<EXT, DB: Database>(context: &mut Context<EXT, DB>) {
// clear error and journaled state.
let _ = context.evm.take_error();
context.evm.inner.journaled_state.clear();
}

/// Reward beneficiary with gas fee.
#[inline]
pub fn reward_beneficiary<SPEC: Spec, EXT, DB: Database>(
Expand Down
6 changes: 6 additions & 0 deletions crates/revm/src/journaled_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ impl JournaledState {
}
}

/// Clears the JournaledState. Preserving only the spec.
pub fn clear(&mut self) {
let spec = self.spec;
*self = Self::new(spec, HashSet::new());
}

/// Does cleanup and returns modified state.
///
/// This resets the [JournaledState] to its initial state in [Self::new]
Expand Down
5 changes: 4 additions & 1 deletion documentation/src/crates/revm/handler.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,7 @@ Is a list of functions that are called after the execution. They are called in t
Returns the state changes and the result of the execution.

* `end`:
Always called as the last function of the handler.
Always called after transaction. End handler will not be called if validation fails.

* `clear`:
Clears journal state and error and it is always called for the cleanup.

0 comments on commit aceb093

Please sign in to comment.