-
Notifications
You must be signed in to change notification settings - Fork 14
core/vm, core/state: return error if gas is insufficient for cost #540
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
base: kaustinen7-gas-costs
Are you sure you want to change the base?
core/vm, core/state: return error if gas is insufficient for cost #540
Conversation
core/state/access_events.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it be expected1 + expected2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure I understand the question, it is expected1+expected2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in your implementation, the required gas is expected1+expected2, but the returned value is params.WarmStorageReadCostEIP2929 which i believe is a mistake
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right it's a rebase error, in the source branch it's the correct behavior
core/state/access_events.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if consumed < expected1, then the availableGas-consumed will be incorrect
although the returned expected gas cost is correct...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense: we are returning the expected gas and we are saving a confusing check. I can fix it if you don't like it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
come to think of if, it's really confusing. I'll fix it if we don't go your way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would prefer to using my approach to aviod the complexity in the first place
core/vm/eips.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's very weird.
Theoretically, if the available gas is not sufficient, we should terminate the execution and deduct all the remaining gas outside.
It's very weird to deduct the gas cost regardless of the available gas is sufficient or not...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not that weird, any error condition would just "fall through". It's used a lot in languages that support passing errors.
core/vm/evm.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the len(ret) > 0 is unnecessary, as the gas cost for empty code is always zero.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems to come from a rebase, I agree this is now useless
core/vm/operations_verkle.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should use contract.Gas - gas
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah that was introduced when I moved from op to gas. Makes sense that it works, but we need to update the spec tests to make sure that this could have been caught.
95344c7 to
515e4b3
Compare
gballet
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense and it seems to work with the execution spec tests. I am quite surprised that the contract creation passes, I would have to check with the testnet
core/state/access_events.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure I understand the question, it is expected1+expected2
core/state/access_events.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense: we are returning the expected gas and we are saving a confusing check. I can fix it if you don't like it.
core/state/access_events.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
come to think of if, it's really confusing. I'll fix it if we don't go your way.
core/vm/operations_verkle.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah that was introduced when I moved from op to gas. Makes sense that it works, but we need to update the spec tests to make sure that this could have been caught.
|
|
||
| // Check warm read cost | ||
| gas = ae.BasicDataGas(testAddr, false, math.MaxUint64, false) | ||
| gas, _ = ae.BasicDataGas(testAddr, false, math.MaxUint64, false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if a new parameter is added that could indicate an issue, it should be checked in tests.
core/vm/eips.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not that weird, any error condition would just "fall through". It's used a lot in languages that support passing errors.
That's not an issue: we are using this to build the witness, and the gas costs correspond to that use case. Once the witness has been paid for, the next access in the same transaction will be cheaper, we want it to be this way. If the transaction reverts, then the next transaction will use a new access list, and so the witness will be cleared for future gas costs.
That is indeed a very nice simplification, I am not very happy with the fact that "we don't care about the exact amount of gas being consumed". I agree that it doesn't seem to have any impact and so we don't really care since everything will be consumed. We just need to make sure that it won't create any corner case. I just want to test it very thoroughly before I give it a go. |
|
as it were, I discovered that the execution spec tests were just skipping all the tests while saying 'passed'. Among other issues, it means that moving op -> gas broke but wasn't reported as broken and I have to run through a few hoops to see if this PR really works (I mean, if it's more broken than the base branch is) |
Let's say we want to access account basic data and account code hash in a single operation, and gas is only sufficient for the step 1. At that point the account basic data hasn't been resolved yet and shouldn't be included in the witness.
This can also occur in deeper call frames. For example, in opCall, several state entries may be cached in the access event before the operation reverts due to an out-of-gas error. In such cases, should we clear the cached entries? Or should we leave them as-is—knowing they can affect gas cost computation in subsequent operations within the same transaction, but from a different call frame? |
it should be, because otherwise the stateless client will not be able to read back the basic data when trying to verify the block. Now, I know you want to do everything atomic, but that's not the spec and the spec can change but it takes time and we're in agreement that the first step is to sync the verkle testnet. This is the behavior that is needed to sync the verkle testnet.
no
yes, we should do that. The gas costs are about paying for increasing the witness size. If an addition to the witness was paid for by a transaction, it's fair that any subsequent accesses for that transaction will not pay the full cost. |
abb0094 to
69068cd
Compare
| func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, availableGas uint64) (uint64, uint64) { | ||
| // a contract creation. If the available gas is insufficient to cover the total | ||
| // cost, a false flag is returned. | ||
| func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, availableGas uint64) (uint64, bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This single change breaks 243 execution spec tests out of 532
69068cd to
fcc690e
Compare
fcc690e to
69068cd
Compare
In the current implementation, we attempt to compute the required gas cost for
each operation, which is a good approach. By checking against the available gas,
we can terminate the computation early if there’s enough gas to cover the cost.
However, this introduces several issues:
(a) During gas cost computation, the access list may be mutated. Some operations
may require access to multiple state entries. It’s possible that part of the access
succeeds and gets cached in the access list, while the remaining accesses fail
due to an out-of-gas (OOG) error.
The semantics around whether the successfully cached entries should be reverted
in such cases is unclear. Under the current implementation, these entries are not
reverted, which could lead to inconsistent behavior in subsequent gas cost computations.
(b) Technically, it’s unnecessary to compute the exact gas cost if the available
gas is already insufficient. A simpler alternative is to have the gas cost function
return an error when there’s not enough gas. This error can then be propagated up
the call stack, ultimately terminating EVM execution and consuming all remaining gas.
For reference, see the implementation in gasCallEIP2929:
https://github.com/ethereum/go-ethereum/blob/master/core/vm/operations_acl.go#L196
In the current implementation, the required gas cost is also ambiguous. e.g. if
two state entries are accessed in the operation, the real gas cost should be
cost_1 + cost_2, unfortunately in some places, only cost_1 is returned. I would
prefer to simply return an error.