diff options
author | chriseth <chris@ethereum.org> | 2016-11-21 18:06:56 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-11-21 18:06:56 +0800 |
commit | afda210afd468ab161a2f0cb01fbab88d12bb120 (patch) | |
tree | 1e642fa848a41f7ef9df4cb0b9d0f269ccf648fb | |
parent | b46a14f4a8e128c08336763abf8bbf7c111f464d (diff) | |
parent | 910269a29f56265e42bce73694d45481cbd5fd54 (diff) | |
download | dexon-solidity-afda210afd468ab161a2f0cb01fbab88d12bb120.tar dexon-solidity-afda210afd468ab161a2f0cb01fbab88d12bb120.tar.gz dexon-solidity-afda210afd468ab161a2f0cb01fbab88d12bb120.tar.bz2 dexon-solidity-afda210afd468ab161a2f0cb01fbab88d12bb120.tar.lz dexon-solidity-afda210afd468ab161a2f0cb01fbab88d12bb120.tar.xz dexon-solidity-afda210afd468ab161a2f0cb01fbab88d12bb120.tar.zst dexon-solidity-afda210afd468ab161a2f0cb01fbab88d12bb120.zip |
Merge pull request #1382 from ethereum/payable-constructor
Payable constructor
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 25 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.h | 1 | ||||
-rw-r--r-- | libsolidity/interface/InterfaceHandler.cpp | 1 | ||||
-rw-r--r-- | test/contracts/Wallet.cpp | 2 | ||||
-rw-r--r-- | test/libsolidity/SolidityABIJSON.cpp | 2 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 33 |
7 files changed, 43 insertions, 22 deletions
diff --git a/Changelog.md b/Changelog.md index ce3343d8..6d080556 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Features: Bugfixes: * Inline assembly: calculate stack height warning correctly even when local variables are used. + * Support the ``payable`` keyword on constructors. * Parser: disallow empty enum definitions. * Type checker: disallow conversion between different enum types. * Interface JSON: do not include trailing new line. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 2e3f5d7f..8d60d6b3 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -102,6 +102,13 @@ void ContractCompiler::initializeContext( m_context.resetVisitedNodes(&_contract); } +void ContractCompiler::appendCallValueCheck() +{ + // Throw if function is not payable but call contained ether. + m_context << Instruction::CALLVALUE; + m_context.appendConditionalJumpTo(m_context.errorTag()); +} + void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract) { // Determine the arguments that are used for the base constructors. @@ -137,6 +144,8 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c appendConstructor(*constructor); else if (auto c = m_context.nextConstructor(_contract)) appendBaseConstructor(*c); + else + appendCallValueCheck(); } size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract) @@ -184,6 +193,9 @@ void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _construc void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor) { CompilerContext::LocationSetter locationSetter(m_context, _constructor); + if (!_constructor.isPayable()) + appendCallValueCheck(); + // copy constructor arguments from code to memory and then to stack, they are supplied after the actual program if (!_constructor.parameters().empty()) { @@ -251,11 +263,8 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac if (fallback) { if (!fallback->isPayable()) - { - // Throw if function is not payable but call contained ether. - m_context << Instruction::CALLVALUE; - m_context.appendConditionalJumpTo(m_context.errorTag()); - } + appendCallValueCheck(); + eth::AssemblyItem returnTag = m_context.pushNewTag(); fallback->accept(*this); m_context << returnTag; @@ -274,11 +283,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac // We have to allow this for libraries, because value of the previous // call is still visible in the delegatecall. if (!functionType->isPayable() && !_contract.isLibrary()) - { - // Throw if function is not payable but call contained ether. - m_context << Instruction::CALLVALUE; - m_context.appendConditionalJumpTo(m_context.errorTag()); - } + appendCallValueCheck(); eth::AssemblyItem returnTag = m_context.pushNewTag(); m_context << CompilerUtils::dataStartOffset; diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 2244a3be..10febcf7 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -80,6 +80,7 @@ private: void appendBaseConstructor(FunctionDefinition const& _constructor); void appendConstructor(FunctionDefinition const& _constructor); void appendFunctionSelector(ContractDefinition const& _contract); + void appendCallValueCheck(); /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers. /// From memory if @a _fromMemory is true, otherwise from call data. /// Expects source offset on the stack, which is removed. diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp index 0f7b6c35..9944bb22 100644 --- a/libsolidity/interface/InterfaceHandler.cpp +++ b/libsolidity/interface/InterfaceHandler.cpp @@ -68,6 +68,7 @@ Json::Value InterfaceHandler::abiInterface(ContractDefinition const& _contractDe method["type"] = "constructor"; auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType(); solAssert(!!externalFunction, ""); + method["payable"] = externalFunction->isPayable(); method["inputs"] = populateParameters( externalFunction->parameterNames(), externalFunction->parameterTypeNames(_contractDef.isLibrary()) diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index ec968058..4966b26c 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -368,7 +368,7 @@ contract Wallet is multisig, multiowned, daylimit { // constructor - just pass on the owner array to the multiowned and // the limit to daylimit - function Wallet(address[] _owners, uint _required, uint _daylimit) + function Wallet(address[] _owners, uint _required, uint _daylimit) payable multiowned(_owners, _required) daylimit(_daylimit) { } diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 5cabb7fa..a8a39b0b 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -524,6 +524,7 @@ BOOST_AUTO_TEST_CASE(constructor_abi) "type": "bool" } ], + "payable": false, "type": "constructor" } ])"; @@ -567,6 +568,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi) "type": "uint8" } ], + "payable": false, "type": "constructor" } ] diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index ed95d687..4abe0894 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1337,6 +1337,7 @@ BOOST_AUTO_TEST_CASE(struct_accessor) BOOST_AUTO_TEST_CASE(balance) { char const* sourceCode = "contract test {\n" + " function test() payable {}\n" " function getBalance() returns (uint256 balance) {\n" " return address(this).balance;\n" " }\n" @@ -1348,6 +1349,7 @@ BOOST_AUTO_TEST_CASE(balance) BOOST_AUTO_TEST_CASE(blockchain) { char const* sourceCode = "contract test {\n" + " function test() payable {}\n" " function someInfo() payable returns (uint256 value, address coinbase, uint256 blockNumber) {\n" " value = msg.value;\n" " coinbase = block.coinbase;\n" @@ -1563,6 +1565,7 @@ BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_greater_size) BOOST_AUTO_TEST_CASE(send_ether) { char const* sourceCode = "contract test {\n" + " function test() payable {}\n" " function a(address addr, uint amount) returns (uint ret) {\n" " addr.send(amount);\n" " return address(this).balance;\n" @@ -1675,6 +1678,7 @@ BOOST_AUTO_TEST_CASE(log_in_constructor) BOOST_AUTO_TEST_CASE(suicide) { char const* sourceCode = "contract test {\n" + " function test() payable {}\n" " function a(address receiver) returns (uint ret) {\n" " suicide(receiver);\n" " return 10;\n" @@ -1691,6 +1695,7 @@ BOOST_AUTO_TEST_CASE(suicide) BOOST_AUTO_TEST_CASE(selfdestruct) { char const* sourceCode = "contract test {\n" + " function test() payable {}\n" " function a(address receiver) returns (uint ret) {\n" " selfdestruct(receiver);\n" " return 10;\n" @@ -2956,24 +2961,24 @@ BOOST_AUTO_TEST_CASE(generic_call) BOOST_AUTO_TEST_CASE(generic_callcode) { char const* sourceCode = R"**( - contract receiver { + contract Receiver { uint public received; function receive(uint256 x) payable { received = x; } } - contract sender { + contract Sender { uint public received; - function sender() payable { } + function Sender() payable { } function doSend(address rec) returns (uint d) { bytes4 signature = bytes4(bytes32(sha3("receive(uint256)"))); rec.callcode.value(2)(signature, 23); - return receiver(rec).received(); + return Receiver(rec).received(); } } )**"; - compileAndRun(sourceCode, 0, "receiver"); + compileAndRun(sourceCode, 0, "Receiver"); u160 const c_receiverAddress = m_contractAddress; - compileAndRun(sourceCode, 50, "sender"); + compileAndRun(sourceCode, 50, "Sender"); u160 const c_senderAddress = m_contractAddress; BOOST_CHECK(callContractFunction("doSend(address)", c_receiverAddress) == encodeArgs(0)); BOOST_CHECK(callContractFunction("received()") == encodeArgs(23)); @@ -2988,16 +2993,18 @@ BOOST_AUTO_TEST_CASE(generic_callcode) BOOST_AUTO_TEST_CASE(generic_delegatecall) { char const* sourceCode = R"**( - contract receiver { + contract Receiver { uint public received; address public sender; uint public value; + function Receiver() payable {} function receive(uint256 x) payable { received = x; sender = msg.sender; value = msg.value; } } - contract sender { + contract Sender { uint public received; address public sender; uint public value; + function Sender() payable {} function doSend(address rec) payable { bytes4 signature = bytes4(bytes32(sha3("receive(uint256)"))); @@ -3005,9 +3012,9 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall) } } )**"; - compileAndRun(sourceCode, 0, "receiver"); + compileAndRun(sourceCode, 0, "Receiver"); u160 const c_receiverAddress = m_contractAddress; - compileAndRun(sourceCode, 50, "sender"); + compileAndRun(sourceCode, 50, "Sender"); u160 const c_senderAddress = m_contractAddress; BOOST_CHECK(m_sender != c_senderAddress); // just for sanity BOOST_CHECK(callContractFunctionWithValue("doSend(address)", 11, c_receiverAddress) == encodeArgs()); @@ -4818,6 +4825,7 @@ BOOST_AUTO_TEST_CASE(failing_send) } } contract Main { + function Main() payable {} function callHelper(address _a) returns (bool r, uint bal) { r = !_a.send(5); bal = this.balance; @@ -4840,6 +4848,7 @@ BOOST_AUTO_TEST_CASE(send_zero_ether) } } contract Main { + function Main() payable {} function s() returns (bool) { var r = new Receiver(); return r.send(0); @@ -6341,6 +6350,7 @@ BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library) char const* sourceCode = R"( library lib {} contract c { + function c() payable {} function f(address x) returns (bool) { return x.send(1); } @@ -7279,6 +7289,7 @@ BOOST_AUTO_TEST_CASE(failed_create) contract D { function D() payable {} } contract C { uint public x; + function C() payable {} function f(uint amount) returns (address) { x++; return (new D).value(amount)(); @@ -7392,7 +7403,7 @@ BOOST_AUTO_TEST_CASE(mutex) } contract Fund is mutexed { uint shares; - function Fund() { shares = msg.value; } + function Fund() payable { shares = msg.value; } function withdraw(uint amount) protected returns (uint) { // NOTE: It is very bad practice to write this function this way. // Please refer to the documentation of how to do this properly. |