mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #1382 from ethereum/payable-constructor
Payable constructor
This commit is contained in:
		
						commit
						afda210afd
					
				| @ -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. | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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.
 | ||||
|  | ||||
| @ -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()) | ||||
|  | ||||
| @ -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) { | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -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" | ||||
| 		} | ||||
| 	] | ||||
|  | ||||
| @ -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.
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user