fix(evm): rescue gas before inspector callback to preserve remaining gas#171
Open
fix(evm): rescue gas before inspector callback to preserve remaining gas#171
Conversation
…ue_gas GasInspector::call_end (revm) calls gas.spend_all() on error results such as OutOfGas. This is called from frame_end in inspect_frame_run and inspect_frame_init, before frame_return_result has a chance to call rescue_gas(). The rescue_gas mechanism saves the remaining gas of a frame halted by MegaETH's additional limits (e.g. state growth limit) so it can be refunded to the sender in last_frame_result. With the inspector path, spend_all() zeroes gas.remaining() first, so rescue_gas() reads 0 and the refund is lost. This causes different gas_used values compared to the non-inspector path, resulting in a receipts root mismatch when validating blocks. Fix: save gas.remaining() before calling frame_end, then restore it afterwards if the inspector callback reduced it. The inspector still receives the unmodified (pre-spend_all) gas value for its callbacks, which is fine since GasInspector computes last_gas_cost from the delta between step() and step_end() readings rather than from the final remaining value.
flyq
approved these changes
Feb 27, 2026
Move rescue_gas to happen before the inspector's frame_end callback instead of saving/restoring gas around it. This is structurally correct because TX-level limit exceeds are always detected during execution, never newly discovered in before_frame_return_result. - Add try_rescue_gas method on AdditionalLimit that checks check_limit() - Call try_rescue_gas in after_frame_run and frame_init (before_frame_init early-return path) - Remove rescue_gas from before_frame_return_result - Remove save/restore gas pattern from inspect_frame_run and inspect_frame_init - Add tests with GasSpendingInspector covering both inspect_frame_init and inspect_frame_run paths
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
Move
rescue_gasto happen before the inspector'sframe_endcallback instead of saving/restoring gas around it.This prevents inspectors like
TracerEip3155(which callsGas::spend_all()on error results incall_end) from zeroinggas.remaining()before rescue_gas captures it.Changes
try_rescue_gasmethod onAdditionalLimitthat checkscheck_limit()and rescues gas if a TX-level limit was exceededtry_rescue_gasinafter_frame_run(coversframe_runandinspect_frame_runpaths) and inframe_init(before_frame_initearly-return path)rescue_gasfrombefore_frame_return_result— just mark the result as exceeding limitinspect_frame_runandinspect_frame_initGasSpendingInspectortest inspector and two tests covering bothinspect_frame_initandinspect_frame_runpaths