diff --git a/.circleci/config.yml b/.circleci/config.yml index 529360379..a8ccc4a41 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -466,7 +466,7 @@ jobs: <<: *build_ubuntu2004 environment: MAKEFLAGS: -j 10 - CMAKE_OPTIONS: -DCMAKE_BUILD_TYPE=Release -DSOLC_LINK_STATIC=1 -DUSE_CVC4=OFF -DUSE_Z3=OFF + CMAKE_OPTIONS: -DCMAKE_BUILD_TYPE=Release -DUSE_Z3_DLOPEN=ON -DUSE_CVC4=OFF -DSOLC_STATIC_STDLIBS=ON steps: - checkout - run: *run_build diff --git a/CMakeLists.txt b/CMakeLists.txt index d5ded5abb..28a6eb2bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ if (IS_BIG_ENDIAN) endif() option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) +option(SOLC_STATIC_STDLIBS "Link solc against static versions of libgcc and libstdc++ on supported platforms" OFF) # Setup cccache. include(EthCcache) @@ -51,8 +52,27 @@ configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/license.h.in" include/licens include(EthOptions) configure_project(TESTS) -find_package(Z3 4.6.0) -if (${Z3_FOUND}) +find_package(Z3 4.8.0) +if(${USE_Z3_DLOPEN}) + add_definitions(-DHAVE_Z3) + add_definitions(-DHAVE_Z3_DLOPEN) + find_package(Python3 COMPONENTS Interpreter) + if(${Z3_FOUND}) + get_target_property(Z3_HEADER_HINTS z3::libz3 INTERFACE_INCLUDE_DIRECTORIES) + endif() + find_path(Z3_HEADER_PATH z3.h HINTS ${Z3_HEADER_HINTS}) + if(Z3_HEADER_PATH) + set(Z3_FOUND TRUE) + else() + message(SEND_ERROR "Dynamic loading of Z3 requires Z3 headers to be present at build time.") + endif() + if(NOT ${Python3_FOUND}) + message(SEND_ERROR "Dynamic loading of Z3 requires Python 3 to be present at build time.") + endif() + if(${SOLC_LINK_STATIC}) + message(SEND_ERROR "solc cannot be linked statically when dynamically loading Z3.") + endif() +elseif (${Z3_FOUND}) add_definitions(-DHAVE_Z3) message("Z3 SMT solver found. This enables optional SMT checking with Z3.") endif() diff --git a/Changelog.md b/Changelog.md index f5751e93d..6abaa1fe6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -45,6 +45,7 @@ Language Features: * Wasm backend: Add ``i32.select`` and ``i64.select`` instructions. Compiler Features: + * Build System: Optionally support dynamic loading of Z3 and use that mechanism for Linux release builds. * Code Generator: Avoid memory allocation for default value if it is not used. * SMTChecker: Report struct values in counterexamples from CHC engine. * SMTChecker: Support early returns in the CHC engine. diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 87155270c..313717101 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -209,6 +209,9 @@ endif() # SMT Solvers integration option(USE_Z3 "Allow compiling with Z3 SMT solver integration" ON) +if(UNIX AND NOT APPLE) + option(USE_Z3_DLOPEN "Dynamically load the Z3 SMT solver instead of linking against it." OFF) +endif() option(USE_CVC4 "Allow compiling with CVC4 SMT solver integration" ON) if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 948ded2da..25d82ec93 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -312,7 +312,7 @@ The following are dependencies for all builds of Solidity: +-----------------------------------+-------------------------------------------------------+ | `Git`_ | Command-line tool for retrieving source code. | +-----------------------------------+-------------------------------------------------------+ -| `z3`_ (version 4.6+, Optional) | For use with SMT checker. | +| `z3`_ (version 4.8+, Optional) | For use with SMT checker. | +-----------------------------------+-------------------------------------------------------+ | `cvc4`_ (Optional) | For use with SMT checker. | +-----------------------------------+-------------------------------------------------------+ diff --git a/libsmtutil/CMakeLists.txt b/libsmtutil/CMakeLists.txt index 8c8b3677d..af7f47b60 100644 --- a/libsmtutil/CMakeLists.txt +++ b/libsmtutil/CMakeLists.txt @@ -24,10 +24,25 @@ else() set(cvc4_SRCS) endif() +if (${USE_Z3_DLOPEN}) + file(GLOB Z3_HEADERS ${Z3_HEADER_PATH}/z3*.h) + set(Z3_WRAPPER ${CMAKE_CURRENT_BINARY_DIR}/z3wrapper.cpp) + add_custom_command( + OUTPUT ${Z3_WRAPPER} + COMMAND ${Python3_EXECUTABLE} genz3wrapper.py ${Z3_HEADERS} > ${Z3_WRAPPER} + DEPENDS ${Z3_HEADERS} genz3wrapper.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + set(z3_SRCS ${z3_SRCS} ${Z3_WRAPPER} Z3Loader.cpp Z3Loader.h) +endif() + add_library(smtutil ${sources} ${z3_SRCS} ${cvc4_SRCS}) target_link_libraries(smtutil PUBLIC solutil Boost::boost) -if (${Z3_FOUND}) +if (${USE_Z3_DLOPEN}) + target_include_directories(smtutil PUBLIC ${Z3_HEADER_PATH}) + target_link_libraries(smtutil PUBLIC ${CMAKE_DL_LIBS}) +elseif (${Z3_FOUND}) target_link_libraries(smtutil PUBLIC z3::libz3) endif() diff --git a/libsmtutil/SMTPortfolio.cpp b/libsmtutil/SMTPortfolio.cpp index 4718d36c4..de00c1f79 100644 --- a/libsmtutil/SMTPortfolio.cpp +++ b/libsmtutil/SMTPortfolio.cpp @@ -42,7 +42,7 @@ SMTPortfolio::SMTPortfolio( { m_solvers.emplace_back(make_unique(move(_smtlib2Responses), move(_smtCallback), m_queryTimeout)); #ifdef HAVE_Z3 - if (_enabledSolvers.z3) + if (_enabledSolvers.z3 && Z3Interface::available()) m_solvers.emplace_back(make_unique(m_queryTimeout)); #endif #ifdef HAVE_CVC4 diff --git a/libsmtutil/Z3Interface.cpp b/libsmtutil/Z3Interface.cpp index 46e601fdf..8d7a03392 100644 --- a/libsmtutil/Z3Interface.cpp +++ b/libsmtutil/Z3Interface.cpp @@ -21,12 +21,23 @@ #include #include -#include +#ifdef HAVE_Z3_DLOPEN +#include +#endif using namespace std; using namespace solidity::smtutil; using namespace solidity::util; +bool Z3Interface::available() +{ +#ifdef HAVE_Z3_DLOPEN + return Z3Loader::get().available(); +#else + return true; +#endif +} + Z3Interface::Z3Interface(std::optional _queryTimeout): SolverInterface(_queryTimeout), m_solver(m_context) diff --git a/libsmtutil/Z3Interface.h b/libsmtutil/Z3Interface.h index 16c8ceb2b..d565b62f1 100644 --- a/libsmtutil/Z3Interface.h +++ b/libsmtutil/Z3Interface.h @@ -30,6 +30,8 @@ class Z3Interface: public SolverInterface, public boost::noncopyable public: Z3Interface(std::optional _queryTimeout = {}); + static bool available(); + void reset() override; void push() override; diff --git a/libsmtutil/Z3Loader.cpp b/libsmtutil/Z3Loader.cpp new file mode 100644 index 000000000..86b17aa03 --- /dev/null +++ b/libsmtutil/Z3Loader.cpp @@ -0,0 +1,70 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include +#include +#include +#include + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include + +using namespace std; +using namespace solidity; +using namespace solidity::smtutil; + +Z3Loader const& Z3Loader::get() +{ + static Z3Loader z3; + return z3; +} + +void* Z3Loader::loadSymbol(char const* _name) const +{ + smtAssert(m_handle, "Attempted to use dynamically loaded Z3, even though it is not available."); + void* sym = dlsym(m_handle, _name); + smtAssert(sym, string("Symbol \"") + _name + "\" not found in libz3.so"); + return sym; +} + +bool Z3Loader::available() const +{ + if (m_handle == nullptr) + return false; + unsigned major = 0; + unsigned minor = 0; + unsigned build = 0; + unsigned rev = 0; + Z3_get_version(&major, &minor, &build, &rev); + return major == Z3_MAJOR_VERSION && minor == Z3_MINOR_VERSION; +} + +Z3Loader::Z3Loader() +{ + string libname{"libz3.so." + to_string(Z3_MAJOR_VERSION) + "." + to_string(Z3_MINOR_VERSION)}; + m_handle = dlmopen(LM_ID_NEWLM, libname.c_str(), RTLD_NOW); +} + +Z3Loader::~Z3Loader() +{ + if (m_handle) + dlclose(m_handle); +} diff --git a/libsmtutil/Z3Loader.h b/libsmtutil/Z3Loader.h new file mode 100644 index 000000000..5e755cb24 --- /dev/null +++ b/libsmtutil/Z3Loader.h @@ -0,0 +1,38 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +namespace solidity::smtutil +{ + +class Z3Loader +{ +public: + Z3Loader(Z3Loader const&) = delete; + Z3Loader& operator=(Z3Loader const&) = delete; + static Z3Loader const& get(); + void* loadSymbol(char const* _name) const; + bool available() const; +private: + Z3Loader(); + ~Z3Loader(); + void* m_handle = nullptr; +}; + +} \ No newline at end of file diff --git a/libsmtutil/genz3wrapper.py b/libsmtutil/genz3wrapper.py new file mode 100755 index 000000000..9b24b32db --- /dev/null +++ b/libsmtutil/genz3wrapper.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# ------------------------------------------------------------------------------ +# 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 +#------------------------------------------------------------------------------ +# +# Script that generates a dlsym-wrapper for Z3 from the header files. +# Expects all Z3 headers as arguments and outputs the wrapper code to stdout. + +import sys +import re + +# Patterns to match Z3 API entry point definitions. +def_pat = re.compile(" *def_API(.*)") +extradef_pat = re.compile(" *extra_API(.*)") +# Pattern to extract name and arguments from the above. +def_args_pat = re.compile("\('([^']*)'[^\(\)]*\((.*)\)\s*\)") +# Pattern to extract a list of arguments from the above. +arg_list_pat = re.compile("[^\(]*\([^\)]*\)[, ]*") + +def generateEntryPoint(line, args): + m = def_args_pat.match(args) + if not m: + raise Exception('Could not parse entry point definition: ' + line) + name = m.group(1) + num_args = len(arg_list_pat.findall(m.group(2))) + arglist = ', '.join(f"_{i}" for i in range(num_args)) + paramlist = ', '.join(f"ArgType<&{name}, {i}> _{i}" for i in range(num_args)) + print(f'ResultType<&{name}> Z3_API {name}({paramlist})') + print('{') + print(f'\tstatic auto sym = reinterpret_cast(Z3Loader::get().loadSymbol(\"{name}\"));') + print(f'\treturn sym({arglist});') + print('}') + + +print(r"""// This file is auto-generated from genz3wrapper.py +#include +#include +#include + +namespace +{ + +template +struct FunctionTrait; + +template +struct FunctionTrait +{ + using ResultType = R; + template + using ArgType = std::tuple_element_t>; +}; + +template +using ResultType = typename FunctionTrait::ResultType; + +template +using ArgType = typename FunctionTrait::template ArgType; + +} + +using namespace solidity; +using namespace solidity::smtutil; + +extern "C" +{ + +void Z3_API Z3_set_error_handler(Z3_context c, Z3_error_handler h) +{ + static auto sym = reinterpret_cast(Z3Loader::get().loadSymbol("Z3_set_error_handler")); + sym(c, h); +} +""") + +for header in sys.argv[1:]: + with open(header, 'r') as f: + for line in f: + line = line.strip('\r\n\t ') + m = def_pat.match(line) + if m: + generateEntryPoint(line, m.group(1).strip('\r\n\t ')) + m = extradef_pat.match(line) + if m: + generateEntryPoint(line, m.group(1).strip('\r\n\t ')) + +print('}') diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 62e759632..957df4519 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1437,19 +1437,27 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type) { solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.isDynamicallySized(), ""); - solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!"); solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented."); string functionName = "array_push_zero_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (array) -> slot, offset { - let oldLen := (array) - if iszero(lt(oldLen, )) { () } - sstore(array, add(oldLen, 1)) + + let data := sload(array) + let oldLen := (data) + (array, data, oldLen, add(oldLen, 1)) + + let oldLen := (array) + if iszero(lt(oldLen, )) { () } + sstore(array, add(oldLen, 1)) + slot, offset := (array, oldLen) })") ("functionName", functionName) + ("isBytes", _type.isByteArray()) + ("increaseBytesSize", _type.isByteArray() ? increaseByteArraySizeFunction(_type) : "") + ("extractLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "") ("panic", panicFunction(PanicCode::ResourceError)) ("fetchLength", arrayLengthFunction(_type)) ("indexAccess", storageArrayIndexAccessFunction(_type)) diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 7e8bd7d11..1b47a24f5 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -23,6 +23,10 @@ #include +#ifdef HAVE_Z3_DLOPEN +#include +#endif + using namespace std; using namespace solidity; using namespace solidity::util; @@ -83,7 +87,10 @@ void BMC::analyze(SourceUnit const& _source, map +#ifdef HAVE_Z3_DLOPEN +#include +#endif + #include using namespace std; @@ -58,7 +62,9 @@ CHC::CHC( m_queryTimeout(_timeout) { bool usesZ3 = _enabledSolvers.z3; -#ifndef HAVE_Z3 +#ifdef HAVE_Z3 + usesZ3 = usesZ3 && Z3Interface::available(); +#else usesZ3 = false; #endif if (!usesZ3) @@ -88,16 +94,19 @@ void CHC::analyze(SourceUnit const& _source) } bool ranSolver = true; -#ifndef HAVE_Z3 - ranSolver = dynamic_cast(m_interface.get())->unhandledQueries().empty(); -#endif + if (auto const* smtLibInterface = dynamic_cast(m_interface.get())) + ranSolver = smtLibInterface->unhandledQueries().empty(); if (!ranSolver && !m_noSolverWarning) { m_noSolverWarning = true; m_outerErrorReporter.warning( 3996_error, SourceLocation(), +#ifdef HAVE_Z3_DLOPEN + "CHC analysis was not possible since libz3.so." + to_string(Z3_MAJOR_VERSION) + "." + to_string(Z3_MINOR_VERSION) + " was not found." +#else "CHC analysis was not possible since no integrated z3 SMT solver was found." +#endif ); } else @@ -762,7 +771,7 @@ void CHC::resetSourceAnalysis() bool usesZ3 = false; #ifdef HAVE_Z3 - usesZ3 = m_enabledSolvers.z3; + usesZ3 = m_enabledSolvers.z3 && Z3Interface::available(); if (usesZ3) { /// z3::fixedpoint does not have a reset mechanism, so we need to create another. diff --git a/libsolidity/formal/ModelChecker.cpp b/libsolidity/formal/ModelChecker.cpp index eab27589b..809b805c6 100644 --- a/libsolidity/formal/ModelChecker.cpp +++ b/libsolidity/formal/ModelChecker.cpp @@ -17,6 +17,9 @@ // SPDX-License-Identifier: GPL-3.0 #include +#ifdef HAVE_Z3 +#include +#endif using namespace std; using namespace solidity; @@ -63,7 +66,7 @@ solidity::smtutil::SMTSolverChoice ModelChecker::availableSolvers() { smtutil::SMTSolverChoice available = smtutil::SMTSolverChoice::None(); #ifdef HAVE_Z3 - available.z3 = true; + available.z3 = solidity::smtutil::Z3Interface::available(); #endif #ifdef HAVE_CVC4 available.cvc4 = true; diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt index 3fbdefe6a..a94638090 100644 --- a/solc/CMakeLists.txt +++ b/solc/CMakeLists.txt @@ -20,4 +20,9 @@ if(SOLC_LINK_STATIC AND UNIX AND NOT APPLE) LINK_SEARCH_START_STATIC ON LINK_SEARCH_END_STATIC ON ) -endif() \ No newline at end of file +elseif(SOLC_STATIC_STDLIBS AND UNIX AND NOT APPLE) + set_target_properties( + solc PROPERTIES + LINK_FLAGS "-static-libgcc -static-libstdc++" + ) +endif() diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index e3c2872fd..cea3a8847 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -821,28 +821,6 @@ BOOST_AUTO_TEST_CASE(constructor) ) } -BOOST_AUTO_TEST_CASE(blockchain) -{ - char const* sourceCode = R"( - contract test { - constructor() payable {} - function someInfo() public payable returns (uint256 value, address coinbase, uint256 blockNumber) { - value = msg.value; - coinbase = block.coinbase; - blockNumber = block.number; - } - } - )"; - m_evmcHost->tx_context.block_coinbase = EVMHost::convertToEVMC(h160("0x1212121212121212121212121212121212121212")); - m_evmcHost->newBlock(); - m_evmcHost->newBlock(); - m_evmcHost->newBlock(); - m_evmcHost->newBlock(); - m_evmcHost->newBlock(); - compileAndRun(sourceCode, 27); - ABI_CHECK(callContractFunctionWithValue("someInfo()", 28), encodeArgs(28, u256("0x1212121212121212121212121212121212121212"), 7)); -} - BOOST_AUTO_TEST_CASE(send_ether) { char const* sourceCode = R"( @@ -905,25 +883,6 @@ BOOST_AUTO_TEST_CASE(transfer_ether) ) } -BOOST_AUTO_TEST_CASE(uncalled_blockhash) -{ - char const* code = R"( - contract C { - function f() public view returns (bytes32) - { - return (blockhash)(block.number - 1); - } - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - compileAndRun(code, 0, "C"); - bytes result = callContractFunction("f()"); - BOOST_REQUIRE_EQUAL(result.size(), 32); - BOOST_CHECK(result[0] != 0 || result[1] != 0 || result[2] != 0); - ) -} - BOOST_AUTO_TEST_CASE(selfdestruct) { char const* sourceCode = R"( @@ -1381,41 +1340,6 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses) BOOST_REQUIRE(callContractFunction("getBalance()") == encodeArgs(u256(20 - 5), u256(5))); } -BOOST_AUTO_TEST_CASE(gaslimit) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - return block.gaslimit; - } - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - - compileAndRun(sourceCode); - auto result = callContractFunction("f()"); - ABI_CHECK(result, encodeArgs(gasLimit())); - ) -} - -BOOST_AUTO_TEST_CASE(gasprice) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - return tx.gasprice; - } - } - )"; - ALSO_VIA_YUL( - DISABLE_EWASM_TESTRUN() - - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(gasPrice())); - ) -} - BOOST_AUTO_TEST_CASE(blockhash) { char const* sourceCode = R"( diff --git a/test/libsolidity/semanticTests/array/copying/empty_bytes_copy.sol b/test/libsolidity/semanticTests/array/copying/empty_bytes_copy.sol index 91b6fd1a0..63ef7fba9 100644 --- a/test/libsolidity/semanticTests/array/copying/empty_bytes_copy.sol +++ b/test/libsolidity/semanticTests/array/copying/empty_bytes_copy.sol @@ -22,6 +22,8 @@ contract C { return data[0]; } } +// ==== +// compileViaYul: also // ---- // fromMemory() -> 0x00 // fromCalldata(bytes): 0x40, 0x60, 0x00, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 diff --git a/test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol b/test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol index dc99dbbd5..45bd4bfec 100644 --- a/test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol +++ b/test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol @@ -18,6 +18,8 @@ contract C { return array[index]; } } +// ==== +// compileViaYul: also // ---- // l() -> 0 // g(uint256): 70 -> diff --git a/test/libsolidity/semanticTests/state/block_coinbase.sol b/test/libsolidity/semanticTests/state/block_coinbase.sol new file mode 100644 index 000000000..45cb64c5d --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_coinbase.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (address payable) { + return block.coinbase; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x7878787878787878787878787878787878787878 +// f() -> 0x7878787878787878787878787878787878787878 +// f() -> 0x7878787878787878787878787878787878787878 diff --git a/test/libsolidity/semanticTests/state/block_difficulty.sol b/test/libsolidity/semanticTests/state/block_difficulty.sol new file mode 100644 index 000000000..1b5966fb0 --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_difficulty.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (uint) { + return block.difficulty; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 200000000 +// f() -> 200000000 +// f() -> 200000000 diff --git a/test/libsolidity/semanticTests/state/block_gaslimit.sol b/test/libsolidity/semanticTests/state/block_gaslimit.sol new file mode 100644 index 000000000..7fb313825 --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_gaslimit.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (uint) { + return block.gaslimit; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 20000000 +// f() -> 20000000 +// f() -> 20000000 diff --git a/test/libsolidity/semanticTests/state/block_number.sol b/test/libsolidity/semanticTests/state/block_number.sol new file mode 100644 index 000000000..a08261b92 --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_number.sol @@ -0,0 +1,12 @@ +contract C { + constructor() {} + function f() public returns (uint) { + return block.number; + } +} +// ==== +// compileViaYul: also +// ---- +// constructor() +// f() -> 2 +// f() -> 3 diff --git a/test/libsolidity/semanticTests/state/block_timestamp.sol b/test/libsolidity/semanticTests/state/block_timestamp.sol new file mode 100644 index 000000000..4b4bba38d --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_timestamp.sol @@ -0,0 +1,12 @@ +contract C { + constructor() {} + function f() public returns (uint) { + return block.timestamp; + } +} +// ==== +// compileViaYul: also +// ---- +// constructor() # This is the 1st block # +// f() -> 0x1e # This is the 2nd block (each block is "15 seconds") # +// f() -> 0x2d # This is the 3rd block # diff --git a/test/libsolidity/semanticTests/state/blockhash_basic.sol b/test/libsolidity/semanticTests/state/blockhash_basic.sol new file mode 100644 index 000000000..528c8aea6 --- /dev/null +++ b/test/libsolidity/semanticTests/state/blockhash_basic.sol @@ -0,0 +1,23 @@ +contract C { + bytes32 public genesisHash; + bytes32 public currentHash; + constructor() { + require(block.number == 1); + genesisHash = blockhash(0); + currentHash = blockhash(1); + } + function f(uint blockNumber) public returns (bytes32) { + return blockhash(blockNumber); + } +} +// ==== +// compileViaYul: also +// ---- +// constructor() +// genesisHash() -> 0x3737373737373737373737373737373737373737373737373737373737373737 +// currentHash() -> 0 +// f(uint256): 0 -> 0x3737373737373737373737373737373737373737373737373737373737373737 +// f(uint256): 1 -> 0x3737373737373737373737373737373737373737373737373737373737373738 +// f(uint256): 255 -> 0x00 +// f(uint256): 256 -> 0x00 +// f(uint256): 257 -> 0x00 diff --git a/test/libsolidity/semanticTests/state/gasleft.sol b/test/libsolidity/semanticTests/state/gasleft.sol new file mode 100644 index 000000000..0339ff17a --- /dev/null +++ b/test/libsolidity/semanticTests/state/gasleft.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (bool) { + return gasleft() > 0; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> true +// f() -> true +// f() -> true diff --git a/test/libsolidity/semanticTests/state/msg_data.sol b/test/libsolidity/semanticTests/state/msg_data.sol new file mode 100644 index 000000000..6217da3b6 --- /dev/null +++ b/test/libsolidity/semanticTests/state/msg_data.sol @@ -0,0 +1,13 @@ +contract C { + function f() public returns (bytes calldata) { + return msg.data; + } + function g(uint,bool) public returns (bytes calldata) { + return msg.data; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x20, 4, 17219911917854084299749778639755835327755045716242581057573779540915269926912 +// g(uint256,bool): 1234, true -> 0x20, 0x44, 35691323728519381642872894128098848782337736632589179916067422734266033766400, 33268574187263889506619096617382224251268236217415066441681855047532544, 26959946667150639794667015087019630673637144422540572481103610249216 diff --git a/test/libsolidity/semanticTests/state/msg_sender.sol b/test/libsolidity/semanticTests/state/msg_sender.sol new file mode 100644 index 000000000..6348bb477 --- /dev/null +++ b/test/libsolidity/semanticTests/state/msg_sender.sol @@ -0,0 +1,9 @@ +contract C { + function f() public returns (address payable) { + return msg.sender; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x1212121212121212121212121212120000000012 diff --git a/test/libsolidity/semanticTests/state/msg_sig.sol b/test/libsolidity/semanticTests/state/msg_sig.sol new file mode 100644 index 000000000..e7c7b1b75 --- /dev/null +++ b/test/libsolidity/semanticTests/state/msg_sig.sol @@ -0,0 +1,13 @@ +contract C { + function f() public returns (bytes4) { + return msg.sig; + } + function g() public returns (bytes4) { + return msg.sig; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 +// g() -> 0xe2179b8e00000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/state/msg_value.sol b/test/libsolidity/semanticTests/state/msg_value.sol new file mode 100644 index 000000000..fa7ca3db6 --- /dev/null +++ b/test/libsolidity/semanticTests/state/msg_value.sol @@ -0,0 +1,10 @@ +contract C { + function f() public payable returns (uint) { + return msg.value; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0 +// f(), 12 ether -> 12000000000000000000 diff --git a/test/libsolidity/semanticTests/state/tx_gasprice.sol b/test/libsolidity/semanticTests/state/tx_gasprice.sol new file mode 100644 index 000000000..321e2f55e --- /dev/null +++ b/test/libsolidity/semanticTests/state/tx_gasprice.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (uint) { + return tx.gasprice; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 3000000000 +// f() -> 3000000000 +// f() -> 3000000000 diff --git a/test/libsolidity/semanticTests/state/tx_origin.sol b/test/libsolidity/semanticTests/state/tx_origin.sol new file mode 100644 index 000000000..caa27d180 --- /dev/null +++ b/test/libsolidity/semanticTests/state/tx_origin.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (address payable) { + return tx.origin; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x9292929292929292929292929292929292929292 +// f() -> 0x9292929292929292929292929292929292929292 +// f() -> 0x9292929292929292929292929292929292929292 diff --git a/test/libsolidity/semanticTests/state/uncalled_blockhash.sol b/test/libsolidity/semanticTests/state/uncalled_blockhash.sol new file mode 100644 index 000000000..9a964335d --- /dev/null +++ b/test/libsolidity/semanticTests/state/uncalled_blockhash.sol @@ -0,0 +1,9 @@ +contract C { + function f() public returns (bytes32) { + return (blockhash)(block.number - 1); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x3737373737373737373737373737373737373737373737373737373737373738 diff --git a/test/libsolidity/syntaxTests/types/magic_block.sol b/test/libsolidity/syntaxTests/types/magic_block.sol new file mode 100644 index 000000000..d0eae923b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/magic_block.sol @@ -0,0 +1,16 @@ +contract C { + function f() public view returns (address payable) { + return block.coinbase; + } + function g() public view returns (uint) { + return block.difficulty; + } + function h() public view returns (uint) { + return block.gaslimit; + } + function i() public view returns (uint) { + return block.timestamp; + } +} +// ==== +// EVMVersion: =istanbul diff --git a/test/tools/ossfuzz/protoToAbiV2.cpp b/test/tools/ossfuzz/protoToAbiV2.cpp index a454a2723..cee5a1cf7 100644 --- a/test/tools/ossfuzz/protoToAbiV2.cpp +++ b/test/tools/ossfuzz/protoToAbiV2.cpp @@ -1,10 +1,98 @@ #include +#include + +/// Convenience macros +/// Returns a valid Solidity integer width w such that 8 <= w <= 256. +#define INTWIDTH(z, n, _ununsed) BOOST_PP_MUL(BOOST_PP_ADD(n, 1), 8) +/// Using declaration that aliases long boost multiprecision types with +/// s(u) where is a valid Solidity integer width and "s" +/// stands for "signed" and "u" for "unsigned". +#define USINGDECL(z, n, sign) \ + using BOOST_PP_CAT(BOOST_PP_IF(sign, s, u), INTWIDTH(z, n,)) = \ + boost::multiprecision::number< \ + boost::multiprecision::cpp_int_backend< \ + INTWIDTH(z, n,), \ + INTWIDTH(z, n,), \ + BOOST_PP_IF( \ + sign, \ + boost::multiprecision::signed_magnitude, \ + boost::multiprecision::unsigned_magnitude \ + ), \ + boost::multiprecision::unchecked, \ + void \ + > \ + >; +/// Instantiate the using declarations for signed and unsigned integer types. +BOOST_PP_REPEAT(32, USINGDECL, 1) +BOOST_PP_REPEAT(32, USINGDECL, 0) +/// Case implementation that returns an integer value of the specified type. +/// For signed integers, we divide by two because the range for boost multiprecision +/// types is double that of Solidity integer types. Example, 8-bit signed boost +/// number range is [-255, 255] but Solidity `int8` range is [-128, 127] +#define CASEIMPL(z, n, sign) \ + case INTWIDTH(z, n,): \ + stream << BOOST_PP_IF( \ + sign, \ + integerValue< \ + BOOST_PP_CAT( \ + BOOST_PP_IF(sign, s, u), \ + INTWIDTH(z, n,) \ + )>(_counter) / 2, \ + integerValue< \ + BOOST_PP_CAT( \ + BOOST_PP_IF(sign, s, u), \ + INTWIDTH(z, n,) \ + )>(_counter) \ + ); \ + break; +/// Switch implementation that instantiates case statements for (un)signed +/// Solidity integer types. +#define SWITCHIMPL(sign) \ + ostringstream stream; \ + switch (_intWidth) \ + { \ + BOOST_PP_REPEAT(32, CASEIMPL, sign) \ + } \ + return stream.str(); + using namespace std; -using namespace solidity; using namespace solidity::util; using namespace solidity::test::abiv2fuzzer; +namespace +{ +template +static V integerValue(unsigned _counter) +{ + V value = V( + u256(solidity::util::keccak256(solidity::util::h256(_counter))) % u256(boost::math::tools::max_value()) + ); + if (value % 2 == 0) + return value * (-1); + else + return value; +} + +static string signedIntegerValue(unsigned _counter, unsigned _intWidth) +{ + SWITCHIMPL(1) +} + +static string unsignedIntegerValue(unsigned _counter, unsigned _intWidth) +{ + SWITCHIMPL(0) +} + +static string integerValue(unsigned _counter, unsigned _intWidth, bool _signed) +{ + if (_signed) + return signedIntegerValue(_counter, _intWidth); + else + return unsignedIntegerValue(_counter, _intWidth); +} +} + string ProtoConverter::getVarDecl( string const& _type, string const& _varName, @@ -1013,11 +1101,7 @@ string ValueGetterVisitor::visit(BoolType const&) string ValueGetterVisitor::visit(IntegerType const& _type) { - return integerValueAsString( - _type.is_signed(), - getIntWidth(_type), - counter() - ); + return integerValue(counter(), getIntWidth(_type), _type.is_signed()); } string ValueGetterVisitor::visit(FixedByteType const& _type) @@ -1041,48 +1125,6 @@ string ValueGetterVisitor::visit(DynamicByteArrayType const& _type) ); } -std::string ValueGetterVisitor::integerValueAsString(bool _sign, unsigned _width, unsigned _counter) -{ - if (_sign) - return intValueAsString(_width, _counter); - else - return uintValueAsString(_width, _counter); -} - -/* Input(s) - * - Unsigned integer to be hashed - * - Width of desired uint value - * Processing - * - Take hash of first parameter and mask it with the max unsigned value for given bit width - * Output - * - string representation of uint value - */ -std::string ValueGetterVisitor::uintValueAsString(unsigned _width, unsigned _counter) -{ - solAssert( - (_width % 8 == 0), - "Proto ABIv2 Fuzzer: Unsigned integer width is not a multiple of 8" - ); - return maskUnsignedIntToHex(_counter, _width/4); -} - -/* Input(s) - * - counter to be hashed to derive a value for Integer type - * - Width of desired int value - * Processing - * - Take hash of first parameter and mask it with the max signed value for given bit width - * Output - * - string representation of int value - */ -std::string ValueGetterVisitor::intValueAsString(unsigned _width, unsigned _counter) -{ - solAssert( - (_width % 8 == 0), - "Proto ABIv2 Fuzzer: Signed integer width is not a multiple of 8" - ); - return maskUnsignedIntToHex(_counter, ((_width/4) - 1)); -} - std::string ValueGetterVisitor::croppedString( unsigned _numBytes, unsigned _counter, @@ -1153,9 +1195,10 @@ std::string ValueGetterVisitor::fixedByteValueAsString(unsigned _width, unsigned std::string ValueGetterVisitor::addressValueAsString(unsigned _counter) { - return Whiskers(R"(address())") - ("value", uintValueAsString(160, _counter)) - .render(); + // TODO: Isabelle encoder expects address literal to be exactly + // 20 bytes and a hex string. + // Example: 0x0102030405060708090a0102030405060708090a + return "address(" + maskUnsignedIntToHex(_counter, 40) + ")"; } std::string ValueGetterVisitor::variableLengthValueAsString( diff --git a/test/tools/ossfuzz/protoToAbiV2.h b/test/tools/ossfuzz/protoToAbiV2.h index ee50947bc..146e444a2 100644 --- a/test/tools/ossfuzz/protoToAbiV2.h +++ b/test/tools/ossfuzz/protoToAbiV2.h @@ -792,9 +792,6 @@ private: return m_counter++; } - static std::string intValueAsString(unsigned _width, unsigned _counter); - static std::string uintValueAsString(unsigned _width, unsigned _counter); - static std::string integerValueAsString(bool _sign, unsigned _width, unsigned _counter); static std::string addressValueAsString(unsigned _counter); static std::string fixedByteValueAsString(unsigned _width, unsigned _counter);