Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.Sign up
GitHub is where the world builds software
Millions of developers and companies build, ship, and maintain their software on GitHub — the largest and most advanced development platform in the world.
failed calls due to address.call() gas computation #2999
We came up with a minimal example demonstrating the issue and deployed it on the mainnet. The example consists of two contracts:
The issue occurs in transactions that invoke
This behaviour is highly counterintuitive: If the transaction succeeds with x gas, it should also succeed with >x gas.
The cause of this behaviour is the code that
, i.e. as
This code in ExpressionCompiler.cpp seems to be responsible for generating the above code.
In the above example, the call in the transaction with 48574 gas succeeds because the amount of available gas before the call is 25709; the subtraction hence underflows and provides an upper limit of
When the amount of gas for the transaction lies between 48575 and 49388, the internal call runs out of gas because the subtraction no longer underflows and the maximum amount of gas hence lies between 0 and 813, while the call requires at least 814 gas to succeed.
Once the amount of gas for the transaction is at least 49389, the internal call succeeds again because now a sufficient amount of gas is supplied for the call even after subtracting 25710.
Most importantly, the behavior of
It is not clear to us what the benefit of retaining 25710 gas in the calling context is. The comment in ExpressionCompiler.cpp
leads us to believe that
Finally, if retaining the gas for the calling context is actually desirable, then the underflow issue should be fixed.
This issue was jointly discovered by
Thanks for the detailed investigation!
That particular piece of code originates way before the Tangerine Whistle hardfork which introduced the new rules for gas calculation in messages. Before, one had to make sure that enough gas is left to cover the possible account creation and value transfer fees, after subtracting the message gas. After the fork, due to truncation rules, this is not needed anymore.
See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md where
The 25710 comprises of 25000 for possible account creation, 700 for the call and 10 for the cost of PUSH, GAS, SUB.
If we consider the last Solidity version to only output Tangerine Whistle compatible code, we could reduce that code path to a single instruction of
Actually we've realised in a call with @chriseth that shouldn't we just use a large number, such as -1 (all bits set) instead of
It can also be optimised as
@vbuterin any thoughts?
Great bug research,
Is there any workaround for the time being? Maybe an older compiler version that works?
one small problem:
fix get decimal safe. bug was detected while testing on mainnet here https://etherscan.io/tx/0x1bbf3bfaefb1b11ad71bed23ef977fbc84d5000e286ee8969f9cc6996057bca4 Root cause of the bug is compiler issue described here ethereum/solidity#2999