-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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
EIP-1930: call variant with strict gas to ensure a specific amount is sent #1930
Comments
How do we get that (and in general any EIP) merged in? |
Could you merge the PR @Arachnid @nicksavers @gcolvin @Souptacular? |
As I've mentioned in #881 (comment), there is no need to implement this at the EVM level. This can be done at language level - either in the compiler, introducing new keywords for "call with extra checks"; or in the program, using macros or function modifiers or decorators or whatever-is-available in the language of choice. Otherwise, we'll run out of unassigned opcodes by the end of the year. |
As mentioned in the proposal as well as in my reply : These solution are not perfect (either forbid certain behavior or are susceptible to gas pricing change). They also waste gas for computing what is already computed by the evm. Ideally the version of the opcode proposed here should replace the old one. Then if you worry about opcode space, we can version the old one out when times come. |
I agree with @veox - this is a niche requirement that can be satisfied with current opcodes. I think it's a bad idea to use up two new opcodes, and add client complexity, for it. |
Even if it was, I don't think this is a valid argument. But as mentioned in the proposal this is already an issue for meta-transactions and introspection (like #165), two use-cases far from being niche. Furthermore, it is apparent that everybody assumes that the behaviour of the current CALL opcodes is to enforce the gas somehow (maybe because forgetting about EIP-150, they wrongly think there would be not enough gas to complete the transaction if the call throw because it did not receive the amount of gas specified)
As mentioned in the proposal, this is not trivial and it depends on gas pricing. This is a concern shared by @tschubotz and the rest of GnosisSafe team that decided because of this to not fix the issue on their smart contract but to do it at the interface level, leaving unaware client on their own and at risk of making their users loose funds. But this also shared by @chriseth in a different context when proposing #90 (which resulted in #150) where he states:
Ultimately we should replace the old CALLs opcode with the ones proposed here. If space is lacking we can version smart contracts and remove old opcodes. To allow for limitless gas call (not available in the current draft) we could give the value 0 a special meaning : "give all gas available". This would allow to replicate the current use cases, making old opcodes obsolete. The fear of lack of space is not warranted. It is very likely that more and more opcode will become obsolete as we discover better ways to do things. Let's accept it. When we run out of opcode space, we can start versioning smart contract and remove obsolete opcodes. For now though, as we still have some space left, we should not block ourselves from fixing issues affecting valid use cases. |
It's not terribly difficult. Increasing opcode gas costs is never likely to happen, precisely because people already have this kind of dependency on it. See for instance this recent Twitter thread between Vitalik and I: https://twitter.com/VitalikButerin/status/1128076287678013442 |
This is a possible take on it. But that does not remove the risk completely. That depends on the community. It is worth nothing that the argument also include:
If so, we could argue that the opcode space is not that much a concern. We have still more than 100 opcodes left But regardless, since replacing the old opcode with these new one would be objectively better, I still vote for the proposal. Let's improve the evm when we can. |
This would require - per the EIP - 3 new opcodes. For some reason As mentioned by @Arachnid this can be implemented in EVM directly with minimal extra gas costs: (solidity code)
The fact that this requires 3 (4?) new opcodes plus the fact that this can already be implemented in EVM are both arguments for me to not implement this as this can be (easily) implemented using EVM already.
@Arachnid Huh? We have had several times in the past hard forks that opcode gas costs are actually increased, because of possible DoS attacks or simply that execution costs of certain operations rise (e.g. Istanbul proposed EIP 1884 or in the past for example EIP 150. I know that gas costs increases in general are not wanted because there are hardcoded gas limits in some contracts. However, if this leads to DoS attacks (see EIP 150) then to protect the network these costs should be raised anyways. In general, contract developers should be made aware that they cannot rely on gas costs of operations being constant. |
STATIC_CALL is mentioned
This naive implementation is not sufficient as explained in the proposal. Please read the proposal and the materials linked (EIP-150 in particular). As @Arachnid say, it is technically possible to do it via assembly and we can imagine solidity adding a parameter or a variant to the various "calls" for that but this would be dependent on gas opcode pricing. This is also a waste of gas for something that evm already compute.
I agree and the current *CALL opcodes do not allow this if the contract want to ensure the call will get a specific amount of gas. |
@wighawag Woops - I meant |
|
By the way, we have this proposal (https://eips.ethereum.org/EIPS/eip-1702) currently considered to add versioning to smart contract so we could remove obsolete opcodes when need arise |
I added a variant spec that do not require new opcodes but relies on existing contract not using some specific range of values |
For the sake of concision, option a) could be compressed: add new variants of the CALL, DELEGATE_CALL, STATIC_CALL opcodes where the gas specified is enforced so that if the gas left at the point of call is not enough to give the specified gas to the destination, the current call revert The following is not clear, I don't understand it: "Note that solidity for example do not use value like 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF as it is more expensive than passing the gasLeft." |
Can anyone guide me with numbers of gas consumed for gasleft() and STATICCALL |
@DGKSK8LIFE Thanks! what about cost of using gasleft() itself and hardhat's console.log? anyone? |
the first link I sent references the gas cost of a static call of a pure function; might be related to gasleft() @livingrock7 |
There has been no activity on this issue for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review. |
I would like to move this EIP forward, since there is 2 potential spec, I guess I might as well split into 2 proposals. but would like feedback on that: Do we add new opcodes or do we handle the new semantic with special gas value (first bit being considered as a flag) |
Generally speaking, it is always preferable to have multiple smaller specifications rather than fewer larger specifications. If it is possible to split this up into multiple smaller specifications then you are strongly encouraged to do so. Note about this EIP idea in general: Contracts should never have any hard coded values related to gas in them because gas costs can and do change as hardware and technology changes. While 300,000 gas may work today, it may not work tomorrow and vice versa. |
@MicahZoltu ok, thanks for the input, I guess what I would like to see is whether option A or B is the way to go and focus on one. but if it undecided I might as well create 2 spec. re gas, this is not important for the rationale mentioned here, the gas can be provided by the user (meta-transaction for example) Actually the whole point of this EIP is to avoid the use of hard-coded gas value |
There has been no activity on this issue for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review. |
Closing this for housekeeping purposes. Feel free to continue using this issue for discussion about EIP-1930. Note: If 1930 is pulled out of stagnant, the discussions-to link should be moved to a thread on Ethereum Magicians. |
EIP : https://eips.ethereum.org/EIPS/eip-1930
eip: 1930
title: CALLs with strict gas semantic. Revert if not enough gas available.
author: Ronan Sandford (@wighawag)
type: Standards Track
discussions-to: #1930
category: Core
status: Draft
created: 2019-04-10
Simple Summary
Add the ability for smart contract to execute calls with a specific amount of gas. If this is not possible the execution should revert.
Abstract
The current CALL, DELEGATE_CALL, STATIC_CALL opcode do not enforce the gas being sent, they simply consider the gas value as a maximum. This pose serious problem for applications that require the call to be executed with a precise amount of gas.
This is for example the case for meta-transaction where the contract needs to ensure the call is executed exactly as the signing user intended.
But this is also the case for common use cases, like checking "on-chain" if a smart contract support a specific interface (via EIP-165 for example).
The solution presented here is to add new call semantic that enforce the amount of gas specified : the call either proceed with the exact amount of gas or do not get executed and the current call revert.
Specification
There are 2 possibilities
a) one is to add opcode variant that have a stricter gas semantic
b) The other is to consider a specific gas value range (one that have never been used before) to have strict gas semantic, while leaving other values as before
Here are the details description
option a)
Rational for a)
This solution has the merit to avoid any possibility of old contract be affected by the change. On the other hand it introduce 3 new opcodes. With EIP-1702, we could render the old opcode obsolete though.
option b)
For all opcode that allow to pass gas to another contract, do the following:
Rational for b)
This solution relies on the fact that no contract would have given any value bigger or equal to 0x8000000000000000000000000000000000000000000000000000000000000000
Note that solidity for example do not use value like 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF as it is more expensive than passing the gasLeft.
Its main benefit though is that it does not require extra opcodes.
strict gas semantic
To be precise, regarding the strict gas semantic, based on EIP-150, the current call must revert unless G >= I x 64/63 where G is gas left at the point of call (after deducing the cost of the call itself) and I is the gas specified.
So instead of
see https://github.com/ethereum/go-ethereum/blob/7504dbd6eb3f62371f86b06b03ffd665690951f2/core/vm/gas.go#L41-L48
we would have
Rationale
Currently the gas specified as part of these opcodes is simply a maximum value. And due to the behavior of EIP-150 it is possible for an external call to be given less gas than intended (less than the gas specified as part of the CALL) while the rest of the current call is given enough to continue and succeed. Indeed since with EIP-150, the external call is given at max
G - Math.floor(G/64)
where G is the gasleft() at the point of the CALL, the rest of the current call is givenMath.floor(G/64)
which can be plenty enough for the transaction to succeed. For example, when G = 6,400,000 the rest of the transaction will be given 100,000 gas plenty enough in many case to succeed.This is an issue for contracts that require external call to only fails if they would fails with enough gas. This requirement is present in smart contract wallet and meta transaction in general, where the one executing the transaction is not the signer of the execution data. Because in such case, the contract needs to ensure the call is executed exactly as the signing user intended.
But this is also true for simple use case, like checking if a contract implement an interface via EIP-165. Indeed as specified by such EIP, the
supporstInterface
method is bounded to use 30,000 gas so that it is theorically possible to ensure that the throw is not a result of a lack of gas. Unfortunately due to how the different CALL opcodes behave contracts can't simply rely on the gas value specified. They have to ensure by other means that there is enough gas for the call.Indeed, if the caller do not ensure that 30,000 gas or more is provided to the callee, the callee might throw because of a lack of gas (and not because it does not support the interface), and the parent call will be given up to 476 gas to continue. This would result in the caller interepreting wrongly that the callee is not implementing the interface in question.
While such requirement can be enforced by checking the gas left according to EIP-150 and the precise gas required before the call (see solution presented in that bug report or after the call (see the native meta transaction implementation here, it would be much better if the EVM allowed us to strictly specify how much gas is to be given to the CALL so contract implementations do not need to follow EIP-150 behavior and the current gas pricing so closely.
This would also allow the behaviour of EIP-150 to be changed without having to affect contract that require this strict gas behaviour.
As mentioned, such strict gas behaviour is important for smart contract wallet and meta transaction in general.
The issue is actually already a problem in the wild as can be seen in the case of Gnosis safe which did not consider the behavior of EIP-150 and thus fails to check the gas properly, requiring the safe owners to add otherwise unnecessary extra gas to their signed message to avoid the possibility of losing funds. See safe-global/safe-smart-account#100
As for EIP-165, the issue already exists in the example implementation presented in the EIP. Please see the details of the issue here
The same issue exists also on OpenZeppelin implementation, a library used by many. It does not for perform any check on gas before calling
supportsInterface
with 30,000 gas (see here and is thus vulnerable to the issue mentioned.While such issue can be prevented today by checking the gas with EIP-150 in mind, a solution at the opcode level is more elegant.
Indeed, the two possible ways to currently enforce that the correct amount of gas is sent are as follow :
where E is the gas required for the operation between the call to
gasleft()
and the actual call PLUS the gas cost of the call itself.While it is possible to simply over estimate
E
to prevent call to be executed if not enough gas is provided to the current call it would be better to have the EVM do the precise work itself. As gas pricing continue to evolve, this is important to have a mechanism to ensure a specific amount of gas is passed to the call so such mechanism can be used without having to relies on a specific gas pricing.This solution does not require to compute a
E
value and thus do not relies on a specific gas pricing (except for the behaviour of EIP-150) since if the call is given not enough gas and fails for that reason, the condition above will always fail, ensuring the current call will revert.But this check still pass if the gas given was less AND the external call reverted or succeeded EARLY (so that the gas left after the call > txGas / 63).
This can be an issue if the code executed as part of the CALL is reverting as a result of a check against the gas provided. Like a meta transaction in a meta transaction.
Similarly to the the previous solution, an EVM mechanism would be much better.
Backwards Compatibility
for specification a) : Backwards compatible as it introduce new opcodes.
for specification b) : Backwards compatible as it use value range outside of what is used by existing contract (to be verified)
The text was updated successfully, but these errors were encountered: