RFC: custom MegaTransactionError variants without abandoning OpHandler#321
Draft
mark0-cn wants to merge 1 commit into
Draft
RFC: custom MegaTransactionError variants without abandoning OpHandler#321mark0-cn wants to merge 1 commit into
mark0-cn wants to merge 1 commit into
Conversation
Replace the stale TODO (which assumed a custom transaction-error enum requires abandoning OpHandler) with an accurate note: OpHandler is generic over its error type, so a local newtype error adapter can satisfy the From<OpTransactionError> bound without reimplementing the handler. Full design options are discussed in the RFC PR.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is an RFC / discussion PR (draft, not meant to merge as-is) about lifting a known limitation:
MegaTransactionError(crates/mega-evm/src/evm/result.rs) is forced to be a type alias forop_revm::OpTransactionError, so mega-evm cannot surface its own transaction validation error variants — even thoughMegaHaltReasonis already a first-classBase(..) + customenum.The only code change here corrects the stale
TODOinresult.rs: the previous note claimed a custom tx-error enum requires abandoningOpHandlerand reimplementing every handler method. That is not the cheapest path. I'd like to agree on a direction before implementing.Problem
MegaHaltReason(result.rs:62) is a proper enum:Base(OpHaltReason)plus mega variants (DataLimitExceeded,ComputeGasLimitExceeded,VolatileDataAccessOutOfGas,SystemTxInvalidCallee, …). Transaction validation errors get no such treatment —MegaTransactionError = OpTransactionError(result.rs:56).Consequence: mega-specific validation failures are surfaced as untyped strings. The system-transaction callee-whitelist rejection returns
FromStringError::from_string("Mega system transaction callee is not in the whitelist")(evm/execution.rs:104), landing inEVMError::Custom(String)— not structured, not matchable by embedders. There is even aMegaHaltReason::SystemTxInvalidCallee { callee }variant (result.rs:94) that is effectively unused because the real rejection path is a string.Root cause (verified against revm 27.1.0 / op-revm 8.1.0)
op_revm::OpHandler<EVM, ERROR, FRAME>is generic overERROR, and itsHandlerimpl bounds it (op-revm-8.1.0/src/handler.rs:68-71):ERROR::from(OpTransactionError::..)internally (e.g.handler.rs:396).EvmTrError<EVM>is an auto-trait (blanket impl atrevm-handler-8.1.0/src/handler.rs:37) requiringFrom<InvalidTransaction> + From<InvalidHeader> + From<DBError> + From<ContextError<DBError>> + FromStringError.MegaHandler(evm/execution.rs:47-59,408-424) wrapsOpHandleranddelegate!svalidate_env/validate_against_state_and_deduct_caller/reimburse_caller/refundto it, inheriting theFrom<OpTransactionError>bound. The concrete error isEVMError<DB::Error, MegaTransactionError>(evm/factory.rs:126,evm/interfaces.rs:125).MegaTransactionErrora custom enum we'd needEVMError<DB, MegaTransactionError>: From<OpTransactionError>. revm provides no genericFrom<TX>forEVMError(onlyFrom<InvalidTransaction>whenTX: From<InvalidTransaction>,From<DBError>,From<ContextError>,FromStringError), and we cannot addimpl From<OpTransactionError> for EVMError<DBError, MegaTransactionError>here:FromandEVMErrorare foreign andDBErroris an uncovered type parameter ⇒ orphan-rule violation.Why
MegaHaltReasonescapes this:HaltReasonis an associated type (type HaltReason = MegaHaltReasonatexecution.rs:424), set directly with explicitFrom/TryFromimpls — no generic bound forces a foreign conversion. The error type is a generic parameter with aFrom<OpTransactionError>bound, so the constraint propagates.Options
Option A — Local newtype error adapter (recommended)
Keep delegating to
OpHandler. Introduce a local newtype and makeMegaTransactionErrora real enum:Because
MegaErroris local, it can implement the whole handler-error surface, including the blocked conversion:From<OpTransactionError>(→Base),From<MegaTransactionError>,From<InvalidTransaction>,From<InvalidHeader>,From<DBError>,From<ContextError<DBError>>,FromStringError,IsTxError,Debug/Display/Error(EvmTrErrorthen comes for free via its blanket). InstantiateMegaHandler<_, MegaError<DB::Error>, EthFrame<..>>and unwrap at theHandler::runboundary so the publicExecuteEvm::Errorstays the naturalEVMError<DB::Error, MegaTransactionError>.OpHandlerdelegation and all upstream validation logic stay intact. Mirrors the provenMegaHaltReasondesign. Mega validation failures (starting withSystemTxInvalidCallee) become structured.MegaTransactionErrorbecomes an enum), a breaking change for embedders matching on it.OpHandleris unnecessary.Option B — Reimplement the handler without
OpHandler(the current TODO's path)Set
ERROR = EVMError<DB, MegaTransactionError>directly (revm's blankets cover it onceMegaTransactionError: From<InvalidTransaction>), and reimplement the four delegated methods, convertingOpTransactionError → MegaTransactionError::Baseexplicitly viamap_err(noFrombound, no orphan issue).EVMError<DB, MegaTransactionError>.validate_env/validate_against_state_and_deduct_caller/reimburse_caller/refundlogic and keep it in sync across every op-revm bump — real, recurring drift risk.Option C — Upstream change in op-revm
Ask op-revm to make the
OpTransactionError → ERRORconversion injectable (assoc fn / closure /MapTxError) instead of a hardFrom<OpTransactionError>bound.Option D — Status quo
Keep the alias; represent mega validation failures as
EVMError::Custom(String)or as halt reasons.SystemTxInvalidCalleestays defined-but-unused.Recommendation
Option A. It unblocks structured mega validation errors with bounded, mechanical effort and no reimplementation of upstream handler logic, and it makes the tx-error story symmetric with
MegaHaltReason. Option C is worth raising upstream in parallel as the longer-term cleanup.Proposed scope of the first implementation
MegaTransactionErrortoBase(OpTransactionError) + SystemTxInvalidCallee { callee }(+serde,Display,Error,From<InvalidTransaction>), mirroringMegaHaltReason.MegaErrornewtype adapter + trait impls; thread it as the handlerERROR, unwrap atHandler::run.evm/execution.rs:104(and the REX5 system-tx nonce/chain-id checks) through the new variant instead offrom_string.blockexecutor,bin/mega-evme,bin/mega-t8n) and provideFrom/accessors; call out the breaking error-type change for embedders and coordinate a version bump.Open questions
alloy_evm::Evm::Error/ block-executor error conversion impose bounds beyond whatEVMError<DB, MegaTransactionError>already satisfies via revm blankets? (Expected: no, givenMegaTransactionError: Error + Display.)EVMError<DB, MegaTransactionError>(cleaner type) vs. exposeMegaErrordirectly (fewer conversions)? I lean unwrap.SystemTxInvalidCallee, or also the REX5 system-tx nonce/chain-id checks)?Offer to implement
If the team is happy with Option A (or prefers B/C), I'd be glad to take this from RFC to implementation myself: the
MegaTransactionErrorenum +MegaErroradapter, rerouting the system-tx validation path, the downstream migration, and full test coverage (structured-variant round-trip à la the existingMegaHaltReasontests, plus the system-tx callee rejection path). Happy to split it into reviewable steps and iterate on the direction first.