From 4264625c69a30c4534e977ce3ca709bb95103dad Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 5 Feb 2017 20:21:14 +0000 Subject: [PATCH 01/10] Implement address.transfer() --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 4 +++- libsolidity/ast/Types.h | 1 + libsolidity/codegen/ExpressionCompiler.cpp | 19 +++++++++++++++---- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 45aaf04a5..37f20c716 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Features: * Add ``assert(condition)``, which throws if condition is false. + * Introduce ``.transfer(value)`` for sending Ether. * Code generator: Support ``revert()`` to abort with rolling back, but not consuming all gas. * Inline assembly: Support ``revert`` (EIP140) as an opcode. * Type system: Support explicit conversion of external function to address. diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 96b3ed170..7fccccbc0 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -464,7 +464,8 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons {"call", make_shared(strings(), strings{"bool"}, FunctionType::Location::Bare, true, false, true)}, {"callcode", make_shared(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true, false, true)}, {"delegatecall", make_shared(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)}, - {"send", make_shared(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)} + {"send", make_shared(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)}, + {"transfer", make_shared(strings{"uint"}, strings(), FunctionType::Location::Transfer)} }; else return MemberList::MemberMap(); @@ -2097,6 +2098,7 @@ string FunctionType::identifier() const case Location::BareDelegateCall: id += "baredelegatecall"; break; case Location::Creation: id += "creation"; break; case Location::Send: id += "send"; break; + case Location::Transfer: id += "transfer"; break; case Location::SHA3: id += "sha3"; break; case Location::Selfdestruct: id += "selfdestruct"; break; case Location::Revert: id += "revert"; break; diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 3546e5220..022b67c48 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -826,6 +826,7 @@ public: BareDelegateCall, ///< DELEGATECALL without function hash Creation, ///< external call using CREATE Send, ///< CALL, but without data and gas + Transfer, ///< CALL, but without data and throws on error SHA3, ///< SHA3 Selfdestruct, ///< SELFDESTRUCT Revert, ///< REVERT diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 2ed19a833..b0031513c 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -616,6 +616,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) arguments.front()->accept(*this); break; case Location::Send: + case Location::Transfer: _functionCall.expression().accept(*this); // Provide the gas stipend manually at first because we may send zero ether. // Will be zeroed if we send more than zero ether. @@ -625,9 +626,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) *arguments.front()->annotation().type, *function.parameterTypes().front(), true ); - // gas <- gas * !value - m_context << Instruction::SWAP1 << Instruction::DUP2; - m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1; + if (function.location() != Location::Transfer) + { + // gas <- gas * !value + m_context << Instruction::SWAP1 << Instruction::DUP2; + m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1; + } appendExternalFunctionCall( FunctionType( TypePointers{}, @@ -644,6 +648,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) ), {} ); + if (function.location() == Location::Transfer) + { + // Check if zero (out of stack or not enough balance). + m_context << Instruction::ISZERO; + m_context.appendConditionalInvalid(); + } break; case Location::Selfdestruct: arguments.front()->accept(*this); @@ -960,6 +970,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) case FunctionType::Location::Bare: case FunctionType::Location::BareCallCode: case FunctionType::Location::BareDelegateCall: + case FunctionType::Location::Transfer: _memberAccess.expression().accept(*this); m_context << funType->externalIdentifier(); break; @@ -1041,7 +1052,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) ); m_context << Instruction::BALANCE; } - else if ((set{"send", "call", "callcode", "delegatecall"}).count(member)) + else if ((set{"send", "transfer", "call", "callcode", "delegatecall"}).count(member)) utils().convertType( *_memberAccess.expression().annotation().type, IntegerType(0, IntegerType::Modifier::Address), From 16e48219d3071038bf7f696f234b50f6370a1171 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 5 Feb 2017 22:43:36 +0000 Subject: [PATCH 02/10] Add test for address.transfer() --- test/libsolidity/SolidityEndToEndTest.cpp | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 68f8fbef3..cb0cc1684 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1681,6 +1681,42 @@ BOOST_AUTO_TEST_CASE(send_ether) BOOST_CHECK_EQUAL(balanceAt(address), amount); } +BOOST_AUTO_TEST_CASE(transfer_ether) +{ + char const* sourceCode = R"( + contract A { + function A() payable {} + function a(address addr, uint amount) returns (uint) { + addr.transfer(amount); + return this.balance; + } + function b(address addr, uint amount) { + addr.transfer(amount); + } + } + + contract B { + } + + contract C { + function () payable { + throw; + } + } + )"; + compileAndRun(sourceCode, 0, "B"); + u160 const nonPayableRecipient = m_contractAddress; + compileAndRun(sourceCode, 0, "C"); + u160 const oogRecipient = m_contractAddress; + compileAndRun(sourceCode, 20, "A"); + u160 payableRecipient(23); + BOOST_CHECK(callContractFunction("a(address,uint256)", payableRecipient, 10) == encodeArgs(10)); + BOOST_CHECK_EQUAL(balanceAt(payableRecipient), 10); + BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); + BOOST_CHECK(callContractFunction("b(address,uint256)", nonPayableRecipient, 10) == encodeArgs()); + BOOST_CHECK(callContractFunction("b(address,uint256)", oogRecipient, 10) == encodeArgs()); +} + BOOST_AUTO_TEST_CASE(log0) { char const* sourceCode = R"( From ba437ef31a95f40f510a475e8b329f061e929b90 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 6 Feb 2017 16:14:43 +0000 Subject: [PATCH 03/10] Add type checking test for address methods --- .../SolidityNameAndTypeResolution.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index a1ebc3008..bb2746147 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5108,6 +5108,24 @@ BOOST_AUTO_TEST_CASE(early_exit_on_fatal_errors) CHECK_ERROR(text, DeclarationError, "Identifier not found or not unique"); } +BOOST_AUTO_TEST_CASE(address_methods) +{ + char const* text = R"( + contract C { + function f() { + address addr; + uint balance = addr.balance; + bool callRet = addr.call(); + bool callcodeRet = addr.callcode(); + bool delegatecallRet = addr.delegatecall(); + bool sendRet = addr.send(1); + addr.transfer(1); + } + } + )"; + CHECK_SUCCESS(text); +} + BOOST_AUTO_TEST_SUITE_END() } From 81006dae98ee18c33994af0274de10857774ff70 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 6 Feb 2017 16:54:05 +0000 Subject: [PATCH 04/10] Support gas modifier on addr.transfer() --- libsolidity/ast/Types.cpp | 3 ++- libsolidity/codegen/ExpressionCompiler.cpp | 3 ++- test/libsolidity/SolidityEndToEndTest.cpp | 6 ++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 7fccccbc0..3e3a3818c 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2315,9 +2315,10 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con case Location::Bare: case Location::BareCallCode: case Location::BareDelegateCall: + case Location::Transfer: { MemberList::MemberMap members; - if (m_location != Location::BareDelegateCall && m_location != Location::DelegateCall) + if (m_location != Location::BareDelegateCall && m_location != Location::DelegateCall && m_location != Location::Transfer) { if (m_isPayable) members.push_back(MemberList::Member( diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index b0031513c..92b915639 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -620,7 +620,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) _functionCall.expression().accept(*this); // Provide the gas stipend manually at first because we may send zero ether. // Will be zeroed if we send more than zero ether. - m_context << u256(eth::GasCosts::callStipend); + if (!function.gasSet()) + m_context << u256(eth::GasCosts::callStipend); arguments.front()->accept(*this); utils().convertType( *arguments.front()->annotation().type, diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index cb0cc1684..30430aefe 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1693,6 +1693,10 @@ BOOST_AUTO_TEST_CASE(transfer_ether) function b(address addr, uint amount) { addr.transfer(amount); } + function c(address addr, uint amount, uint gas) returns (uint) { + addr.transfer.gas(gas)(amount); + return this.balance; + } } contract B { @@ -1715,6 +1719,8 @@ BOOST_AUTO_TEST_CASE(transfer_ether) BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); BOOST_CHECK(callContractFunction("b(address,uint256)", nonPayableRecipient, 10) == encodeArgs()); BOOST_CHECK(callContractFunction("b(address,uint256)", oogRecipient, 10) == encodeArgs()); + BOOST_CHECK(callContractFunction("c(address,uint256,uint256)", payableRecipient, 1, 9000) == encodeArgs(9)); + BOOST_CHECK(callContractFunction("c(address,uint256,uint256)", payableRecipient, 1, 0) == encodeArgs()); } BOOST_AUTO_TEST_CASE(log0) From a36e2ce0cbb7e1830e25dacd8d79f207c9bcca6a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 8 Feb 2017 23:37:23 +0000 Subject: [PATCH 05/10] Document transfer() --- docs/control-structures.rst | 1 + docs/miscellaneous.rst | 5 +++-- docs/types.rst | 6 +++++- docs/units-and-global-variables.rst | 2 ++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index df8ac729c..ebc459657 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -395,6 +395,7 @@ Currently, Solidity automatically generates a runtime exception in the following #. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). #. If your contract receives Ether via a public getter function. #. If you call a zero-initialized variable of internal function type. +#. If a ``.transfer()`` fails. While a user-provided exception is generated in the following situations: #. Calling ``throw``. diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 3c57507ef..7b0305d5a 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -465,8 +465,9 @@ Global Variables - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` - ``super``: the contract one level higher in the inheritance hierarchy - ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address -- ``
.balance`` (``uint256``): balance of the address in Wei -- ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to address, returns ``false`` on failure +- ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei +- ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure +- ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure .. index:: visibility, public, private, external, internal diff --git a/docs/types.rst b/docs/types.rst index 69c23e6e0..342b67f4e 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -64,7 +64,7 @@ expression ``x << y`` is equivalent to ``x * 2**y`` and ``x >> y`` is equivalent to ``x / 2**y``. This means that shifting negative numbers sign extends. Shifting by a negative amount throws a runtime exception. -.. index:: address, balance, send, call, callcode, delegatecall +.. index:: address, balance, send, call, callcode, delegatecall, transfer .. _address: @@ -102,6 +102,10 @@ and to send Ether (in units of wei) to an address using the ``send`` function: to make safe Ether transfers, always check the return value of ``send`` or even better: Use a pattern where the recipient withdraws the money. +* ``transfer`` + +Transfer operates the same way as ``send``, with the exception that it will cause a exception if the transfer has failed. + * ``call``, ``callcode`` and ``delegatecall`` Furthermore, to interface with contracts that do not adhere to the ABI, diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 72741b672..49fe5d84c 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -130,6 +130,8 @@ Address Related balance of the :ref:`address` in Wei ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure +``
.transfer(uint256 amount)``: + send given amount of Wei to :ref:`address`, throws on failure For more information, see the section on :ref:`address`. From c674155e584d2a1d6a88c49485e281e2a12b85d0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 10 Feb 2017 14:38:43 +0000 Subject: [PATCH 06/10] Do not keep the gas stipend if sending non-zero value --- libsolidity/codegen/ExpressionCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 92b915639..4956871d7 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -627,7 +627,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) *arguments.front()->annotation().type, *function.parameterTypes().front(), true ); - if (function.location() != Location::Transfer) + if (!function.gasSet()) { // gas <- gas * !value m_context << Instruction::SWAP1 << Instruction::DUP2; From c46c68dfd8a5a8d82c19335c20d2bfa3aa8dd9ec Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 10 Feb 2017 23:15:32 +0000 Subject: [PATCH 07/10] Prefer .transfer() over .send() in the documentation --- docs/types.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/types.rst b/docs/types.rst index 342b67f4e..f7d1d54fa 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -80,31 +80,31 @@ Operators: Members of Addresses ^^^^^^^^^^^^^^^^^^^^ -* ``balance`` and ``send`` +* ``balance`` and ``transfer`` For a quick reference, see :ref:`address_related`. It is possible to query the balance of an address using the property ``balance`` -and to send Ether (in units of wei) to an address using the ``send`` function: +and to send Ether (in units of wei) to an address using the ``transfer`` function: :: address x = 0x123; address myAddress = this; - if (x.balance < 10 && myAddress.balance >= 10) x.send(10); + if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10); .. note:: - If ``x`` is a contract address, its code (more specifically: its fallback function, if present) will be executed together with the ``send`` call (this is a limitation of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted. In this case, ``send`` returns ``false``. + If ``x`` is a contract address, its code (more specifically: its fallback function, if present) will be executed together with the ``transfer`` call (this is a limitation of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted and the current contract will stop with an exception. + +* ``send`` + +Send is the low-level counterpart of ``transfer``. If the execution fails, the current contract will not stop with an exception, but ``send`` will return ``false``. .. warning:: There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order - to make safe Ether transfers, always check the return value of ``send`` or even better: - Use a pattern where the recipient withdraws the money. - -* ``transfer`` - -Transfer operates the same way as ``send``, with the exception that it will cause a exception if the transfer has failed. + to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better: + use a pattern where the recipient withdraws the money. * ``call``, ``callcode`` and ``delegatecall`` From cde027d144643883106d8bcef034c9e8ace2b3b2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 12 Feb 2017 14:07:52 +0000 Subject: [PATCH 08/10] Fix test for gas overloading in .transfer() --- test/libsolidity/SolidityEndToEndTest.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 30430aefe..f77a8935b 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1707,11 +1707,22 @@ BOOST_AUTO_TEST_CASE(transfer_ether) throw; } } + + contract D { + // This takes about ~3600 gas, which exceeds the 2300 gas stipend. + function () payable { + bytes32 tmp = 1; + for (uint i = 0; i < 20; i++) + tmp = sha3(tmp); + } + } )"; compileAndRun(sourceCode, 0, "B"); u160 const nonPayableRecipient = m_contractAddress; compileAndRun(sourceCode, 0, "C"); u160 const oogRecipient = m_contractAddress; + compileAndRun(sourceCode, 0, "D"); + u160 const expensiveRecipient = m_contractAddress; compileAndRun(sourceCode, 20, "A"); u160 payableRecipient(23); BOOST_CHECK(callContractFunction("a(address,uint256)", payableRecipient, 10) == encodeArgs(10)); @@ -1719,8 +1730,8 @@ BOOST_AUTO_TEST_CASE(transfer_ether) BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); BOOST_CHECK(callContractFunction("b(address,uint256)", nonPayableRecipient, 10) == encodeArgs()); BOOST_CHECK(callContractFunction("b(address,uint256)", oogRecipient, 10) == encodeArgs()); - BOOST_CHECK(callContractFunction("c(address,uint256,uint256)", payableRecipient, 1, 9000) == encodeArgs(9)); - BOOST_CHECK(callContractFunction("c(address,uint256,uint256)", payableRecipient, 1, 0) == encodeArgs()); + BOOST_CHECK(callContractFunction("c(address,uint256,uint256)", expensiveRecipient, 1, 9000) == encodeArgs(9)); + BOOST_CHECK(callContractFunction("c(address,uint256,uint256)", expensiveRecipient, 1, 0) == encodeArgs()); } BOOST_AUTO_TEST_CASE(log0) From 59514d8268585f02aa1abb1fdb1b56bcf6e56ef4 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 13 Feb 2017 02:17:17 +0000 Subject: [PATCH 09/10] Remove obsolete .send() entry from FAQ --- docs/frequently-asked-questions.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index d2fc5b1ce..8a68ae5b9 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -660,16 +660,6 @@ https://github.com/ethereum/wiki/wiki/Subtleties After a successful CREATE operation's sub-execution, if the operation returns x, 5 * len(x) gas is subtracted from the remaining gas before the contract is created. If the remaining gas is less than 5 * len(x), then no gas is subtracted, the code of the created contract becomes the empty string, but this is not treated as an exceptional condition - no reverts happen. -How do I use ``.send()``? -========================= - -If you want to send 20 Ether from a contract to the address ``x``, you use ``x.send(20 ether);``. -Here, ``x`` can be a plain address or a contract. If the contract already explicitly defines -a function ``send`` (and thus overwrites the special function), you can use ``address(x).send(20 ether);``. - -Note that the call to ``send`` may fail in certain conditions, such as if you have insufficient funds, so you should always check the return value. -``send`` returns ``true`` if the send was successful and ``false`` otherwise. - What does the following strange check do in the Custom Token contract? ====================================================================== From 4d290e551c2d563671f9d56744883d3f3dff98ec Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 24 Feb 2017 00:27:36 +0000 Subject: [PATCH 10/10] Disallow setting .gas() on .transfer() --- libsolidity/ast/Types.cpp | 3 +-- libsolidity/codegen/ExpressionCompiler.cpp | 12 ++++-------- test/libsolidity/SolidityEndToEndTest.cpp | 17 ----------------- 3 files changed, 5 insertions(+), 27 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 3e3a3818c..7fccccbc0 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2315,10 +2315,9 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con case Location::Bare: case Location::BareCallCode: case Location::BareDelegateCall: - case Location::Transfer: { MemberList::MemberMap members; - if (m_location != Location::BareDelegateCall && m_location != Location::DelegateCall && m_location != Location::Transfer) + if (m_location != Location::BareDelegateCall && m_location != Location::DelegateCall) { if (m_isPayable) members.push_back(MemberList::Member( diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 4956871d7..fd4d87a58 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -620,19 +620,15 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) _functionCall.expression().accept(*this); // Provide the gas stipend manually at first because we may send zero ether. // Will be zeroed if we send more than zero ether. - if (!function.gasSet()) - m_context << u256(eth::GasCosts::callStipend); + m_context << u256(eth::GasCosts::callStipend); arguments.front()->accept(*this); utils().convertType( *arguments.front()->annotation().type, *function.parameterTypes().front(), true ); - if (!function.gasSet()) - { - // gas <- gas * !value - m_context << Instruction::SWAP1 << Instruction::DUP2; - m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1; - } + // gas <- gas * !value + m_context << Instruction::SWAP1 << Instruction::DUP2; + m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1; appendExternalFunctionCall( FunctionType( TypePointers{}, diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f77a8935b..cb0cc1684 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1693,10 +1693,6 @@ BOOST_AUTO_TEST_CASE(transfer_ether) function b(address addr, uint amount) { addr.transfer(amount); } - function c(address addr, uint amount, uint gas) returns (uint) { - addr.transfer.gas(gas)(amount); - return this.balance; - } } contract B { @@ -1707,22 +1703,11 @@ BOOST_AUTO_TEST_CASE(transfer_ether) throw; } } - - contract D { - // This takes about ~3600 gas, which exceeds the 2300 gas stipend. - function () payable { - bytes32 tmp = 1; - for (uint i = 0; i < 20; i++) - tmp = sha3(tmp); - } - } )"; compileAndRun(sourceCode, 0, "B"); u160 const nonPayableRecipient = m_contractAddress; compileAndRun(sourceCode, 0, "C"); u160 const oogRecipient = m_contractAddress; - compileAndRun(sourceCode, 0, "D"); - u160 const expensiveRecipient = m_contractAddress; compileAndRun(sourceCode, 20, "A"); u160 payableRecipient(23); BOOST_CHECK(callContractFunction("a(address,uint256)", payableRecipient, 10) == encodeArgs(10)); @@ -1730,8 +1715,6 @@ BOOST_AUTO_TEST_CASE(transfer_ether) BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); BOOST_CHECK(callContractFunction("b(address,uint256)", nonPayableRecipient, 10) == encodeArgs()); BOOST_CHECK(callContractFunction("b(address,uint256)", oogRecipient, 10) == encodeArgs()); - BOOST_CHECK(callContractFunction("c(address,uint256,uint256)", expensiveRecipient, 1, 9000) == encodeArgs(9)); - BOOST_CHECK(callContractFunction("c(address,uint256,uint256)", expensiveRecipient, 1, 0) == encodeArgs()); } BOOST_AUTO_TEST_CASE(log0)