Merge pull request #2966 from ethereum/useStaticCall

Use STATICCALL for pure function calls.
This commit is contained in:
Alex Beregszaszi 2018-03-06 17:07:03 +01:00 committed by GitHub
commit 14b12ae745
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 88 additions and 7 deletions

View File

@ -2,8 +2,9 @@
Features:
* C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature.
* Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature.
* Code Generator: Assert that ``k != 0`` for ``mulmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature.
* Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead).
* Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimenal 0.5.0 feature.
* Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default).
* Standard JSON: Reject badly formatted invalid JSON inputs.
* Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature.

View File

@ -472,6 +472,13 @@ The following statements are considered modifying the state:
.. note::
Getter methods are marked ``view``.
.. note::
If invalid explicit type conversions are used, state modifications are possible
even though a ``view`` function was called.
You can switch the compiler to use ``STATICCALL`` when calling such functions and thus
prevent modifications to the state on the level of the EVM by adding
``pragma experimental "v0.5.0";``
.. warning::
The compiler does not enforce yet that a ``view`` method is not modifying state. It raises a warning though.
@ -502,6 +509,18 @@ In addition to the list of state modifying statements explained above, the follo
}
}
.. note::
If invalid explicit type conversions are used, state modifications are possible
even though a ``pure`` function was called.
You can switch the compiler to use ``STATICCALL`` when calling such functions and thus
prevent modifications to the state on the level of the EVM by adding
``pragma experimental "v0.5.0";``
.. warning::
It is not possible to prevent functions from reading the state at the level
of the EVM, it is only possible to prevent them from writing to the state
(i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not).
.. warning::
Before version 0.4.17 the compiler didn't enforce that ``pure`` is not reading the state.

View File

@ -29,8 +29,8 @@ namespace solidity
enum class ExperimentalFeature
{
SMTChecker,
ABIEncoderV2, // new ABI encoder that makes use of JULIA
SMTChecker,
V050, // v0.5.0 breaking changes
Test,
TestOnlyAnalysis
@ -45,8 +45,8 @@ static const std::map<ExperimentalFeature, bool> ExperimentalFeatureOnlyAnalysis
static const std::map<std::string, ExperimentalFeature> ExperimentalFeatureNames =
{
{ "SMTChecker", ExperimentalFeature::SMTChecker },
{ "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 },
{ "SMTChecker", ExperimentalFeature::SMTChecker },
{ "v0.5.0", ExperimentalFeature::V050 },
{ "__test", ExperimentalFeature::Test },
{ "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis },

View File

@ -1610,6 +1610,10 @@ void ExpressionCompiler::appendExternalFunctionCall(
bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall;
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
bool useStaticCall =
_functionType.stateMutability() <= StateMutability::View &&
m_context.experimentalFeatureActive(ExperimentalFeature::V050) &&
m_context.evmVersion().hasStaticCall();
unsigned retSize = 0;
if (returnSuccessCondition)
@ -1738,6 +1742,8 @@ void ExpressionCompiler::appendExternalFunctionCall(
// [value,] addr, gas (stack top)
if (isDelegateCall)
solAssert(!_functionType.valueSet(), "Value set for delegatecall");
else if (useStaticCall)
solAssert(!_functionType.valueSet(), "Value set for staticcall");
else if (_functionType.valueSet())
m_context << dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
else
@ -1769,10 +1775,13 @@ void ExpressionCompiler::appendExternalFunctionCall(
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB;
}
// Order is important here, STATICCALL might overlap with DELEGATECALL.
if (isDelegateCall)
m_context << Instruction::DELEGATECALL;
else if (isCallCode)
m_context << Instruction::CALLCODE;
else if (useStaticCall)
m_context << Instruction::STATICCALL;
else
m_context << Instruction::CALL;

View File

@ -21,13 +21,20 @@
* Unit tests for the solidity expression compiler, testing the behaviour of the code.
*/
#include <test/libsolidity/SolidityExecutionFramework.h>
#include <test/TestHelper.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/interface/EVMVersion.h>
#include <libevmasm/Assembly.h>
#include <boost/test/unit_test.hpp>
#include <functional>
#include <string>
#include <tuple>
#include <boost/test/unit_test.hpp>
#include <libevmasm/Assembly.h>
#include <libsolidity/interface/Exceptions.h>
#include <test/libsolidity/SolidityExecutionFramework.h>
using namespace std;
using namespace std::placeholders;
@ -10778,6 +10785,51 @@ BOOST_AUTO_TEST_CASE(snark)
BOOST_CHECK(callContractFunction("verifyTx()") == encodeArgs(true));
}
BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure)
{
char const* sourceCode = R"(
pragma experimental "v0.5.0";
contract C {
uint x;
function f() public returns (uint) {
x = 3;
return 1;
}
}
interface CView {
function f() view external returns (uint);
}
interface CPure {
function f() pure external returns (uint);
}
contract D {
function f() public returns (uint) {
return (new C()).f();
}
function fview() public returns (uint) {
return (CView(new C())).f();
}
function fpure() public returns (uint) {
return (CPure(new C())).f();
}
}
)";
compileAndRun(sourceCode, 0, "D");
// This should work (called via CALL)
ABI_CHECK(callContractFunction("f()"), encodeArgs(1));
if (dev::test::Options::get().evmVersion().hasStaticCall())
{
// These should throw (called via STATICCALL)
ABI_CHECK(callContractFunction("fview()"), encodeArgs());
ABI_CHECK(callContractFunction("fpure()"), encodeArgs());
}
else
{
ABI_CHECK(callContractFunction("fview()"), encodeArgs(1));
ABI_CHECK(callContractFunction("fpure()"), encodeArgs(1));
}
}
BOOST_AUTO_TEST_SUITE_END()
}