NomicFoundation / edr

An Ethereum development runtime implementation that can be reused to build new developer tools.
MIT License
57 stars 12 forks source link

Can we replay an on-chain optimism transaction using `revm`? #377

Closed Wodann closed 6 months ago

Wodann commented 6 months ago

Terminology: I'll use the word vanilla to mean the unaltered EVM as proposed in the Ethereum white/yellow paper.

In its current form, it's impossible to run REVM both in vanilla and Optimism mode. Rust features are supposed to be additive such that enabling a feature doesn't disable some other feature. Unfortunately, in REVM the optimism feature flag doesn't quite work this way. When you enable the optimism feature flag, you can no longer use a vanilla EVM. Instead:

As a result, vanilla EVM exeuction code in EDR would need to set missing struct fields at construction and handle missing enum variants in match statements.

I did some experiments to see whether I could decouple the SpecId and HaltReason. The results are in this draft PR: https://github.com/bluealloy/revm/pull/1378. With this design it's possible to configure the EVM per chain, allowing:

I did not research the OpCode struct, which provides convenience functions for retrieving opcode name, accessing the jump table, etc. However, it seems possible to extend the opcode! macro to implement chain-specific OpCode structs that are able to retrieve this information for that particular chain

Based on the design pattern used in the draft PR, I think we can use the same pattern as I used for HaltReason to also convert the TxEnv and InnerEvmContext to use composition instead of a feature flag inside the structs.

With those changes, I think that would remove the last occurrences of the optimism feature flag inside "core" REVM code.

Alternatives

If we cannot make the base REVM implementation generic for alternative chains, we can use the generic EXT (i.e. external context) to pass in chain-specific data for the block, transaction, SpecId, etc.

This doesn't allow us to override the HaltReason, ExecutionResult, etc.; preventing us from returning chain specific errors this way. We can circumvent this by setting chain-specific results in the external context.

This solution doesn't follow the normal control-flow of Rust, so will be "hacky".