aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2016-11-21 18:06:56 +0800
committerGitHub <noreply@github.com>2016-11-21 18:06:56 +0800
commitafda210afd468ab161a2f0cb01fbab88d12bb120 (patch)
tree1e642fa848a41f7ef9df4cb0b9d0f269ccf648fb
parentb46a14f4a8e128c08336763abf8bbf7c111f464d (diff)
parent910269a29f56265e42bce73694d45481cbd5fd54 (diff)
downloaddexon-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.md1
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp25
-rw-r--r--libsolidity/codegen/ContractCompiler.h1
-rw-r--r--libsolidity/interface/InterfaceHandler.cpp1
-rw-r--r--test/contracts/Wallet.cpp2
-rw-r--r--test/libsolidity/SolidityABIJSON.cpp2
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp33
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.