mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #2966 from ethereum/useStaticCall
Use STATICCALL for pure function calls.
This commit is contained in:
commit
14b12ae745
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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 },
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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()
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user