Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exec API #1971

Open
rakita opened this issue Jan 6, 2025 · 3 comments
Open

Exec API #1971

rakita opened this issue Jan 6, 2025 · 3 comments

Comments

@rakita
Copy link
Member

rakita commented Jan 6, 2025

This is more a braindump and not sure if it is feasible.

We already have a

trait Exec {
  Tx: TX,
  Block: BLOCK

   fn exec(&mut self, tx: Self::Tx) -> ...
}

But this is implemented on top of Evm that has both context and handler. This is not ideal.

What we could do is implement it on top of Context. as this is only thing needs if you want to execute mainnet (Example https://github.com/paradigmxyz/revm-inspectors/pull/246/files#diff-46eeb2ab3ba4789e3dc52c466aae384684de97495e482bf1b08c8008ff96a343R72)

But what with a Optimism, one of requirements is to have a separate Evm setup and Evm exec, we would like both Mainnet and Optimism to use Exec trait

The idea would be to add OpContext<CTX>(CTX) and implement Exec trait on top of it. Additionally, we can add types that would be cast from this type to Context and vice versa. Without newtype we couldn't differentiate between chains.

Additionally, we should have a InspectorExec that is going to be implemented for Contextso context can be used without creating a new InspectorEvm type.

@rakita
Copy link
Member Author

rakita commented Jan 10, 2025

In general this should look like this:

Rought example of how OpExec traits needs to look

/// Wrapper around Context so we can have EvmExec trait implemented for it.
pub struct OpCtx<TX,BLOCK,CFG,DB: Database,JOURNAL: Journal<Database=DB>>(Context<BLOCK,TX,CFG,DB,JOURNAL,L1BlockInfo>);

trait OpExec: TransactionSetter + BlockSetter {
    type Output;

    fn op_exec(&mut self) -> Self::Output;

    fn op_exec_with_tx(&mut self, tx: Self::Transaction) -> Self::Output {
        self.set_tx(tx);
        self.op_exec()
    }
}

/// Inspector is little bit tricky to get right, as Inspector require context to run. Having Ctx as associated type is probably a way forward. 
/// If needed we can leave this for later.
trait OpInspect: OpExec {
    fn inspect(&mut self, insp: ()) -> Self::Output;

    fn inspect_with_tx(&mut self, tx: Self::Transaction, insp: Inspector) -> Self::Output {
        self.set_tx(tx);
        self.inspect(insp)
    }
}

trait OpInto {
    type Block: Block;
    // tx,cfg, etc..

    fn into_optimism(self) -> OpCtx<TX,..>;

    fn into_mainnet(self) -> Ctx<TX,..>;
}

We just need Context to execute transaction: https://github.com/paradigmxyz/revm-inspectors/blob/5a062c36b5059ffacb09601fbc8df7fd13bcbea6/tests/it/utils.rs#L64-L66

This is where all traits need to implemented

// Gives exec_op if context is properly structured
impl OpExec for Context {}
impl OpInspect for Context {}

// Gives parity of functions to OpCtx
impl OpExec for OpCtx {}
impl OpInspect for OpCtx {}

// Gives one unified trait for every EVM to use
impl Inspect for OpCtx {} // Inspect trait does not exist yet! Can be skipped.
impl Exec for OpCtx {}

// Gives ability to convert between OP/Main and switch Inspect/Exec trait impl.
impl OpInto for Context {}
impl OpInto for OpCtx {}

This is how we can expect to use it:

// This will simplify API for using EVMs as
fn api(tx: OpTransaction<TxEnv>) {
    let mut ctx = Context::new(...);
    ctx.set_block(BlockEnv::default());
    // exec as mainnet;
    let out = ctx.exec(tx);
    // set L1BlockInfo se we can use op_exec.
    let mut ctx = ctx.with_chat(L1BlockInfo::default());
    // exec as optimism if we have L1Block type set;
    let out= ctx.op_exec(tx);
    // still execute mainnet as default.
    let out = ctx.exec(tx);

    // converting it to optimism
    let mut ctx = ctx.into_optimism();
    // exec is now optimism!
    ctx.exec(tx);
    // we can potentially add to allow executing mainnet.
    ctx.exec_commit_mainnet(tx);

    // Inspectors should be used as. And it is done with optimism handler.
    ctx.inspect(tx, Inspector::new());
    // I renamed and switched names a little here. Not sure if `inspect_previous` is good name.
    let ctx = ctx.into_mainnet();
    // now we have returned to mainnet.
    ctx.inspect_previous(Inspector::new());
}

@Wodann
Copy link
Contributor

Wodann commented Jan 10, 2025

If I am a user of the API, why do I want to have the distinction of executing for mainnet vs optimism?

Don't I just want to execute the Optimism transaction, have the Optimism EVM implementation figure out how to execute it, and then return an Optimism output?

@rakita
Copy link
Member Author

rakita commented Jan 10, 2025

If I am a user of the API, why do I want to have the distinction of executing for mainnet vs optimism?

That is why there is one Exec trait and more specialized trait for OpExec. Idea is to have a setup where you figure out what you want to run and than return Box< dyn Exec> for example, this would separate the settup from using Evm.

Don't I just want to execute the Optimism transaction, have the Optimism EVM implementation figure out how to execute it, and then return an Optimism output?

Op transaction is not a type but a trait, bcs of that it is impossible to implement all of them on the same type (aka Context). That is why you have newtype for OpContext. If we had a API that would change transaction type then we would need to consume Ctx and return a new one. What you are saying is better and I tried a few times to add something similar, but I am not sure if it is possible to have.

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

No branches or pull requests

2 participants