diff --git a/CMakeLists.txt b/CMakeLists.txt index 39a235c58..d39a5cca1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ add_subdirectory(libethereum) add_subdirectory(libevm) add_subdirectory(libnatspec) add_subdirectory(libp2p) +add_subdirectory(external-dependencies) if (JSCONSOLE) add_subdirectory(libjsengine) diff --git a/TestHelper.cpp b/TestHelper.cpp index 476d1ecf9..f7da0238e 100644 --- a/TestHelper.cpp +++ b/TestHelper.cpp @@ -262,7 +262,21 @@ void ImportTest::importTransaction(json_spirit::mObject& _o) { RLPStream transactionRLPStream = createRLPStreamFromTransactionFields(_o); RLP transactionRLP(transactionRLPStream.out()); - m_transaction = Transaction(transactionRLP.data(), CheckTransaction::Everything); + try + { + m_transaction = Transaction(transactionRLP.data(), CheckTransaction::Everything); + } + catch (InvalidSignature) + { + // create unsigned transaction + m_transaction = _o["to"].get_str().empty() ? + Transaction(toInt(_o["value"]), toInt(_o["gasPrice"]), toInt(_o["gasLimit"]), importData(_o), toInt(_o["nonce"])) : + Transaction(toInt(_o["value"]), toInt(_o["gasPrice"]), toInt(_o["gasLimit"]), Address(_o["to"].get_str()), importData(_o), toInt(_o["nonce"])); + } + catch (Exception& _e) + { + cnote << "invalid transaction" << boost::diagnostic_information(_e); + } } } @@ -328,7 +342,7 @@ void ImportTest::exportTest(bytes const& _output, State const& _statePost) { // export output - m_TestObject["out"] = _output.size() > 4096 ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add); + m_TestObject["out"] = (_output.size() > 4096 && !Options::get().fulloutput) ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add); // export logs m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries()); @@ -567,8 +581,7 @@ void userDefinedTest(std::function doTests) auto& filename = Options::get().singleTestFile; auto& testname = Options::get().singleTestName; - int currentVerbosity = g_logVerbosity; - g_logVerbosity = 12; + VerbosityHolder sentinel(12); try { cnote << "Testing user defined test: " << filename; @@ -588,19 +601,16 @@ void userDefinedTest(std::function doTests) oSingleTest[pos->first] = pos->second; json_spirit::mValue v_singleTest(oSingleTest); - doTests(v_singleTest, false); + doTests(v_singleTest, test::Options::get().fillTests); } catch (Exception const& _e) { BOOST_ERROR("Failed Test with Exception: " << diagnostic_information(_e)); - g_logVerbosity = currentVerbosity; } catch (std::exception const& _e) { BOOST_ERROR("Failed Test with Exception: " << _e.what()); - g_logVerbosity = currentVerbosity; } - g_logVerbosity = currentVerbosity; } void executeTests(const string& _name, const string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function doTests) @@ -760,6 +770,8 @@ Options::Options() else singleTestName = std::move(name1); } + else if (arg == "--fulloutput") + fulloutput = true; } } @@ -769,7 +781,6 @@ Options const& Options::get() return instance; } - LastHashes lastHashes(u256 _currentBlockNumber) { LastHashes ret; diff --git a/TestHelper.h b/TestHelper.h index fc6c77fad..8f0c73bf3 100644 --- a/TestHelper.h +++ b/TestHelper.h @@ -184,6 +184,7 @@ public: bool stats = false; ///< Execution time stats std::string statsOutFile; ///< Stats output file. "out" for standard output bool checkState = false;///< Throw error when checking test states + bool fulloutput = false;///< Replace large output to just it's length /// Test selection /// @{ diff --git a/libsolidity/GasMeter.cpp b/libsolidity/GasMeter.cpp index 43eb3f956..5f4426548 100644 --- a/libsolidity/GasMeter.cpp +++ b/libsolidity/GasMeter.cpp @@ -23,8 +23,9 @@ #include #include #include +#include #include -#include +#include #include using namespace std; @@ -47,30 +48,47 @@ public: m_compiler.setSource(_sourceCode); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(), "Compiling contract failed"); - StructuralGasEstimator estimator; AssemblyItems const* items = m_compiler.getRuntimeAssemblyItems(""); ASTNode const& sourceUnit = m_compiler.getAST(); BOOST_REQUIRE(items != nullptr); - m_gasCosts = estimator.breakToStatementLevel( - estimator.performEstimation(*items, vector({&sourceUnit})), + m_gasCosts = GasEstimator::breakToStatementLevel( + GasEstimator::structuralEstimation(*items, vector({&sourceUnit})), {&sourceUnit} ); } - void testCreationTimeGas(string const& _sourceCode, string const& _contractName = "") + void testCreationTimeGas(string const& _sourceCode) { compileAndRun(_sourceCode); auto state = make_shared(); - GasMeter meter(state); - GasMeter::GasConsumption gas; - for (AssemblyItem const& item: *m_compiler.getAssemblyItems(_contractName)) - gas += meter.estimateMax(item); - u256 bytecodeSize(m_compiler.getRuntimeBytecode(_contractName).size()); + PathGasMeter meter(*m_compiler.getAssemblyItems()); + GasMeter::GasConsumption gas = meter.estimateMax(0, state); + u256 bytecodeSize(m_compiler.getRuntimeBytecode().size()); gas += bytecodeSize * c_createDataGas; BOOST_REQUIRE(!gas.isInfinite); BOOST_CHECK(gas.value == m_gasUsed); } + /// Compares the gas computed by PathGasMeter for the given signature (but unknown arguments) + /// against the actual gas usage computed by the VM on the given set of argument variants. + void testRunTimeGas(string const& _sig, vector _argumentVariants) + { + u256 gasUsed = 0; + FixedHash<4> hash(dev::sha3(_sig)); + for (bytes const& arguments: _argumentVariants) + { + sendMessage(hash.asBytes() + arguments, false, 0); + gasUsed = max(gasUsed, m_gasUsed); + } + + GasMeter::GasConsumption gas = GasEstimator::functionalEstimation( + *m_compiler.getRuntimeAssemblyItems(), + _sig + ); + BOOST_REQUIRE(!gas.isInfinite); + BOOST_CHECK(gas.value == m_gasUsed); + } + protected: map m_gasCosts; }; @@ -149,6 +167,67 @@ BOOST_AUTO_TEST_CASE(updating_store) testCreationTimeGas(sourceCode); } +BOOST_AUTO_TEST_CASE(branches) +{ + char const* sourceCode = R"( + contract test { + uint data; + uint data2; + function f(uint x) { + if (x > 7) + data2 = 1; + else + data = 1; + } + } + )"; + testCreationTimeGas(sourceCode); + testRunTimeGas("f(uint256)", vector{encodeArgs(2), encodeArgs(8)}); +} + +BOOST_AUTO_TEST_CASE(function_calls) +{ + char const* sourceCode = R"( + contract test { + uint data; + uint data2; + function f(uint x) { + if (x > 7) + data2 = g(x**8) + 1; + else + data = 1; + } + function g(uint x) internal returns (uint) { + return data2; + } + } + )"; + testCreationTimeGas(sourceCode); + testRunTimeGas("f(uint256)", vector{encodeArgs(2), encodeArgs(8)}); +} + +BOOST_AUTO_TEST_CASE(multiple_external_functions) +{ + char const* sourceCode = R"( + contract test { + uint data; + uint data2; + function f(uint x) { + if (x > 7) + data2 = g(x**8) + 1; + else + data = 1; + } + function g(uint x) returns (uint) { + return data2; + } + } + )"; + testCreationTimeGas(sourceCode); + testRunTimeGas("f(uint256)", vector{encodeArgs(2), encodeArgs(8)}); + testRunTimeGas("g(uint256)", vector{encodeArgs(2)}); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/libsolidity/SolidityABIJSON.cpp b/libsolidity/SolidityABIJSON.cpp index f9bf78d0a..f7390dc93 100644 --- a/libsolidity/SolidityABIJSON.cpp +++ b/libsolidity/SolidityABIJSON.cpp @@ -568,6 +568,33 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi) checkInterface(sourceCode, interface); } +BOOST_AUTO_TEST_CASE(strings_and_arrays) +{ + // bug #1801 + char const* sourceCode = R"( + contract test { + function f(string a, bytes b, uint[] c) external {} + } + )"; + + char const* interface = R"( + [ + { + "constant" : false, + "name": "f", + "inputs": [ + { "name": "a", "type": "string" }, + { "name": "b", "type": "bytes" }, + { "name": "c", "type": "uint256[]" } + ], + "outputs": [], + "type" : "function" + } + ] + )"; + checkInterface(sourceCode, interface); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/libsolidity/SolidityCompiler.cpp b/libsolidity/SolidityCompiler.cpp deleted file mode 100644 index dda7847ed..000000000 --- a/libsolidity/SolidityCompiler.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum 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. - - cpp-ethereum 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 cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Unit tests for the solidity compiler. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace dev::eth; - -namespace dev -{ -namespace solidity -{ -namespace test -{ - -namespace -{ - -bytes compileContract(const string& _sourceCode) -{ - Parser parser; - ASTPointer sourceUnit; - BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(CharStream(_sourceCode)))); - NameAndTypeResolver resolver({}); - resolver.registerDeclarations(*sourceUnit); - for (ASTPointer const& node: sourceUnit->getNodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); - } - for (ASTPointer const& node: sourceUnit->getNodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - BOOST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract)); - } - for (ASTPointer const& node: sourceUnit->getNodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - Compiler compiler; - compiler.compileContract(*contract, map{}); - - // debug - //compiler.streamAssembly(cout); - return compiler.getAssembledBytecode(); - } - BOOST_FAIL("No contract found in source."); - return bytes(); -} - -/// Checks that @a _compiledCode is present starting from offset @a _offset in @a _expectation. -/// This is necessary since the compiler will add boilerplate add the beginning that is not -/// tested here. -void checkCodePresentAt(bytes const& _compiledCode, bytes const& _expectation, unsigned _offset) -{ - BOOST_REQUIRE(_compiledCode.size() >= _offset + _expectation.size()); - auto checkStart = _compiledCode.begin() + _offset; - BOOST_CHECK_EQUAL_COLLECTIONS(checkStart, checkStart + _expectation.size(), - _expectation.begin(), _expectation.end()); -} - -} // end anonymous namespace - -BOOST_AUTO_TEST_SUITE(SolidityCompiler) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - char const* sourceCode = "contract test {\n" - " function f() { var x = 2; }\n" - "}\n"; - bytes code = compileContract(sourceCode); - - unsigned boilerplateSize = 73; - bytes expectation({byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), 0x0, // initialize local variable x - byte(Instruction::PUSH1), 0x2, - byte(Instruction::SWAP1), - byte(Instruction::POP), - byte(Instruction::JUMPDEST), - byte(Instruction::POP), - byte(Instruction::JUMP)}); - checkCodePresentAt(code, expectation, boilerplateSize); -} - -BOOST_AUTO_TEST_CASE(ifStatement) -{ - char const* sourceCode = "contract test {\n" - " function f() { bool x; if (x) 77; else if (!x) 78; else 79; }" - "}\n"; - bytes code = compileContract(sourceCode); - unsigned shift = 60; - unsigned boilerplateSize = 73; - bytes expectation({ - byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), 0x0, - byte(Instruction::DUP1), - byte(Instruction::ISZERO), - byte(Instruction::PUSH1), byte(0x0f + shift), // "false" target - byte(Instruction::JUMPI), - // "if" body - byte(Instruction::PUSH1), 0x4d, - byte(Instruction::POP), - byte(Instruction::PUSH1), byte(0x21 + shift), - byte(Instruction::JUMP), - // new check "else if" condition - byte(Instruction::JUMPDEST), - byte(Instruction::DUP1), - byte(Instruction::ISZERO), - byte(Instruction::ISZERO), - byte(Instruction::PUSH1), byte(0x1c + shift), - byte(Instruction::JUMPI), - // "else if" body - byte(Instruction::PUSH1), 0x4e, - byte(Instruction::POP), - byte(Instruction::PUSH1), byte(0x20 + shift), - byte(Instruction::JUMP), - // "else" body - byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), 0x4f, - byte(Instruction::POP), - }); - checkCodePresentAt(code, expectation, boilerplateSize); -} - -BOOST_AUTO_TEST_CASE(loops) -{ - char const* sourceCode = "contract test {\n" - " function f() { while(true){1;break;2;continue;3;return;4;} }" - "}\n"; - bytes code = compileContract(sourceCode); - unsigned shift = 60; - unsigned boilerplateSize = 73; - bytes expectation({byte(Instruction::JUMPDEST), - byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), 0x1, - byte(Instruction::ISZERO), - byte(Instruction::PUSH1), byte(0x21 + shift), - byte(Instruction::JUMPI), - byte(Instruction::PUSH1), 0x1, - byte(Instruction::POP), - byte(Instruction::PUSH1), byte(0x21 + shift), - byte(Instruction::JUMP), // break - byte(Instruction::PUSH1), 0x2, - byte(Instruction::POP), - byte(Instruction::PUSH1), byte(0x2 + shift), - byte(Instruction::JUMP), // continue - byte(Instruction::PUSH1), 0x3, - byte(Instruction::POP), - byte(Instruction::PUSH1), byte(0x22 + shift), - byte(Instruction::JUMP), // return - byte(Instruction::PUSH1), 0x4, - byte(Instruction::POP), - byte(Instruction::PUSH1), byte(0x2 + shift), - byte(Instruction::JUMP), - byte(Instruction::JUMPDEST), - byte(Instruction::JUMPDEST), - byte(Instruction::JUMP)}); - - checkCodePresentAt(code, expectation, boilerplateSize); -} - -BOOST_AUTO_TEST_SUITE_END() - -} -} -} // end namespaces diff --git a/libsolidity/SolidityEndToEndTest.cpp b/libsolidity/SolidityEndToEndTest.cpp index 503615a5a..f12abd48e 100644 --- a/libsolidity/SolidityEndToEndTest.cpp +++ b/libsolidity/SolidityEndToEndTest.cpp @@ -566,16 +566,16 @@ BOOST_AUTO_TEST_CASE(strings) BOOST_AUTO_TEST_CASE(empty_string_on_stack) { - char const* sourceCode = "contract test {\n" - " function run(bytes0 empty, uint8 inp) returns(uint16 a, bytes0 b, bytes4 c) {\n" - " var x = \"abc\";\n" - " var y = \"\";\n" - " var z = inp;\n" - " a = z; b = y; c = x;" - " }\n" - "}\n"; + char const* sourceCode = R"( + contract test { + function run() external returns(bytes2 ret) { + var y = ""; + ret = y; + } + } + )"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("run(bytes0,uint8)", string(), byte(0x02)) == encodeArgs(0x2, string(""), string("abc\0"))); + BOOST_CHECK(callContractFunction("run()") == encodeArgs(byte(0x00))); } BOOST_AUTO_TEST_CASE(inc_dec_operators) @@ -2396,7 +2396,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data) callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK(m_logs[0].data == encodeArgs(10, 4, 15) + FixedHash<4>(dev::sha3("deposit()")).asBytes()); + BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::sha3("deposit()")).asBytes()); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)"))); } @@ -2420,7 +2420,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK(m_logs[0].data == encodeArgs(10, 3, 15) + asBytes("ABC")); + BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 3) + asBytes("ABC")); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)"))); } @@ -2531,6 +2531,27 @@ BOOST_AUTO_TEST_CASE(sha3_with_bytes) BOOST_CHECK(callContractFunction("foo()") == encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(iterated_sha3_with_bytes) +{ + char const* sourceCode = R"( + contract c { + bytes data; + function foo() returns (bytes32) + { + data.length = 3; + data[0] = "x"; + data[1] = "y"; + data[2] = "z"; + return sha3("b", sha3(data), "a"); + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("foo()") == encodeArgs( + u256(dev::sha3(bytes{'b'} + dev::sha3("xyz").asBytes() + bytes{'a'})) + )); +} + BOOST_AUTO_TEST_CASE(generic_call) { char const* sourceCode = R"**( @@ -3786,30 +3807,6 @@ BOOST_AUTO_TEST_CASE(packed_storage_structs_delete) BOOST_CHECK(m_state.storage(m_contractAddress).empty()); } -BOOST_AUTO_TEST_CASE(packed_storage_structs_with_bytes0) -{ - char const* sourceCode = R"( - contract C { - struct str { uint8 a; bytes0 b; uint8 c; } - uint8 a; - bytes0 x; - uint8 b; - str data; - function test() returns (bool) { - a = 2; - b = 3; - data.a = 4; - data.c = 5; - delete x; - delete data.b; - return a == 2 && b == 3 && data.a == 4 && data.c == 5; - } - } - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("test()") == encodeArgs(true)); -} - BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_first) { char const* sourceCode = R"( @@ -4080,7 +4077,6 @@ BOOST_AUTO_TEST_CASE(struct_delete_member) } )"; compileAndRun(sourceCode, 0, "test"); - auto res = callContractFunction("deleteMember()"); BOOST_CHECK(callContractFunction("deleteMember()") == encodeArgs(0)); } @@ -4106,10 +4102,136 @@ BOOST_AUTO_TEST_CASE(struct_delete_struct_in_mapping) } )"; compileAndRun(sourceCode, 0, "test"); - auto res = callContractFunction("deleteIt()"); BOOST_CHECK(callContractFunction("deleteIt()") == encodeArgs(0)); } +BOOST_AUTO_TEST_CASE(evm_exceptions_out_of_band_access) +{ + char const* sourceCode = R"( + contract A { + uint[3] arr; + bool public test = false; + function getElement(uint i) returns (uint) + { + return arr[i]; + } + function testIt() returns (bool) + { + uint i = this.getElement(5); + test = true; + return true; + } + } + )"; + compileAndRun(sourceCode, 0, "A"); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(false)); + BOOST_CHECK(callContractFunction("testIt()") == encodeArgs()); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(false)); +} + +BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_call_fail) +{ + char const* sourceCode = R"( + contract A { + function A() + { + this.call("123"); + } + } + contract B { + uint public test = 1; + function testIt() + { + A a = new A(); + ++test; + } + } + )"; + compileAndRun(sourceCode, 0, "B"); + + BOOST_CHECK(callContractFunction("testIt()") == encodeArgs()); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(2)); +} + +BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund) +{ + char const* sourceCode = R"( + contract A { + uint public test = 1; + uint[3] arr; + function A() + { + test = arr[5]; + ++test; + } + } + )"; + BOOST_CHECK(compileAndRunWthoutCheck(sourceCode, 0, "A").empty()); +} + +BOOST_AUTO_TEST_CASE(positive_integers_to_signed) +{ + char const* sourceCode = R"( + contract test { + int8 public x = 2; + int8 public y = 127; + int16 public q = 250; + } + )"; + compileAndRun(sourceCode, 0, "test"); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(2)); + BOOST_CHECK(callContractFunction("y()") == encodeArgs(127)); + BOOST_CHECK(callContractFunction("q()") == encodeArgs(250)); +} + +BOOST_AUTO_TEST_CASE(failing_send) +{ + char const* sourceCode = R"( + contract Helper { + uint[] data; + function () { + data[9]; // trigger exception + } + } + contract Main { + function callHelper(address _a) returns (bool r, uint bal) { + r = !_a.send(5); + bal = this.balance; + } + } + )"; + compileAndRun(sourceCode, 0, "Helper"); + u160 const c_helperAddress = m_contractAddress; + compileAndRun(sourceCode, 20, "Main"); + BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20)); +} + +BOOST_AUTO_TEST_CASE(reusing_memory) +{ + // Invoke some features that use memory and test that they do not interfere with each other. + char const* sourceCode = R"( + contract Helper { + uint public flag; + function Helper(uint x) { + flag = x; + } + } + contract Main { + mapping(uint => uint) map; + function f(uint x) returns (uint) { + map[x] = x; + return (new Helper(uint(sha3(this.g(map[x]))))).flag(); + } + function g(uint a) returns (uint) + { + return map[a]; + } + } + )"; + compileAndRun(sourceCode, 0, "Main"); + BOOST_REQUIRE(callContractFunction("f(uint256)", 0x34) == encodeArgs(dev::sha3(dev::toBigEndian(u256(0x34))))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/libsolidity/SolidityNameAndTypeResolution.cpp b/libsolidity/SolidityNameAndTypeResolution.cpp index c52bbf9de..3948a4a23 100644 --- a/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/libsolidity/SolidityNameAndTypeResolution.cpp @@ -558,16 +558,6 @@ BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } -// todo delete when implemented -BOOST_AUTO_TEST_CASE(arrays_in_internal_functions) -{ - char const* text = R"( - contract Test { - function foo(address[] addresses) {} - })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); -} - BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion) { char const* text = R"( @@ -1579,7 +1569,6 @@ BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName) BOOST_CHECK(*Type::fromElementaryTypeName(Token::UInt256) == *make_shared(256, IntegerType::Modifier::Unsigned)); BOOST_CHECK(*Type::fromElementaryTypeName(Token::Byte) == *make_shared(1)); - BOOST_CHECK(*Type::fromElementaryTypeName(Token::Bytes0) == *make_shared(0)); BOOST_CHECK(*Type::fromElementaryTypeName(Token::Bytes1) == *make_shared(1)); BOOST_CHECK(*Type::fromElementaryTypeName(Token::Bytes2) == *make_shared(2)); BOOST_CHECK(*Type::fromElementaryTypeName(Token::Bytes3) == *make_shared(3)); @@ -1666,16 +1655,6 @@ BOOST_AUTO_TEST_CASE(local_const_variable) BOOST_CHECK_THROW(parseTextAndResolveNames(text), ParserError); } -BOOST_AUTO_TEST_CASE(bytes0_array) -{ - char const* text = R"( - contract Foo { - bytes0[] illegalArray; - } - )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); -} - BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve) { char const* sourceCode = R"( @@ -1783,6 +1762,133 @@ BOOST_AUTO_TEST_CASE(uninitialized_var) BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); } +BOOST_AUTO_TEST_CASE(string) +{ + char const* sourceCode = R"( + contract C { + string s; + function f(string x) external { s = x; } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(string_index) +{ + char const* sourceCode = R"( + contract C { + string s; + function f() { var a = s[2]; } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(string_length) +{ + char const* sourceCode = R"( + contract C { + string s; + function f() { var a = s.length; } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(negative_integers_to_signed_out_of_bound) +{ + char const* sourceCode = R"( + contract test { + int8 public i = -129; + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(negative_integers_to_signed_min) +{ + char const* sourceCode = R"( + contract test { + int8 public i = -128; + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound) +{ + char const* sourceCode = R"( + contract test { + int8 public j = 128; + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound_max) +{ + char const* sourceCode = R"( + contract test { + int8 public j = 127; + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(negative_integers_to_unsigned) +{ + char const* sourceCode = R"( + contract test { + uint8 public x = -1; + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(positive_integers_to_unsigned_out_of_bound) +{ + char const* sourceCode = R"( + contract test { + uint8 public x = 700; + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(overwrite_memory_location_external) +{ + char const* sourceCode = R"( + contract C { + function f(uint[] memory a) external {} + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(overwrite_storage_location_external) +{ + char const* sourceCode = R"( + contract C { + function f(uint[] storage a) external {} + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(storage_location_local_variables) +{ + char const* sourceCode = R"( + contract C { + function f() { + uint[] storage x; + uint[] memory y; + uint[] memory z; + } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/libsolidity/SolidityOptimizer.cpp b/libsolidity/SolidityOptimizer.cpp index 744fc48ae..397ee6316 100644 --- a/libsolidity/SolidityOptimizer.cpp +++ b/libsolidity/SolidityOptimizer.cpp @@ -355,7 +355,8 @@ BOOST_AUTO_TEST_CASE(store_tags_as_unions) if (_instr == eth::Instruction::SHA3) numSHA3s++; }); - BOOST_CHECK_EQUAL(2, numSHA3s); +// TEST DISABLED UNTIL 93693404 IS IMPLEMENTED +// BOOST_CHECK_EQUAL(2, numSHA3s); } BOOST_AUTO_TEST_CASE(cse_intermediate_swap) @@ -440,6 +441,16 @@ BOOST_AUTO_TEST_CASE(cse_double_negation) checkCSE({Instruction::DUP5, Instruction::NOT, Instruction::NOT}, {Instruction::DUP5}); } +BOOST_AUTO_TEST_CASE(cse_double_iszero) +{ + checkCSE({Instruction::GT, Instruction::ISZERO, Instruction::ISZERO}, {Instruction::GT}); + checkCSE({Instruction::GT, Instruction::ISZERO}, {Instruction::GT, Instruction::ISZERO}); + checkCSE( + {Instruction::ISZERO, Instruction::ISZERO, Instruction::ISZERO}, + {Instruction::ISZERO} + ); +} + BOOST_AUTO_TEST_CASE(cse_associativity) { AssemblyItems input{ @@ -908,6 +919,31 @@ BOOST_AUTO_TEST_CASE(cse_equality_on_initially_known_stack) BOOST_CHECK(find(output.begin(), output.end(), AssemblyItem(u256(1))) != output.end()); } +BOOST_AUTO_TEST_CASE(cse_access_previous_sequence) +{ + // Tests that the code generator detects whether it tries to access SLOAD instructions + // from a sequenced expression which is not in its scope. + eth::KnownState state = createInitialState(AssemblyItems{ + u256(0), + Instruction::SLOAD, + u256(1), + Instruction::ADD, + u256(0), + Instruction::SSTORE + }); + // now stored: val_1 + 1 (value at sequence 1) + // if in the following instructions, the SLOAD cresolves to "val_1 + 1", + // this cannot be generated because we cannot load from sequence 1 anymore. + AssemblyItems input{ + u256(0), + Instruction::SLOAD, + }; + BOOST_CHECK_THROW(getCSE(input, state), StackTooDeepException); + // @todo for now, this throws an exception, but it should recover to the following + // (or an even better version) at some point: + // 0, SLOAD, 1, ADD, SSTORE, 0 SLOAD +} + BOOST_AUTO_TEST_CASE(control_flow_graph_remove_unused) { // remove parts of the code that are unused @@ -1004,6 +1040,83 @@ BOOST_AUTO_TEST_CASE(block_deduplicator) BOOST_CHECK_EQUAL(pushTags.size(), 2); } +BOOST_AUTO_TEST_CASE(block_deduplicator_loops) +{ + AssemblyItems input{ + u256(0), + eth::Instruction::SLOAD, + AssemblyItem(PushTag, 1), + AssemblyItem(PushTag, 2), + eth::Instruction::JUMPI, + eth::Instruction::JUMP, + AssemblyItem(Tag, 1), + u256(5), + u256(6), + eth::Instruction::SSTORE, + AssemblyItem(PushTag, 1), + eth::Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(5), + u256(6), + eth::Instruction::SSTORE, + AssemblyItem(PushTag, 2), + eth::Instruction::JUMP, + }; + BlockDeduplicator dedup(input); + dedup.deduplicate(); + + set pushTags; + for (AssemblyItem const& item: input) + if (item.type() == PushTag) + pushTags.insert(item.data()); + BOOST_CHECK_EQUAL(pushTags.size(), 1); +} + +BOOST_AUTO_TEST_CASE(computing_constants) +{ + char const* sourceCode = R"( + contract c { + uint a; + uint b; + uint c; + function set() returns (uint a, uint b, uint c) { + a = 0x77abc0000000000000000000000000000000000000000000000000000000001; + b = 0x817416927846239487123469187231298734162934871263941234127518276; + g(); + } + function g() { + b = 0x817416927846239487123469187231298734162934871263941234127518276; + c = 0x817416927846239487123469187231298734162934871263941234127518276; + } + function get() returns (uint ra, uint rb, uint rc) { + ra = a; + rb = b; + rc = c ; + } + } + )"; + compileBothVersions(sourceCode); + compareVersions("set()"); + compareVersions("get()"); + + m_optimize = true; + m_optimizeRuns = 1; + bytes optimizedBytecode = compileAndRun(sourceCode, 0, "c"); + bytes complicatedConstant = toBigEndian(u256("0x817416927846239487123469187231298734162934871263941234127518276")); + unsigned occurrences = 0; + for (auto iter = optimizedBytecode.cbegin(); iter < optimizedBytecode.cend(); ++occurrences) + iter = search(iter, optimizedBytecode.cend(), complicatedConstant.cbegin(), complicatedConstant.cend()) + 1; + BOOST_CHECK_EQUAL(2, occurrences); + + bytes constantWithZeros = toBigEndian(u256("0x77abc0000000000000000000000000000000000000000000000000000000001")); + BOOST_CHECK(search( + optimizedBytecode.cbegin(), + optimizedBytecode.cend(), + constantWithZeros.cbegin(), + constantWithZeros.cend() + ) == optimizedBytecode.cend()); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/libsolidity/SolidityParser.cpp b/libsolidity/SolidityParser.cpp index cad0e1f2c..438e650bf 100644 --- a/libsolidity/SolidityParser.cpp +++ b/libsolidity/SolidityParser.cpp @@ -873,6 +873,47 @@ BOOST_AUTO_TEST_CASE(var_array) BOOST_CHECK_THROW(parseText(text), ParserError); } +BOOST_AUTO_TEST_CASE(location_specifiers_for_params) +{ + char const* text = R"( + contract Foo { + function f(uint[] storage constant x, uint[] memory y) { } + } + )"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(location_specifiers_for_locals) +{ + char const* text = R"( + contract Foo { + function f() { + uint[] storage x; + uint[] memory y; + } + } + )"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(location_specifiers_for_state) +{ + char const* text = R"( + contract Foo { + uint[] memory x; + })"; + BOOST_CHECK_THROW(parseText(text), ParserError); +} + +BOOST_AUTO_TEST_CASE(location_specifiers_with_var) +{ + char const* text = R"( + contract Foo { + function f() { var memory x; } + })"; + BOOST_CHECK_THROW(parseText(text), ParserError); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/libsolidity/SolidityTypes.cpp b/libsolidity/SolidityTypes.cpp index 6b6306479..718798a5a 100644 --- a/libsolidity/SolidityTypes.cpp +++ b/libsolidity/SolidityTypes.cpp @@ -77,13 +77,13 @@ BOOST_AUTO_TEST_CASE(storage_layout_mapping) BOOST_AUTO_TEST_CASE(storage_layout_arrays) { - BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared(1), 32).getStorageSize() == 1); - BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared(1), 33).getStorageSize() == 2); - BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared(2), 31).getStorageSize() == 2); - BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared(7), 8).getStorageSize() == 2); - BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared(7), 9).getStorageSize() == 3); - BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared(31), 9).getStorageSize() == 9); - BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared(32), 9).getStorageSize() == 9); + BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared(1), 32).getStorageSize() == 1); + BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared(1), 33).getStorageSize() == 2); + BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared(2), 31).getStorageSize() == 2); + BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared(7), 8).getStorageSize() == 2); + BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared(7), 9).getStorageSize() == 3); + BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared(31), 9).getStorageSize() == 9); + BOOST_CHECK(ArrayType(ReferenceType::Location::Storage, make_shared(32), 9).getStorageSize() == 9); } BOOST_AUTO_TEST_SUITE_END() diff --git a/libsolidity/solidityExecutionFramework.h b/libsolidity/solidityExecutionFramework.h index fa25fb12c..44590b1c8 100644 --- a/libsolidity/solidityExecutionFramework.h +++ b/libsolidity/solidityExecutionFramework.h @@ -42,21 +42,25 @@ class ExecutionFramework public: ExecutionFramework() { g_logVerbosity = 0; } - bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") + bytes const& compileAndRunWthoutCheck(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") { m_compiler.reset(false, m_addStandardSources); m_compiler.addSource("", _sourceCode); - ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize), "Compiling contract failed"); - + ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); bytes code = m_compiler.getBytecode(_contractName); sendMessage(code, true, _value); + return m_output; + } + + bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") + { + compileAndRunWthoutCheck(_sourceCode, _value, _contractName); BOOST_REQUIRE(!m_output.empty()); return m_output; } template - bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, - Args const&... _arguments) + bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, Args const&... _arguments) { FixedHash<4> hash(dev::sha3(_sig)); sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value); @@ -74,21 +78,30 @@ public: { bytes solidityResult = callContractFunction(_sig, _arguments...); bytes cppResult = callCppAndEncodeResult(_cppFunction, _arguments...); - BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match." - "\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult)); + BOOST_CHECK_MESSAGE( + solidityResult == cppResult, + "Computed values do not match.\nSolidity: " + + toHex(solidityResult) + + "\nC++: " + + toHex(cppResult)); } template - void testSolidityAgainstCppOnRange(std::string _sig, CppFunction const& _cppFunction, - u256 const& _rangeStart, u256 const& _rangeEnd) + void testSolidityAgainstCppOnRange(std::string _sig, CppFunction const& _cppFunction, u256 const& _rangeStart, u256 const& _rangeEnd) { for (u256 argument = _rangeStart; argument < _rangeEnd; ++argument) { bytes solidityResult = callContractFunction(_sig, argument); bytes cppResult = callCppAndEncodeResult(_cppFunction, argument); - BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match." - "\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult) + - "\nArgument: " + toHex(encode(argument))); + BOOST_CHECK_MESSAGE( + solidityResult == cppResult, + "Computed values do not match.\nSolidity: " + + toHex(solidityResult) + + "\nC++: " + + toHex(cppResult) + + "\nArgument: " + + toHex(encode(argument)) + ); } } @@ -135,8 +148,12 @@ protected: { m_state.addBalance(m_sender, _value); // just in case eth::Executive executive(m_state, eth::LastHashes(), 0); - eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) - : eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); + eth::ExecutionResult res; + executive.setResultRecipient(res); + eth::Transaction t = + _isCreation ? + eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) : + eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); bytes transactionRLP = t.rlp(); try { @@ -155,16 +172,17 @@ protected: else { BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); - BOOST_REQUIRE(!executive.call(m_contractAddress, m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas, m_sender)); + BOOST_REQUIRE(!executive.call(m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas)); } BOOST_REQUIRE(executive.go()); m_state.noteSending(m_sender); executive.finalize(); m_gasUsed = executive.gasUsed(); - m_output = executive.out().toVector(); + m_output = std::move(res.output); // FIXME: Looks like Framework needs ExecutiveResult embedded m_logs = executive.logs(); } + size_t m_optimizeRuns = 200; bool m_optimize = false; bool m_addStandardSources = false; dev::solidity::CompilerStack m_compiler;