diff options
40 files changed, 1039 insertions, 241 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 537a9521..24bea3b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.4.18") +set(PROJECT_VERSION "0.4.19") project(solidity VERSION ${PROJECT_VERSION}) option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) diff --git a/Changelog.md b/Changelog.md index acd9ee5e..45521f3e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,8 +1,16 @@ -### 0.4.18 (unreleased) +### 0.4.19 (unreleased) + +Features: + * Syntax Checker: Turn the usage of ``callcode`` into an error as experimental 0.5.0 feature. + * Type Checker: More detailed errors for invalid array lengths (such as division by zero). + +Bugfixes: + +### 0.4.18 (2017-10-18) Features: * Code Generator: Always use all available gas for calls as experimental 0.5.0 feature - (previously, some amount was retained in order to work in pre-tangerine whistle + (previously, some amount was retained in order to work in pre-Tangerine-Whistle EVM versions) * Parser: Better error message for unexpected trailing comma in parameter lists. * Standard JSON: Support the ``outputSelection`` field for selective compilation of supplied sources. @@ -15,14 +23,17 @@ Features: Bugfixes: * Code Generator: Allocate one byte per memory byte array element instead of 32. + * Code Generator: Do not accept data with less than four bytes (truncated function + signature) for regular function calls - fallback function is invoked instead. * Optimizer: Remove unused stack computation results. * Parser: Fix source location of VariableDeclarationStatement. + * Type Checker: Allow ``gas`` in view functions. + * Type Checker: Do not mark event parameters as shadowing state variables. + * Type Checker: Prevent duplicate event declarations. * Type Checker: Properly check array length and don't rely on an assertion in code generation. * Type Checker: Properly support overwriting members inherited from ``address`` in a contract (such as ``balance``, ``transfer``, etc.) - * Type Checker: Prevent duplicate event declarations. - * Type Checker: Do not mark event parameters as shadowing state variables. - * Type Checker: Allow ``gas`` in view functions. + * Type Checker: Validate each number literal in tuple expressions even if they are not assigned from. ### 0.4.17 (2017-09-21) diff --git a/docs/bugs.json b/docs/bugs.json index ac322a48..c642793a 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,5 +1,12 @@ [ { + "name": "ZeroFunctionSelector", + "summary": "It is possible to craft the name of a function such that it is executed instead of the fallback function in very specific circumstances.", + "description": "If a function has a selector consisting only of zeros, is payable and part of a contract that does not have a fallback function and at most five external functions in total, this function is called instead of the fallback function if Ether is sent to the contract without data.", + "fixed": "0.4.18", + "severity": "very low" + }, + { "name": "DelegateCallReturnValue", "summary": "The low-level .delegatecall() does not return the execution outcome, but converts the value returned by the functioned called to a boolean instead.", "description": "The return value of the low-level .delegatecall() function is taken from a position in memory, where the call data or the return data resides. This value is interpreted as a boolean and put onto the stack. This means if the called function returns at least 32 zero bytes, .delegatecall() returns false even if the call was successuful.", diff --git a/docs/bugs.rst b/docs/bugs.rst index 55771a35..7629830d 100644 --- a/docs/bugs.rst +++ b/docs/bugs.rst @@ -48,7 +48,7 @@ fixed publish The date at which the bug became known publicly, optional severity - Severity of the bug: low, medium, high. Takes into account + Severity of the bug: very low, low, medium, high. Takes into account discoverability in contract tests, likelihood of occurrence and potential damage by exploits. conditions diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index c3686ebf..cca45428 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1,6 +1,7 @@ { "0.1.0": { "bugs": [ + "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction", @@ -17,6 +18,7 @@ }, "0.1.1": { "bugs": [ + "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction", @@ -33,6 +35,7 @@ }, "0.1.2": { "bugs": [ + "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction", @@ -49,6 +52,7 @@ }, "0.1.3": { "bugs": [ + "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction", @@ -65,6 +69,7 @@ }, "0.1.4": { "bugs": [ + "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction", @@ -81,6 +86,7 @@ }, "0.1.5": { "bugs": [ + "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction", @@ -97,6 +103,7 @@ }, "0.1.6": { "bugs": [ + "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction", @@ -114,6 +121,7 @@ }, "0.1.7": { "bugs": [ + "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction", @@ -131,6 +139,7 @@ }, "0.2.0": { "bugs": [ + "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction", @@ -148,6 +157,7 @@ }, "0.2.1": { "bugs": [ + "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction", @@ -165,6 +175,7 @@ }, "0.2.2": { "bugs": [ + "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction", @@ -182,6 +193,7 @@ }, "0.3.0": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -199,6 +211,7 @@ }, "0.3.1": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -215,6 +228,7 @@ }, "0.3.2": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -231,6 +245,7 @@ }, "0.3.3": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -246,6 +261,7 @@ }, "0.3.4": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -261,6 +277,7 @@ }, "0.3.5": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -276,6 +293,7 @@ }, "0.3.6": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -289,6 +307,7 @@ }, "0.4.0": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -302,6 +321,7 @@ }, "0.4.1": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -315,6 +335,7 @@ }, "0.4.10": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -324,6 +345,7 @@ }, "0.4.11": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral" @@ -332,6 +354,7 @@ }, "0.4.12": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput" ], @@ -339,6 +362,7 @@ }, "0.4.13": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput" ], @@ -346,24 +370,36 @@ }, "0.4.14": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue" ], "released": "2017-07-31" }, "0.4.15": { - "bugs": [], + "bugs": [ + "ZeroFunctionSelector" + ], "released": "2017-08-08" }, "0.4.16": { - "bugs": [], + "bugs": [ + "ZeroFunctionSelector" + ], "released": "2017-08-24" }, "0.4.17": { - "bugs": [], + "bugs": [ + "ZeroFunctionSelector" + ], "released": "2017-09-21" }, + "0.4.18": { + "bugs": [], + "released": "2017-10-18" + }, "0.4.2": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -376,6 +412,7 @@ }, "0.4.3": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -387,6 +424,7 @@ }, "0.4.4": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -397,6 +435,7 @@ }, "0.4.5": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -408,6 +447,7 @@ }, "0.4.6": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -418,6 +458,7 @@ }, "0.4.7": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -427,6 +468,7 @@ }, "0.4.8": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -436,6 +478,7 @@ }, "0.4.9": { "bugs": [ + "ZeroFunctionSelector", "DelegateCallReturnValue", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", diff --git a/docs/index.rst b/docs/index.rst index cb093bd6..351f8ad7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -61,6 +61,9 @@ Available Solidity Integrations * `Solium <https://github.com/duaraghav8/Solium/>`_ A commandline linter for Solidity which strictly follows the rules prescribed by the `Solidity Style Guide <http://solidity.readthedocs.io/en/latest/style-guide.html>`_. + +* `Solhint <https://github.com/protofire/solhint>`_ + Solidity linter that provides security, style guide and best practice rules for smart contract validation. * `Visual Studio Code extension <http://juan.blanco.ws/solidity-contracts-in-visual-studio-code/>`_ Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler. diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 71607745..b660cf02 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -230,6 +230,8 @@ Or, on Windows: Command-Line Build ------------------ +**Be sure to install External Dependencies (see above) before build.** + Solidity project uses CMake to configure the build. Building Solidity is quite similar on Linux, macOS and other Unices: diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 139c8a42..59ab7962 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -221,8 +221,7 @@ activate themselves. // absolute unix timestamps (seconds since 1970-01-01) // or time periods in seconds. address public beneficiary; - uint public auctionStart; - uint public biddingTime; + uint public auctionEnd; // Current state of the auction. address public highestBidder; @@ -251,8 +250,7 @@ activate themselves. address _beneficiary ) { beneficiary = _beneficiary; - auctionStart = now; - biddingTime = _biddingTime; + auctionEnd = now + _biddingTime; } /// Bid on the auction with the value sent @@ -268,7 +266,7 @@ activate themselves. // Revert the call if the bidding // period is over. - require(now <= (auctionStart + biddingTime)); + require(now <= auctionEnd); // If the bid is not higher, send the // money back. @@ -322,7 +320,7 @@ activate themselves. // external contracts. // 1. Conditions - require(now >= (auctionStart + biddingTime)); // auction did not yet end + require(now >= auctionEnd); // auction did not yet end require(!ended); // this function has already been called // 2. Effects @@ -382,7 +380,6 @@ high or low invalid bids. } address public beneficiary; - uint public auctionStart; uint public biddingEnd; uint public revealEnd; bool public ended; @@ -410,7 +407,6 @@ high or low invalid bids. address _beneficiary ) { beneficiary = _beneficiary; - auctionStart = now; biddingEnd = now + _biddingTime; revealEnd = biddingEnd + _revealTime; } diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 5d47937b..8c7e08f6 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -39,7 +39,7 @@ namespace { template <typename _T> -inline _T contentsGeneric(std::string const& _file) +inline _T readFile(std::string const& _file) { _T ret; size_t const c_elementSize = sizeof(typename _T::value_type); @@ -61,9 +61,23 @@ inline _T contentsGeneric(std::string const& _file) } -string dev::contentsString(string const& _file) +string dev::readFileAsString(string const& _file) { - return contentsGeneric<string>(_file); + return readFile<string>(_file); +} + +string dev::readStandardInput() +{ + string ret; + while (!cin.eof()) + { + string tmp; + // NOTE: this will read until EOF or NL + getline(cin, tmp); + ret.append(tmp); + ret.append("\n"); + } + return ret; } void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename) diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index d84362cf..33769ec3 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -32,7 +32,10 @@ namespace dev /// Retrieve and returns the contents of the given file as a std::string. /// If the file doesn't exist or isn't readable, returns an empty container / bytes. -std::string contentsString(std::string const& _file); +std::string readFileAsString(std::string const& _file); + +/// Retrieve and returns the contents of standard input (until EOF). +std::string readStandardInput(); /// Write the given binary data into the given file, replacing the file if it pre-exists. /// Throws exception on error. diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index d4ef3888..5c68194b 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -47,6 +47,32 @@ void CodeFragment::finalise(CompilerState const& _cs) } } +namespace +{ +/// Returns true iff the instruction is valid in "inline assembly". +bool validAssemblyInstruction(string us) +{ + auto it = c_instructions.find(us); + return !( + it == c_instructions.end() || + solidity::isPushInstruction(it->second) + ); +} + +/// Returns true iff the instruction is valid as a function. +bool validFunctionalInstruction(string us) +{ + auto it = c_instructions.find(us); + return !( + it == c_instructions.end() || + solidity::isPushInstruction(it->second) || + solidity::isDupInstruction(it->second) || + solidity::isSwapInstruction(it->second) || + it->second == solidity::Instruction::JUMPDEST + ); +} +} + CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM): m_readFile(_readFile) { @@ -80,7 +106,7 @@ CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback auto sr = _t.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>(); string s(sr.begin(), sr.end()); string us = boost::algorithm::to_upper_copy(s); - if (_allowASM && c_instructions.count(us)) + if (_allowASM && c_instructions.count(us) && validAssemblyInstruction(us)) m_asm.append(c_instructions.at(us)); else if (_s.defs.count(s)) m_asm.append(_s.defs.at(s).m_asm); @@ -114,22 +140,6 @@ CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback } } -namespace -{ -/// Returns true iff the instruction is valid as a function. -bool validFunctionalInstruction(string us) -{ - auto it = c_instructions.find(us); - return !( - it == c_instructions.end() || - solidity::isPushInstruction(it->second) || - solidity::isDupInstruction(it->second) || - solidity::isSwapInstruction(it->second) || - it->second == solidity::Instruction::JUMPDEST - ); -} -} - void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) { if (_t.tag() == 0 && _t.empty()) diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 6636ad97..bc3b7cf1 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -28,6 +28,7 @@ using namespace std; using namespace dev; using namespace dev::solidity; +/// FIXME: this is pretty much a copy of TypeChecker::endVisit(BinaryOperation) void ConstantEvaluator::endVisit(UnaryOperation const& _operation) { TypePointer const& subType = _operation.subExpression().annotation().type; @@ -37,6 +38,7 @@ void ConstantEvaluator::endVisit(UnaryOperation const& _operation) _operation.annotation().type = t; } +/// FIXME: this is pretty much a copy of TypeChecker::endVisit(BinaryOperation) void ConstantEvaluator::endVisit(BinaryOperation const& _operation) { TypePointer const& leftType = _operation.leftExpression().annotation().type; @@ -46,9 +48,24 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) if (!dynamic_cast<RationalNumberType const*>(rightType.get())) m_errorReporter.fatalTypeError(_operation.rightExpression().location(), "Invalid constant expression."); TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType); - if (Token::isCompareOp(_operation.getOperator())) - commonType = make_shared<BoolType>(); - _operation.annotation().type = commonType; + if (!commonType) + { + m_errorReporter.typeError( + _operation.location(), + "Operator " + + string(Token::toString(_operation.getOperator())) + + " not compatible with types " + + leftType->toString() + + " and " + + rightType->toString() + ); + commonType = leftType; + } + _operation.annotation().commonType = commonType; + _operation.annotation().type = + Token::isCompareOp(_operation.getOperator()) ? + make_shared<BoolType>() : + commonType; } void ConstantEvaluator::endVisit(Literal const& _literal) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index ffa538b6..bd8ee597 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -150,10 +150,18 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) if (_memberAccess.memberName() == "callcode") if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get())) if (type->kind() == FunctionType::Kind::BareCallCode) - m_errorReporter.warning( - _memberAccess.location(), - "\"callcode\" has been deprecated in favour of \"delegatecall\"." - ); + { + if (m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + m_errorReporter.typeError( + _memberAccess.location(), + "\"callcode\" has been deprecated in favour of \"delegatecall\"." + ); + else + m_errorReporter.warning( + _memberAccess.location(), + "\"callcode\" has been deprecated in favour of \"delegatecall\"." + ); + } if (m_constructor && m_currentContract) if (ContractType const* type = dynamic_cast<ContractType const*>(_memberAccess.expression().annotation().type.get())) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c2fba565..746e762e 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1293,6 +1293,12 @@ bool TypeChecker::visit(TupleExpression const& _tuple) { components[i]->accept(*this); types.push_back(type(*components[i])); + + // Note: code generation will visit each of the expression even if they are not assigned from. + if (types[i]->category() == Type::Category::RationalNumber && components.size() > 1) + if (!dynamic_cast<RationalNumberType const&>(*types[i]).mobileType()) + m_errorReporter.fatalTypeError(components[i]->location(), "Invalid rational number."); + if (_tuple.isInlineArray()) solAssert(!!types[i], "Inline array cannot have empty components"); if (_tuple.isInlineArray()) diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 15735368..46675e51 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -57,6 +57,7 @@ class UserDefinedTypeName; class FunctionTypeName; class Mapping; class ArrayTypeName; +class InlineAssembly; class Statement; class Block; class PlaceholderStatement; diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 429db532..74565ae4 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -251,13 +251,10 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac FunctionDefinition const* fallback = _contract.fallbackFunction(); eth::AssemblyItem notFound = m_context.newTag(); - // shortcut messages without data if we have many functions in order to be able to receive - // ether with constant gas - if (interfaceFunctions.size() > 5 || fallback) - { - m_context << Instruction::CALLDATASIZE << Instruction::ISZERO; - m_context.appendConditionalJumpTo(notFound); - } + // directly jump to fallback if the data is too short to contain a function selector + // also guards against short data + m_context << u256(4) << Instruction::CALLDATASIZE << Instruction::LT; + m_context.appendConditionalJumpTo(notFound); // retrieve the function signature hash from the calldata if (!interfaceFunctions.empty()) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 7c8c089e..2d2f05ec 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -494,10 +494,10 @@ void SMTChecker::createVariable(VariableDeclaration const& _varDecl, bool _setTo { solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, ""); solAssert(m_nextFreeSequenceCounter.count(&_varDecl) == 0, ""); - solAssert(m_Variables.count(&_varDecl) == 0, ""); + solAssert(m_variables.count(&_varDecl) == 0, ""); m_currentSequenceCounter[&_varDecl] = 0; m_nextFreeSequenceCounter[&_varDecl] = 1; - m_Variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); + m_variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); setValue(_varDecl, _setToZero); } else @@ -566,7 +566,7 @@ smt::Expression SMTChecker::maxValue(IntegerType const& _t) smt::Expression SMTChecker::expr(Expression const& _e) { - if (!m_Expressions.count(&_e)) + if (!m_expressions.count(&_e)) { solAssert(_e.annotation().type, ""); switch (_e.annotation().type->category()) @@ -575,24 +575,24 @@ smt::Expression SMTChecker::expr(Expression const& _e) { if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(_e.annotation().type.get())) solAssert(!rational->isFractional(), ""); - m_Expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); break; } case Type::Category::Integer: - m_Expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); break; case Type::Category::Bool: - m_Expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); + m_expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); break; default: solAssert(false, "Type not implemented."); } } - return m_Expressions.at(&_e); + return m_expressions.at(&_e); } smt::Expression SMTChecker::var(Declaration const& _decl) { - solAssert(m_Variables.count(&_decl), ""); - return m_Variables.at(&_decl); + solAssert(m_variables.count(&_decl), ""); + return m_variables.at(&_decl); } diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index d23fd201..faaac639 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -103,8 +103,8 @@ private: std::shared_ptr<smt::SolverInterface> m_interface; std::map<Declaration const*, int> m_currentSequenceCounter; std::map<Declaration const*, int> m_nextFreeSequenceCounter; - std::map<Expression const*, smt::Expression> m_Expressions; - std::map<Declaration const*, smt::Expression> m_Variables; + std::map<Expression const*, smt::Expression> m_expressions; + std::map<Declaration const*, smt::Expression> m_variables; ErrorReporter& m_errorReporter; FunctionDefinition const* m_currentFunction = nullptr; diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index cbd766fb..c627057a 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -64,6 +64,8 @@ void SMTLib2Interface::pop() Expression SMTLib2Interface::newFunction(string _name, Sort _domain, Sort _codomain) { + solAssert(!m_variables.count(_name), ""); + m_variables[_name] = SMTVariableType::Function; write( "(declare-fun |" + _name + @@ -78,12 +80,16 @@ Expression SMTLib2Interface::newFunction(string _name, Sort _domain, Sort _codom Expression SMTLib2Interface::newInteger(string _name) { + solAssert(!m_variables.count(_name), ""); + m_variables[_name] = SMTVariableType::Integer; write("(declare-const |" + _name + "| Int)"); return SolverInterface::newInteger(move(_name)); } Expression SMTLib2Interface::newBool(string _name) { + solAssert(!m_variables.count(_name), ""); + m_variables[_name] = SMTVariableType::Bool; write("(declare-const |" + _name + "| Bool)"); return SolverInterface::newBool(std::move(_name)); } @@ -145,7 +151,8 @@ string SMTLib2Interface::checkSatAndGetValuesCommand(vector<Expression> const& _ for (size_t i = 0; i < _expressionsToEvaluate.size(); i++) { auto const& e = _expressionsToEvaluate.at(i); - // TODO they don't have to be ints... + solAssert(m_variables.count(e.name), ""); + solAssert(m_variables[e.name] == SMTVariableType::Integer, ""); command += "(declare-const |EVALEXPR_" + to_string(i) + "| Int)\n"; command += "(assert (= |EVALEXPR_" + to_string(i) + "| " + toSExpr(e) + "))\n"; } diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h index 63188acd..e827449f 100644 --- a/libsolidity/formal/SMTLib2Interface.h +++ b/libsolidity/formal/SMTLib2Interface.h @@ -68,6 +68,14 @@ private: ReadCallback::Callback m_queryCallback; std::vector<std::string> m_accumulatedOutput; + + enum class SMTVariableType { + Function, + Integer, + Bool + }; + + std::map<std::string,SMTVariableType> m_variables; }; } diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index 0ceed3a7..6111b2c8 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -73,28 +73,37 @@ void Z3Interface::addAssertion(Expression const& _expr) pair<CheckResult, vector<string>> Z3Interface::check(vector<Expression> const& _expressionsToEvaluate) { CheckResult result; - switch (m_solver.check()) + vector<string> values; + try { - case z3::check_result::sat: - result = CheckResult::SATISFIABLE; - break; - case z3::check_result::unsat: - result = CheckResult::UNSATISFIABLE; - break; - case z3::check_result::unknown: - result = CheckResult::UNKNOWN; - break; - default: - solAssert(false, ""); + switch (m_solver.check()) + { + case z3::check_result::sat: + result = CheckResult::SATISFIABLE; + break; + case z3::check_result::unsat: + result = CheckResult::UNSATISFIABLE; + break; + case z3::check_result::unknown: + result = CheckResult::UNKNOWN; + break; + default: + solAssert(false, ""); + } + + if (result != CheckResult::UNSATISFIABLE) + { + z3::model m = m_solver.get_model(); + for (Expression const& e: _expressionsToEvaluate) + values.push_back(toString(m.eval(toZ3Expr(e)))); + } } - - vector<string> values; - if (result != CheckResult::UNSATISFIABLE) + catch (z3::exception const&) { - z3::model m = m_solver.get_model(); - for (Expression const& e: _expressionsToEvaluate) - values.push_back(toString(m.eval(toZ3Expr(e)))); + result = CheckResult::ERROR; + values.clear(); } + return make_pair(result, values); } @@ -118,8 +127,7 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) {">=", 2}, {"+", 2}, {"-", 2}, - {"*", 2}, - {">=", 2} + {"*", 2} }; string const& n = _expr.name; if (m_functions.count(n)) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index b99fe4ee..5713256a 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -747,22 +747,32 @@ void CompilerStack::compileContract( } } +string const CompilerStack::lastContractName() const +{ + if (m_contracts.empty()) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found.")); + // try to find some user-supplied contract + string contractName; + for (auto const& it: m_sources) + for (ASTPointer<ASTNode> const& node: it.second.ast->nodes()) + if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) + contractName = contract->fullyQualifiedName(); + return contractName; +} + CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const { if (m_contracts.empty()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found.")); - string contractName = _contractName; - if (_contractName.empty()) - // try to find some user-supplied contract - for (auto const& it: m_sources) - for (ASTPointer<ASTNode> const& node: it.second.ast->nodes()) - if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) - contractName = contract->fullyQualifiedName(); - auto it = m_contracts.find(contractName); + + auto it = m_contracts.find(_contractName); + if (it != m_contracts.end()) + return it->second; + // To provide a measure of backward-compatibility, if a contract is not located by its // fully-qualified name, a lookup will be attempted purely on the contract's name to see // if anything will satisfy. - if (it == m_contracts.end() && contractName.find(":") == string::npos) + if (_contractName.find(":") == string::npos) { for (auto const& contractEntry: m_contracts) { @@ -773,12 +783,13 @@ CompilerStack::Contract const& CompilerStack::contract(string const& _contractNa string foundName; getline(ss, source, ':'); getline(ss, foundName, ':'); - if (foundName == contractName) return contractEntry.second; + if (foundName == _contractName) + return contractEntry.second; } - // If we get here, both lookup methods failed. - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found.")); } - return it->second; + + // If we get here, both lookup methods failed. + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract \"" + _contractName + "\" not found.")); } CompilerStack::Source const& CompilerStack::source(string const& _sourceName) const diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index c567ac2c..b377b3aa 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -155,10 +155,10 @@ public: std::map<std::string, unsigned> sourceIndices() const; /// @returns the previously used scanner, useful for counting lines during error reporting. - Scanner const& scanner(std::string const& _sourceName = "") const; + Scanner const& scanner(std::string const& _sourceName) const; /// @returns the parsed source unit with the supplied name. - SourceUnit const& ast(std::string const& _sourceName = "") const; + SourceUnit const& ast(std::string const& _sourceName) const; /// Helper function for logs printing. Do only use in error cases, it's quite expensive. /// line and columns are numbered starting from 1 with following order: @@ -168,48 +168,51 @@ public: /// @returns a list of the contract names in the sources. std::vector<std::string> contractNames() const; + /// @returns the name of the last contract. + std::string const lastContractName() const; + /// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use std::string const filesystemFriendlyName(std::string const& _contractName) const; /// @returns the assembled object for a contract. - eth::LinkerObject const& object(std::string const& _contractName = "") const; + eth::LinkerObject const& object(std::string const& _contractName) const; /// @returns the runtime object for the contract. - eth::LinkerObject const& runtimeObject(std::string const& _contractName = "") const; + eth::LinkerObject const& runtimeObject(std::string const& _contractName) const; /// @returns the bytecode of a contract that uses an already deployed contract via DELEGATECALL. /// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to /// substituted by the actual address. Note that this sequence starts end ends in three X /// characters but can contain anything in between. - eth::LinkerObject const& cloneObject(std::string const& _contractName = "") const; + eth::LinkerObject const& cloneObject(std::string const& _contractName) const; /// @returns normal contract assembly items - eth::AssemblyItems const* assemblyItems(std::string const& _contractName = "") const; + eth::AssemblyItems const* assemblyItems(std::string const& _contractName) const; /// @returns runtime contract assembly items - eth::AssemblyItems const* runtimeAssemblyItems(std::string const& _contractName = "") const; + eth::AssemblyItems const* runtimeAssemblyItems(std::string const& _contractName) const; /// @returns the string that provides a mapping between bytecode and sourcecode or a nullptr /// if the contract does not (yet) have bytecode. - std::string const* sourceMapping(std::string const& _contractName = "") const; + std::string const* sourceMapping(std::string const& _contractName) const; /// @returns the string that provides a mapping between runtime bytecode and sourcecode. /// if the contract does not (yet) have bytecode. - std::string const* runtimeSourceMapping(std::string const& _contractName = "") const; + std::string const* runtimeSourceMapping(std::string const& _contractName) const; /// @return a verbose text representation of the assembly. /// @arg _sourceCodes is the map of input files to source code strings /// Prerequisite: Successful compilation. - std::string assemblyString(std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; + std::string assemblyString(std::string const& _contractName, StringMap _sourceCodes = StringMap()) const; /// @returns a JSON representation of the assembly. /// @arg _sourceCodes is the map of input files to source code strings /// Prerequisite: Successful compilation. - Json::Value assemblyJSON(std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; + Json::Value assemblyJSON(std::string const& _contractName, StringMap _sourceCodes = StringMap()) const; /// @returns a JSON representing the contract ABI. /// Prerequisite: Successful call to parse or compile. - Json::Value const& contractABI(std::string const& _contractName = "") const; + Json::Value const& contractABI(std::string const& _contractName) const; /// @returns a JSON representing the contract's user documentation. /// Prerequisite: Successful call to parse or compile. @@ -276,8 +279,8 @@ private: ); void link(); - Contract const& contract(std::string const& _contractName = "") const; - Source const& source(std::string const& _sourceName = "") const; + Contract const& contract(std::string const& _contractName) const; + Source const& source(std::string const& _sourceName) const; /// @returns the parsed contract with the supplied name. Throws an exception if the contract /// does not exist. diff --git a/lllc/main.cpp b/lllc/main.cpp index 912ce16a..5679bc2b 100644 --- a/lllc/main.cpp +++ b/lllc/main.cpp @@ -35,15 +35,15 @@ using namespace dev::solidity; using namespace dev::eth; static string const VersionString = - string(ETH_PROJECT_VERSION) + - (string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)) + - (string(SOL_VERSION_BUILDINFO).empty() ? "" : "+" + string(SOL_VERSION_BUILDINFO)); + string(ETH_PROJECT_VERSION) + + (string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)) + + (string(SOL_VERSION_BUILDINFO).empty() ? "" : "+" + string(SOL_VERSION_BUILDINFO)); static void help() { cout << "Usage lllc [OPTIONS] <file>" << endl - << "Options:" << endl + << "Options:" << endl << " -b,--binary Parse, compile and assemble; output byte code in binary." << endl << " -x,--hex Parse, compile and assemble; output byte code in hex." << endl << " -a,--assembly Only parse and compile; show assembly." << endl @@ -51,12 +51,12 @@ static void help() << " -o,--optimise Turn on/off the optimiser; off by default." << endl << " -h,--help Show this help message and exit." << endl << " -V,--version Show the version and exit." << endl; - exit(0); + exit(0); } static void version() { - cout << "LLLC, the Lovely Little Language Compiler " << endl; + cout << "LLLC, the Lovely Little Language Compiler" << endl; cout << "Version: " << VersionString << endl; exit(0); } @@ -118,39 +118,39 @@ int main(int argc, char** argv) string src; if (infile.empty()) - { - string s; - while (!cin.eof()) - { - getline(cin, s); - src.append(s); - } - } + src = readStandardInput(); else - src = contentsString(infile); + src = readFileAsString(infile); vector<string> errors; if (src.empty()) + { errors.push_back("Empty file."); + } else if (mode == Disassemble) { cout << disassemble(fromHex(src)) << endl; } else if (mode == Binary || mode == Hex) { - auto bs = compileLLL(src, optimise ? true : false, &errors, contentsString); + auto bs = compileLLL(src, optimise ? true : false, &errors, readFileAsString); if (mode == Hex) cout << toHex(bs) << endl; else if (mode == Binary) cout.write((char const*)bs.data(), bs.size()); } else if (mode == ParseTree) + { cout << parseLLL(src) << endl; + } else if (mode == Assembly) - cout << compileLLLToAsm(src, optimise ? true : false, &errors, contentsString) << endl; + { + cout << compileLLLToAsm(src, optimise ? true : false, &errors, readFileAsString) << endl; + } + for (auto const& i: errors) cerr << i << endl; - if ( errors.size() ) + if (errors.size()) return 1; return 0; } diff --git a/scripts/test_emscripten.sh b/scripts/test_emscripten.sh index b01b33bb..4996e957 100755 --- a/scripts/test_emscripten.sh +++ b/scripts/test_emscripten.sh @@ -36,6 +36,10 @@ DIR=$(mktemp -d) echo "Preparing solc-js..." git clone --depth 1 https://github.com/ethereum/solc-js "$DIR" cd "$DIR" + # disable "prepublish" script which downloads the latest version + # (we will replace it anyway and it is often incorrectly cached + # on travis) + npm config set script.prepublish '' npm install # Replace soljson with current build diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 1686dc2e..9e2cb77a 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -424,20 +424,13 @@ void CommandLineInterface::readInputFilesAndConfigureRemappings() continue; } - m_sourceCodes[infile.string()] = dev::contentsString(infile.string()); + m_sourceCodes[infile.string()] = dev::readFileAsString(infile.string()); path = boost::filesystem::canonical(infile).string(); } m_allowedDirectories.push_back(boost::filesystem::path(path).remove_filename()); } if (addStdin) - { - string s; - while (!cin.eof()) - { - getline(cin, s); - m_sourceCodes[g_stdinFileName].append(s + '\n'); - } - } + m_sourceCodes[g_stdinFileName] = dev::readStandardInput(); } bool CommandLineInterface::parseLibraryOption(string const& _input) @@ -447,7 +440,7 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) try { if (fs::is_regular_file(_input)) - data = contentsString(_input); + data = readFileAsString(_input); } catch (fs::filesystem_error const&) { @@ -698,7 +691,7 @@ bool CommandLineInterface::processInput() return ReadCallback::Result{false, "Not a valid file."}; else { - auto contents = dev::contentsString(canonicalPath.string()); + auto contents = dev::readFileAsString(canonicalPath.string()); m_sourceCodes[path.string()] = contents; return ReadCallback::Result{true, contents}; } @@ -731,13 +724,7 @@ bool CommandLineInterface::processInput() if (m_args.count(g_argStandardJSON)) { - string input; - while (!cin.eof()) - { - string tmp; - getline(cin, tmp); - input.append(tmp + "\n"); - } + string input = dev::readStandardInput(); StandardCompiler compiler(fileReader); cout << compiler.compile(input) << endl; return true; @@ -951,9 +938,10 @@ void CommandLineInterface::handleAst(string const& _argStr) for (auto const& sourceCode: m_sourceCodes) asts.push_back(&m_compiler->ast(sourceCode.first)); map<ASTNode const*, eth::GasMeter::GasConsumption> gasCosts; - if (m_compiler->runtimeAssemblyItems()) + // FIXME: shouldn't this be done for every contract? + if (m_compiler->runtimeAssemblyItems(m_compiler->lastContractName())) gasCosts = GasEstimator::breakToStatementLevel( - GasEstimator::structuralEstimation(*m_compiler->runtimeAssemblyItems(), asts), + GasEstimator::structuralEstimation(*m_compiler->runtimeAssemblyItems(m_compiler->lastContractName()), asts), asts ); diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index cc25bea7..2c61c0a6 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -172,78 +172,13 @@ public: { return bytes(); } + //@todo might be extended in the future template <class Arg> static bytes encodeDyn(Arg const& _arg) { return encodeArgs(u256(0x20), u256(_arg.size()), _arg); } - class ContractInterface - { - public: - ContractInterface(ExecutionFramework& _framework): m_framework(_framework) {} - - void setNextValue(u256 const& _value) { m_nextValue = _value; } - - protected: - template <class... Args> - bytes const& call(std::string const& _sig, Args const&... _arguments) - { - auto const& ret = m_framework.callContractFunctionWithValue(_sig, m_nextValue, _arguments...); - m_nextValue = 0; - return ret; - } - - void callString(std::string const& _name, std::string const& _arg) - { - BOOST_CHECK(call(_name + "(string)", u256(0x20), _arg.length(), _arg).empty()); - } - - void callStringAddress(std::string const& _name, std::string const& _arg1, u160 const& _arg2) - { - BOOST_CHECK(call(_name + "(string,address)", u256(0x40), _arg2, _arg1.length(), _arg1).empty()); - } - - void callStringAddressBool(std::string const& _name, std::string const& _arg1, u160 const& _arg2, bool _arg3) - { - BOOST_CHECK(call(_name + "(string,address,bool)", u256(0x60), _arg2, _arg3, _arg1.length(), _arg1).empty()); - } - - void callStringBytes32(std::string const& _name, std::string const& _arg1, h256 const& _arg2) - { - BOOST_CHECK(call(_name + "(string,bytes32)", u256(0x40), _arg2, _arg1.length(), _arg1).empty()); - } - - u160 callStringReturnsAddress(std::string const& _name, std::string const& _arg) - { - bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg); - BOOST_REQUIRE(ret.size() == 0x20); - BOOST_CHECK(std::count(ret.begin(), ret.begin() + 12, 0) == 12); - return u160(u256(h256(ret))); - } - - std::string callAddressReturnsString(std::string const& _name, u160 const& _arg) - { - bytesConstRef const ret(&call(_name + "(address)", _arg)); - BOOST_REQUIRE(ret.size() >= 0x40); - u256 offset(h256(ret.cropped(0, 0x20))); - BOOST_REQUIRE_EQUAL(offset, 0x20); - u256 len(h256(ret.cropped(0x20, 0x20))); - BOOST_REQUIRE_EQUAL(ret.size(), 0x40 + ((len + 0x1f) / 0x20) * 0x20); - return ret.cropped(0x40, size_t(len)).toString(); - } - - h256 callStringReturnsBytes32(std::string const& _name, std::string const& _arg) - { - bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg); - BOOST_REQUIRE(ret.size() == 0x20); - return h256(ret); - } - - private: - u256 m_nextValue; - ExecutionFramework& m_framework; - }; private: template <class CppFunction, class... Args> diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 72b26453..c06c3997 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -74,7 +74,10 @@ IPCSocket::IPCSocket(string const& _path): m_path(_path) BOOST_FAIL("Error creating IPC socket object"); if (connect(m_socket, reinterpret_cast<struct sockaddr const*>(&saun), sizeof(struct sockaddr_un)) < 0) + { + close(m_socket); BOOST_FAIL("Error connecting to IPC socket: " << _path); + } #endif } @@ -336,22 +339,25 @@ Json::Value RPCSession::rpcCall(string const& _methodName, vector<string> const& return result["result"]; } +string const& RPCSession::accountCreate() +{ + m_accounts.push_back(personal_newAccount("")); + personal_unlockAccount(m_accounts.back(), "", 100000); + return m_accounts.back(); +} + string const& RPCSession::accountCreateIfNotExists(size_t _id) { - if (_id >= m_accounts.size()) - { - m_accounts.push_back(personal_newAccount("")); - personal_unlockAccount(m_accounts.back(), "", 100000); - } + while ((_id + 1) > m_accounts.size()) + accountCreate(); return m_accounts[_id]; } RPCSession::RPCSession(const string& _path): m_ipcSocket(_path) { - string account = personal_newAccount(""); - personal_unlockAccount(account, "", 100000); - m_accounts.push_back(account); + accountCreate(); + // This will pre-fund the accounts create prior. test_setChainParams(m_accounts); } diff --git a/test/RPCSession.h b/test/RPCSession.h index eae6a09c..63f1dd21 100644 --- a/test/RPCSession.h +++ b/test/RPCSession.h @@ -121,6 +121,7 @@ public: Json::Value rpcCall(std::string const& _methodName, std::vector<std::string> const& _args = std::vector<std::string>(), bool _canFail = false); std::string const& account(size_t _id) const { return m_accounts.at(_id); } + std::string const& accountCreate(); std::string const& accountCreateIfNotExists(size_t _id); private: diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index 73a5d1ed..c9c744af 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -24,6 +24,7 @@ #include <tuple> #include <boost/test/unit_test.hpp> #include <test/libsolidity/SolidityExecutionFramework.h> +#include <test/contracts/ContractInterface.h> using namespace std; using namespace dev::test; @@ -230,7 +231,6 @@ protected: BOOST_REQUIRE(!m_output.empty()); } - using ContractInterface = SolidityExecutionFramework::ContractInterface; class RegistrarInterface: public ContractInterface { public: diff --git a/test/contracts/ContractInterface.h b/test/contracts/ContractInterface.h new file mode 100644 index 00000000..052b0db2 --- /dev/null +++ b/test/contracts/ContractInterface.h @@ -0,0 +1,99 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <boost/test/unit_test.hpp> +#include <test/ExecutionFramework.h> + +#include <functional> + +namespace dev +{ +namespace test +{ + +class ContractInterface +{ +public: + ContractInterface(ExecutionFramework& _framework): m_framework(_framework) {} + + void setNextValue(u256 const& _value) { m_nextValue = _value; } + +protected: + template <class... Args> + bytes const& call(std::string const& _sig, Args const&... _arguments) + { + auto const& ret = m_framework.callContractFunctionWithValue(_sig, m_nextValue, _arguments...); + m_nextValue = 0; + return ret; + } + + void callString(std::string const& _name, std::string const& _arg) + { + BOOST_CHECK(call(_name + "(string)", u256(0x20), _arg.length(), _arg).empty()); + } + + void callStringAddress(std::string const& _name, std::string const& _arg1, u160 const& _arg2) + { + BOOST_CHECK(call(_name + "(string,address)", u256(0x40), _arg2, _arg1.length(), _arg1).empty()); + } + + void callStringAddressBool(std::string const& _name, std::string const& _arg1, u160 const& _arg2, bool _arg3) + { + BOOST_CHECK(call(_name + "(string,address,bool)", u256(0x60), _arg2, _arg3, _arg1.length(), _arg1).empty()); + } + + void callStringBytes32(std::string const& _name, std::string const& _arg1, h256 const& _arg2) + { + BOOST_CHECK(call(_name + "(string,bytes32)", u256(0x40), _arg2, _arg1.length(), _arg1).empty()); + } + + u160 callStringReturnsAddress(std::string const& _name, std::string const& _arg) + { + bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg); + BOOST_REQUIRE(ret.size() == 0x20); + BOOST_CHECK(std::count(ret.begin(), ret.begin() + 12, 0) == 12); + return u160(u256(h256(ret))); + } + + std::string callAddressReturnsString(std::string const& _name, u160 const& _arg) + { + bytesConstRef const ret(&call(_name + "(address)", _arg)); + BOOST_REQUIRE(ret.size() >= 0x40); + u256 offset(h256(ret.cropped(0, 0x20))); + BOOST_REQUIRE_EQUAL(offset, 0x20); + u256 len(h256(ret.cropped(0x20, 0x20))); + BOOST_REQUIRE_EQUAL(ret.size(), 0x40 + ((len + 0x1f) / 0x20) * 0x20); + return ret.cropped(0x40, size_t(len)).toString(); + } + + h256 callStringReturnsBytes32(std::string const& _name, std::string const& _arg) + { + bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg); + BOOST_REQUIRE(ret.size() == 0x20); + return h256(ret); + } + +private: + u256 m_nextValue; + ExecutionFramework& m_framework; +}; + +} +} // end namespaces + diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp index 2d66bce1..ace97e15 100644 --- a/test/liblll/Compiler.cpp +++ b/test/liblll/Compiler.cpp @@ -24,6 +24,7 @@ #include <memory> #include <boost/test/unit_test.hpp> #include <liblll/Compiler.h> +#include <libdevcore/FixedHash.h> using namespace std; @@ -37,9 +38,9 @@ namespace test namespace { -bool successCompile(std::string const& _sourceCode) +bool successCompile(string const& _sourceCode) { - std::vector<std::string> errors; + vector<string> errors; bytes bytecode = eth::compileLLL(_sourceCode, false, &errors); if (!errors.empty()) return false; @@ -121,6 +122,533 @@ BOOST_AUTO_TEST_CASE(switch_inconsistent_return_count) BOOST_CHECK(!successCompile(sourceCode)); } +BOOST_AUTO_TEST_CASE(disallowed_asm_instructions) +{ + for (unsigned i = 1; i <= 32; i++) + BOOST_CHECK(!successCompile("(asm PUSH" + boost::lexical_cast<string>(i) + ")")); +} + +BOOST_AUTO_TEST_CASE(disallowed_functional_asm_instructions) +{ + for (unsigned i = 1; i <= 32; i++) + BOOST_CHECK(!successCompile("(PUSH" + boost::lexical_cast<string>(i) + ")")); + for (unsigned i = 1; i <= 16; i++) + BOOST_CHECK(!successCompile("(DUP" + boost::lexical_cast<string>(i) + ")")); + for (unsigned i = 1; i <= 16; i++) + BOOST_CHECK(!successCompile("(SWAP" + boost::lexical_cast<string>(i) + ")")); + BOOST_CHECK(!successCompile("(JUMPDEST)")); +} + +BOOST_AUTO_TEST_CASE(valid_opcodes_functional) +{ + vector<string> opcodes_bytecode { + "00", + "6000600001", + "6000600002", + "6000600003", + "6000600004", + "6000600005", + "6000600006", + "6000600007", + "60006000600008", + "60006000600009", + "600060000a", + "600060000b", + "6000600010", + "6000600011", + "6000600012", + "6000600013", + "6000600014", + "600015", + "6000600016", + "6000600017", + "6000600018", + "600019", + "600060001a", + "6000600020", + "30", + "600031", + "32", + "33", + "34", + "600035", + "36", + "60006000600037", + "38", + "60006000600039", + "3a", + "60003b", + "60006000600060003c", + "3d", + "6000600060003e", + "600040", + "41", + "42", + "43", + "44", + "45", + "600050", + "600051", + "6000600052", + "6000600053", + "600054", + "6000600055", + "600056", + "6000600057", + "58", + "59", + "5a", + "60ff", + "61ffff", + "62ffffff", + "63ffffffff", + "64ffffffffff", + "65ffffffffffff", + "66ffffffffffffff", + "67ffffffffffffffff", + "68ffffffffffffffffff", + "69ffffffffffffffffffff", + "6affffffffffffffffffffff", + "6bffffffffffffffffffffffff", + "6cffffffffffffffffffffffffff", + "6dffffffffffffffffffffffffffff", + "6effffffffffffffffffffffffffffff", + "6fffffffffffffffffffffffffffffffff", + "70ffffffffffffffffffffffffffffffffff", + "71ffffffffffffffffffffffffffffffffffff", + "72ffffffffffffffffffffffffffffffffffffff", + "73ffffffffffffffffffffffffffffffffffffffff", + "74ffffffffffffffffffffffffffffffffffffffffff", + "75ffffffffffffffffffffffffffffffffffffffffffff", + "76ffffffffffffffffffffffffffffffffffffffffffffff", + "77ffffffffffffffffffffffffffffffffffffffffffffffff", + "78ffffffffffffffffffffffffffffffffffffffffffffffffff", + "79ffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7affffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "60006000a0", + "600060006000a1", + "6000600060006000a2", + "60006000600060006000a3", + "600060006000600060006000a4", + "600060006000f0", + "6000600060006000600060006000f1", + "6000600060006000600060006000f2", + "60006000f3", + "600060006000600060006000f4", + "600060006000600060006000fa", + "60006000fd", + "fe", + "6000ff" + }; + + vector<string> opcodes_lll { + "{ (STOP) }", + "{ (ADD 0 0) }", + "{ (MUL 0 0) }", + "{ (SUB 0 0) }", + "{ (DIV 0 0) }", + "{ (SDIV 0 0) }", + "{ (MOD 0 0) }", + "{ (SMOD 0 0) }", + "{ (ADDMOD 0 0 0) }", + "{ (MULMOD 0 0 0) }", + "{ (EXP 0 0) }", + "{ (SIGNEXTEND 0 0) }", + "{ (LT 0 0) }", + "{ (GT 0 0) }", + "{ (SLT 0 0) }", + "{ (SGT 0 0) }", + "{ (EQ 0 0) }", + "{ (ISZERO 0) }", + "{ (AND 0 0) }", + "{ (OR 0 0) }", + "{ (XOR 0 0) }", + "{ (NOT 0) }", + "{ (BYTE 0 0) }", + "{ (KECCAK256 0 0) }", + "{ (ADDRESS) }", + "{ (BALANCE 0) }", + "{ (ORIGIN) }", + "{ (CALLER) }", + "{ (CALLVALUE) }", + "{ (CALLDATALOAD 0) }", + "{ (CALLDATASIZE) }", + "{ (CALLDATACOPY 0 0 0) }", + "{ (CODESIZE) }", + "{ (CODECOPY 0 0 0) }", + "{ (GASPRICE) }", + "{ (EXTCODESIZE 0) }", + "{ (EXTCODECOPY 0 0 0 0) }", + "{ (RETURNDATASIZE) }", + "{ (RETURNDATACOPY 0 0 0) }", + "{ (BLOCKHASH 0) }", + "{ (COINBASE) }", + "{ (TIMESTAMP) }", + "{ (NUMBER) }", + "{ (DIFFICULTY) }", + "{ (GASLIMIT) }", + "{ (POP 0) }", + "{ (MLOAD 0) }", + "{ (MSTORE 0 0) }", + "{ (MSTORE8 0 0) }", + "{ (SLOAD 0) }", + "{ (SSTORE 0 0) }", + "{ (JUMP 0) }", + "{ (JUMPI 0 0) }", + "{ (PC) }", + "{ (MSIZE) }", + "{ (GAS) }", + "{ 0xff }", + "{ 0xffff }", + "{ 0xffffff }", + "{ 0xffffffff }", + "{ 0xffffffffff }", + "{ 0xffffffffffff }", + "{ 0xffffffffffffff }", + "{ 0xffffffffffffffff }", + "{ 0xffffffffffffffffff }", + "{ 0xffffffffffffffffffff }", + "{ 0xffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", + "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", + "{ (LOG0 0 0) }", + "{ (LOG1 0 0 0) }", + "{ (LOG2 0 0 0 0) }", + "{ (LOG3 0 0 0 0 0) }", + "{ (LOG4 0 0 0 0 0 0) }", + "{ (CREATE 0 0 0) }", + "{ (CALL 0 0 0 0 0 0 0) }", + "{ (CALLCODE 0 0 0 0 0 0 0) }", + "{ (RETURN 0 0) }", + "{ (DELEGATECALL 0 0 0 0 0 0) }", + "{ (STATICCALL 0 0 0 0 0 0) }", + "{ (REVERT 0 0) }", + "{ (INVALID) }", + "{ (SELFDESTRUCT 0) }" + }; + + for (size_t i = 0; i < opcodes_bytecode.size(); i++) { + vector<string> errors; + bytes code = eth::compileLLL(opcodes_lll[i], false, &errors); + + BOOST_REQUIRE_MESSAGE(errors.empty(), opcodes_lll[i]); + + BOOST_CHECK_EQUAL(toHex(code), opcodes_bytecode[i]); + } +} + +BOOST_AUTO_TEST_CASE(valid_opcodes_asm) +{ + vector<string> opcodes_bytecode { + "00", + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "0a", + "0b", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "1a", + "20", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "3a", + "3b", + "3c", + "3d", + "3e", + "40", + "41", + "42", + "43", + "44", + "45", + "50", + "51", + "52", + "53", + "54", + "55", + "56", + "57", + "58", + "59", + "5a", + "5b", + "60ff", + "61ffff", + "62ffffff", + "63ffffffff", + "64ffffffffff", + "65ffffffffffff", + "66ffffffffffffff", + "67ffffffffffffffff", + "68ffffffffffffffffff", + "69ffffffffffffffffffff", + "6affffffffffffffffffffff", + "6bffffffffffffffffffffffff", + "6cffffffffffffffffffffffffff", + "6dffffffffffffffffffffffffffff", + "6effffffffffffffffffffffffffffff", + "6fffffffffffffffffffffffffffffffff", + "70ffffffffffffffffffffffffffffffffff", + "71ffffffffffffffffffffffffffffffffffff", + "72ffffffffffffffffffffffffffffffffffffff", + "73ffffffffffffffffffffffffffffffffffffffff", + "74ffffffffffffffffffffffffffffffffffffffffff", + "75ffffffffffffffffffffffffffffffffffffffffffff", + "76ffffffffffffffffffffffffffffffffffffffffffffff", + "77ffffffffffffffffffffffffffffffffffffffffffffffff", + "78ffffffffffffffffffffffffffffffffffffffffffffffffff", + "79ffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7affffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "80", + "81", + "82", + "83", + "84", + "85", + "86", + "87", + "88", + "89", + "8a", + "8b", + "8c", + "8d", + "8e", + "8f", + "90", + "91", + "92", + "93", + "94", + "95", + "96", + "97", + "98", + "99", + "9a", + "9b", + "9c", + "9d", + "9e", + "9f", + "a0", + "a1", + "a2", + "a3", + "a4", + "f0", + "f1", + "f2", + "f3", + "f4", + "fa", + "fd", + "fe", + "ff" + }; + + vector<string> opcodes_lll { + "{ (asm STOP) }", + "{ (asm ADD) }", + "{ (asm MUL) }", + "{ (asm SUB) }", + "{ (asm DIV) }", + "{ (asm SDIV ) }", + "{ (asm MOD) }", + "{ (asm SMOD) }", + "{ (asm ADDMOD) }", + "{ (asm MULMOD) }", + "{ (asm EXP) }", + "{ (asm SIGNEXTEND) }", + "{ (asm LT) }", + "{ (asm GT) }", + "{ (asm SLT) }", + "{ (asm SGT) }", + "{ (asm EQ) }", + "{ (asm ISZERO) }", + "{ (asm AND) }", + "{ (asm OR) }", + "{ (asm XOR) }", + "{ (asm NOT) }", + "{ (asm BYTE) }", + "{ (asm KECCAK256) }", + "{ (asm ADDRESS) }", + "{ (asm BALANCE) }", + "{ (asm ORIGIN) }", + "{ (asm CALLER) }", + "{ (asm CALLVALUE) }", + "{ (asm CALLDATALOAD) }", + "{ (asm CALLDATASIZE) }", + "{ (asm CALLDATACOPY) }", + "{ (asm CODESIZE) }", + "{ (asm CODECOPY) }", + "{ (asm GASPRICE) }", + "{ (asm EXTCODESIZE)}", + "{ (asm EXTCODECOPY) }", + "{ (asm RETURNDATASIZE) }", + "{ (asm RETURNDATACOPY) }", + "{ (asm BLOCKHASH) }", + "{ (asm COINBASE) }", + "{ (asm TIMESTAMP) }", + "{ (asm NUMBER) }", + "{ (asm DIFFICULTY) }", + "{ (asm GASLIMIT) }", + "{ (asm POP) }", + "{ (asm MLOAD) }", + "{ (asm MSTORE) }", + "{ (asm MSTORE8) }", + "{ (asm SLOAD) }", + "{ (asm SSTORE) }", + "{ (asm JUMP ) }", + "{ (asm JUMPI ) }", + "{ (asm PC) }", + "{ (asm MSIZE) }", + "{ (asm GAS) }", + "{ (asm JUMPDEST) }", + "{ (asm 0xff) }", + "{ (asm 0xffff) }", + "{ (asm 0xffffff) }", + "{ (asm 0xffffffff) }", + "{ (asm 0xffffffffff) }", + "{ (asm 0xffffffffffff) }", + "{ (asm 0xffffffffffffff) }", + "{ (asm 0xffffffffffffffff) }", + "{ (asm 0xffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", + "{ (asm DUP1) }", + "{ (asm DUP2) }", + "{ (asm DUP3) }", + "{ (asm DUP4) }", + "{ (asm DUP5) }", + "{ (asm DUP6) }", + "{ (asm DUP7) }", + "{ (asm DUP8) }", + "{ (asm DUP9) }", + "{ (asm DUP10) }", + "{ (asm DUP11) }", + "{ (asm DUP12) }", + "{ (asm DUP13) }", + "{ (asm DUP14) }", + "{ (asm DUP15) }", + "{ (asm DUP16) }", + "{ (asm SWAP1) }", + "{ (asm SWAP2) }", + "{ (asm SWAP3) }", + "{ (asm SWAP4) }", + "{ (asm SWAP5) }", + "{ (asm SWAP6) }", + "{ (asm SWAP7) }", + "{ (asm SWAP8) }", + "{ (asm SWAP9) }", + "{ (asm SWAP10) }", + "{ (asm SWAP11) }", + "{ (asm SWAP12) }", + "{ (asm SWAP13) }", + "{ (asm SWAP14) }", + "{ (asm SWAP15) }", + "{ (asm SWAP16) }", + "{ (asm LOG0) }", + "{ (asm LOG1) }", + "{ (asm LOG2) }", + "{ (asm LOG3) }", + "{ (asm LOG4) }", + "{ (asm CREATE) }", + "{ (asm CALL) }", + "{ (asm CALLCODE) }", + "{ (asm RETURN) }", + "{ (asm DELEGATECALL) }", + "{ (asm STATICCALL) }", + "{ (asm REVERT) }", + "{ (asm INVALID) }", + "{ (asm SELFDESTRUCT) }" + }; + + for (size_t i = 0; i < opcodes_bytecode.size(); i++) { + vector<string> errors; + bytes code = eth::compileLLL(opcodes_lll[i], false, &errors); + + BOOST_REQUIRE_MESSAGE(errors.empty(), opcodes_lll[i]); + + BOOST_CHECK_EQUAL(toHex(code), opcodes_bytecode[i]); + } +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index 3bdc40a0..ea9703ea 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -81,7 +81,7 @@ AnalysisFramework::parseAnalyseAndReturnError( } } - return make_pair(&m_compiler.ast(), firstError); + return make_pair(&m_compiler.ast(""), firstError); } SourceUnit const* AnalysisFramework::parseAndAnalyse(string const& _source) diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 56ac8cf5..358d3c72 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -119,7 +119,7 @@ BOOST_AUTO_TEST_CASE(location_test) shared_ptr<string const> n = make_shared<string>(""); AssemblyItems items = compileContract(sourceCode); vector<SourceLocation> locations = - vector<SourceLocation>(18, SourceLocation(2, 75, n)) + + vector<SourceLocation>(24, SourceLocation(2, 75, n)) + vector<SourceLocation>(32, SourceLocation(20, 72, n)) + vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + vector<SourceLocation>(2, SourceLocation(58, 67, n)) + diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index c2886f5b..86e8201b 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -51,8 +51,8 @@ public: m_compiler.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); - AssemblyItems const* items = m_compiler.runtimeAssemblyItems(""); - ASTNode const& sourceUnit = m_compiler.ast(); + AssemblyItems const* items = m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()); + ASTNode const& sourceUnit = m_compiler.ast(""); BOOST_REQUIRE(items != nullptr); m_gasCosts = GasEstimator::breakToStatementLevel( GasEstimator::structuralEstimation(*items, vector<ASTNode const*>({&sourceUnit})), @@ -64,13 +64,13 @@ public: { compileAndRun(_sourceCode); auto state = make_shared<KnownState>(); - PathGasMeter meter(*m_compiler.assemblyItems()); + PathGasMeter meter(*m_compiler.assemblyItems(m_compiler.lastContractName())); GasMeter::GasConsumption gas = meter.estimateMax(0, state); - u256 bytecodeSize(m_compiler.runtimeObject().bytecode.size()); + u256 bytecodeSize(m_compiler.runtimeObject(m_compiler.lastContractName()).bytecode.size()); // costs for deployment gas += bytecodeSize * GasCosts::createDataGas; // costs for transaction - gas += gasForTransaction(m_compiler.object().bytecode, true); + gas += gasForTransaction(m_compiler.object(m_compiler.lastContractName()).bytecode, true); BOOST_REQUIRE(!gas.isInfinite); BOOST_CHECK(gas.value == m_gasUsed); @@ -91,7 +91,7 @@ public: } gas += GasEstimator::functionalEstimation( - *m_compiler.runtimeAssemblyItems(), + *m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()), _sig ); BOOST_REQUIRE(!gas.isInfinite); @@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs) if (first->first->location().intersects(second->first->location())) { BOOST_CHECK_MESSAGE(false, "Source locations should not overlap!"); - auto scannerFromSource = [&](string const&) -> Scanner const& { return m_compiler.scanner(); }; + auto scannerFromSource = [&](string const& _sourceName) -> Scanner const& { return m_compiler.scanner(_sourceName); }; SourceReferenceFormatter::printSourceLocation(cout, &first->first->location(), scannerFromSource); SourceReferenceFormatter::printSourceLocation(cout, &second->first->location(), scannerFromSource); } diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 42f7525f..33962730 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -46,7 +46,7 @@ public: m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); BOOST_REQUIRE_MESSAGE(m_compilerStack.parseAndAnalyze(), "Parsing contract failed"); - Json::Value generatedInterface = m_compilerStack.contractABI(""); + Json::Value generatedInterface = m_compilerStack.contractABI(m_compilerStack.lastContractName()); Json::Value expectedInterface; BOOST_REQUIRE(m_reader.parse(_expectedInterfaceString, expectedInterface)); BOOST_CHECK_MESSAGE( diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 648c13cb..9a837113 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2899,6 +2899,25 @@ BOOST_AUTO_TEST_CASE(default_fallback_throws) ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); } +BOOST_AUTO_TEST_CASE(short_data_calls_fallback) +{ + char const* sourceCode = R"( + contract A { + uint public x; + // Signature is d88e0b00 + function fow() { x = 3; } + function () { x = 2; } + } + )"; + compileAndRun(sourceCode); + // should call fallback + sendMessage(asBytes("\xd8\x8e\x0b"), false, 0); + ABI_CHECK(callContractFunction("x()"), encodeArgs(2)); + // should call function + sendMessage(asBytes(string("\xd8\x8e\x0b") + string(1, 0)), false, 0); + ABI_CHECK(callContractFunction("x()"), encodeArgs(3)); +} + BOOST_AUTO_TEST_CASE(event) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 342d0875..b0daaba9 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -69,7 +69,7 @@ public: ); BOOST_ERROR("Compiling contract failed"); } - eth::LinkerObject obj = m_compiler.object(_contractName); + eth::LinkerObject obj = m_compiler.object(_contractName.empty() ? m_compiler.lastContractName() : _contractName); BOOST_REQUIRE(obj.linkReferences.empty()); sendMessage(obj.bytecode + _arguments, true, _value); return m_output; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 2c720d03..e5990e9b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -4783,6 +4783,16 @@ BOOST_AUTO_TEST_CASE(warn_about_callcode) } )"; CHECK_WARNING(text, "\"callcode\" has been deprecated in favour of \"delegatecall\""); + text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + var x = address(0x12).callcode; + x; + } + } + )"; + CHECK_ERROR(text, TypeError, "\"callcode\" has been deprecated in favour of \"delegatecall\""); } BOOST_AUTO_TEST_CASE(no_warn_about_callcode_as_function) @@ -5537,7 +5547,7 @@ BOOST_AUTO_TEST_CASE(invalid_mobile_type) } } )"; - CHECK_ERROR(text, TypeError, "Invalid mobile type."); + CHECK_ERROR(text, TypeError, "Invalid rational number."); } BOOST_AUTO_TEST_CASE(warns_msg_value_in_non_payable_public_function) @@ -7096,6 +7106,53 @@ BOOST_AUTO_TEST_CASE(non_external_fallback) CHECK_ERROR(text, TypeError, "Fallback function must be defined as \"external\"."); } +BOOST_AUTO_TEST_CASE(invalid_literal_in_tuple) +{ + char const* text = R"( + contract C { + function f() pure public { + uint x; + (x, ) = (1E111); + } + } + )"; + CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type"); + text = R"( + contract C { + function f() pure public { + uint x; + (x, ) = (1, 1E111); + } + } + )"; + CHECK_ERROR(text, TypeError, "Invalid rational number."); + text = R"( + contract C { + function f() pure public { + uint x; + (x, ) = (1E111, 1); + } + } + )"; + CHECK_ERROR(text, TypeError, "Invalid rational number."); + text = R"( + contract C { + function f() pure public { + (2**270, 1); + } + } + )"; + CHECK_ERROR(text, TypeError, "Invalid rational number."); + text = R"( + contract C { + function f() pure public { + ((2**270) / 2**100, 1); + } + } + )"; + CHECK_SUCCESS(text); +} + BOOST_AUTO_TEST_CASE(warn_about_sha3) { char const* text = R"( @@ -7190,6 +7247,12 @@ BOOST_AUTO_TEST_CASE(array_length_invalid_expression) } )"; CHECK_ERROR(text, TypeError, "Invalid literal value."); + text = R"( + contract C { + uint[3/0] ids; + } + )"; + CHECK_ERROR(text, TypeError, "Operator / not compatible with types int_const 3 and int_const 0"); } BOOST_AUTO_TEST_CASE(no_address_members_on_contract) diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index d83773bc..fb09451f 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -51,9 +51,9 @@ public: Json::Value generatedDocumentation; if (_userDocumentation) - generatedDocumentation = m_compilerStack.natspecUser(""); + generatedDocumentation = m_compilerStack.natspecUser(m_compilerStack.lastContractName()); else - generatedDocumentation = m_compilerStack.natspecDev(""); + generatedDocumentation = m_compilerStack.natspecDev(m_compilerStack.lastContractName()); Json::Value expectedDocumentation; m_reader.parse(_expectedDocumentationString, expectedDocumentation); BOOST_CHECK_MESSAGE( |