Change function type to include and propagate payable and constant modifier.

This commit is contained in:
chriseth 2016-08-31 20:43:24 +02:00
parent 962531af96
commit 9c64edf110
8 changed files with 123 additions and 62 deletions

View File

@ -349,7 +349,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
typeError(_inheritance.location(), "Libraries cannot be inherited from."); typeError(_inheritance.location(), "Libraries cannot be inherited from.");
auto const& arguments = _inheritance.arguments(); auto const& arguments = _inheritance.arguments();
TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes(); TypePointers parameterTypes = ContractType(*base).newExpressionType()->parameterTypes();
if (!arguments.empty() && parameterTypes.size() != arguments.size()) if (!arguments.empty() && parameterTypes.size() != arguments.size())
{ {
typeError( typeError(
@ -1264,15 +1264,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
"Circular reference for contract creation (cannot create instance of derived or same contract)." "Circular reference for contract creation (cannot create instance of derived or same contract)."
); );
auto contractType = make_shared<ContractType>(*contract); _newExpression.annotation().type = FunctionType::newExpressionType(*contract);
TypePointers parameterTypes = contractType->constructorType()->parameterTypes();
_newExpression.annotation().type = make_shared<FunctionType>(
parameterTypes,
TypePointers{contractType},
strings(),
strings(),
FunctionType::Location::Creation
);
} }
else if (type->category() == Type::Category::Array) else if (type->category() == Type::Category::Array)
{ {

View File

@ -362,8 +362,8 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
if (isAddress()) if (isAddress())
return { return {
{"balance", make_shared<IntegerType >(256)}, {"balance", make_shared<IntegerType >(256)},
{"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)}, {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true, false, true)},
{"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)}, {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true, false, true)},
{"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)}, {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)} {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)}
}; };
@ -1329,16 +1329,10 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) con
return members; return members;
} }
shared_ptr<FunctionType const> const& ContractType::constructorType() const shared_ptr<FunctionType const> const& ContractType::newExpressionType() const
{ {
if (!m_constructorType) if (!m_constructorType)
{ m_constructorType = FunctionType::newExpressionType(m_contract);
FunctionDefinition const* constructor = m_contract.constructor();
if (constructor)
m_constructorType = make_shared<FunctionType>(*constructor);
else
m_constructorType = make_shared<FunctionType>(TypePointers(), TypePointers());
}
return m_constructorType; return m_constructorType;
} }
@ -1738,7 +1732,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
swap(retParamNames, m_returnParameterNames); swap(retParamNames, m_returnParameterNames);
} }
FunctionType::FunctionType(const EventDefinition& _event): FunctionType::FunctionType(EventDefinition const& _event):
m_location(Location::Event), m_isConstant(true), m_declaration(&_event) m_location(Location::Event), m_isConstant(true), m_declaration(&_event)
{ {
TypePointers params; TypePointers params;
@ -1754,6 +1748,35 @@ FunctionType::FunctionType(const EventDefinition& _event):
swap(paramNames, m_parameterNames); swap(paramNames, m_parameterNames);
} }
FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _contract)
{
FunctionDefinition const* constructor = _contract.constructor();
TypePointers parameters;
strings parameterNames;
bool payable = false;
if (constructor)
{
for (ASTPointer<VariableDeclaration> const& var: constructor->parameters())
{
parameterNames.push_back(var->name());
parameters.push_back(var->annotation().type);
}
payable = constructor->isPayable();
}
return make_shared<FunctionType>(
parameters,
TypePointers{make_shared<ContractType>(_contract)},
parameterNames,
strings{""},
Location::Creation,
false,
nullptr,
false,
payable
);
}
vector<string> FunctionType::parameterNames() const vector<string> FunctionType::parameterNames() const
{ {
if (!bound()) if (!bound())
@ -1872,7 +1895,12 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
if (variable && retParamTypes.empty()) if (variable && retParamTypes.empty())
return FunctionTypePointer(); return FunctionTypePointer();
return make_shared<FunctionType>(paramTypes, retParamTypes, m_parameterNames, m_returnParameterNames, m_location, m_arbitraryParameters); return make_shared<FunctionType>(
paramTypes, retParamTypes,
m_parameterNames, m_returnParameterNames,
m_location, m_arbitraryParameters,
m_declaration, m_isConstant, m_isPayable
);
} }
MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) const MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) const
@ -1891,7 +1919,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
MemberList::MemberMap members; MemberList::MemberMap members;
if (m_location != Location::BareDelegateCall && m_location != Location::DelegateCall) if (m_location != Location::BareDelegateCall && m_location != Location::DelegateCall)
{ {
if (!m_declaration || m_isPayable) // If this is an actual function, it has to be payable if (m_isPayable)
members.push_back(MemberList::Member( members.push_back(MemberList::Member(
"value", "value",
make_shared<FunctionType>( make_shared<FunctionType>(
@ -1902,6 +1930,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
Location::SetValue, Location::SetValue,
false, false,
nullptr, nullptr,
false,
false,
m_gasSet, m_gasSet,
m_valueSet m_valueSet
) )
@ -1918,6 +1948,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
Location::SetGas, Location::SetGas,
false, false,
nullptr, nullptr,
false,
false,
m_gasSet, m_gasSet,
m_valueSet m_valueSet
) )
@ -2023,6 +2055,8 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
m_location, m_location,
m_arbitraryParameters, m_arbitraryParameters,
m_declaration, m_declaration,
m_isConstant,
m_isPayable,
m_gasSet || _setGas, m_gasSet || _setGas,
m_valueSet || _setValue, m_valueSet || _setValue,
m_bound m_bound
@ -2068,6 +2102,8 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound)
location, location,
m_arbitraryParameters, m_arbitraryParameters,
m_declaration, m_declaration,
m_isConstant,
m_isPayable,
m_gasSet, m_gasSet,
m_valueSet, m_valueSet,
_bound _bound

View File

@ -640,9 +640,8 @@ public:
bool isSuper() const { return m_super; } bool isSuper() const { return m_super; }
ContractDefinition const& contractDefinition() const { return m_contract; } ContractDefinition const& contractDefinition() const { return m_contract; }
/// Returns the function type of the constructor. Note that the location part of the function type /// Returns the function type of the constructor modified to return an object of the contract's type.
/// is not used, as this type cannot be the type of a variable or expression. FunctionTypePointer const& newExpressionType() const;
FunctionTypePointer const& constructorType() const;
/// @returns the identifier of the function with the given name or Invalid256 if such a name does /// @returns the identifier of the function with the given name or Invalid256 if such a name does
/// not exist. /// not exist.
@ -820,21 +819,32 @@ public:
explicit FunctionType(VariableDeclaration const& _varDecl); explicit FunctionType(VariableDeclaration const& _varDecl);
/// Creates the function type of an event. /// Creates the function type of an event.
explicit FunctionType(EventDefinition const& _event); explicit FunctionType(EventDefinition const& _event);
/// Function type constructor to be used for a plain type (not derived from a declaration).
FunctionType( FunctionType(
strings const& _parameterTypes, strings const& _parameterTypes,
strings const& _returnParameterTypes, strings const& _returnParameterTypes,
Location _location = Location::Internal, Location _location = Location::Internal,
bool _arbitraryParameters = false bool _arbitraryParameters = false,
bool _constant = false,
bool _payable = false
): FunctionType( ): FunctionType(
parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_parameterTypes),
parseElementaryTypeVector(_returnParameterTypes), parseElementaryTypeVector(_returnParameterTypes),
strings(), strings(),
strings(), strings(),
_location, _location,
_arbitraryParameters _arbitraryParameters,
nullptr,
_constant,
_payable
) )
{ {
} }
/// @returns the type of the "new Contract" function, i.e. basically the constructor.
static FunctionTypePointer newExpressionType(ContractDefinition const& _contract);
/// Detailed constructor, use with care.
FunctionType( FunctionType(
TypePointers const& _parameterTypes, TypePointers const& _parameterTypes,
TypePointers const& _returnParameterTypes, TypePointers const& _returnParameterTypes,
@ -843,6 +853,8 @@ public:
Location _location = Location::Internal, Location _location = Location::Internal,
bool _arbitraryParameters = false, bool _arbitraryParameters = false,
Declaration const* _declaration = nullptr, Declaration const* _declaration = nullptr,
bool _isConstant = false,
bool _isPayable = false,
bool _gasSet = false, bool _gasSet = false,
bool _valueSet = false, bool _valueSet = false,
bool _bound = false bool _bound = false
@ -856,6 +868,8 @@ public:
m_gasSet(_gasSet), m_gasSet(_gasSet),
m_valueSet(_valueSet), m_valueSet(_valueSet),
m_bound(_bound), m_bound(_bound),
m_isConstant(_isConstant),
m_isPayable(_isPayable),
m_declaration(_declaration) m_declaration(_declaration)
{} {}

View File

@ -242,6 +242,12 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
m_context << notFound; m_context << notFound;
if (fallback) 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());
}
eth::AssemblyItem returnTag = m_context.pushNewTag(); eth::AssemblyItem returnTag = m_context.pushNewTag();
fallback->accept(*this); fallback->accept(*this);
m_context << returnTag; m_context << returnTag;

View File

@ -583,6 +583,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
Location::Bare, Location::Bare,
false, false,
nullptr, nullptr,
false,
false,
true, true,
true true
), ),

