diff options
author | Daniel Kirchner <daniel@ekpyron.org> | 2018-08-15 20:40:20 +0800 |
---|---|---|
committer | chriseth <chris@ethereum.org> | 2018-08-15 22:06:48 +0800 |
commit | 7ca0aaaf6f62aafd0fe36ae6b7dc777361ae40e3 (patch) | |
tree | 95019017cb653af8109ca7ac0b5f4216103eae3f | |
parent | 2ed793c4d345de909332651145265a21a04e92d1 (diff) | |
download | dexon-solidity-7ca0aaaf6f62aafd0fe36ae6b7dc777361ae40e3.tar.gz dexon-solidity-7ca0aaaf6f62aafd0fe36ae6b7dc777361ae40e3.tar.zst dexon-solidity-7ca0aaaf6f62aafd0fe36ae6b7dc777361ae40e3.zip |
Add ``staticcall`` to ``address``.
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | docs/units-and-global-variables.rst | 4 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 12 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 6 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 6 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 11 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 69 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 16 |
8 files changed, 116 insertions, 9 deletions
diff --git a/Changelog.md b/Changelog.md index 177a071b..bef429b1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -71,6 +71,7 @@ Breaking Changes: * View Pure Checker: Strictly enfore state mutability. This was already the case in the experimental 0.5.0 mode. Language Features: + * Genreal: Add ``staticcall`` to ``address``. * General: Allow appending ``calldata`` keyword to types, to explicitly specify data location for arguments of external functions. * General: Support ``pop()`` for storage arrays. * General: Scoping rules now follow the C99-style. diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index ceabee4e..a8086ddb 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -153,7 +153,7 @@ Mathematical and Cryptographic Functions It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net. -.. index:: balance, send, transfer, call, callcode, delegatecall +.. index:: balance, send, transfer, call, callcode, delegatecall, staticcall .. _address_related: Address Related @@ -171,6 +171,8 @@ Address Related issue low-level ``CALLCODE`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable ``<address>.delegatecall(bytes memory) returns (bool)``: issue low-level ``DELEGATECALL`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable +``<address>.staticcall(bytes memory) returns (bool)``: + issue low-level ``STATICCALL`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable For more information, see the section on :ref:`address`. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 43e894e5..8d6da28b 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1359,7 +1359,8 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement) if ( kind == FunctionType::Kind::BareCall || kind == FunctionType::Kind::BareCallCode || - kind == FunctionType::Kind::BareDelegateCall + kind == FunctionType::Kind::BareDelegateCall || + kind == FunctionType::Kind::BareStaticCall ) m_errorReporter.warning(_statement.location(), "Return value of low-level calls not used."); else if (kind == FunctionType::Kind::Send) @@ -1754,6 +1755,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) return false; } + if (functionType->kind() == FunctionType::Kind::BareStaticCall && !m_evmVersion.hasStaticCall()) + m_errorReporter.typeError(_functionCall.location(), "\"staticcall\" is not supported by the VM version."); + auto returnTypes = allowDynamicTypes ? functionType->returnParameterTypes() : @@ -1834,7 +1838,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) else if ( functionType->kind() == FunctionType::Kind::BareCall || functionType->kind() == FunctionType::Kind::BareCallCode || - functionType->kind() == FunctionType::Kind::BareDelegateCall + functionType->kind() == FunctionType::Kind::BareDelegateCall || + functionType->kind() == FunctionType::Kind::BareStaticCall ) { if (arguments.empty()) @@ -1882,7 +1887,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if ( functionType->kind() == FunctionType::Kind::BareCall || functionType->kind() == FunctionType::Kind::BareCallCode || - functionType->kind() == FunctionType::Kind::BareDelegateCall + functionType->kind() == FunctionType::Kind::BareDelegateCall || + functionType->kind() == FunctionType::Kind::BareStaticCall ) msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it."; else if ( diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c9dca126..ccf31543 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -621,6 +621,7 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons {"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)}, {"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareDelegateCall, false)}, {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)}, + {"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)}, {"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)} }; else @@ -2522,6 +2523,7 @@ string FunctionType::richIdentifier() const case Kind::BareCall: id += "barecall"; break; case Kind::BareCallCode: id += "barecallcode"; break; case Kind::BareDelegateCall: id += "baredelegatecall"; break; + case Kind::BareStaticCall: id += "barestaticcall"; break; case Kind::Creation: id += "creation"; break; case Kind::Send: id += "send"; break; case Kind::Transfer: id += "transfer"; break; @@ -2705,6 +2707,7 @@ unsigned FunctionType::sizeOnStack() const case Kind::BareCall: case Kind::BareCallCode: case Kind::BareDelegateCall: + case Kind::BareStaticCall: case Kind::Internal: case Kind::ArrayPush: case Kind::ArrayPop: @@ -2772,6 +2775,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con case Kind::BareCall: case Kind::BareCallCode: case Kind::BareDelegateCall: + case Kind::BareStaticCall: { MemberList::MemberMap members; if (m_kind == Kind::External) @@ -2911,6 +2915,7 @@ bool FunctionType::isBareCall() const case Kind::BareCall: case Kind::BareCallCode: case Kind::BareDelegateCall: + case Kind::BareStaticCall: case Kind::ECRecover: case Kind::SHA256: case Kind::RIPEMD160: @@ -3054,6 +3059,7 @@ bool FunctionType::padArguments() const case Kind::BareCall: case Kind::BareCallCode: case Kind::BareDelegateCall: + case Kind::BareStaticCall: case Kind::SHA256: case Kind::RIPEMD160: case Kind::KECCAK256: diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index d8e73ab9..0b1b5d6d 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -904,6 +904,7 @@ public: BareCall, ///< CALL without function hash BareCallCode, ///< CALLCODE without function hash BareDelegateCall, ///< DELEGATECALL without function hash + BareStaticCall, ///< STATICCALL without function hash Creation, ///< external call using CREATE Send, ///< CALL, but without data and gas Transfer, ///< CALL, but without data and throws on error @@ -935,7 +936,7 @@ public: ABIEncodeWithSelector, ABIEncodeWithSignature, ABIDecode, - GasLeft ///< gasleft() + GasLeft, ///< gasleft() }; virtual Category category() const override { return Category::Function; } @@ -1051,7 +1052,7 @@ public: /// @returns true iff the function type is equal to the given type, ignoring state mutability differences. bool equalExcludingStateMutability(FunctionType const& _other) const; - /// @returns true if the ABI is used for this call (only meaningful for external calls) + /// @returns true if the ABI is NOT used for this call (only meaningful for external calls) bool isBareCall() const; Kind const& kind() const { return m_kind; } StateMutability stateMutability() const { return m_stateMutability; } @@ -1090,6 +1091,7 @@ public: case FunctionType::Kind::BareCall: case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareDelegateCall: + case FunctionType::Kind::BareStaticCall: return true; default: return false; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 7a4548f5..9baad7d1 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -571,6 +571,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case FunctionType::Kind::BareCall: case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareDelegateCall: + case FunctionType::Kind::BareStaticCall: _functionCall.expression().accept(*this); appendExternalFunctionCall(function, arguments); break; @@ -1172,6 +1173,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) case FunctionType::Kind::BareCall: case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareDelegateCall: + case FunctionType::Kind::BareStaticCall: case FunctionType::Kind::Transfer: _memberAccess.expression().accept(*this); m_context << funType->externalIdentifier(); @@ -1273,7 +1275,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) ); m_context << Instruction::BALANCE; } - else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member)) + else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall", "staticcall"}).count(member)) utils().convertType( *_memberAccess.expression().annotation().type, IntegerType(160, IntegerType::Modifier::Address), @@ -1825,10 +1827,13 @@ void ExpressionCompiler::appendExternalFunctionCall( utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack()); auto funKind = _functionType.kind(); - bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall; + + solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), ""); + + bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall; 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.evmVersion().hasStaticCall(); + bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (_functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall()); bool haveReturndatacopy = m_context.evmVersion().supportsReturndata(); unsigned retSize = 0; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 8a334e5e..b3618ad9 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -3592,6 +3592,19 @@ BOOST_AUTO_TEST_CASE(default_fallback_throws) )YY"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); + + if (dev::test::Options::get().evmVersion().hasStaticCall()) + { + char const* sourceCode = R"YY( + contract A { + function f() public returns (bool) { + return address(this).staticcall(""); + } + } + )YY"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); + } } BOOST_AUTO_TEST_CASE(short_data_calls_fallback) @@ -4215,6 +4228,49 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall) BOOST_CHECK_EQUAL(balanceAt(c_senderAddress), 50 + 11); } +BOOST_AUTO_TEST_CASE(generic_staticcall) +{ + if (dev::test::Options::get().evmVersion().hasStaticCall()) + { + char const* sourceCode = R"**( + contract A { + uint public x; + constructor() public { x = 42; } + function pureFunction(uint256 p) public pure returns (uint256) { return p; } + function viewFunction(uint256 p) public view returns (uint256) { return p + x; } + function nonpayableFunction(uint256 p) public returns (uint256) { x = p; return x; } + function assertFunction(uint256 p) public view returns (uint256) { assert(x == p); return x; } + } + contract C { + function f(address a) public view returns (bool) + { + return a.staticcall(abi.encodeWithSignature("pureFunction(uint256)", 23)); + } + function g(address a) public view returns (bool) + { + return a.staticcall(abi.encodeWithSignature("viewFunction(uint256)", 23)); + } + function h(address a) public view returns (bool) + { + return a.staticcall(abi.encodeWithSignature("nonpayableFunction(uint256)", 23)); + } + function i(address a, uint256 v) public view returns (bool) + { + return a.staticcall(abi.encodeWithSignature("assertFunction(uint256)", v)); + } + } + )**"; + compileAndRun(sourceCode, 0, "A"); + u160 const c_addressA = m_contractAddress; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f(address)", c_addressA), encodeArgs(true)); + ABI_CHECK(callContractFunction("g(address)", c_addressA), encodeArgs(true)); + ABI_CHECK(callContractFunction("h(address)", c_addressA), encodeArgs(false)); + ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 42), encodeArgs(true)); + ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 23), encodeArgs(false)); + } +} + BOOST_AUTO_TEST_CASE(library_call_in_homestead) { char const* sourceCode = R"( @@ -12216,6 +12272,19 @@ BOOST_AUTO_TEST_CASE(bare_call_invalid_address) compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); ABI_CHECK(callContractFunction("h()"), encodeArgs(u256(1))); + + if (dev::test::Options::get().evmVersion().hasStaticCall()) + { + char const* sourceCode = R"YY( + contract C { + function f() external returns (bool) { + return address(0x4242).staticcall(""); + } + } + )YY"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); + } } BOOST_AUTO_TEST_CASE(delegatecall_return_value) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 55e81867..fbb2f09c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -433,6 +433,22 @@ BOOST_AUTO_TEST_CASE(getter_is_memory_type) } } +BOOST_AUTO_TEST_CASE(address_staticcall) +{ + char const* sourceCode = R"( + contract C { + function f() public view returns(bool) { + return address(0x4242).staticcall(""); + } + } + )"; + + if (dev::test::Options::get().evmVersion().hasStaticCall()) + CHECK_SUCCESS_NO_WARNINGS(sourceCode); + else + CHECK_ERROR(sourceCode, TypeError, "\"staticcall\" is not supported by the VM version."); +} + BOOST_AUTO_TEST_SUITE_END() } |