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:
|
Features:
|
||||||
* C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature.
|
* 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: 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).
|
* Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default).
|
||||||
* Standard JSON: Reject badly formatted invalid JSON inputs.
|
* Standard JSON: Reject badly formatted invalid JSON inputs.
|
||||||
* Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature.
|
* 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::
|
.. note::
|
||||||
Getter methods are marked ``view``.
|
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::
|
.. warning::
|
||||||
The compiler does not enforce yet that a ``view`` method is not modifying state. It raises a warning though.
|
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::
|
.. warning::
|
||||||
Before version 0.4.17 the compiler didn't enforce that ``pure`` is not reading the state.
|
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
|
enum class ExperimentalFeature
|
||||||
{
|
{
|
||||||
SMTChecker,
|
|
||||||
ABIEncoderV2, // new ABI encoder that makes use of JULIA
|
ABIEncoderV2, // new ABI encoder that makes use of JULIA
|
||||||
|
SMTChecker,
|
||||||
V050, // v0.5.0 breaking changes
|
V050, // v0.5.0 breaking changes
|
||||||
Test,
|
Test,
|
||||||
TestOnlyAnalysis
|
TestOnlyAnalysis
|
||||||
@ -45,8 +45,8 @@ static const std::map<ExperimentalFeature, bool> ExperimentalFeatureOnlyAnalysis
|
|||||||
|
|
||||||
static const std::map<std::string, ExperimentalFeature> ExperimentalFeatureNames =
|
static const std::map<std::string, ExperimentalFeature> ExperimentalFeatureNames =
|
||||||
{
|
{
|
||||||
{ "SMTChecker", ExperimentalFeature::SMTChecker },
|
|
||||||
{ "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 },
|
{ "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 },
|
||||||
|
{ "SMTChecker", ExperimentalFeature::SMTChecker },
|
||||||
{ "v0.5.0", ExperimentalFeature::V050 },
|
{ "v0.5.0", ExperimentalFeature::V050 },
|
||||||
{ "__test", ExperimentalFeature::Test },
|
{ "__test", ExperimentalFeature::Test },
|
||||||
{ "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis },
|
{ "__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 returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall;
|
||||||
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
|
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
|
||||||
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
|
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;
|
unsigned retSize = 0;
|
||||||
if (returnSuccessCondition)
|
if (returnSuccessCondition)
|
||||||
@ -1738,6 +1742,8 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
// [value,] addr, gas (stack top)
|
// [value,] addr, gas (stack top)
|
||||||
if (isDelegateCall)
|
if (isDelegateCall)
|
||||||
solAssert(!_functionType.valueSet(), "Value set for delegatecall");
|
solAssert(!_functionType.valueSet(), "Value set for delegatecall");
|
||||||
|
else if (useStaticCall)
|
||||||
|
solAssert(!_functionType.valueSet(), "Value set for staticcall");
|
||||||
else if (_functionType.valueSet())
|
else if (_functionType.valueSet())
|
||||||
m_context << dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
|
m_context << dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
|
||||||
else
|
else
|
||||||
@ -1769,10 +1775,13 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
|
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
|
||||||
m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB;
|
m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB;
|
||||||
}
|
}
|
||||||
|
// Order is important here, STATICCALL might overlap with DELEGATECALL.
|
||||||
if (isDelegateCall)
|
if (isDelegateCall)
|
||||||
m_context << Instruction::DELEGATECALL;
|
m_context << Instruction::DELEGATECALL;
|
||||||
else if (isCallCode)
|
else if (isCallCode)
|
||||||
m_context << Instruction::CALLCODE;
|
m_context << Instruction::CALLCODE;
|
||||||
|
else if (useStaticCall)
|
||||||
|
m_context << Instruction::STATICCALL;
|
||||||
else
|
else
|
||||||
m_context << Instruction::CALL;
|
m_context << Instruction::CALL;
|
||||||
|
|
||||||
|
@ -21,13 +21,20 @@
|
|||||||
* Unit tests for the solidity expression compiler, testing the behaviour of the code.
|
* 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 <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#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;
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
@ -10778,6 +10785,51 @@ BOOST_AUTO_TEST_CASE(snark)
|
|||||||
BOOST_CHECK(callContractFunction("verifyTx()") == encodeArgs(true));
|
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()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user