View File

@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE(location_test)
AssemblyItems items = compileContract(sourceCode); AssemblyItems items = compileContract(sourceCode);
vector<SourceLocation> locations = vector<SourceLocation> locations =
vector<SourceLocation>(18, SourceLocation(2, 75, n)) + vector<SourceLocation>(18, SourceLocation(2, 75, n)) +
vector<SourceLocation>(28, SourceLocation(20, 72, n)) + vector<SourceLocation>(31, SourceLocation(20, 72, n)) +
vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} +
vector<SourceLocation>(4, SourceLocation(58, 67, n)) + vector<SourceLocation>(4, SourceLocation(58, 67, n)) +
vector<SourceLocation>(3, SourceLocation(20, 72, n)); vector<SourceLocation>(3, SourceLocation(20, 72, n));

View File

@ -1526,8 +1526,10 @@ BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_greater_size)
} }
})"; })";
compileAndRun(sourceCode); compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("UintToBytes(uint16)", u256("0x6162")) == BOOST_CHECK(
encodeArgs(string("\0\0\0\0\0\0ab", 8))); callContractFunction("UintToBytes(uint16)", u256("0x6162")) ==
encodeArgs(string("\0\0\0\0\0\0ab", 8))
);
} }
BOOST_AUTO_TEST_CASE(send_ether) BOOST_AUTO_TEST_CASE(send_ether)
@ -2053,7 +2055,7 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
contract helper { contract helper {
function() { } // can receive ether function() payable { } // can receive ether
} }
contract test { contract test {
helper h; helper h;
@ -2065,6 +2067,7 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses)
} }
)"; )";
compileAndRun(sourceCode, 20); compileAndRun(sourceCode, 20);
BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 20 - 5);
BOOST_REQUIRE(callContractFunction("getBalance()") == encodeArgs(u256(20 - 5), u256(5))); BOOST_REQUIRE(callContractFunction("getBalance()") == encodeArgs(u256(20 - 5), u256(5)));
} }
@ -2938,11 +2941,10 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall)
uint public received; uint public received;
address public sender; address public sender;
uint public value; uint public value;
function sender() payable {}
function doSend(address rec) payable function doSend(address rec) payable
{ {
bytes4 signature = bytes4(bytes32(sha3("receive(uint256)"))); bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
rec.delegatecall(signature, 23); if (rec.delegatecall(signature, 23)) {}
} }
} }
)**"; )**";
@ -5961,7 +5963,7 @@ BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library)
function f(address x) returns (bool) { function f(address x) returns (bool) {
return x.send(1); return x.send(1);
} }
function () {} function () payable {}
} }
)"; )";
compileAndRun(sourceCode, 0, "lib"); compileAndRun(sourceCode, 0, "lib");
@ -6879,7 +6881,7 @@ BOOST_AUTO_TEST_CASE(skip_dynamic_types_for_structs)
BOOST_AUTO_TEST_CASE(failed_create) BOOST_AUTO_TEST_CASE(failed_create)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
contract D { } contract D { function D() payable {} }
contract C { contract C {
uint public x; uint public x;
function f(uint amount) returns (address) { function f(uint amount) returns (address) {
@ -7029,7 +7031,7 @@ BOOST_AUTO_TEST_CASE(mutex)
else else
return fund.withdrawUnprotected(10); return fund.withdrawUnprotected(10);
} }
function() { function() payable {
callDepth++; callDepth++;
if (callDepth < 4) if (callDepth < 4)
attackInternal(); attackInternal();
@ -7104,55 +7106,47 @@ BOOST_AUTO_TEST_CASE(payable_function)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
contract C { contract C {
uint public a;
function f() payable returns (uint) { function f() payable returns (uint) {
return msg.value; return msg.value;
} }
function() payable returns (uint) { function() payable {
return msg.value; a = msg.value + 1;
} }
} }
)"; )";
compileAndRun(sourceCode, 0, "C"); compileAndRun(sourceCode, 0, "C");
BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs(u256(27))); BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs(u256(27)));
BOOST_CHECK(callContractFunctionWithValue("", 27) == encodeArgs(u256(27)));
}
BOOST_AUTO_TEST_CASE(non_payable_throw_constructor)
{
char const* sourceCode = R"(
contract C {
function C() { }
}
contract D {
function D() payable {}
function f() returns (uint) {
(new C).value(2)();
return 2;
}
}
)";
compileAndRun(sourceCode, 27, "D");
BOOST_CHECK(callContractFunction("f()") == encodeArgs());
BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27); BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27);
BOOST_CHECK(callContractFunctionWithValue("", 27) == encodeArgs());
BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27 + 27);
BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(28)));
BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27 + 27);
} }
BOOST_AUTO_TEST_CASE(non_payable_throw) BOOST_AUTO_TEST_CASE(non_payable_throw)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
contract C { contract C {
string public a; uint public a;
function f() returns (uint) { function f() returns (uint) {
return msg.value; return msg.value;
} }
function() returns (uint) { function() {
return msg.value; a = msg.value + 1;
} }
} }
)"; )";
compileAndRun(sourceCode, 0, "C"); compileAndRun(sourceCode, 0, "C");
BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs()); BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs());
BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
BOOST_CHECK(callContractFunction("") == encodeArgs());
BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(1)));
BOOST_CHECK(callContractFunctionWithValue("", 27) == encodeArgs()); BOOST_CHECK(callContractFunctionWithValue("", 27) == encodeArgs());
BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(1)));
BOOST_CHECK(callContractFunctionWithValue("a()", 27) == encodeArgs()); BOOST_CHECK(callContractFunctionWithValue("a()", 27) == encodeArgs());
BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
} }
BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier) BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier)
@ -7169,6 +7163,7 @@ BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier)
)"; )";
compileAndRun(sourceCode); compileAndRun(sourceCode);
BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs()); BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs());
BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
} }

View File

@ -3911,7 +3911,23 @@ BOOST_AUTO_TEST_CASE(calling_nonpayable)
char const* text = R"( char const* text = R"(
contract receiver { function nopay() {} } contract receiver { function nopay() {} }
contract test { contract test {
function_argument_mem_to_storage f() { (new receiver()).nopay.value(10)(); } function f() { (new receiver()).nopay.value(10)(); }
}
)";
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
}
BOOST_AUTO_TEST_CASE(non_payable_constructor)
{
char const* text = R"(
contract C {
function C() { }
}
contract D {
function f() returns (uint) {
(new C).value(2)();
return 2;
}
} }
)"; )";
BOOST_CHECK(expectError(text) == Error::Type::TypeError); BOOST_CHECK(expectError(text) == Error::Type::TypeError);