Do not return gas cost along with ExceptionalHaltReason in OperationResult unless OOG #7919
+617
−700
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.
PR description
Rationale
While working on something unrelated I discovered that EVM halt events also require to pass in a gas cost. The meaning of this gas cost is not totally clear across all halt event types (insufficient gas, stack underflow/overflow, invalid jump destination, illegal state change, ...), so this inconsistency makes it harder for devs building new EVM features, see example on code from initial verkle gas schedule efforts. In the Ethereum Yellow paper it is mentioned that at the start of a transaction an upfront gas cost is taken captive and, then, if code is executed correctly the remaining unused gas is refunded to the account. In the event of an EVM error, halting the code execution, no gas is refunded and so the entire upfront gas made captive is consumed. Therefore, clients do not need any cost information upon halting.
Tracer cases
There is the case of tracers that have access to
OperationResult
when callingtracePostExecution
. IMO, the only case where returning a gas cost along with the halt condition might make sense is the insufficient gas case. This seems to show exactly in which amount of gas charging did the EVM halted on. It does not make any sense at all to report a gas cost when the reason for the halt was not related to gas. In fact, I would question if it's really useful to report the gas cost at all even in the insufficient gas case because this is not specific enough (was it the static gas or a dynamic part? which type of gas exactly?). Currently the default in clients is to compound all gas costs for an opcode execution in advance of any DB lookups or computation. But one could incrementally compute the cost before any computation and check for out of gas incrementally also. I believe this will happen with stateless clients. This is also used by tracing APIs (in particular trace_* calls) but the current spec does not mention any standards around EVM halting cases. Right now I'm told that Nethermind and Reth implement these but I haven't checked how they handle this case in particular. A quick scan on Reth's website didn't show many details on this edge case either.Changes
This PR refactors and streamlines the
OperationResult
, the outcome of executing an opcode, to split up between halting and successful results. It also enforces the creation of new haltOperationResults
through static factory methods for better encapsulation, that always create a new instance since, on a halt event, the execution context is not recoverable so the overhead is negligible.I'm proposing that we get rid of this gas cost reporting in
OperationResult
when anExceptionalHaltReason
is used, to make it all consistent. I changed some tests to cope with this modification so please review carefully. On the other hand, if someone feels strongly we could very well keep reporting gas cost upon insufficient gas.EDIT: Latest tip allows for gasCost reporting for insufficient gas only.
Thanks for sending a pull request! Have you done the following?
doc-change-required
label to this PR if updates are required.Locally, you can run these tests to catch failures early:
./gradlew build
./gradlew acceptanceTest
./gradlew integrationTest
./gradlew ethereum:referenceTests:referenceTests