diff options
author | Alex Beregszaszi <alex@rtfs.hu> | 2018-03-07 00:07:03 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-07 00:07:03 +0800 |
commit | 14b12ae7452388516d0c4eb833b0c83fe5156b44 (patch) | |
tree | ff814ac430ddc4fc56ba8753777e5bf32f7663b7 | |
parent | 83dacbf669f58ad40e3035837d4ea8a846c46949 (diff) | |
parent | 3057aeece495276265d9632b97e3faffcb57fe71 (diff) | |
download | dexon-solidity-14b12ae7452388516d0c4eb833b0c83fe5156b44.tar dexon-solidity-14b12ae7452388516d0c4eb833b0c83fe5156b44.tar.gz dexon-solidity-14b12ae7452388516d0c4eb833b0c83fe5156b44.tar.bz2 dexon-solidity-14b12ae7452388516d0c4eb833b0c83fe5156b44.tar.lz dexon-solidity-14b12ae7452388516d0c4eb833b0c83fe5156b44.tar.xz dexon-solidity-14b12ae7452388516d0c4eb833b0c83fe5156b44.tar.zst dexon-solidity-14b12ae7452388516d0c4eb833b0c83fe5156b44.zip |
Merge pull request #2966 from ethereum/useStaticCall
Use STATICCALL for pure function calls.
-rw-r--r-- | Changelog.md | 3 | ||||
-rw-r--r-- | docs/contracts.rst | 19 | ||||
-rw-r--r-- | libsolidity/ast/ExperimentalFeatures.h | 4 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 9 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 60 |
5 files changed, 88 insertions, 7 deletions
diff --git a/Changelog.md b/Changelog.md index dd8d6e49..c643c943 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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. diff --git a/docs/contracts.rst b/docs/contracts.rst index 67d4a249..121c4de0 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -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. diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index a17778b4..30ea7ec5 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -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 }, diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index d27af7db..7162cb0d 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -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; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index ebb2f3ff..33cd1419 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -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() } |