aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md19
-rw-r--r--docs/style-guide.rst2
-rw-r--r--libsolidity/analysis/TypeChecker.cpp28
-rw-r--r--libsolidity/ast/AST.h4
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp17
-rw-r--r--libsolidity/ast/Types.cpp92
-rw-r--r--libsolidity/ast/Types.h26
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp14
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp6
-rw-r--r--libsolidity/interface/InterfaceHandler.cpp2
-rw-r--r--libsolidity/parsing/Parser.cpp7
-rw-r--r--libsolidity/parsing/Token.h2
-rw-r--r--solc/CommandLineInterface.cpp40
-rw-r--r--test/contracts/AuctionRegistrar.cpp7
-rw-r--r--test/contracts/FixedFeeRegistrar.cpp2
-rw-r--r--test/contracts/Wallet.cpp2
-rw-r--r--test/libsolidity/ASTJSON.cpp16
-rw-r--r--test/libsolidity/Assembly.cpp2
-rw-r--r--test/libsolidity/SolidityABIJSON.cpp69
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp142
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp108
-rw-r--r--test/libsolidity/SolidityParser.cpp10
22 files changed, 504 insertions, 113 deletions
diff --git a/Changelog.md b/Changelog.md
index bd8d924f..e0967243 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -7,13 +7,16 @@ enforce some safety features. The most important change is
Breaking Changes:
* Source files have to specify the compiler version they are
- compatible with using e.g. `pragma solidity ^0.4.0;` or
- `pragma solidity >=0.4.0 <0.4.8;`
- * Contracts that want to receive Ether have to implement a fallback
- function (contracts now throw if no fallback function is defined
- and no function matches the signature).
- * Failing contract creation through "new" throws now.
- * Throw on division / modulus by zero
+ compatible with using e.g. ``pragma solidity ^0.4.0;`` or
+ ``pragma solidity >=0.4.0 <0.4.8;``
+ * Functions that want to receive Ether have to specify the
+ new ``payable`` modifier (otherwise they throw).
+ * Contracts that want to receive Ether with a plain "send"
+ have to implement a fallback function with the ``payable``
+ modifier. Contracts now throw if no payable fallback
+ function is defined and no function matches the signature.
+ * Failing contract creation through "new" throws.
+ * Division / modulus by zero throws
* Function call throws if target contract does not have code
* Modifiers are required to contain ``_`` (use ``if (false) _`` as a workaround if needed).
* Modifiers: return does not skip part in modifier after ``_``
@@ -34,11 +37,13 @@ Features:
* Commandline interface: Using ``-`` as filename allows reading from stdin.
* Interface Json: Fallback function is now part of the ABI.
* Interface: Version string now semver compatible.
+ * Code generator: Do not provide "new account gas" if we know the called account exists.
Bugfixes:
* JSON AST: nodes were added at wrong parent
* Why3 translator: crash fix for exponentiation
+ * Commandline Interface: linking libraries with underscores in their name.
* Type Checker: Fallback function cannot return data anymore.
* Code Generator: Fix crash when sha3() was used on unsupported types.
diff --git a/docs/style-guide.rst b/docs/style-guide.rst
index f4b419c0..d2d922df 100644
--- a/docs/style-guide.rst
+++ b/docs/style-guide.rst
@@ -355,7 +355,7 @@ No::
selfdestruct(owner);
}
-For long function declarations, it is recommended to drop each arguent onto
+For long function declarations, it is recommended to drop each argument onto
it's own line at the same indentation level as the function body. The closing
parenthesis and opening bracket should be placed on their own line as well at
the same indentation level as the function declaration.
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index bc03da01..06e26a76 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -272,6 +272,7 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr
if (
overriding->visibility() != function->visibility() ||
overriding->isDeclaredConst() != function->isDeclaredConst() ||
+ overriding->isPayable() != function->isPayable() ||
overridingType != functionType
)
typeError(overriding->location(), "Override changes extended function signature.");
@@ -348,7 +349,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
typeError(_inheritance.location(), "Libraries cannot be inherited from.");
auto const& arguments = _inheritance.arguments();
- TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes();
+ TypePointers parameterTypes = ContractType(*base).newExpressionType()->parameterTypes();
if (!arguments.empty() && parameterTypes.size() != arguments.size())
{
typeError(
@@ -416,6 +417,15 @@ bool TypeChecker::visit(StructDefinition const& _struct)
bool TypeChecker::visit(FunctionDefinition const& _function)
{
bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*_function.scope()).isLibrary();
+ if (_function.isPayable())
+ {
+ if (isLibraryFunction)
+ typeError(_function.location(), "Library functions cannot be payable.");
+ if (!_function.isConstructor() && !_function.name().empty() && !_function.isPartOfExternalInterface())
+ typeError(_function.location(), "Internal functions cannot be payable.");
+ if (_function.isDeclaredConst())
+ typeError(_function.location(), "Functions cannot be constant and payable at the same time.");
+ }
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
if (!type(*var)->canLiveOutsideStorage())
@@ -1256,15 +1266,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
"Circular reference for contract creation (cannot create instance of derived or same contract)."
);
- auto contractType = make_shared<ContractType>(*contract);
- TypePointers parameterTypes = contractType->constructorType()->parameterTypes();
- _newExpression.annotation().type = make_shared<FunctionType>(
- parameterTypes,
- TypePointers{contractType},
- strings(),
- strings(),
- FunctionType::Location::Creation
- );
+ _newExpression.annotation().type = FunctionType::newExpressionType(*contract);
}
else if (type->category() == Type::Category::Array)
{
@@ -1328,14 +1330,16 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
fatalTypeError(
_memberAccess.location(),
"Member \"" + memberName + "\" not found or not visible "
- "after argument-dependent lookup in " + exprType->toString()
+ "after argument-dependent lookup in " + exprType->toString() +
+ (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "")
);
}
else if (possibleMembers.size() > 1)
fatalTypeError(
_memberAccess.location(),
"Member \"" + memberName + "\" not unique "
- "after argument-dependent lookup in " + exprType->toString()
+ "after argument-dependent lookup in " + exprType->toString() +
+ (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "")
);
auto& annotation = _memberAccess.annotation();
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 761d85fe..8fd1584d 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -540,6 +540,7 @@ public:
bool _isDeclaredConst,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters,
+ bool _isPayable,
ASTPointer<Block> const& _body
):
CallableDeclaration(_location, _name, _visibility, _parameters, _returnParameters),
@@ -547,6 +548,7 @@ public:
ImplementationOptional(_body != nullptr),
m_isConstructor(_isConstructor),
m_isDeclaredConst(_isDeclaredConst),
+ m_isPayable(_isPayable),
m_functionModifiers(_modifiers),
m_body(_body)
{}
@@ -556,6 +558,7 @@ public:
bool isConstructor() const { return m_isConstructor; }
bool isDeclaredConst() const { return m_isDeclaredConst; }
+ bool isPayable() const { return m_isPayable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); }
Block const& body() const { return *m_body; }
@@ -578,6 +581,7 @@ public:
private:
bool m_isConstructor;
bool m_isDeclaredConst;
+ bool m_isPayable;
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<Block> m_body;
};
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 49ee6d34..0ea5e093 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -22,6 +22,7 @@
#include <libsolidity/ast/ASTJsonConverter.h>
#include <boost/algorithm/string/join.hpp>
+#include <libdevcore/UTF8.h>
#include <libsolidity/ast/AST.h>
using namespace std;
@@ -397,9 +398,21 @@ bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node)
bool ASTJsonConverter::visit(Literal const& _node)
{
char const* tokenString = Token::toString(_node.token());
+ size_t invalidPos = 0;
+ Json::Value value{_node.value()};
+ if (!dev::validate(_node.value(), invalidPos))
+ value = Json::nullValue;
+ Token::Value subdenomination = Token::Value(_node.subDenomination());
addJsonNode(_node, "Literal", {
- make_pair("string", tokenString ? tokenString : Json::Value()),
- make_pair("value", _node.value()),
+ make_pair("token", tokenString ? tokenString : Json::Value()),
+ make_pair("value", value),
+ make_pair("hexvalue", toHex(_node.value())),
+ make_pair(
+ "subdenomination",
+ subdenomination == Token::Illegal ?
+ Json::nullValue :
+ Json::Value{Token::toString(subdenomination)}
+ ),
make_pair("type", type(_node))
});
return true;
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index d86a2caf..a0c1626d 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -362,8 +362,8 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
if (isAddress())
return {
{"balance", make_shared<IntegerType >(256)},
- {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)},
- {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, 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, false, true)},
{"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)}
};
@@ -1329,16 +1329,10 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) con
return members;
}
-shared_ptr<FunctionType const> const& ContractType::constructorType() const
+shared_ptr<FunctionType const> const& ContractType::newExpressionType() const
{
if (!m_constructorType)
- {
- FunctionDefinition const* constructor = m_contract.constructor();
- if (constructor)
- m_constructorType = make_shared<FunctionType>(*constructor);
- else
- m_constructorType = make_shared<FunctionType>(TypePointers(), TypePointers());
- }
+ m_constructorType = FunctionType::newExpressionType(m_contract);
return m_constructorType;
}
@@ -1653,6 +1647,7 @@ TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) cons
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
m_location(_isInternal ? Location::Internal : Location::External),
m_isConstant(_function.isDeclaredConst()),
+ m_isPayable(_function.isPayable()),
m_declaration(&_function)
{
TypePointers params;
@@ -1737,7 +1732,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
swap(retParamNames, m_returnParameterNames);
}
-FunctionType::FunctionType(const EventDefinition& _event):
+FunctionType::FunctionType(EventDefinition const& _event):
m_location(Location::Event), m_isConstant(true), m_declaration(&_event)
{
TypePointers params;
@@ -1753,6 +1748,35 @@ FunctionType::FunctionType(const EventDefinition& _event):
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
{
if (!bound())
@@ -1871,7 +1895,12 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
if (variable && retParamTypes.empty())
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
@@ -1889,20 +1918,25 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
{
MemberList::MemberMap members;
if (m_location != Location::BareDelegateCall && m_location != Location::DelegateCall)
- members.push_back(MemberList::Member(
- "value",
- make_shared<FunctionType>(
- parseElementaryTypeVector({"uint"}),
- TypePointers{copyAndSetGasOrValue(false, true)},
- strings(),
- strings(),
- Location::SetValue,
- false,
- nullptr,
- m_gasSet,
- m_valueSet
- )
- ));
+ {
+ if (m_isPayable)
+ members.push_back(MemberList::Member(
+ "value",
+ make_shared<FunctionType>(
+ parseElementaryTypeVector({"uint"}),
+ TypePointers{copyAndSetGasOrValue(false, true)},
+ strings(),
+ strings(),
+ Location::SetValue,
+ false,
+ nullptr,
+ false,
+ false,
+ m_gasSet,
+ m_valueSet
+ )
+ ));
+ }
if (m_location != Location::Creation)
members.push_back(MemberList::Member(
"gas",
@@ -1914,6 +1948,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
Location::SetGas,
false,
nullptr,
+ false,
+ false,
m_gasSet,
m_valueSet
)
@@ -2019,6 +2055,8 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
m_location,
m_arbitraryParameters,
m_declaration,
+ m_isConstant,
+ m_isPayable,
m_gasSet || _setGas,
m_valueSet || _setValue,
m_bound
@@ -2064,6 +2102,8 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound)
location,
m_arbitraryParameters,
m_declaration,
+ m_isConstant,
+ m_isPayable,
m_gasSet,
m_valueSet,
_bound
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 1282e5d8..9173f39a 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -640,9 +640,8 @@ public:
bool isSuper() const { return m_super; }
ContractDefinition const& contractDefinition() const { return m_contract; }
- /// Returns the function type of the constructor. Note that the location part of the function type
- /// is not used, as this type cannot be the type of a variable or expression.
- FunctionTypePointer const& constructorType() const;
+ /// Returns the function type of the constructor modified to return an object of the contract's type.
+ FunctionTypePointer const& newExpressionType() const;
/// @returns the identifier of the function with the given name or Invalid256 if such a name does
/// not exist.
@@ -820,21 +819,32 @@ public:
explicit FunctionType(VariableDeclaration const& _varDecl);
/// Creates the function type of an event.
explicit FunctionType(EventDefinition const& _event);
+ /// Function type constructor to be used for a plain type (not derived from a declaration).
FunctionType(
strings const& _parameterTypes,
strings const& _returnParameterTypes,
Location _location = Location::Internal,
- bool _arbitraryParameters = false
+ bool _arbitraryParameters = false,
+ bool _constant = false,
+ bool _payable = false
): FunctionType(
parseElementaryTypeVector(_parameterTypes),
parseElementaryTypeVector(_returnParameterTypes),
strings(),
strings(),
_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(
TypePointers const& _parameterTypes,
TypePointers const& _returnParameterTypes,
@@ -843,6 +853,8 @@ public:
Location _location = Location::Internal,
bool _arbitraryParameters = false,
Declaration const* _declaration = nullptr,
+ bool _isConstant = false,
+ bool _isPayable = false,
bool _gasSet = false,
bool _valueSet = false,
bool _bound = false
@@ -856,6 +868,8 @@ public:
m_gasSet(_gasSet),
m_valueSet(_valueSet),
m_bound(_bound),
+ m_isConstant(_isConstant),
+ m_isPayable(_isPayable),
m_declaration(_declaration)
{}
@@ -905,6 +919,7 @@ public:
}
bool hasDeclaration() const { return !!m_declaration; }
bool isConstant() const { return m_isConstant; }
+ bool isPayable() const { return m_isPayable; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> documentation() const;
@@ -942,6 +957,7 @@ private:
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
bool const m_bound = false; ///< true iff the function is called as arg1.fun(arg2, ..., argn)
bool m_isConstant = false;
+ bool m_isPayable = false;
Declaration const* m_declaration = nullptr;
};
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 9d77ccdc..33571bc0 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -242,6 +242,12 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
m_context << notFound;
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();
fallback->accept(*this);
m_context << returnTag;
@@ -255,7 +261,15 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
FunctionTypePointer const& functionType = it.second;
solAssert(functionType->hasDeclaration(), "");
CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration());
+
m_context << callDataUnpackerEntryPoints.at(it.first);
+ if (!functionType->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();
m_context << CompilerUtils::dataStartOffset;
appendCalldataUnpacker(functionType->parameterTypes());
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 4a81e27d..1cc4a50d 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -583,6 +583,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
Location::Bare,
false,
nullptr,
+ false,
+ false,
true,
true
),
@@ -1524,11 +1526,13 @@ void ExpressionCompiler::appendExternalFunctionCall(
m_context << u256(0);
m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
+ bool existenceChecked = false;
// Check the the target contract exists (has code) for non-low-level calls.
if (funKind == FunctionKind::External || funKind == FunctionKind::CallCode || funKind == FunctionKind::DelegateCall)
{
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
m_context.appendConditionalJumpTo(m_context.errorTag());
+ existenceChecked = true;
}
if (_functionType.gasSet())
@@ -1540,7 +1544,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
u256 gasNeededByCaller = eth::GasCosts::callGas + 10;
if (_functionType.valueSet())
gasNeededByCaller += eth::GasCosts::callValueTransferGas;
- if (!isCallCode && !isDelegateCall)
+ if (!isCallCode && !isDelegateCall && !existenceChecked)
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
m_context <<
gasNeededByCaller <<
diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp
index d39f8285..6e3ae78f 100644
--- a/libsolidity/interface/InterfaceHandler.cpp
+++ b/libsolidity/interface/InterfaceHandler.cpp
@@ -52,6 +52,7 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
method["type"] = "function";
method["name"] = it.second->declaration().name();
method["constant"] = it.second->isConstant();
+ method["payable"] = it.second->isPayable();
method["inputs"] = populateParameters(
externalFunctionType->parameterNames(),
externalFunctionType->parameterTypeNames(_contractDef.isLibrary())
@@ -81,6 +82,7 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
Json::Value method;
method["type"] = "fallback";
method["constant"] = externalFunctionType->isConstant();
+ method["payable"] = externalFunctionType->isPayable();
abi.append(method);
}
for (auto const& it: _contractDef.interfaceEvents())
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 1bffa5d7..0e99d1e7 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -305,6 +305,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
options.allowLocationSpecifier = true;
ASTPointer<ParameterList> parameters(parseParameterList(options));
bool isDeclaredConst = false;
+ bool isPayable = false;
Declaration::Visibility visibility(Declaration::Visibility::Default);
vector<ASTPointer<ModifierInvocation>> modifiers;
while (true)
@@ -315,6 +316,11 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
isDeclaredConst = true;
m_scanner->next();
}
+ else if (m_scanner->currentToken() == Token::Payable)
+ {
+ isPayable = true;
+ m_scanner->next();
+ }
else if (token == Token::Identifier)
modifiers.push_back(parseModifierInvocation());
else if (Token::isVisibilitySpecifier(token))
@@ -354,6 +360,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
isDeclaredConst,
modifiers,
returnParameters,
+ isPayable,
block
);
}
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
index 15d4860f..cc85b610 100644
--- a/libsolidity/parsing/Token.h
+++ b/libsolidity/parsing/Token.h
@@ -166,6 +166,7 @@ namespace solidity
K(Memory, "memory", 0) \
K(Modifier, "modifier", 0) \
K(New, "new", 0) \
+ K(Payable, "payable", 0) \
K(Public, "public", 0) \
K(Pragma, "pragma", 0) \
K(Private, "private", 0) \
@@ -229,7 +230,6 @@ namespace solidity
K(Let, "let", 0) \
K(Match, "match", 0) \
K(Of, "of", 0) \
- K(Payable, "payable", 0) \
K(Relocatable, "relocatable", 0) \
K(Static, "static", 0) \
K(Switch, "switch", 0) \
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index fbef56f0..f0a34632 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -778,37 +778,43 @@ void CommandLineInterface::actOnInput()
bool CommandLineInterface::link()
{
+ // Map from how the libraries will be named inside the bytecode to their addresses.
+ map<string, h160> librariesReplacements;
+ int const placeholderSize = 40; // 20 bytes or 40 hex characters
+ for (auto const& library: m_libraries)
+ {
+ string const& name = library.first;
+ // Library placeholders are 40 hex digits (20 bytes) that start and end with '__'.
+ // This leaves 36 characters for the library name, while too short library names are
+ // padded on the right with '_' and too long names are truncated.
+ string replacement = "__";
+ for (size_t i = 0; i < placeholderSize - 4; ++i)
+ replacement.push_back(i < name.size() ? name[i] : '_');
+ replacement += "__";
+ librariesReplacements[replacement] = library.second;
+ }
for (auto& src: m_sourceCodes)
{
auto end = src.second.end();
for (auto it = src.second.begin(); it != end;)
{
while (it != end && *it != '_') ++it;
- auto insertStart = it;
- while (it != end && *it == '_') ++it;
- auto nameStart = it;
- while (it != end && *it != '_') ++it;
- auto nameEnd = it;
- while (it != end && *it == '_') ++it;
- auto insertEnd = it;
-
- if (insertStart == end)
- break;
-
- if (insertEnd - insertStart != 40)
+ if (it == end) break;
+ if (end - it < placeholderSize)
{
- cerr << "Error in binary object file " << src.first << " at position " << (insertStart - src.second.begin()) << endl;
+ cerr << "Error in binary object file " << src.first << " at position " << (end - src.second.begin()) << endl;
return false;
}
- string name(nameStart, nameEnd);
- if (m_libraries.count(name))
+ string name(it, it + placeholderSize);
+ if (librariesReplacements.count(name))
{
- string hexStr(toHex(m_libraries.at(name).asBytes()));
- copy(hexStr.begin(), hexStr.end(), insertStart);
+ string hexStr(toHex(librariesReplacements.at(name).asBytes()));
+ copy(hexStr.begin(), hexStr.end(), it);
}
else
cerr << "Reference \"" << name << "\" in file \"" << src.first << "\" still unresolved." << endl;
+ it += placeholderSize;
}
}
return true;
diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp
index 9161f2ce..05da3490 100644
--- a/test/contracts/AuctionRegistrar.cpp
+++ b/test/contracts/AuctionRegistrar.cpp
@@ -115,11 +115,6 @@ contract GlobalRegistrar is Registrar, AuctionSystem {
// TODO: Populate with hall-of-fame.
}
- function() {
- // prevent people from just sending funds to the registrar
- throw;
- }
-
function onAuctionEnd(string _name) internal {
var auction = m_auctions[_name];
var record = m_toRecord[_name];
@@ -136,7 +131,7 @@ contract GlobalRegistrar is Registrar, AuctionSystem {
}
}
- function reserve(string _name) external {
+ function reserve(string _name) external payable {
if (bytes(_name).length == 0)
throw;
bool needAuction = requiresAuction(_name);
diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp
index c37873cf..af8ee595 100644
--- a/test/contracts/FixedFeeRegistrar.cpp
+++ b/test/contracts/FixedFeeRegistrar.cpp
@@ -73,7 +73,7 @@ contract FixedFeeRegistrar is Registrar {
modifier onlyrecordowner(string _name) { if (m_record(_name).owner == msg.sender) _; }
- function reserve(string _name) {
+ function reserve(string _name) payable {
Record rec = m_record(_name);
if (rec.owner == 0 && msg.value >= c_fee) {
rec.owner = msg.sender;
diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp
index d3e1b8d7..b4f29a87 100644
--- a/test/contracts/Wallet.cpp
+++ b/test/contracts/Wallet.cpp
@@ -378,7 +378,7 @@ contract Wallet is multisig, multiowned, daylimit {
}
// gets called when no other function matches
- function() {
+ function() payable {
// just being sent some cash?
if (msg.value > 0)
Deposit(msg.sender, msg.value);
diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp
index 882496d9..a0fc5dd7 100644
--- a/test/libsolidity/ASTJSON.cpp
+++ b/test/libsolidity/ASTJSON.cpp
@@ -195,6 +195,22 @@ BOOST_AUTO_TEST_CASE(placeholder_statement)
BOOST_CHECK_EQUAL(placeholder["src"], "26:1:1");
}
+BOOST_AUTO_TEST_CASE(non_utf8)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C { function f() { var x = hex\"ff\"; } }");
+ c.parse();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
+ Json::Value literal = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][1];
+ BOOST_CHECK_EQUAL(literal["name"], "Literal");
+ BOOST_CHECK_EQUAL(literal["attributes"]["hexvalue"], "ff");
+ BOOST_CHECK_EQUAL(literal["attributes"]["token"], Json::nullValue);
+ BOOST_CHECK_EQUAL(literal["attributes"]["value"], Json::nullValue);
+ BOOST_CHECK(literal["attributes"]["type"].asString().find("invalid") != string::npos);
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp
index 81332f4f..8d7a3540 100644
--- a/test/libsolidity/Assembly.cpp
+++ b/test/libsolidity/Assembly.cpp
@@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE(location_test)
AssemblyItems items = compileContract(sourceCode);
vector<SourceLocation> locations =
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>(4, SourceLocation(58, 67, n)) +
vector<SourceLocation>(3, SourceLocation(20, 72, n));
diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp
index ec621104..185ba3bf 100644
--- a/test/libsolidity/SolidityABIJSON.cpp
+++ b/test/libsolidity/SolidityABIJSON.cpp
@@ -68,6 +68,7 @@ BOOST_AUTO_TEST_CASE(basic_test)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -107,6 +108,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -124,6 +126,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods)
{
"name": "g",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -153,6 +156,7 @@ BOOST_AUTO_TEST_CASE(multiple_params)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -188,6 +192,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
{
"name": "c",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -205,6 +210,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -235,6 +241,7 @@ BOOST_AUTO_TEST_CASE(const_function)
{
"name": "foo",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -256,6 +263,7 @@ BOOST_AUTO_TEST_CASE(const_function)
{
"name": "boo",
"constant": true,
+ "payable" : false,
"type": "function",
"inputs": [{
"name": "a",
@@ -284,6 +292,7 @@ BOOST_AUTO_TEST_CASE(events)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -361,6 +370,7 @@ BOOST_AUTO_TEST_CASE(inherited)
{
"name": "baseFunction",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs":
[{
@@ -376,6 +386,7 @@ BOOST_AUTO_TEST_CASE(inherited)
{
"name": "derivedFunction",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs":
[{
@@ -429,6 +440,7 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -469,6 +481,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter)
{
"name": "f",
"constant": false,
+ "payable" : false,
"type": "function",
"inputs": [
{
@@ -536,6 +549,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi)
[
{
"constant" : false,
+ "payable" : false,
"inputs" : [],
"name" : "ret",
"outputs" : [
@@ -573,6 +587,7 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays)
[
{
"constant" : false,
+ "payable" : false,
"name": "f",
"inputs": [
{ "name": "a", "type": "string" },
@@ -600,6 +615,7 @@ BOOST_AUTO_TEST_CASE(library_function)
[
{
"constant" : false,
+ "payable" : false,
"name": "f",
"inputs": [
{ "name": "b", "type": "test.StructType storage" },
@@ -629,6 +645,59 @@ BOOST_AUTO_TEST_CASE(include_fallback_function)
[
{
"constant" : false,
+ "payable": false,
+ "type" : "fallback"
+ }
+ ]
+ )";
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(payable_function)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() {}
+ function g() payable {}
+ }
+ )";
+
+ char const* interface = R"(
+ [
+ {
+ "constant" : false,
+ "payable": false,
+ "inputs": [],
+ "name": "f",
+ "outputs": [],
+ "type" : "function"
+ },
+ {
+ "constant" : false,
+ "payable": true,
+ "inputs": [],
+ "name": "g",
+ "outputs": [],
+ "type" : "function"
+ }
+ ]
+ )";
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(payable_fallback_unction)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function () payable {}
+ }
+ )";
+
+ char const* interface = R"(
+ [
+ {
+ "constant" : false,
+ "payable": true,
"type" : "fallback"
}
]
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index f3fdef15..1ecd7a2c 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -1320,7 +1320,7 @@ BOOST_AUTO_TEST_CASE(balance)
BOOST_AUTO_TEST_CASE(blockchain)
{
char const* sourceCode = "contract test {\n"
- " function someInfo() returns (uint256 value, address coinbase, uint256 blockNumber) {\n"
+ " function someInfo() payable returns (uint256 value, address coinbase, uint256 blockNumber) {\n"
" value = msg.value;\n"
" coinbase = block.coinbase;\n"
" blockNumber = block.number;\n"
@@ -1342,7 +1342,7 @@ BOOST_AUTO_TEST_CASE(msg_sig)
}
)";
compileAndRun(sourceCode);
- BOOST_CHECK(callContractFunctionWithValue("foo(uint256)", 13) == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes())));
+ BOOST_CHECK(callContractFunction("foo(uint256)") == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes())));
}
BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same)
@@ -1358,7 +1358,7 @@ BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same)
}
)";
compileAndRun(sourceCode);
- BOOST_CHECK(callContractFunctionWithValue("foo(uint256)", 13) == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes())));
+ BOOST_CHECK(callContractFunction("foo(uint256)") == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes())));
}
BOOST_AUTO_TEST_CASE(now)
@@ -1526,8 +1526,10 @@ BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_greater_size)
}
})";
compileAndRun(sourceCode);
- BOOST_CHECK(callContractFunction("UintToBytes(uint16)", u256("0x6162")) ==
- encodeArgs(string("\0\0\0\0\0\0ab", 8)));
+ BOOST_CHECK(
+ callContractFunction("UintToBytes(uint16)", u256("0x6162")) ==
+ encodeArgs(string("\0\0\0\0\0\0ab", 8))
+ );
}
BOOST_AUTO_TEST_CASE(send_ether)
@@ -2053,11 +2055,11 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses)
{
char const* sourceCode = R"(
contract helper {
- function() { } // can receive ether
+ function() payable { } // can receive ether
}
contract test {
helper h;
- function test() { h = new helper(); h.send(5); }
+ function test() payable { h = new helper(); h.send(5); }
function getBalance() returns (uint256 myBalance, uint256 helperBalance) {
myBalance = this.balance;
helperBalance = h.balance;
@@ -2065,6 +2067,7 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses)
}
)";
compileAndRun(sourceCode, 20);
+ BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 20 - 5);
BOOST_REQUIRE(callContractFunction("getBalance()") == encodeArgs(u256(20 - 5), u256(5)));
}
@@ -2073,7 +2076,7 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic)
char const* sourceCode = R"(
contract helper {
bool flag;
- function getBalance() returns (uint256 myBalance) {
+ function getBalance() payable returns (uint256 myBalance) {
return this.balance;
}
function setFlag() { flag = true; }
@@ -2081,8 +2084,8 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic)
}
contract test {
helper h;
- function test() { h = new helper(); }
- function sendAmount(uint amount) returns (uint256 bal) {
+ function test() payable { h = new helper(); }
+ function sendAmount(uint amount) payable returns (uint256 bal) {
return h.getBalance.value(amount)();
}
function outOfGas() returns (bool ret) {
@@ -2098,8 +2101,8 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic)
compileAndRun(sourceCode, 20);
BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5));
// call to helper should not succeed but amount should be transferred anyway
- BOOST_REQUIRE(callContractFunction("outOfGas()", 5) == bytes());
- BOOST_REQUIRE(callContractFunction("checkState()", 5) == encodeArgs(false, 20 - 5));
+ BOOST_REQUIRE(callContractFunction("outOfGas()") == bytes());
+ BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5));
}
BOOST_AUTO_TEST_CASE(gas_for_builtin)
@@ -2121,14 +2124,14 @@ BOOST_AUTO_TEST_CASE(value_complex)
{
char const* sourceCode = R"(
contract helper {
- function getBalance() returns (uint256 myBalance) {
+ function getBalance() payable returns (uint256 myBalance) {
return this.balance;
}
}
contract test {
helper h;
- function test() { h = new helper(); }
- function sendAmount(uint amount) returns (uint256 bal) {
+ function test() payable { h = new helper(); }
+ function sendAmount(uint amount) payable returns (uint256 bal) {
var x1 = h.getBalance.value(amount);
uint someStackElement = 20;
var x2 = x1.gas(1000);
@@ -2144,13 +2147,13 @@ BOOST_AUTO_TEST_CASE(value_insane)
{
char const* sourceCode = R"(
contract helper {
- function getBalance() returns (uint256 myBalance) {
+ function getBalance() payable returns (uint256 myBalance) {
return this.balance;
}
}
contract test {
helper h;
- function test() { h = new helper(); }
+ function test() payable { h = new helper(); }
function sendAmount(uint amount) returns (uint256 bal) {
var x1 = h.getBalance.value;
var x2 = x1(amount).gas;
@@ -2169,7 +2172,7 @@ BOOST_AUTO_TEST_CASE(value_for_constructor)
contract Helper {
bytes3 name;
bool flag;
- function Helper(bytes3 x, bool f) {
+ function Helper(bytes3 x, bool f) payable {
name = x;
flag = f;
}
@@ -2178,7 +2181,7 @@ BOOST_AUTO_TEST_CASE(value_for_constructor)
}
contract Main {
Helper h;
- function Main() {
+ function Main() payable {
h = (new Helper).value(10)("abc", true);
}
function getFlag() returns (bool ret) { return h.getFlag(); }
@@ -2352,7 +2355,7 @@ BOOST_AUTO_TEST_CASE(function_modifier)
{
char const* sourceCode = R"(
contract C {
- function getOne() nonFree returns (uint r) { return 1; }
+ function getOne() payable nonFree returns (uint r) { return 1; }
modifier nonFree { if (msg.value > 0) _; }
}
)";
@@ -2559,7 +2562,7 @@ BOOST_AUTO_TEST_CASE(event)
char const* sourceCode = R"(
contract ClientReceipt {
event Deposit(address indexed _from, bytes32 indexed _id, uint _value);
- function deposit(bytes32 _id, bool _manually) {
+ function deposit(bytes32 _id, bool _manually) payable {
if (_manually) {
bytes32 s = 0x19dacbf83c5de6658e14cbf7bcae5c15eca2eedecf1c66fbca928e4d351bea0f;
log3(bytes32(msg.value), s, bytes32(msg.sender), _id);
@@ -2624,7 +2627,7 @@ BOOST_AUTO_TEST_CASE(event_anonymous_with_topics)
char const* sourceCode = R"(
contract ClientReceipt {
event Deposit(address indexed _from, bytes32 indexed _id, uint indexed _value, uint indexed _value2, bytes32 data) anonymous;
- function deposit(bytes32 _id, bool _manually) {
+ function deposit(bytes32 _id, bool _manually) payable {
Deposit(msg.sender, _id, msg.value, 2, "abc");
}
}
@@ -2648,7 +2651,7 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data)
char const* sourceCode = R"(
contract ClientReceipt {
event Deposit(address _from, bytes32 _id, uint _value, bool _flag);
- function deposit(bytes32 _id) {
+ function deposit(bytes32 _id) payable {
Deposit(msg.sender, _id, msg.value, true);
}
}
@@ -2874,9 +2877,10 @@ BOOST_AUTO_TEST_CASE(generic_call)
char const* sourceCode = R"**(
contract receiver {
uint public received;
- function receive(uint256 x) { received = x; }
+ function receive(uint256 x) payable { received = x; }
}
contract sender {
+ function sender() payable {}
function doSend(address rec) returns (uint d)
{
bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
@@ -2897,10 +2901,11 @@ BOOST_AUTO_TEST_CASE(generic_callcode)
char const* sourceCode = R"**(
contract receiver {
uint public received;
- function receive(uint256 x) { received = x; }
+ function receive(uint256 x) payable { received = x; }
}
contract sender {
uint public received;
+ function sender() payable { }
function doSend(address rec) returns (uint d)
{
bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
@@ -2930,16 +2935,16 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall)
uint public received;
address public sender;
uint public value;
- function receive(uint256 x) { received = x; sender = msg.sender; value = msg.value; }
+ function receive(uint256 x) payable { received = x; sender = msg.sender; value = msg.value; }
}
contract sender {
uint public received;
address public sender;
uint public value;
- function doSend(address rec)
+ function doSend(address rec) payable
{
bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
- rec.delegatecall(signature, 23);
+ if (rec.delegatecall(signature, 23)) {}
}
}
)**";
@@ -5958,7 +5963,7 @@ BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library)
function f(address x) returns (bool) {
return x.send(1);
}
- function () {}
+ function () payable {}
}
)";
compileAndRun(sourceCode, 0, "lib");
@@ -6876,7 +6881,7 @@ BOOST_AUTO_TEST_CASE(skip_dynamic_types_for_structs)
BOOST_AUTO_TEST_CASE(failed_create)
{
char const* sourceCode = R"(
- contract D { }
+ contract D { function D() payable {} }
contract C {
uint public x;
function f(uint amount) returns (address) {
@@ -7026,7 +7031,7 @@ BOOST_AUTO_TEST_CASE(mutex)
else
return fund.withdrawUnprotected(10);
}
- function() {
+ function() payable {
callDepth++;
if (callDepth < 4)
attackInternal();
@@ -7087,6 +7092,81 @@ BOOST_AUTO_TEST_CASE(calling_nonexisting_contract_throws)
BOOST_CHECK(callContractFunction("h()") == encodeArgs(u256(7)));
}
+BOOST_AUTO_TEST_CASE(payable_constructor)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function C() payable { }
+ }
+ )";
+ compileAndRun(sourceCode, 27, "C");
+}
+
+BOOST_AUTO_TEST_CASE(payable_function)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint public a;
+ function f() payable returns (uint) {
+ return msg.value;
+ }
+ function() payable {
+ a = msg.value + 1;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs(u256(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)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint public a;
+ function f() returns (uint) {
+ return msg.value;
+ }
+ function() {
+ a = msg.value + 1;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ 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_EQUAL(balanceAt(m_contractAddress), 0);
+ BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunctionWithValue("a()", 27) == encodeArgs());
+ BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
+}
+
+BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier)
+{
+ char const* sourceCode = R"(
+ contract C {
+ modifier tryCircumvent {
+ if (false) _; // avoid the function, we should still not accept ether
+ }
+ function f() tryCircumvent returns (uint) {
+ return msg.value;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs());
+ BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
+}
+
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 8d4890d1..ab0f9c7b 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -3852,7 +3852,113 @@ BOOST_AUTO_TEST_CASE(modifier_without_underscore)
modifier m() {}
}
)";
- BOOST_CHECK(expectError(text, true) == Error::Type::SyntaxError);
+ BOOST_CHECK(expectError(text) == Error::Type::SyntaxError);
+}
+
+BOOST_AUTO_TEST_CASE(payable_in_library)
+{
+ char const* text = R"(
+ library test {
+ function f() payable {}
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(payable_external)
+{
+ char const* text = R"(
+ contract test {
+ function f() payable external {}
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(payable_internal)
+{
+ char const* text = R"(
+ contract test {
+ function f() payable internal {}
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(payable_private)
+{
+ char const* text = R"(
+ contract test {
+ function f() payable private {}
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(illegal_override_payable)
+{
+ char const* text = R"(
+ contract B { function f() payable {} }
+ contract C is B { function f() {} }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable)
+{
+ char const* text = R"(
+ contract B { function f() {} }
+ contract C is B { function f() payable {} }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(payable_constant_conflict)
+{
+ char const* text = R"(
+ contract C { function f() payable constant {} }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(calling_payable)
+{
+ char const* text = R"(
+ contract receiver { function pay() payable {} }
+ contract test {
+ funciton f() { (new receiver()).pay.value(10)(); }
+ recevier r = new receiver();
+ function g() { r.pay.value(10)(); }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(calling_nonpayable)
+{
+ char const* text = R"(
+ contract receiver { function nopay() {} }
+ contract test {
+ 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_AUTO_TEST_CASE(warn_nonpresent_pragma)
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index 0c0e343b..a81a9828 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -1231,6 +1231,16 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_conversion_leading_zeroes_check)
BOOST_CHECK(!successParse(text));
}
+BOOST_AUTO_TEST_CASE(payable_accessor)
+{
+ char const* text = R"(
+ contract test {
+ uint payable x;
+ }
+ )";
+ BOOST_CHECK(!successParse(text));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}