Skip to content

Conversation

@alessandromazza98
Copy link

Problem

  • as of now the Evm trait hard codes the ResultAndState<Self::HaltReason> without providing the possibility to use a custom state, different than the evm state.
  • The ResultAndState though has the possibility to use a generic S (it defaults to EvmState).
  • This way it's not possible to use a custom state in the evm.

Solution

  • add a new associated type State: Into<EvmState> and use it in the ResultAndState type: ResultAndState<Self::HaltReason, Self::State>.
  • we still need the State associated type to be convertible into a EvmState now, but at least we let implementers of the Evm trait to use a custom state that wraps the EvmState with something more.

What do you think? @mattsse

@alessandromazza98
Copy link
Author

hey @mattsse sorry to bump this PR, I'd happy if you give it a look. I think this PR is important as it lets implementers to return a custom state (that still needs to be converted to the evm state somehow) but at least you can pass the evm a state that contain more info than the usual evm one

Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's an example for this?

with still we still only can commit the evmstate so this the expectation that this is like MyState {EvmState, extrastuff}?

@alessandromazza98
Copy link
Author

alessandromazza98 commented Aug 28, 2025

what's an example for this?

with still we still only can commit the evmstate so this the expectation that this is like MyState {EvmState, extrastuff}?

correct, basically it lets implementers to use a custom state that wraps the evm state. Your example perfectly fits.

This is important because then in the BlockExecutor trait, when we call the self.evm.transact(tx) it returns this custom state. And then we can both commit the evm state and use the extra stuff for other purposes. [you have to think this evm being used in a node's executor]

What do you think?

@klkvr
Copy link
Member

klkvr commented Aug 28, 2025

Do you have any specific usecase that this will enable? I could see how generic state would allow returning more context from EVM, however currently we're still bottlenecked by the database APIs I believe? i.e you can't commit anything besides EvmState to DatabaseCommit impl meaning that any custom state transitions can't be applied.

The way I've thought about state abstraction since the begging was that the key abstraction point will be an Account because state is basically an Address -> Account mapping, and adding other dimensions to it might not be very useful

@alessandromazza98
Copy link
Author

Do you have any specific usecase that this will enable? I could see how generic state would allow returning more context from EVM, however currently we're still bottlenecked by the database APIs I believe? i.e you can't commit anything besides EvmState to DatabaseCommit impl meaning that any custom state transitions can't be applied.

The way I've thought about state abstraction since the begging was that the key abstraction point will be an Account because state is basically an Address -> Account mapping, and adding other dimensions to it might not be very useful

sure I can bring my example here.

I use a custom evm that has a custom state. This custom state is something like:

struct MyEvmState {
    evm_state: EvmState,
    extra_state: ExtraState
}

// this struct contains some data that I need but you can imagine it to
// just be something like this.
struct ExtraState {
    counter: u64,
}

Then in the ExecuteEvm trait implementation that is used when calling evm.transact(tx) I constrain the associated State type to be this MyEvmState. Note here that revm already lets you use a custom State type here...

Then, when I want to use my custom evm in the executor of the node, I need to also impl the BlockExecutor trait so that it can not only execute the tx on the evm, but create the receipts, commit changes etc... And inside this trait impl I'd like to use the same custom state I used in the ExecuteEvm trait.

But right now, without this PR, I cannot do it because I'm constrained to use an evm that implements this Evm trait and this trait doesn't let me customize the state that is returned because it's hard coded to be the EvmState.

But ideally I'd like this PR to be merged so that in the BlockExecutor trait impl I can then do this:

impl BlockExecutor for MyBlockExecutor {
    fn execute_transaction_with_commit_condition(
        &mut self,
        tx: impl ExecutableTx<Self>,
        f: impl FnOnce(&ExecutionResult<<Self::Evm as Evm>::HaltReason>) -> CommitChanges,
    ) -> Result<Option<u64>, BlockExecutionError> {
        // ... some code here for gas checks and so on

        // execute the tx
        let ResultAndState {
                result,
                state: my_custom_state,
            } = self.evm.transact(tx)?        

         // get evm state
        let evm_state = my_custom_state.evm_state;
        // commit them to the db
        self.evm.db_mut().commit(evm_state);
        // get my extra state
        let extra_state = my_custom_state.extra_state;
        // and then do whatever I want here with my extra state.
        // For example I can increment a counter in MyBlockExecutor that is used somewhere in the code
        self.counter += extra_state.counter;
        
        // ... rest of the code
    }
}

@klkvr
Copy link
Member

klkvr commented Aug 29, 2025

yeah but what would this abstraction use enable exactly? is it required to re-implement some existing EVM? I don't think this pattern can be integrated into e.g reth right now

And even in your example the counter increment won't be propagated to the next transaction

@alessandromazza98
Copy link
Author

alessandromazza98 commented Aug 29, 2025

yeah but what would this abstraction use enable exactly? is it required to re-implement some existing EVM? I don't think this pattern can be integrated into e.g reth right now

The idea is to use this on a custom node that uses Reth SDK, not reth (meaning the ethereum or op-reth optimism node) directly.

And even in your example the counter increment won't be propagated to the next transaction

That's true but only because my example is simple. I actually have a method on my custom evm where I commit the extra state like this:

// execute the tx
let ResultAndState {
        result,
        state: my_custom_state,
    } = self.evm.transact(tx)?        

 // get evm state
let evm_state = my_custom_state.evm_state;
// commit them to the db
self.evm.db_mut().commit(evm_state);
// get my extra state
let extra_state = my_custom_state.extra_state;
// commit it
self.evm.commit_extra_state(extra_state);

The main blocker without this PR is that I cannot return a custom state from the evm.transact(tx) and so I need to find a different way to get the extra state changes (and later commit them)

@mattsse
Copy link
Member

mattsse commented Aug 29, 2025

is this an extra byproduct that you need to include in the block?

@alessandromazza98
Copy link
Author

is this an extra byproduct that you need to include in the block?

I'll probably need to add it to the block as well. Right now I'm using it in the evm execution though. Basically based on this extra state, some valid (for the vanilla evm) transactions can be flagged as invalid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants