diff options
87 files changed, 6531 insertions, 324 deletions
@@ -34,3 +34,7 @@ docs/utils/__pycache__ # vim stuff *.swp + +# IDE files +.idea +browse.VC.db diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..ba66d79f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "deps"] + path = deps + url = https://github.com/ethereum/cpp-dependencies diff --git a/.travis.yml b/.travis.yml index 3a4115f2..82f79a7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,125 @@ +#------------------------------------------------------------------------------ +# TravisCI configuration file for solidity. +# +# The documentation for solidity is hosted at: +# +# http://solidity.readthedocs.org +# +# TODO - Merge with .travis.yml file which currently lives in the root of +# webthree-umbrella, but actually contains the automation for the Solidity +# Emscripten build, which will also need consolidating into here somehow. +# +# See https://github.com/ethereum/webthree-umbrella/blob/develop/.travis.yml +# +# ------------------------------------------------------------------------------ +# 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 <http://www.gnu.org/licenses/> +# +# (c) 2016 cpp-ethereum contributors. +#------------------------------------------------------------------------------ + language: cpp -cache: ccache +branches: + only: + - develop + - release + - standalone + except: + - /develop-v[0-9]/ matrix: include: - os: linux dist: trusty sudo: required - env: TRAVIS_BUILD_TYPE=Debug + compiler: gcc + services: + - docker + before_install: + - docker pull trzeci/emscripten:sdk-tag-1.35.4-64bit + env: + - TRAVIS_BUILD_TYPE=RelWithDebInfo + - ZIP_SUFFIX=ubuntu-trusty + - os: osx + osx_image: beta-xcode6.2 + env: + - TRAVIS_BUILD_TYPE=RelWithDebInfo + - ZIP_SUFFIX=osx-mavericks + - os: osx + osx_image: xcode7.1 + env: + - TRAVIS_BUILD_TYPE=RelWithDebInfo + - ZIP_SUFFIX=osx-yosemite - os: osx osx_image: xcode7.3 - env: TRAVIS_BUILD_TYPE=Debug + env: + # The use of Debug config here ONLY for El Capitan is a workaround for "The Heisenbug" + # See https://github.com/ethereum/webthree-umbrella/issues/565 + - TRAVIS_BUILD_TYPE=Debug + - ZIP_SUFFIX=osx-elcapitan git: depth: 2 -before_install: ./install_dependencies.sh -script: ./fetch_umbrella_build_and_test.sh
\ No newline at end of file +install: + - ./scripts/install_deps.sh +before_script: + - ./scripts/build_emscripten.sh + - mkdir -p build + && cd build + && cmake .. -DCMAKE_BUILD_TYPE=$TRAVIS_BUILD_TYPE + && make -j2 + && cd .. + && ./scripts/release.sh $ZIP_SUFFIX +script: + # There are a variety of reliability issues with the Solidity unit-tests at the time of + # writing, so we're actually running them 5 times in a row, to try to flush all of these + # issues out as quickly as possible. See https://github.com/ethereum/solidity/issues/769 + - cd $TRAVIS_BUILD_DIR && ./scripts/tests.sh + - cd $TRAVIS_BUILD_DIR && ./scripts/tests.sh + - cd $TRAVIS_BUILD_DIR && ./scripts/tests.sh + - cd $TRAVIS_BUILD_DIR && ./scripts/tests.sh + - cd $TRAVIS_BUILD_DIR && ./scripts/tests.sh +after_success: + - cd $TRAVIS_BUILD_DIR && ./scripts/docs.sh +env: + global: + - ENCRYPTION_LABEL="296c219a3f41" + +# This is the deploy target for the native build (Linux and macOS) +# which generates development ZIPs per commit. We are in agreement +# that this is probably noisy overkill, so will want to renable this +# functionality wrapped in some conditionals so that it only runs +# when building the 'release' branch. +# +#deploy: +# provider: releases +# api_key: +# secure: mGeDrlCbhPNQVqrk5wSqFZe/7C5HUIBWcZECJcFrEldN6ELj3a8mhDX9EWebidyFmZsf3ipKVMycJtXZHlH2kVZ0nZdRulq4bYhLiUFRaFQCHMW35dml5mxO/FPp+jhhZaylDUx+cI6AULbj8DvNFqSCfjx8qimRhJjRY4JHeG71N6g5+LU2/dA01D4Y97BUbQ5dYcmEyuEsriSpXOElIQIIv3+Q6MJNnLzxUA6EXsp4Qt3Qf3R1+EkI/RWOPbQsddpFNJBcBNOscCUFSZV3+ZK9E2RyHbPaL+Da4aJKVpgD7X1TFudq4PClMUTkg8CuJh/kvy9wkpaWyYHbLGQqu1vQ+NQ+vlTAKf8U+1xhC1IkX6nA+g4TlRksZRltRhpkBPnFoaQQGjD9eYyT1V/Htrn8Y/VGbYPBVa0GcEYXG5pDCBPz91RBpYwDcyUg9DEPNI6zYfQY8jA8xqtFwPX79Y22MDXIKhBskK00geuzh7Npy5Rnh4fLqVKMlffvYx3kwp444EFWtQ5jxbRCym2Th6EldkOM8Kble1JvixQtVb4s+DpTuwHCTrXZhwMuJpmwgRx52zyIsGrBPF3MOmdlwl+l0TD8UEJXt0JM1XSF1AROyYwXKDZQ9Qt4sd9ZKSWWaLJMEJf4kkZWYgTEI/FhOWlfshQCt8Z9S9r4fq2ywoo= +# file: $TRAVIS_BUILD_DIR/solidity-develop-$ZIP_SUFFIX.zip +# skip_cleanup: true +# on: +# repo: bobsummerwill/solidity +# branch: standalone_changes + +# This is the deploy target for the Emscripten build, which publishes +# generated JS for particular Solidity commits into +# https://github.com/ethereum/solc-bin/tree/gh-pages/bin +# Disabled, but can be renamed when we have the "standalone" changes +# published back into the 'develop' branch. +# +#deploy: +# provider: script +# script: scripts/travis-emscripten/publish_binary.sh +# skip_cleanup: true +# on: +# branch: develop diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d5bd606..95eaf91c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0.0) -set(ETH_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/../webthree-helpers/cmake" CACHE PATH "The the path to the cmake directory") +set(ETH_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake" CACHE PATH "The the path to the cmake directory") list(APPEND CMAKE_MODULE_PATH ${ETH_CMAKE_DIR}) # Set cmake_policies @@ -26,6 +26,7 @@ include(EthUtils) include(EthOptions) configure_project(TESTS) +add_subdirectory(libdevcore) add_subdirectory(libevmasm) add_subdirectory(libsolidity) add_subdirectory(solc) diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..b46e3155 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,76 @@ +#------------------------------------------------------------------------------ +# Appveyor configuration file for solidity. +# +# The documentation for solidity is hosted at: +# +# http://solidity.readthedocs.org +# +# TODO - Tests currently disabled, because Tests-over-IPC code is using UNIX +# sockets unconditionally at the time of writing. +# +# ------------------------------------------------------------------------------ +# 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 <http://www.gnu.org/licenses/> +# +# (c) 2016 cpp-ethereum contributors. +#------------------------------------------------------------------------------ + +version: 0.3.5.{build} +skip_tags: true +os: Visual Studio 2015 +configuration: + - RelWithDebInfo +cache: build +install: + - git submodule update --init --recursive + - scripts/install_deps.bat + - set ETHEREUM_DEPS_PATH=%APPVEYOR_BUILD_FOLDER%\deps\install +before_build: + - if not exist build mkdir build + - cd build + - cmake -G "Visual Studio 14 2015 Win64" .. -DTESTS=Off +build_script: + - msbuild solidity.sln /p:Configuration=%CONFIGURATION% /m:%NUMBER_OF_PROCESSORS% /v:minimal + - cd %APPVEYOR_BUILD_FOLDER% + - scripts\release.bat %CONFIGURATION% + +#test_script: +# - cd %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION% +# - copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll" . +# - eth.exe --test -d %TMP%/test +# - soltest.exe --ipc /tmp/test/geth.ipc +# - pkill eth + +artifacts: + - path: solidity-develop-windows.zip + name: solidity-develop-windows-zip + +# This is the deploy target for Windows +# which generates development ZIPs per commit. We are in agreement +# that this is probably noisy overkill, so will want to renable this +# functionality wrapped in some conditionals so that it only runs +# when building the 'release' branch. +# +#deploy: +# release: solidity-develop-v$(APPVEYOR_BUILD_VERSION) +# tag: develop-v$(APPVEYOR_BUILD_VERSION) +# description: 'Development build of solidity at commit $(APPVEYOR_REPO_COMMIT).\n\n$(APPVEYOR_REPO_COMMIT_MESSAGE)\n\nCommitted by $(APPVEYOR_REPO_COMMIT_AUTHOR), $(APPVEYOR_REPO_COMMIT_TIMESTAMP).' +# prerelease: true +# provider: GitHub +# auth_token: +# secure: yukM9mHUbzuZSS5WSBLKSW0yGJerJEqAXkFhDhSHBBcKJE7GAryjQsdO9Kxh3yRv +# artifact: solidity-develop-windows-zip +# on: +# branch: standalone_changes diff --git a/cmake/CMakeParseArguments.cmake b/cmake/CMakeParseArguments.cmake new file mode 100644 index 00000000..8553f38f --- /dev/null +++ b/cmake/CMakeParseArguments.cmake @@ -0,0 +1,161 @@ +#.rst: +# CMakeParseArguments +# ------------------- +# +# +# +# CMAKE_PARSE_ARGUMENTS(<prefix> <options> <one_value_keywords> +# <multi_value_keywords> args...) +# +# CMAKE_PARSE_ARGUMENTS() is intended to be used in macros or functions +# for parsing the arguments given to that macro or function. It +# processes the arguments and defines a set of variables which hold the +# values of the respective options. +# +# The <options> argument contains all options for the respective macro, +# i.e. keywords which can be used when calling the macro without any +# value following, like e.g. the OPTIONAL keyword of the install() +# command. +# +# The <one_value_keywords> argument contains all keywords for this macro +# which are followed by one value, like e.g. DESTINATION keyword of the +# install() command. +# +# The <multi_value_keywords> argument contains all keywords for this +# macro which can be followed by more than one value, like e.g. the +# TARGETS or FILES keywords of the install() command. +# +# When done, CMAKE_PARSE_ARGUMENTS() will have defined for each of the +# keywords listed in <options>, <one_value_keywords> and +# <multi_value_keywords> a variable composed of the given <prefix> +# followed by "_" and the name of the respective keyword. These +# variables will then hold the respective value from the argument list. +# For the <options> keywords this will be TRUE or FALSE. +# +# All remaining arguments are collected in a variable +# <prefix>_UNPARSED_ARGUMENTS, this can be checked afterwards to see +# whether your macro was called with unrecognized parameters. +# +# As an example here a my_install() macro, which takes similar arguments +# as the real install() command: +# +# :: +# +# function(MY_INSTALL) +# set(options OPTIONAL FAST) +# set(oneValueArgs DESTINATION RENAME) +# set(multiValueArgs TARGETS CONFIGURATIONS) +# cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}" +# "${multiValueArgs}" ${ARGN} ) +# ... +# +# +# +# Assume my_install() has been called like this: +# +# :: +# +# my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub) +# +# +# +# After the cmake_parse_arguments() call the macro will have set the +# following variables: +# +# :: +# +# MY_INSTALL_OPTIONAL = TRUE +# MY_INSTALL_FAST = FALSE (this option was not used when calling my_install() +# MY_INSTALL_DESTINATION = "bin" +# MY_INSTALL_RENAME = "" (was not used) +# MY_INSTALL_TARGETS = "foo;bar" +# MY_INSTALL_CONFIGURATIONS = "" (was not used) +# MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (no value expected after "OPTIONAL" +# +# +# +# You can then continue and process these variables. +# +# Keywords terminate lists of values, e.g. if directly after a +# one_value_keyword another recognized keyword follows, this is +# interpreted as the beginning of the new option. E.g. +# my_install(TARGETS foo DESTINATION OPTIONAL) would result in +# MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION +# would be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor. + +#============================================================================= +# Copyright 2010 Alexander Neundorf <neundorf@kde.org> +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + + +if(__CMAKE_PARSE_ARGUMENTS_INCLUDED) + return() +endif() +set(__CMAKE_PARSE_ARGUMENTS_INCLUDED TRUE) + + +function(CMAKE_PARSE_ARGUMENTS prefix _optionNames _singleArgNames _multiArgNames) + # first set all result variables to empty/FALSE + foreach(arg_name ${_singleArgNames} ${_multiArgNames}) + set(${prefix}_${arg_name}) + endforeach() + + foreach(option ${_optionNames}) + set(${prefix}_${option} FALSE) + endforeach() + + set(${prefix}_UNPARSED_ARGUMENTS) + + set(insideValues FALSE) + set(currentArgName) + + # now iterate over all arguments and fill the result variables + foreach(currentArg ${ARGN}) + list(FIND _optionNames "${currentArg}" optionIndex) # ... then this marks the end of the arguments belonging to this keyword + list(FIND _singleArgNames "${currentArg}" singleArgIndex) # ... then this marks the end of the arguments belonging to this keyword + list(FIND _multiArgNames "${currentArg}" multiArgIndex) # ... then this marks the end of the arguments belonging to this keyword + + if(${optionIndex} EQUAL -1 AND ${singleArgIndex} EQUAL -1 AND ${multiArgIndex} EQUAL -1) + if(insideValues) + if("${insideValues}" STREQUAL "SINGLE") + set(${prefix}_${currentArgName} ${currentArg}) + set(insideValues FALSE) + elseif("${insideValues}" STREQUAL "MULTI") + list(APPEND ${prefix}_${currentArgName} ${currentArg}) + endif() + else() + list(APPEND ${prefix}_UNPARSED_ARGUMENTS ${currentArg}) + endif() + else() + if(NOT ${optionIndex} EQUAL -1) + set(${prefix}_${currentArg} TRUE) + set(insideValues FALSE) + elseif(NOT ${singleArgIndex} EQUAL -1) + set(currentArgName ${currentArg}) + set(${prefix}_${currentArgName}) + set(insideValues "SINGLE") + elseif(NOT ${multiArgIndex} EQUAL -1) + set(currentArgName ${currentArg}) + set(${prefix}_${currentArgName}) + set(insideValues "MULTI") + endif() + endif() + + endforeach() + + # propagate the result variables to the caller: + foreach(arg_name ${_singleArgNames} ${_multiArgNames} ${_optionNames}) + set(${prefix}_${arg_name} ${${prefix}_${arg_name}} PARENT_SCOPE) + endforeach() + set(${prefix}_UNPARSED_ARGUMENTS ${${prefix}_UNPARSED_ARGUMENTS} PARENT_SCOPE) + +endfunction() diff --git a/cmake/EthBuildInfo.cmake b/cmake/EthBuildInfo.cmake new file mode 100644 index 00000000..cbb9dd24 --- /dev/null +++ b/cmake/EthBuildInfo.cmake @@ -0,0 +1,45 @@ +function(create_build_info NAME) + + # Set build platform; to be written to BuildInfo.h + set(ETH_BUILD_OS "${CMAKE_SYSTEM_NAME}") + + if (CMAKE_COMPILER_IS_MINGW) + set(ETH_BUILD_COMPILER "mingw") + elseif (CMAKE_COMPILER_IS_MSYS) + set(ETH_BUILD_COMPILER "msys") + elseif (CMAKE_COMPILER_IS_GNUCXX) + set(ETH_BUILD_COMPILER "g++") + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set(ETH_BUILD_COMPILER "msvc") + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(ETH_BUILD_COMPILER "clang") + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + set(ETH_BUILD_COMPILER "appleclang") + else () + set(ETH_BUILD_COMPILER "unknown") + endif () + + set(ETH_BUILD_PLATFORM "${ETH_BUILD_OS}/${ETH_BUILD_COMPILER}") + + #cmake build type may be not speCified when using msvc + if (CMAKE_BUILD_TYPE) + set(_cmake_build_type ${CMAKE_BUILD_TYPE}) + else() + set(_cmake_build_type "${CMAKE_CFG_INTDIR}") + endif() + + # Generate header file containing useful build information + add_custom_target(${NAME}_BuildInfo.h ALL + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -DETH_SOURCE_DIR="${PROJECT_SOURCE_DIR}" -DETH_BUILDINFO_IN="${ETH_CMAKE_DIR}/templates/BuildInfo.h.in" -DETH_DST_DIR="${PROJECT_BINARY_DIR}/include/${PROJECT_NAME}" -DETH_CMAKE_DIR="${ETH_CMAKE_DIR}" + -DETH_BUILD_TYPE="${_cmake_build_type}" + -DETH_BUILD_OS="${ETH_BUILD_OS}" + -DETH_BUILD_COMPILER="${ETH_BUILD_COMPILER}" + -DETH_BUILD_PLATFORM="${ETH_BUILD_PLATFORM}" + -DETH_BUILD_NUMBER="${BUILD_NUMBER}" + -DETH_VERSION_SUFFIX="${VERSION_SUFFIX}" + -DPROJECT_VERSION="${PROJECT_VERSION}" + -P "${ETH_SCRIPTS_DIR}/buildinfo.cmake" + ) + include_directories(BEFORE ${PROJECT_BINARY_DIR}) +endfunction() diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake new file mode 100644 index 00000000..066be4c1 --- /dev/null +++ b/cmake/EthCompilerSettings.cmake @@ -0,0 +1,247 @@ +#------------------------------------------------------------------------------ +# EthCompilerSettings.cmake +# +# CMake file for cpp-ethereum project which specifies our compiler settings +# for each supported platform and build configuration. +# +# See http://www.ethdocs.org/en/latest/ethereum-clients/cpp-ethereum/. +# +# Copyright (c) 2014-2016 cpp-ethereum contributors. +#------------------------------------------------------------------------------ + +# Clang seeks to be command-line compatible with GCC as much as possible, so +# most of our compiler settings are common between GCC and Clang. +# +# These settings then end up spanning all POSIX platforms (Linux, OS X, BSD, etc) + +# Use ccache if available +find_program(CCACHE_FOUND ccache) +if(CCACHE_FOUND) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) + set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) + message("Using ccache") +endif(CCACHE_FOUND) + +if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + + # Use ISO C++11 standard language. + set(CMAKE_CXX_FLAGS -std=c++11) + + # Enables all the warnings about constructions that some users consider questionable, + # and that are easy to avoid. Also enable some extra warning flags that are not + # enabled by -Wall. Finally, treat at warnings-as-errors, which forces developers + # to fix warnings as they arise, so they don't accumulate "to be fixed later". + add_compile_options(-Wall) + add_compile_options(-Wextra) + add_compile_options(-Werror) + + # Disable warnings about unknown pragmas (which is enabled by -Wall). I assume we have external + # dependencies (probably Boost) which have some of these. Whatever the case, we shouldn't be + # disabling these globally. Instead, we should pragma around just the problem #includes. + # + # TODO - Track down what breaks if we do NOT do this. + add_compile_options(-Wno-unknown-pragmas) + + # To get the code building on FreeBSD and Arch Linux we seem to need the following + # warning suppression to work around some issues in Boost headers. + # + # See the following reports: + # https://github.com/ethereum/webthree-umbrella/issues/384 + # https://github.com/ethereum/webthree-helpers/pull/170 + # + # The issue manifest as warnings-as-errors like the following: + # + # /usr/local/include/boost/multiprecision/cpp_int.hpp:181:4: error: + # right operand of shift expression '(1u << 63u)' is >= than the precision of the left operand + # + # -fpermissive is a pretty nasty way to address this. It is described as follows: + # + # Downgrade some diagnostics about nonconformant code from errors to warnings. + # Thus, using -fpermissive will allow some nonconforming code to compile. + # + # NB: Have to use this form for the setting, so that it only applies to C++ builds. + # Applying -fpermissive to a C command-line (ie. secp256k1) gives a build error. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") + + # Build everything as shared libraries (.so files) + add_definitions(-DSHAREDLIB) + + # If supported for the target machine, emit position-independent code, suitable for dynamic + # linking and avoiding any limit on the size of the global offset table. + add_compile_options(-fPIC) + + # Configuration-specific compiler settings. + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG") + set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") + + # Additional GCC-specific compiler settings. + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + + # Check that we've got GCC 4.7 or newer. + execute_process( + COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + if (NOT (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)) + message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.7 or greater.") + endif () + + # Strong stack protection was only added in GCC 4.9. + # Use it if we have the option to do so. + # See https://lwn.net/Articles/584225/ + if (GCC_VERSION VERSION_GREATER 4.9 OR GCC_VERSION VERSION_EQUAL 4.9) + add_compile_options(-fstack-protector-strong) + add_compile_options(-fstack-protector) + endif() + + # Additional Clang-specific compiler settings. + elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + + add_compile_options(-fstack-protector) + + # Enable strong stack protection only on Mac and only for OS X Yosemite + # or newer (AppleClang 7.0+). We should be able to re-enable this setting + # on non-Apple Clang as well, if we can work out what expression to use for + # the version detection. + + # The fact that the version-reporting for AppleClang loses the original + # Clang versioning is rather annoying. Ideally we could just have + # a single cross-platform "if version >= 3.4.1" check. + # + # There is debug text in the else clause below, to help us work out what + # such an expression should be, if we can get this running on a Trusty box + # with Clang. Greg Colvin previously replicated the issue there too. + # + # See https://github.com/ethereum/webthree-umbrella/issues/594 + + if (APPLE) + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) + add_compile_options(-fstack-protector-strong) + endif() + else() + message(WARNING "CMAKE_CXX_COMPILER_VERSION = ${CMAKE_CXX_COMPILER_VERSION}") + endif() + + # A couple of extra warnings suppressions which we seemingly + # need when building with Clang. + # + # TODO - Nail down exactly where these warnings are manifesting and + # try to suppress them in a more localized way. Notes in this file + # indicate that the first is needed for sepc256k1 and that the + # second is needed for the (clog, cwarn) macros. These will need + # testing on at least OS X and Ubuntu. + add_compile_options(-Wno-unused-function) + add_compile_options(-Wno-dangling-else) + + # Some Linux-specific Clang settings. We don't want these for OS X. + if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") + + # TODO - Is this even necessary? Why? + # See http://stackoverflow.com/questions/19774778/when-is-it-necessary-to-use-use-the-flag-stdlib-libstdc. + add_compile_options(-stdlib=libstdc++) + + # Tell Boost that we're using Clang's libc++. Not sure exactly why we need to do. + add_definitions(-DBOOST_ASIO_HAS_CLANG_LIBCXX) + + # Use fancy colors in the compiler diagnostics + add_compile_options(-fcolor-diagnostics) + + # See "How to silence unused command line argument error with clang without disabling it?" + # When using -Werror with clang, it transforms "warning: argument unused during compilation" messages + # into errors, which makes sense. + # http://stackoverflow.com/questions/21617158/how-to-silence-unused-command-line-argument-error-with-clang-without-disabling-i + add_compile_options(-Qunused-arguments) + endif() + + if (EMSCRIPTEN) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0 -O3 -s LINKABLE=1 -s DISABLE_EXCEPTION_CATCHING=0 -s NO_EXIT_RUNTIME=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_DYNAMIC_EXECUTION=1") + add_definitions(-DETH_EMSCRIPTEN=1) + endif() + endif() + +# The major alternative compiler to GCC/Clang is Microsoft's Visual C++ compiler, only available on Windows. +elseif (DEFINED MSVC) + + add_compile_options(/MP) # enable parallel compilation + add_compile_options(/EHsc) # specify Exception Handling Model in msvc + add_compile_options(/WX) # enable warnings-as-errors + add_compile_options(/wd4068) # disable unknown pragma warning (4068) + add_compile_options(/wd4996) # disable unsafe function warning (4996) + add_compile_options(/wd4503) # disable decorated name length exceeded, name was truncated (4503) + add_compile_options(/wd4267) # disable conversion from 'size_t' to 'type', possible loss of data (4267) + add_compile_options(/wd4180) # disable qualifier applied to function type has no meaning; ignored (4180) + add_compile_options(/wd4290) # disable C++ exception specification ignored except to indicate a function is not __declspec(nothrow) (4290) + add_compile_options(/wd4244) # disable conversion from 'type1' to 'type2', possible loss of data (4244) + add_compile_options(/wd4800) # disable forcing value to bool 'true' or 'false' (performance warning) (4800) + add_compile_options(-D_WIN32_WINNT=0x0600) # declare Windows Vista API requirement + add_compile_options(-DNOMINMAX) # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions + add_compile_options(-DMINIUPNP_STATICLIB) # define miniupnp static library + + # Always use Release variant of C++ runtime. + # We don't want to provide Debug variants of all dependencies. Some default + # flags set by CMake must be tweaked. + string(REPLACE "/MDd" "/MD" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) + string(REPLACE "/D_DEBUG" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) + string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) + string(REPLACE "/MDd" "/MD" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) + string(REPLACE "/D_DEBUG" "" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) + string(REPLACE "/RTC1" "" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) + set_property(GLOBAL PROPERTY DEBUG_CONFIGURATIONS OFF) + + # disable empty object file warning + set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") + # warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification + # warning LNK4099: pdb was not found with lib + # stack size 16MB + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075 /STACK:16777216") + + # windows likes static + if (NOT ETH_STATIC) + message("Forcing static linkage for MSVC.") + set(ETH_STATIC 1) + endif () + +# If you don't have GCC, Clang or VC++ then you are on your own. Good luck! +else () + message(WARNING "Your compiler is not tested, if you run into any issues, we'd welcome any patches.") +endif () + +if (SANITIZE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=${SANITIZE}") + if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/sanitizer-blacklist.txt") + endif() +endif() + +if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) + set(CMAKE_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS}") + set(CMAKE_C_FLAGS "-g ${CMAKE_C_FLAGS}") + add_definitions(-DETH_PROFILING_GPERF) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lprofiler") +# set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} -lprofiler") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lprofiler") +endif () + +if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU"))) + set(CMAKE_CXX_FLAGS "-g --coverage ${CMAKE_CXX_FLAGS}") + set(CMAKE_C_FLAGS "-g --coverage ${CMAKE_C_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "--coverage ${CMAKE_SHARED_LINKER_FLAGS} -lprofiler") + set(CMAKE_EXE_LINKER_FLAGS "--coverage ${CMAKE_EXE_LINKER_FLAGS} -lprofiler") +endif () + +if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + option(USE_LD_GOLD "Use GNU gold linker" ON) + if (USE_LD_GOLD) + execute_process(COMMAND ${CMAKE_C_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION) + if ("${LD_VERSION}" MATCHES "GNU gold") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=gold") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=gold") + endif () + endif () +endif () + +if(ETH_STATIC) + set(BUILD_SHARED_LIBS OFF) +else() + set(BUILD_SHARED_LIBS ON) +endif(ETH_STATIC) diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake new file mode 100644 index 00000000..72585d11 --- /dev/null +++ b/cmake/EthDependencies.cmake @@ -0,0 +1,136 @@ +# all dependencies that are not directly included in the cpp-ethereum distribution are defined here +# for this to work, download the dependency via the cmake script in extdep or install them manually! + +function(eth_show_dependency DEP NAME) + get_property(DISPLAYED GLOBAL PROPERTY ETH_${DEP}_DISPLAYED) + if (NOT DISPLAYED) + set_property(GLOBAL PROPERTY ETH_${DEP}_DISPLAYED TRUE) + message(STATUS "${NAME} headers: ${${DEP}_INCLUDE_DIRS}") + message(STATUS "${NAME} lib : ${${DEP}_LIBRARIES}") + if (NOT("${${DEP}_DLLS}" STREQUAL "")) + message(STATUS "${NAME} dll : ${${DEP}_DLLS}") + endif() + endif() +endfunction() + +if (DEFINED MSVC) + # by defining CMAKE_PREFIX_PATH variable, cmake will look for dependencies first in our own repository before looking in system paths like /usr/local/ ... + # this must be set to point to the same directory as $ETH_DEPENDENCY_INSTALL_DIR in /extdep directory + + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0.0) + set (ETH_DEPENDENCY_INSTALL_DIR "${CMAKE_CURRENT_LIST_DIR}/../extdep/install/windows/x64") + else() + get_filename_component(DEPS_DIR "${CMAKE_CURRENT_LIST_DIR}/../deps/install" ABSOLUTE) + set(ETH_DEPENDENCY_INSTALL_DIR + "${DEPS_DIR}/x64" # Old location for deps. + "${DEPS_DIR}/win64" # New location for deps. + "${DEPS_DIR}/win64/Release/share" # LLVM shared cmake files. + ) + endif() + set (CMAKE_PREFIX_PATH ${ETH_DEPENDENCY_INSTALL_DIR} ${CMAKE_PREFIX_PATH}) +endif() + +# custom cmake scripts +set(ETH_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR}) +set(ETH_SCRIPTS_DIR ${ETH_CMAKE_DIR}/scripts) + +find_program(CTEST_COMMAND ctest) + +#message(STATUS "CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH}") +#message(STATUS "CMake Helper Path: ${ETH_CMAKE_DIR}") +#message(STATUS "CMake Script Path: ${ETH_SCRIPTS_DIR}") +#message(STATUS "ctest path: ${CTEST_COMMAND}") + +## use multithreaded boost libraries, with -mt suffix +set(Boost_USE_MULTITHREADED ON) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + +# use static boost libraries *.lib + set(Boost_USE_STATIC_LIBS ON) + +elseif (APPLE) + +# use static boost libraries *.a + set(Boost_USE_STATIC_LIBS ON) + +elseif (UNIX) +# use dynamic boost libraries *.dll + set(Boost_USE_STATIC_LIBS OFF) + +endif() + +set(STATIC_LINKING FALSE CACHE BOOL "Build static binaries") + +if (STATIC_LINKING) + + set(Boost_USE_STATIC_LIBS ON) + set(Boost_USE_STATIC_RUNTIME ON) + + set(OpenSSL_USE_STATIC_LIBS ON) + + if (MSVC) + # TODO - Why would we need .a on Windows? Maybe some Cygwin-ism. + # When I work through Windows static linkage, I will remove this, + # if that is possible. + set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) + elseif (APPLE) + # At the time of writing, we are still only PARTIALLY statically linked + # on OS X, with a mixture of statically linked external libraries where + # those are available, and dynamically linked where that is the only + # option we have. Ultimately, the aim would be for everything except + # the runtime libraries to be statically linked. + # + # Still TODO: + # - jsoncpp + # - json-rpc-cpp + # - leveldb (which pulls in snappy, for the dylib at ;east) + # - miniupnp + # - gmp + # + # Two further libraries (curl and zlib) ship as dylibs with the platform + # but again we could build from source and statically link these too. + set(CMAKE_FIND_LIBRARY_SUFFIXES .a .dylib) + else() + set(CMAKE_FIND_LIBRARY_SUFFIXES .a) + endif() + + set(ETH_STATIC ON) +endif() + +find_package(Boost 1.54.0 QUIET REQUIRED COMPONENTS thread date_time system regex chrono filesystem unit_test_framework program_options random) + +eth_show_dependency(Boost boost) + +if (APPLE) + link_directories(/usr/local/lib) + include_directories(/usr/local/include) +endif() + +include_directories(BEFORE "${PROJECT_BINARY_DIR}/include") + +function(eth_use TARGET REQUIRED) + if (NOT TARGET ${TARGET}) + message(FATAL_ERROR "eth_use called for non existing target ${TARGET}") + endif() + + if (TARGET ${PROJECT_NAME}_BuildInfo.h) + add_dependencies(${TARGET} ${PROJECT_NAME}_BuildInfo.h) + endif() + + foreach(MODULE ${ARGN}) + string(REPLACE "::" ";" MODULE_PARTS ${MODULE}) + list(GET MODULE_PARTS 0 MODULE_MAIN) + list(LENGTH MODULE_PARTS MODULE_LENGTH) + if (MODULE_LENGTH GREATER 1) + list(GET MODULE_PARTS 1 MODULE_SUB) + endif() + # TODO: check if file exists if not, throws FATAL_ERROR with detailed description + get_target_property(TARGET_APPLIED ${TARGET} TARGET_APPLIED_${MODULE_MAIN}_${MODULE_SUB}) + if (NOT TARGET_APPLIED) + include(Use${MODULE_MAIN}) + set_target_properties(${TARGET} PROPERTIES TARGET_APPLIED_${MODULE_MAIN}_${MODULE_SUB} TRUE) + eth_apply(${TARGET} ${REQUIRED} ${MODULE_SUB}) + endif() + endforeach() +endfunction() diff --git a/cmake/EthExecutableHelper.cmake b/cmake/EthExecutableHelper.cmake new file mode 100644 index 00000000..921b37e7 --- /dev/null +++ b/cmake/EthExecutableHelper.cmake @@ -0,0 +1,185 @@ +# +# this function requires the following variables to be specified: +# ETH_VERSION +# PROJECT_NAME +# PROJECT_VERSION +# PROJECT_COPYRIGHT_YEAR +# PROJECT_VENDOR +# PROJECT_DOMAIN_SECOND +# PROJECT_DOMAIN_FIRST +# SRC_LIST +# HEADERS +# +# params: +# ICON +# + +macro(eth_add_executable EXECUTABLE) + set (extra_macro_args ${ARGN}) + set (options) + set (one_value_args ICON) + set (multi_value_args UI_RESOURCES WIN_RESOURCES) + cmake_parse_arguments (ETH_ADD_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}") + + if (APPLE) + + add_executable(${EXECUTABLE} MACOSX_BUNDLE ${SRC_LIST} ${HEADERS} ${ETH_ADD_EXECUTABLE_UI_RESOURCES}) + set(PROJECT_VERSION "${ETH_VERSION}") + set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}") + set(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT_YEAR} ${PROJECT_VENDOR}") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_DOMAIN_SECOND}.${PROJECT_DOMAIN_FIRST}") + set(MACOSX_BUNDLE_BUNDLE_NAME ${EXECUTABLE}) + set(MACOSX_BUNDLE_ICON_FILE ${ETH_ADD_EXECUTABLE_ICON}) + set_target_properties(${EXECUTABLE} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/EthereumMacOSXBundleInfo.plist.in") + set_source_files_properties(${EXECUTABLE} PROPERTIES MACOSX_PACKAGE_LOCATION MacOS) + set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/${MACOSX_BUNDLE_ICON_FILE}.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + + else () + add_executable(${EXECUTABLE} ${ETH_ADD_EXECUTABLE_UI_RESOURCES} ${ETH_ADD_EXECUTABLE_WIN_RESOURCES} ${SRC_LIST} ${HEADERS}) + endif() + +endmacro() + +macro(eth_simple_add_executable EXECUTABLE) + add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) + + # Apple does not support statically linked binaries on OS X. That means + # that we can only statically link against our external libraries, but + # we cannot statically link against the C++ runtime libraries and other + # platform libraries (as is possible on Windows and Alpine Linux) to produce + # an entirely transportable binary. + # + # See https://developer.apple.com/library/mac/qa/qa1118/_index.html for more info. + # + # GLIBC also appears not to support static linkage too, which probably means that + # Debian and Ubuntu will only be able to do partially-statically linked + # executables too, just like OS X. + # + # For OS X, at the time of writing, we are left with the following dynamically + # linked dependencies, of which curl and libz might still be fixable: + # + # /usr/lib/libc++.1.dylib + # /usr/lib/libSystem.B.dylib + # /usr/lib/libcurl.4.dylib + # /usr/lib/libz.1.dylib + # + if (STATIC_LINKING AND NOT APPLE) + set(CMAKE_EXE_LINKER_FLAGS "-static ${CMAKE_EXE_LINKER_FLAGS}") + set_target_properties(${EXECUTABLE} PROPERTIES LINK_SEARCH_START_STATIC 1) + set_target_properties(${EXECUTABLE} PROPERTIES LINK_SEARCH_END_STATIC 1) + endif() +endmacro() + +macro(eth_copy_dll EXECUTABLE DLL) + # dlls must be unsubstitud list variable (without ${}) in format + # optimized;path_to_dll.dll;debug;path_to_dlld.dll + if(DEFINED MSVC) + list(GET ${DLL} 1 DLL_RELEASE) + list(GET ${DLL} 3 DLL_DEBUG) + add_custom_command(TARGET ${EXECUTABLE} + PRE_BUILD + COMMAND ${CMAKE_COMMAND} ARGS + -DDLL_RELEASE="${DLL_RELEASE}" + -DDLL_DEBUG="${DLL_DEBUG}" + -DCONF="$<CONFIGURATION>" + -DDESTINATION="${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" + -P "${ETH_SCRIPTS_DIR}/copydlls.cmake" + ) + endif() +endmacro() + +macro(eth_copy_dlls EXECUTABLE) + foreach(dll ${ARGN}) + eth_copy_dll(${EXECUTABLE} ${dll}) + endforeach(dll) +endmacro() + + +macro(eth_install_executable EXECUTABLE) + + if (APPLE) + + # TODO - Why is this different than the branch Linux below, which has the RUNTIME keyword too? + install(TARGETS ${EXECUTABLE} DESTINATION bin) + + elseif (DEFINED MSVC) + + set(COMPONENT ${EXECUTABLE}) + + install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Debug/" + DESTINATION . + CONFIGURATIONS Debug + COMPONENT ${COMPONENT} + ) + + install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Release/" + DESTINATION . + CONFIGURATIONS Release + COMPONENT ${COMPONENT} + ) + + install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/" + DESTINATION . + CONFIGURATIONS RelWithDebInfo + COMPONENT ${COMPONENT} + ) + + else() + install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin) + endif () + +endmacro() + +macro (eth_name KEY VALUE) + if (NOT (APPLE OR WIN32)) + string(TOLOWER ${VALUE} LVALUE ) + set(${KEY} ${LVALUE}) + else() + set(${KEY} ${VALUE}) + endif() +endmacro() + +macro(jsonrpcstub_client_create SPEC CLIENTNAME CLIENTDIR CLIENTFILENAME) + if (ETH_JSON_RPC_STUB) + add_custom_target(${SPEC}stub) + add_custom_command( + TARGET ${SPEC}stub + POST_BUILD + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${SPEC}" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -DETH_SPEC_PATH="${CMAKE_CURRENT_SOURCE_DIR}/${SPEC}" -DETH_SOURCE_DIR="${CMAKE_SOURCE_DIR}" -DETH_CMAKE_DIR="${ETH_CMAKE_DIR}" + -DETH_CLIENT_DIR="${CLIENTDIR}" + -DETH_CLIENT_NAME=${CLIENTNAME} + -DETH_CLIENT_FILENAME=${CLIENTFILENAME} + -DETH_JSON_RPC_STUB="${ETH_JSON_RPC_STUB}" + -P "${ETH_SCRIPTS_DIR}/jsonrpcstub.cmake" + ) + add_dependencies(${EXECUTABLE} ${SPEC}stub) + endif () +endmacro() + +macro(jsonrpcstub_create SPEC SERVERNAME SERVERDIR SERVERFILENAME CLIENTNAME CLIENTDIR CLIENTFILENAME) + if (ETH_JSON_RPC_STUB) + add_custom_target(${SPEC}stub) + add_custom_command( + TARGET ${SPEC}stub + POST_BUILD + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${SPEC}" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -DETH_SPEC_PATH="${CMAKE_CURRENT_SOURCE_DIR}/${SPEC}" -DETH_SOURCE_DIR="${CMAKE_SOURCE_DIR}" -DETH_CMAKE_DIR="${ETH_CMAKE_DIR}" + -DETH_CLIENT_DIR="${CLIENTDIR}" + -DETH_CLIENT_NAME=${CLIENTNAME} + -DETH_CLIENT_FILENAME=${CLIENTFILENAME} + -DETH_SERVER_DIR="${SERVERDIR}" + -DETH_SERVER_NAME=${SERVERNAME} + -DETH_SERVER_FILENAME=${SERVERFILENAME} + -DETH_JSON_RPC_STUB="${ETH_JSON_RPC_STUB}" + -P "${ETH_SCRIPTS_DIR}/jsonrpcstub.cmake" + ) + add_dependencies(${EXECUTABLE} ${SPEC}stub) + endif () +endmacro() + diff --git a/cmake/EthOptions.cmake b/cmake/EthOptions.cmake new file mode 100644 index 00000000..b4efd6c9 --- /dev/null +++ b/cmake/EthOptions.cmake @@ -0,0 +1,40 @@ +macro(configure_project) + set(NAME ${PROJECT_NAME}) + + # features + eth_default_option(PROFILING OFF) + + # components + eth_default_option(TESTS ON) + eth_default_option(TOOLS ON) + + # Define a matching property name of each of the "features". + foreach(FEATURE ${ARGN}) + set(SUPPORT_${FEATURE} TRUE) + endforeach() + + include(EthBuildInfo) + create_build_info(${NAME}) + print_config(${NAME}) +endmacro() + +macro(print_config NAME) + message("") + message("------------------------------------------------------------------------") + message("-- Configuring ${NAME}") + message("------------------------------------------------------------------------") + message("-- CMake Version ${CMAKE_VERSION}") + message("-- CMAKE_BUILD_TYPE Build type ${CMAKE_BUILD_TYPE}") + message("-- TARGET_PLATFORM Target platform ${CMAKE_SYSTEM_NAME}") + message("--------------------------------------------------------------- features") + message("-- PROFILING Profiling support ${PROFILING}") + message("------------------------------------------------------------- components") +if (SUPPORT_TESTS) + message("-- TESTS Build tests ${TESTS}") +endif() +if (SUPPORT_TOOLS) + message("-- TOOLS Build tools ${TOOLS}") +endif() + message("------------------------------------------------------------------------") + message("") +endmacro() diff --git a/cmake/EthPolicy.cmake b/cmake/EthPolicy.cmake new file mode 100644 index 00000000..31b09f15 --- /dev/null +++ b/cmake/EthPolicy.cmake @@ -0,0 +1,35 @@ +# it must be a macro cause policies have scopes +# http://www.cmake.org/cmake/help/v3.0/command/cmake_policy.html +macro (eth_policy) + # link_directories() treats paths relative to the source dir. + cmake_policy(SET CMP0015 NEW) + + # let cmake autolink dependencies on windows + cmake_policy(SET CMP0020 NEW) + + # CMake 2.8.12 and lower allowed the use of targets and files with double + # colons in target_link_libraries, + cmake_policy(SET CMP0028 OLD) + + if (${CMAKE_VERSION} VERSION_GREATER 3.0) + + # fix MACOSX_RPATH + cmake_policy(SET CMP0042 OLD) + + # ignore COMPILE_DEFINITIONS_<Config> properties + cmake_policy(SET CMP0043 OLD) + + # allow VERSION argument in project() + cmake_policy(SET CMP0048 NEW) + + endif() + + if (${CMAKE_VERSION} VERSION_GREATER 3.1) + + # do not interpret if() arguments as variables! + cmake_policy(SET CMP0054 NEW) + + endif() + +endmacro() + diff --git a/cmake/EthUtils.cmake b/cmake/EthUtils.cmake new file mode 100644 index 00000000..68fd35d1 --- /dev/null +++ b/cmake/EthUtils.cmake @@ -0,0 +1,114 @@ +# +# renames the file if it is different from its destination +include(CMakeParseArguments) +# +macro(replace_if_different SOURCE DST) + set(extra_macro_args ${ARGN}) + set(options CREATE) + set(one_value_args) + set(multi_value_args) + cmake_parse_arguments(REPLACE_IF_DIFFERENT "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}") + + if (REPLACE_IF_DIFFERENT_CREATE AND (NOT (EXISTS "${DST}"))) + file(WRITE "${DST}" "") + endif() + + execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files "${SOURCE}" "${DST}" RESULT_VARIABLE DIFFERENT OUTPUT_QUIET ERROR_QUIET) + + if (DIFFERENT) + execute_process(COMMAND ${CMAKE_COMMAND} -E rename "${SOURCE}" "${DST}") + else() + execute_process(COMMAND ${CMAKE_COMMAND} -E remove "${SOURCE}") + endif() +endmacro() + +macro(eth_add_test NAME) + + # parse arguments here + set(commands) + set(current_command "") + foreach (arg ${ARGN}) + if (arg STREQUAL "ARGS") + if (current_command) + list(APPEND commands ${current_command}) + endif() + set(current_command "") + else () + set(current_command "${current_command} ${arg}") + endif() + endforeach(arg) + list(APPEND commands ${current_command}) + + message(STATUS "test: ${NAME} | ${commands}") + + # create tests + set(index 0) + list(LENGTH commands count) + while (index LESS count) + list(GET commands ${index} test_arguments) + + set(run_test "--run_test=${NAME}") + add_test(NAME "${NAME}.${index}" COMMAND testeth ${run_test} ${test_arguments}) + + math(EXPR index "${index} + 1") + endwhile(index LESS count) + + # add target to run them + add_custom_target("test.${NAME}" + DEPENDS testeth + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -DETH_TEST_NAME="${NAME}" -DCTEST_COMMAND="${CTEST_COMMAND}" -P "${ETH_SCRIPTS_DIR}/runtest.cmake" + ) + +endmacro() + +# Creates C resources file from files +function(eth_add_resources RESOURCE_FILE OUT_FILE ETH_RES_DIR) + include("${RESOURCE_FILE}") + set(OUTPUT "${ETH_RESOURCE_LOCATION}/${ETH_RESOURCE_NAME}.hpp") + #message(FATAL_ERROR "res:! ${ETH_RESOURCE_LOCATION}") + include_directories("${ETH_RESOURCE_LOCATION}") + set(${OUT_FILE} "${OUTPUT}" PARENT_SCOPE) + + set(filenames "${RESOURCE_FILE}") + list(APPEND filenames "${ETH_SCRIPTS_DIR}/resources.cmake") + foreach(resource ${ETH_RESOURCES}) + list(APPEND filenames "${${resource}}") + endforeach(resource) + + add_custom_command(OUTPUT ${OUTPUT} + COMMAND ${CMAKE_COMMAND} -DETH_RES_FILE="${RESOURCE_FILE}" -DETH_RES_DIR="${ETH_RES_DIR}" -P "${ETH_SCRIPTS_DIR}/resources.cmake" + DEPENDS ${filenames} + ) +endfunction() + +macro(eth_default_option O DEF) + if (DEFINED ${O}) + if (${${O}}) + set(${O} ON) + else () + set(${O} OFF) + endif() + else () + set(${O} ${DEF}) + endif() +endmacro() + +# In Windows split repositories build we need to be checking whether or not +# Debug/Release or both versions were built for the config phase to run smoothly +macro(eth_check_library_link L) + if (${${L}_LIBRARY} AND ${${L}_LIBRARY} EQUAL "${L}_LIBRARY-NOTFOUND") + unset(${${L}_LIBRARY}) + endif() + if (${${L}_LIBRARY_DEBUG} AND ${${L}_LIBRARY_DEBUG} EQUAL "${L}_LIBRARY_DEBUG-NOTFOUND") + unset(${${L}_LIBRARY_DEBUG}) + endif() + if (${${L}_LIBRARY} AND ${${L}_LIBRARY_DEBUG}) + set(${L}_LIBRARIES optimized ${${L}_LIBRARY} debug ${${L}_LIBRARY_DEBUG}) + elseif (${${L}_LIBRARY}) + set(${L}_LIBRARIES ${${L}_LIBRARY}) + elseif (${${L}_LIBRARY_DEBUG}) + set(${L}_LIBRARIES ${${L}_LIBRARY_DEBUG}) + endif() +endmacro() + diff --git a/cmake/FindJsoncpp.cmake b/cmake/FindJsoncpp.cmake new file mode 100644 index 00000000..e8258b71 --- /dev/null +++ b/cmake/FindJsoncpp.cmake @@ -0,0 +1,50 @@ +# Find jsoncpp +# +# Find the jsoncpp includes and library +# +# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH +# +# This module defines +# JSONCPP_INCLUDE_DIRS, where to find header, etc. +# JSONCPP_LIBRARIES, the libraries needed to use jsoncpp. +# JSONCPP_FOUND, If false, do not try to use jsoncpp. + +# only look in default directories +find_path( + JSONCPP_INCLUDE_DIR + NAMES json/json.h + PATH_SUFFIXES jsoncpp + DOC "jsoncpp include dir" +) + +find_library( + JSONCPP_LIBRARY + NAMES jsoncpp + DOC "jsoncpp library" +) + +set(JSONCPP_INCLUDE_DIRS ${JSONCPP_INCLUDE_DIR}) +set(JSONCPP_LIBRARIES ${JSONCPP_LIBRARY}) + +# debug library on windows +# same naming convention as in qt (appending debug library with d) +# boost is using the same "hack" as us with "optimized" and "debug" +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + + find_library( + JSONCPP_LIBRARY_DEBUG + NAMES jsoncppd + DOC "jsoncpp debug library" + ) + + set(JSONCPP_LIBRARIES optimized ${JSONCPP_LIBRARIES} debug ${JSONCPP_LIBRARY_DEBUG}) + +endif() + +# handle the QUIETLY and REQUIRED arguments and set JSONCPP_FOUND to TRUE +# if all listed variables are TRUE, hide their existence from configuration view +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(jsoncpp DEFAULT_MSG + JSONCPP_LIBRARY JSONCPP_INCLUDE_DIR) +mark_as_advanced (JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY) + diff --git a/cmake/FindPackageHandleStandardArgs.cmake b/cmake/FindPackageHandleStandardArgs.cmake new file mode 100644 index 00000000..6bcf1e78 --- /dev/null +++ b/cmake/FindPackageHandleStandardArgs.cmake @@ -0,0 +1,382 @@ +#.rst: +# FindPackageHandleStandardArgs +# ----------------------------- +# +# +# +# FIND_PACKAGE_HANDLE_STANDARD_ARGS(<name> ... ) +# +# This function is intended to be used in FindXXX.cmake modules files. +# It handles the REQUIRED, QUIET and version-related arguments to +# find_package(). It also sets the <packagename>_FOUND variable. The +# package is considered found if all variables <var1>... listed contain +# valid results, e.g. valid filepaths. +# +# There are two modes of this function. The first argument in both +# modes is the name of the Find-module where it is called (in original +# casing). +# +# The first simple mode looks like this: +# +# :: +# +# FIND_PACKAGE_HANDLE_STANDARD_ARGS(<name> +# (DEFAULT_MSG|"Custom failure message") <var1>...<varN> ) +# +# If the variables <var1> to <varN> are all valid, then +# <UPPERCASED_NAME>_FOUND will be set to TRUE. If DEFAULT_MSG is given +# as second argument, then the function will generate itself useful +# success and error messages. You can also supply a custom error +# message for the failure case. This is not recommended. +# +# The second mode is more powerful and also supports version checking: +# +# :: +# +# FIND_PACKAGE_HANDLE_STANDARD_ARGS(NAME +# [FOUND_VAR <resultVar>] +# [REQUIRED_VARS <var1>...<varN>] +# [VERSION_VAR <versionvar>] +# [HANDLE_COMPONENTS] +# [CONFIG_MODE] +# [FAIL_MESSAGE "Custom failure message"] ) +# +# In this mode, the name of the result-variable can be set either to +# either <UPPERCASED_NAME>_FOUND or <OriginalCase_Name>_FOUND using the +# FOUND_VAR option. Other names for the result-variable are not +# allowed. So for a Find-module named FindFooBar.cmake, the two +# possible names are FooBar_FOUND and FOOBAR_FOUND. It is recommended +# to use the original case version. If the FOUND_VAR option is not +# used, the default is <UPPERCASED_NAME>_FOUND. +# +# As in the simple mode, if <var1> through <varN> are all valid, +# <packagename>_FOUND will be set to TRUE. After REQUIRED_VARS the +# variables which are required for this package are listed. Following +# VERSION_VAR the name of the variable can be specified which holds the +# version of the package which has been found. If this is done, this +# version will be checked against the (potentially) specified required +# version used in the find_package() call. The EXACT keyword is also +# handled. The default messages include information about the required +# version and the version which has been actually found, both if the +# version is ok or not. If the package supports components, use the +# HANDLE_COMPONENTS option to enable handling them. In this case, +# find_package_handle_standard_args() will report which components have +# been found and which are missing, and the <packagename>_FOUND variable +# will be set to FALSE if any of the required components (i.e. not the +# ones listed after OPTIONAL_COMPONENTS) are missing. Use the option +# CONFIG_MODE if your FindXXX.cmake module is a wrapper for a +# find_package(... NO_MODULE) call. In this case VERSION_VAR will be +# set to <NAME>_VERSION and the macro will automatically check whether +# the Config module was found. Via FAIL_MESSAGE a custom failure +# message can be specified, if this is not used, the default message +# will be displayed. +# +# Example for mode 1: +# +# :: +# +# find_package_handle_standard_args(LibXml2 DEFAULT_MSG +# LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) +# +# +# +# LibXml2 is considered to be found, if both LIBXML2_LIBRARY and +# LIBXML2_INCLUDE_DIR are valid. Then also LIBXML2_FOUND is set to +# TRUE. If it is not found and REQUIRED was used, it fails with +# FATAL_ERROR, independent whether QUIET was used or not. If it is +# found, success will be reported, including the content of <var1>. On +# repeated Cmake runs, the same message won't be printed again. +# +# Example for mode 2: +# +# :: +# +# find_package_handle_standard_args(LibXslt +# FOUND_VAR LibXslt_FOUND +# REQUIRED_VARS LibXslt_LIBRARIES LibXslt_INCLUDE_DIRS +# VERSION_VAR LibXslt_VERSION_STRING) +# +# In this case, LibXslt is considered to be found if the variable(s) +# listed after REQUIRED_VAR are all valid, i.e. LibXslt_LIBRARIES and +# LibXslt_INCLUDE_DIRS in this case. The result will then be stored in +# LibXslt_FOUND . Also the version of LibXslt will be checked by using +# the version contained in LibXslt_VERSION_STRING. Since no +# FAIL_MESSAGE is given, the default messages will be printed. +# +# Another example for mode 2: +# +# :: +# +# find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) +# find_package_handle_standard_args(Automoc4 CONFIG_MODE) +# +# In this case, FindAutmoc4.cmake wraps a call to find_package(Automoc4 +# NO_MODULE) and adds an additional search directory for automoc4. Here +# the result will be stored in AUTOMOC4_FOUND. The following +# FIND_PACKAGE_HANDLE_STANDARD_ARGS() call produces a proper +# success/error message. + +#============================================================================= +# Copyright 2007-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/CMakeParseArguments.cmake) + +# internal helper macro +macro(_FPHSA_FAILURE_MESSAGE _msg) + if (${_NAME}_FIND_REQUIRED) + message(FATAL_ERROR "${_msg}") + else () + if (NOT ${_NAME}_FIND_QUIETLY) + message(STATUS "${_msg}") + endif () + endif () +endmacro() + + +# internal helper macro to generate the failure message when used in CONFIG_MODE: +macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) + # <name>_CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: + if(${_NAME}_CONFIG) + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing: ${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") + else() + # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. + # List them all in the error message: + if(${_NAME}_CONSIDERED_CONFIGS) + set(configsText "") + list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) + math(EXPR configsCount "${configsCount} - 1") + foreach(currentConfigIndex RANGE ${configsCount}) + list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) + list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) + set(configsText "${configsText} ${filename} (version ${version})\n") + endforeach() + if (${_NAME}_NOT_FOUND_MESSAGE) + set(configsText "${configsText} Reason given by package: ${${_NAME}_NOT_FOUND_MESSAGE}\n") + endif() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}") + + else() + # Simple case: No Config-file was found at all: + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") + endif() + endif() +endmacro() + + +function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) + +# set up the arguments for CMAKE_PARSE_ARGUMENTS and check whether we are in +# new extended or in the "old" mode: + set(options CONFIG_MODE HANDLE_COMPONENTS) + set(oneValueArgs FAIL_MESSAGE VERSION_VAR FOUND_VAR) + set(multiValueArgs REQUIRED_VARS) + set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) + list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) + + if(${INDEX} EQUAL -1) + set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) + set(FPHSA_REQUIRED_VARS ${ARGN}) + set(FPHSA_VERSION_VAR) + else() + + CMAKE_PARSE_ARGUMENTS(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) + + if(FPHSA_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") + endif() + + if(NOT FPHSA_FAIL_MESSAGE) + set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") + endif() + endif() + +# now that we collected all arguments, process them + + if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") + set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") + endif() + + # In config-mode, we rely on the variable <package>_CONFIG, which is set by find_package() + # when it successfully found the config-file, including version checking: + if(FPHSA_CONFIG_MODE) + list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) + list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) + set(FPHSA_VERSION_VAR ${_NAME}_VERSION) + endif() + + if(NOT FPHSA_REQUIRED_VARS) + message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") + endif() + + list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) + + string(TOUPPER ${_NAME} _NAME_UPPER) + string(TOLOWER ${_NAME} _NAME_LOWER) + + if(FPHSA_FOUND_VAR) + if(FPHSA_FOUND_VAR MATCHES "^${_NAME}_FOUND$" OR FPHSA_FOUND_VAR MATCHES "^${_NAME_UPPER}_FOUND$") + set(_FOUND_VAR ${FPHSA_FOUND_VAR}) + else() + message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_NAME}_FOUND\" and \"${_NAME_UPPER}_FOUND\" are valid names.") + endif() + else() + set(_FOUND_VAR ${_NAME_UPPER}_FOUND) + endif() + + # collect all variables which were not found, so they can be printed, so the + # user knows better what went wrong (#6375) + set(MISSING_VARS "") + set(DETAILS "") + # check if all passed variables are valid + unset(${_FOUND_VAR}) + foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) + if(NOT ${_CURRENT_VAR}) + set(${_FOUND_VAR} FALSE) + set(MISSING_VARS "${MISSING_VARS} ${_CURRENT_VAR}") + else() + set(DETAILS "${DETAILS}[${${_CURRENT_VAR}}]") + endif() + endforeach() + if(NOT "${${_FOUND_VAR}}" STREQUAL "FALSE") + set(${_FOUND_VAR} TRUE) + endif() + + # component handling + unset(FOUND_COMPONENTS_MSG) + unset(MISSING_COMPONENTS_MSG) + + if(FPHSA_HANDLE_COMPONENTS) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(${_NAME}_${comp}_FOUND) + + if(NOT DEFINED FOUND_COMPONENTS_MSG) + set(FOUND_COMPONENTS_MSG "found components: ") + endif() + set(FOUND_COMPONENTS_MSG "${FOUND_COMPONENTS_MSG} ${comp}") + + else() + + if(NOT DEFINED MISSING_COMPONENTS_MSG) + set(MISSING_COMPONENTS_MSG "missing components: ") + endif() + set(MISSING_COMPONENTS_MSG "${MISSING_COMPONENTS_MSG} ${comp}") + + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_FOUND_VAR} FALSE) + set(MISSING_VARS "${MISSING_VARS} ${comp}") + endif() + + endif() + endforeach() + set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") + set(DETAILS "${DETAILS}[c${COMPONENT_MSG}]") + endif() + + # version handling: + set(VERSION_MSG "") + set(VERSION_OK TRUE) + set(VERSION ${${FPHSA_VERSION_VAR}}) + + # check with DEFINED here as the requested or found version may be "0" + if (DEFINED ${_NAME}_FIND_VERSION) + if(DEFINED ${FPHSA_VERSION_VAR}) + + if(${_NAME}_FIND_VERSION_EXACT) # exact version required + # count the dots in the version string + string(REGEX REPLACE "[^.]" "" _VERSION_DOTS "${VERSION}") + # add one dot because there is one dot more than there are components + string(LENGTH "${_VERSION_DOTS}." _VERSION_DOTS) + if (_VERSION_DOTS GREATER ${_NAME}_FIND_VERSION_COUNT) + # Because of the C++ implementation of find_package() ${_NAME}_FIND_VERSION_COUNT + # is at most 4 here. Therefore a simple lookup table is used. + if (${_NAME}_FIND_VERSION_COUNT EQUAL 1) + set(_VERSION_REGEX "[^.]*") + elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 2) + set(_VERSION_REGEX "[^.]*\\.[^.]*") + elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 3) + set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*") + else () + set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") + endif () + string(REGEX REPLACE "^(${_VERSION_REGEX})\\..*" "\\1" _VERSION_HEAD "${VERSION}") + unset(_VERSION_REGEX) + if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _VERSION_HEAD) + set(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") + set(VERSION_OK FALSE) + else () + set(VERSION_MSG "(found suitable exact version \"${VERSION}\")") + endif () + unset(_VERSION_HEAD) + else () + if (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}") + set(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") + set(VERSION_OK FALSE) + else () + set(VERSION_MSG "(found suitable exact version \"${VERSION}\")") + endif () + endif () + unset(_VERSION_DOTS) + + else() # minimum version specified: + if ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}") + set(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"") + set(VERSION_OK FALSE) + else () + set(VERSION_MSG "(found suitable version \"${VERSION}\", minimum required is \"${${_NAME}_FIND_VERSION}\")") + endif () + endif() + + else() + + # if the package was not found, but a version was given, add that to the output: + if(${_NAME}_FIND_VERSION_EXACT) + set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") + else() + set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") + endif() + + endif() + else () + if(VERSION) + set(VERSION_MSG "(found version \"${VERSION}\")") + endif() + endif () + + if(VERSION_OK) + set(DETAILS "${DETAILS}[v${VERSION}(${${_NAME}_FIND_VERSION})]") + else() + set(${_FOUND_VAR} FALSE) + endif() + + + # print the result: + if (${_FOUND_VAR}) + FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") + else () + + if(FPHSA_CONFIG_MODE) + _FPHSA_HANDLE_FAILURE_CONFIG_MODE() + else() + if(NOT VERSION_OK) + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})") + else() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing: ${MISSING_VARS}) ${VERSION_MSG}") + endif() + endif() + + endif () + + set(${_FOUND_VAR} ${${_FOUND_VAR}} PARENT_SCOPE) + +endfunction() diff --git a/cmake/FindPackageMessage.cmake b/cmake/FindPackageMessage.cmake new file mode 100644 index 00000000..a0349d3d --- /dev/null +++ b/cmake/FindPackageMessage.cmake @@ -0,0 +1,57 @@ +#.rst: +# FindPackageMessage +# ------------------ +# +# +# +# FIND_PACKAGE_MESSAGE(<name> "message for user" "find result details") +# +# This macro is intended to be used in FindXXX.cmake modules files. It +# will print a message once for each unique find result. This is useful +# for telling the user where a package was found. The first argument +# specifies the name (XXX) of the package. The second argument +# specifies the message to display. The third argument lists details +# about the find result so that if they change the message will be +# displayed again. The macro also obeys the QUIET argument to the +# find_package command. +# +# Example: +# +# :: +# +# if(X11_FOUND) +# FIND_PACKAGE_MESSAGE(X11 "Found X11: ${X11_X11_LIB}" +# "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") +# else() +# ... +# endif() + +#============================================================================= +# Copyright 2008-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +function(FIND_PACKAGE_MESSAGE pkg msg details) + # Avoid printing a message repeatedly for the same find result. + if(NOT ${pkg}_FIND_QUIETLY) + string(REPLACE "\n" "" details "${details}") + set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) + if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") + # The message has not yet been printed. + message(STATUS "${msg}") + + # Save the find details in the cache to avoid printing the same + # message again. + set("${DETAILS_VAR}" "${details}" + CACHE INTERNAL "Details about finding ${pkg}") + endif() + endif() +endfunction() diff --git a/cmake/FindSolidity.cmake b/cmake/FindSolidity.cmake new file mode 100644 index 00000000..f2086b65 --- /dev/null +++ b/cmake/FindSolidity.cmake @@ -0,0 +1,47 @@ +# Find Solidity +# +# Find the solidity includes and library +# +# This module defines +# Solidity_XXX_LIBRARIES, the libraries needed to use solidity. +# SOLIDITY_INCLUDE_DIRS + +include(EthUtils) +set(LIBS solidity;lll;evmasm) + +set(Solidity_INCLUDE_DIRS "${SOL_DIR}") + +# if the project is a subset of main cpp-ethereum project +# use same pattern for variables as Boost uses +if ((DEFINED solidity_VERSION) OR (DEFINED cpp-ethereum_VERSION)) + + foreach (l ${LIBS}) + string(TOUPPER ${l} L) + set ("Solidity_${L}_LIBRARIES" ${l}) + endforeach() + +else() + + foreach (l ${LIBS}) + string(TOUPPER ${l} L) + find_library(Solidity_${L}_LIBRARY + NAMES ${l} + PATHS ${CMAKE_LIBRARY_PATH} + PATH_SUFFIXES "lib${l}" "${l}" "lib${l}/Debug" "lib${l}/Release" + NO_DEFAULT_PATH + ) + + set(Solidity_${L}_LIBRARIES ${Solidity_${L}_LIBRARY}) + + if (DEFINED MSVC) + find_library(Solidity_${L}_LIBRARY_DEBUG + NAMES ${l} + PATHS ${CMAKE_LIBRARY_PATH} + PATH_SUFFIXES "lib${l}/Debug" + NO_DEFAULT_PATH + ) + eth_check_library_link(Solidity_${L}) + endif() + endforeach() + +endif() diff --git a/cmake/UseDev.cmake b/cmake/UseDev.cmake new file mode 100644 index 00000000..1f4c9a2d --- /dev/null +++ b/cmake/UseDev.cmake @@ -0,0 +1,30 @@ +function(eth_apply TARGET REQUIRED SUBMODULE) + + # Base is where all dependencies for devcore are + if (${SUBMODULE} STREQUAL "base") + # if it's ethereum source dir, always build BuildInfo.h before + eth_use(${TARGET} ${REQUIRED} Dev::buildinfo) + + target_include_directories(${TARGET} SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) + target_link_libraries(${TARGET} ${Boost_THREAD_LIBRARIES}) + target_link_libraries(${TARGET} ${Boost_RANDOM_LIBRARIES}) + target_link_libraries(${TARGET} ${Boost_FILESYSTEM_LIBRARIES}) + target_link_libraries(${TARGET} ${Boost_SYSTEM_LIBRARIES}) + + if (DEFINED MSVC) + target_link_libraries(${TARGET} ${Boost_CHRONO_LIBRARIES}) + target_link_libraries(${TARGET} ${Boost_DATE_TIME_LIBRARIES}) + endif() + + if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") + target_link_libraries(${TARGET} pthread) + endif() + + endif() + + if (${SUBMODULE} STREQUAL "devcore") + eth_use(${TARGET} ${REQUIRED} Dev::base) + target_link_libraries(${TARGET} devcore) + endif() + +endfunction() diff --git a/cmake/UseJsoncpp.cmake b/cmake/UseJsoncpp.cmake new file mode 100644 index 00000000..6f605283 --- /dev/null +++ b/cmake/UseJsoncpp.cmake @@ -0,0 +1,10 @@ +function(eth_apply TARGET REQUIRED) + find_package (Jsoncpp 0.60) + eth_show_dependency(JSONCPP JsonCpp) + if (JSONCPP_FOUND) + target_include_directories(${TARGET} SYSTEM BEFORE PUBLIC ${JSONCPP_INCLUDE_DIRS}) + target_link_libraries(${TARGET} ${JSONCPP_LIBRARIES}) + elseif (NOT ${REQUIRED} STREQUAL "OPTIONAL") + message(FATAL_ERROR "Jsoncpp library not found") + endif() +endfunction() diff --git a/cmake/UseSolidity.cmake b/cmake/UseSolidity.cmake new file mode 100644 index 00000000..46e2cac9 --- /dev/null +++ b/cmake/UseSolidity.cmake @@ -0,0 +1,33 @@ +function(eth_apply TARGET REQUIRED SUBMODULE) + + set(SOL_DIR "${ETH_CMAKE_DIR}/.." CACHE PATH "The path to the solidity directory") + set(SOL_BUILD_DIR_NAME "build" CACHE STRING "The name of the build directory in solidity repo") + set(SOL_BUILD_DIR "${SOL_DIR}/${SOL_BUILD_DIR_NAME}") + set(CMAKE_LIBRARY_PATH ${SOL_BUILD_DIR};${CMAKE_LIBRARY_PATH}) + + find_package(Solidity) + + # Hide confusing blank dependency information when using FindSolidity on itself. + if (NOT(${MODULE_MAIN} STREQUAL Solidity)) + eth_show_dependency(SOLIDITY solidity) + endif() + + target_include_directories(${TARGET} PUBLIC ${Solidity_INCLUDE_DIRS}) + + if (${SUBMODULE} STREQUAL "evmasm") + eth_use(${TARGET} ${REQUIRED} ) + target_link_libraries(${TARGET} ${Solidity_EVMASM_LIBRARIES}) + endif() + + if (${SUBMODULE} STREQUAL "lll") + eth_use(${TARGET} ${REQUIRED} Solidity::evmasm) + target_link_libraries(${TARGET} ${Solidity_LLL_LIBRARIES}) + endif() + + if (${SUBMODULE} STREQUAL "solidity" OR ${SUBMODULE} STREQUAL "") + eth_use(${TARGET} ${REQUIRED} Dev::devcore Solidity::evmasm) + target_link_libraries(${TARGET} ${Solidity_SOLIDITY_LIBRARIES}) + endif() + + target_compile_definitions(${TARGET} PUBLIC ETH_SOLIDITY) +endfunction() diff --git a/cmake/scripts/buildinfo.cmake b/cmake/scripts/buildinfo.cmake new file mode 100644 index 00000000..39359486 --- /dev/null +++ b/cmake/scripts/buildinfo.cmake @@ -0,0 +1,49 @@ +# generates BuildInfo.h +# +# this module expects +# ETH_SOURCE_DIR - main CMAKE_SOURCE_DIR +# ETH_DST_DIR - main CMAKE_BINARY_DIR +# ETH_BUILD_TYPE +# ETH_BUILD_PLATFORM +# ETH_BUILD_NUMBER +# ETH_VERSION_SUFFIX +# +# example usage: +# cmake -DETH_SOURCE_DIR=. -DETH_DST_DIR=build -DETH_BUILD_TYPE=Debug -DETH_BUILD_PLATFORM=Darwin/appleclang -P scripts/buildinfo.cmake + +if (NOT ETH_BUILD_TYPE) + set(ETH_BUILD_TYPE "unknown") +endif() + +if (NOT ETH_BUILD_PLATFORM) + set(ETH_BUILD_PLATFORM "unknown") +endif() + +execute_process( + COMMAND git --git-dir=${ETH_SOURCE_DIR}/.git --work-tree=${ETH_SOURCE_DIR} rev-parse HEAD + OUTPUT_VARIABLE ETH_COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET +) + +if (NOT ETH_COMMIT_HASH) + set(ETH_COMMIT_HASH 0) +endif() + +execute_process( + COMMAND git --git-dir=${ETH_SOURCE_DIR}/.git --work-tree=${ETH_SOURCE_DIR} diff HEAD --shortstat + OUTPUT_VARIABLE ETH_LOCAL_CHANGES OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET +) + +if (ETH_LOCAL_CHANGES) + set(ETH_CLEAN_REPO 0) +else() + set(ETH_CLEAN_REPO 1) +endif() + +set(TMPFILE "${ETH_DST_DIR}/BuildInfo.h.tmp") +set(OUTFILE "${ETH_DST_DIR}/BuildInfo.h") + +configure_file("${ETH_BUILDINFO_IN}" "${TMPFILE}") + +include("${ETH_CMAKE_DIR}/EthUtils.cmake") +replace_if_different("${TMPFILE}" "${OUTFILE}" CREATE) + diff --git a/cmake/templates/BuildInfo.h.in b/cmake/templates/BuildInfo.h.in new file mode 100644 index 00000000..6f9baf50 --- /dev/null +++ b/cmake/templates/BuildInfo.h.in @@ -0,0 +1,11 @@ +#pragma once + +#define ETH_PROJECT_VERSION "@PROJECT_VERSION@" +#define ETH_COMMIT_HASH @ETH_COMMIT_HASH@ +#define ETH_CLEAN_REPO @ETH_CLEAN_REPO@ +#define ETH_BUILD_TYPE @ETH_BUILD_TYPE@ +#define ETH_BUILD_OS @ETH_BUILD_OS@ +#define ETH_BUILD_COMPILER @ETH_BUILD_COMPILER@ +#define ETH_BUILD_PLATFORM @ETH_BUILD_PLATFORM@ +#define ETH_BUILD_NUMBER @ETH_BUILD_NUMBER@ +#define ETH_VERSION_SUFFIX "@ETH_VERSION_SUFFIX@" diff --git a/deps b/deps new file mode 160000 +Subproject f2ede70f33633b26a27299ff39995914db2c692 diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 9096571e..422e2758 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -201,6 +201,7 @@ function finishes. now >= creationTime + 12 days) nextStage(); // The other stages transition by transaction + _ } // Order of the modifiers matters here! diff --git a/docs/contracts.rst b/docs/contracts.rst index c02c1490..81c5d353 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -85,7 +85,7 @@ This means that cyclic creation dependencies are impossible. // Only the creator can alter the name -- // the comparison is possible since contracts // are implicitly convertible to addresses. - if (msg.sender == creator) + if (msg.sender == address(creator)) name = newName; } diff --git a/docs/index.rst b/docs/index.rst index 940fe5e7..5ca5c4a9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -38,9 +38,6 @@ Available Solidity Integrations * `Visual Studio Extension <https://visualstudiogallery.msdn.microsoft.com/96221853-33c4-4531-bdd5-d2ea5acc4799/>`_ Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler. -* `Mix IDE <https://github.com/ethereum/mix/>`_ - Qt based IDE for designing, debugging and testing solidity smart contracts. - * `Package for SublimeText — Solidity language syntax <https://packagecontrol.io/packages/Ethereum/>`_ Solidity syntax highlighting for SublimeText editor. @@ -59,6 +56,12 @@ Available Solidity Integrations * `Vim Solidity <https://github.com/tomlion/vim-solidity/>`_ Plugin for the Vim editor providing syntax highlighting. +Discontinued: + +* `Mix IDE <https://github.com/ethereum/mix/>`_ + Qt based IDE for designing, debugging and testing solidity smart contracts. + + Solidity Tools ------------------------------- diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 26901946..76485745 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -95,6 +95,59 @@ is simplified to code which can also be compiled from even though the instructions contained a jump in the beginning. +.. index:: source mappings + +*************** +Source Mappings +*************** + +As part of the AST output, the compiler provides the range of the source +code that is represented by the respective node in the AST. This can be +used for various purposes ranging from static analysis tools that report +errors based on the AST and debugging tools that highlight local variables +and their uses. + +Furthermore, the compiler can also generate a mapping from the bytecode +to the range in the source code that generated the instruction. This is again +important for static analysis tools that operate on bytecode level and +for displaying the current position in the source code inside a debugger +or for breakpoint handling. + +Both kinds of source mappings use integer indentifiers to refer to source files. +These are regular array indices into a list of source files usually called +``"sourceList"``, which is part of the combined-json and the output of +the json / npm compiler. + +The source mappings inside the AST use the following +notation: + +``s:l:f`` + +Where ``s`` is the byte-offset to the start of the range in the source file, +``l`` is the length of the source range in bytes and ``f`` is the source +index mentioned above. + +The encoding in the source mapping for the bytecode is more complicated: +It is a list of ``s:l:f:j`` separated by ``;``. Each of these +elements corresponds to an instruction, i.e. you cannot use the byte offset +but have to use the instruction offset or PC (program counter). +The fields ``s``, ``l`` and ``f`` are as above and ``j`` can be either +``i``, ``o`` or ``-`` signifying whether a jump instruction goes into a +function, returns from a function or is a regular jump as part of e.g. a loop. + +In order to compress these source mappings especially for bytecode, the +following rules are used: + + - If a field is empty, the value of the preceding element is used. + - If a ``:`` is missing, all following fields are considered empty. + +This means the following source mappings represent the same information: + +``1:2:1;1:9:1;2:1:2;2:1:2;2:1:2`` + +``1:2:1;:9;2::2;;`` + + .. index:: ! commandline compiler, compiler;commandline, ! solc, ! linker .. _commandline-compiler: diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index f1a5dc03..bae6e20b 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -138,6 +138,45 @@ Note that ``.send()`` does **not** throw an exception if the call stack is depleted but rather returns ``false`` in that case. The low-level functions ``.call()``, ``.callcode()`` and ``.delegatecall()`` behave in the same way. +tx.origin +========= + +Never use tx.origin for authorization. Let's say you have a wallet contract like this: + +:: + + contract TxUserWallet { + address owner; + + function TxUserWallet() { + owner = msg.sender; + } + + function transfer(address dest, uint amount) { + if (tx.origin != owner) { throw; } + if (!dest.call.value(amount)()) throw; + } + } + +Now someone tricks you into sending ether to the address of this attack wallet: + +:: + + contract TxAttackWallet { + address owner; + + function TxAttackWallet() { + owner = msg.sender; + } + + function() { + TxUserWallet(msg.sender).transfer(owner, msg.sender.balance); + } + } + +If your wallet had checked msg.sender for authorization, it would get the address of the attack wallet, instead of the owner address. But by checking tx.origin, it gets the original address that kicked off the transaction, which is still the owner address. The attack wallet instantly drains all your funds. + + Minor Details ============= @@ -146,7 +185,11 @@ Minor Details Furthermore, it is not enforced by the EVM, so a contract function that "claims" to be constant might still cause changes to the state. - Types that do not occupy the full 32 bytes might contain "dirty higher order bits". - This is especially important if you access ``msg.data`` - it poses a malleability risk. + This is especially important if you access ``msg.data`` - it poses a malleability risk: + You can craft transactions that call a function ``f(uint8 x)`` with a raw byte argument + of ``0xff000001`` and with ``0x00000001``. Both are fed to the contract and both will + look like the number ``1`` as far as ``x`` is concerned, but ``msg.data`` will + be different, so if you use ``sha3(msg.data)`` for anything, you will get different results. *************** Recommendations diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 99db8147..c509a9d4 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -640,7 +640,7 @@ words. (for example:``MAX_BLOCKS``) Modifiers ========= -Function modifiers should use lowercase words separated by underscores. +Use mixedCase. Avoiding Collisions diff --git a/fetch_umbrella_build_and_test.sh b/fetch_umbrella_build_and_test.sh deleted file mode 100755 index ead54fea..00000000 --- a/fetch_umbrella_build_and_test.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -set -ev - -if [[ "$OSTYPE" != "darwin"* ]] -then - cd docs && sphinx-build -nW -b html -d _build/doctrees . _build/html && cd .. -fi - -SUBREPO=solidity - -cd .. -git clone --depth 3 -b develop https://github.com/ethereum/tests.git -export ETHEREUM_TEST_PATH=$(pwd)/tests/ -git clone --recursive -b develop https://github.com/ethereum/webthree-umbrella.git -cd webthree-umbrella -rm -rf $SUBREPO -mv ../$SUBREPO . -mkdir build -cd build -OPTIONS="" -if [[ "$OSTYPE" != "darwin"* ]] -then - OPTIONS="-DCMAKE_C_COMPILER=/usr/lib/ccache/$CC -DCMAKE_CXX_COMPILER=/usr/lib/ccache/$CXX" -fi -cmake .. -DGUI=0 -DCMAKE_BUILD_TYPE=$TRAVIS_BUILD_TYPE $OPTIONS -make lllc solc soljson soltest - - -# Test runs disabled for macos for now, -# we need to find a way to install eth. -if [[ "$OSTYPE" != "darwin"* ]] -then - eth --test -d /tmp/test & - while [ ! -S /tmp/test/geth.ipc ]; do sleep 2; done - - ./solidity/test/soltest --ipc /tmp/test/geth.ipc - pkill eth -fi - diff --git a/install_dependencies.sh b/install_dependencies.sh deleted file mode 100755 index 737851b5..00000000 --- a/install_dependencies.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env bash - -#------------------------------------------------------------------------------ -# Bash script for installing pre-requisite packages for cpp-ethereum. -# -# The documentation for cpp-ethereum is hosted at: -# -# http://www.ethdocs.org/en/latest/ethereum-clients/cpp-ethereum/ -# -# (c) 2016 cpp-ethereum contributors. -#------------------------------------------------------------------------------ - -if [[ "$OSTYPE" == "darwin"* ]]; then - - if echo `sw_vers` | grep "10.11"; then - echo OS X El Capitan detected - elif echo `sw_vers` | grep "10.10"; then - echo OS X Yosemite detected - else - echo Unsupported OS X version. We only support Yosemite and El Capitan. - exit 1 - fi - - brew update - brew upgrade - - brew install boost - brew install cmake - brew install cryptopp - brew install miniupnpc - brew install leveldb - brew install gmp - brew install jsoncpp - brew install libmicrohttpd - brew install libjson-rpc-cpp - brew install homebrew/versions/llvm37 - -elif [[ "$OSTYPE" == "linux-gnu" ]]; then - - # NOTE - These steps are ONLY correct for Ubuntu Trusty. We need to add - # further conditionals in here for the other Ubuntu versions, and carry on - # from there if we then want to get this pattern working for Debian, - # OpenSUSE, Fedora, Arch Linux, Raspian, FreeBSD, etc. - - # Add additional PPAs which we need to be able to build cpp-ethereum on - # Ubuntu Trusty. That includes our own PPAs and a PPA for getting CMake 3.x - # on Trusty. - sudo add-apt-repository -y ppa:ethereum/ethereum - sudo add-apt-repository -y ppa:ethereum/ethereum-dev - sudo apt-add-repository -y ppa:george-edison55/cmake-3.x - sudo apt-get -y update - - # Install binaries for nearly all of our dependencies - sudo apt-get -y install \ - python-sphinx \ - build-essential \ - cmake \ - git \ - libboost-all-dev \ - libcurl4-openssl-dev \ - libcryptopp-dev \ - libgmp-dev \ - libjsoncpp-dev \ - libleveldb-dev \ - libmicrohttpd-dev \ - libminiupnpc-dev \ - libz-dev \ - opencl-headers \ - eth - - # The exception is libjson-rpc-cpp, which we have to build from source for - # reliable results. The only binaries available for this package are those - # we made ourselves against the (now very old) v0.4.2 release, which are unreliable, - # so instead we build the latest release label (v0.6.0) from source, which works just - # fine. We should update our PPA. - # - # See https://github.com/ethereum/webthree-umbrella/issues/513 - # - # Hmm. Arachnid is still getting this issue on OS X, which already has v0.6.0, so - # it isn't as simple as just updating all our builds to that version, though that is - # sufficient for us to get CircleCI and TravisCI working. We still haven't got to - # the bottom of this issue, and are going to need to debug it in some scenario where - # we can reproduce it 100%, which MIGHT end up being within our automation here, but - # against a build-from-source-with-extra-printfs() of v0.4.2. - sudo apt-get -y install libargtable2-dev libedit-dev - git clone git://github.com/cinemast/libjson-rpc-cpp.git - cd libjson-rpc-cpp - git checkout v0.6.0 - mkdir build - cd build - cmake .. -DCOMPILE_TESTS=NO - make - sudo make install - sudo ldconfig - cd ../.. - - # And install the English language package and reconfigure the locales. - # We really shouldn't need to do this, and should instead force our locales to "C" - # within our application runtimes, because this issue shows up on multiple Linux distros, - # and each will need fixing in the install steps, where we should really just fix it once - # in the code. - # - # See https://github.com/ethereum/webthree-umbrella/issues/169 - sudo apt-get -y install language-pack-en-base - sudo dpkg-reconfigure locales - -fi diff --git a/libdevcore/ABI.h b/libdevcore/ABI.h new file mode 100644 index 00000000..5b7d160d --- /dev/null +++ b/libdevcore/ABI.h @@ -0,0 +1,100 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file ABI.h + * @author Gav Wood <i@gavwood.com> + * @date 2014 + */ + +#pragma once + +#include <libdevcore/Common.h> +#include <libdevcore/FixedHash.h> +#include <libdevcore/CommonData.h> +#include <libdevcore/SHA3.h> + +namespace dev +{ +namespace eth +{ + +inline string32 toString32(std::string const& _s) +{ + string32 ret; + for (unsigned i = 0; i < 32; ++i) + ret[i] = i < _s.size() ? _s[i] : 0; + return ret; +} + +template <class T> struct ABISerialiser {}; +template <unsigned N> struct ABISerialiser<FixedHash<N>> { static bytes serialise(FixedHash<N> const& _t) { static_assert(N <= 32, "Cannot serialise hash > 32 bytes."); static_assert(N > 0, "Cannot serialise zero-length hash."); return bytes(32 - N, 0) + _t.asBytes(); } }; +template <> struct ABISerialiser<u256> { static bytes serialise(u256 const& _t) { return h256(_t).asBytes(); } }; +template <> struct ABISerialiser<u160> { static bytes serialise(u160 const& _t) { return bytes(12, 0) + h160(_t).asBytes(); } }; +template <> struct ABISerialiser<string32> { static bytes serialise(string32 const& _t) { bytes ret; bytesConstRef((byte const*)_t.data(), 32).populate(bytesRef(&ret)); return ret; } }; +template <> struct ABISerialiser<std::string> +{ + static bytes serialise(std::string const& _t) + { + bytes ret = h256(u256(32)).asBytes() + h256(u256(_t.size())).asBytes(); + ret.resize(ret.size() + (_t.size() + 31) / 32 * 32); + bytesConstRef(&_t).populate(bytesRef(&ret).cropped(64)); + return ret; + } +}; + +inline bytes abiInAux() { return {}; } +template <class T, class ... U> bytes abiInAux(T const& _t, U const& ... _u) +{ + return ABISerialiser<T>::serialise(_t) + abiInAux(_u ...); +} + +template <class ... T> bytes abiIn(std::string _id, T const& ... _t) +{ + return sha3(_id).ref().cropped(0, 4).toBytes() + abiInAux(_t ...); +} + +template <class T> struct ABIDeserialiser {}; +template <unsigned N> struct ABIDeserialiser<FixedHash<N>> { static FixedHash<N> deserialise(bytesConstRef& io_t) { static_assert(N <= 32, "Parameter sizes must be at most 32 bytes."); FixedHash<N> ret; io_t.cropped(32 - N, N).populate(ret.ref()); io_t = io_t.cropped(32); return ret; } }; +template <> struct ABIDeserialiser<u256> { static u256 deserialise(bytesConstRef& io_t) { u256 ret = fromBigEndian<u256>(io_t.cropped(0, 32)); io_t = io_t.cropped(32); return ret; } }; +template <> struct ABIDeserialiser<u160> { static u160 deserialise(bytesConstRef& io_t) { u160 ret = fromBigEndian<u160>(io_t.cropped(12, 20)); io_t = io_t.cropped(32); return ret; } }; +template <> struct ABIDeserialiser<string32> { static string32 deserialise(bytesConstRef& io_t) { string32 ret; io_t.cropped(0, 32).populate(bytesRef((byte*)ret.data(), 32)); io_t = io_t.cropped(32); return ret; } }; +template <> struct ABIDeserialiser<std::string> +{ + static std::string deserialise(bytesConstRef& io_t) + { + unsigned o = (uint16_t)u256(h256(io_t.cropped(0, 32))); + unsigned s = (uint16_t)u256(h256(io_t.cropped(o, 32))); + std::string ret; + ret.resize(s); + io_t.cropped(o + 32, s).populate(bytesRef((byte*)ret.data(), s)); + io_t = io_t.cropped(32); + return ret; + } +}; + +template <class T> T abiOut(bytes const& _data) +{ + bytesConstRef o(&_data); + return ABIDeserialiser<T>::deserialise(o); +} + +template <class T> T abiOut(bytesConstRef& _data) +{ + return ABIDeserialiser<T>::deserialise(_data); +} + +} +} diff --git a/libdevcore/Assertions.h b/libdevcore/Assertions.h new file mode 100644 index 00000000..7b4a4a76 --- /dev/null +++ b/libdevcore/Assertions.h @@ -0,0 +1,111 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** + * @file Assertions.h + * @author Christian <c@ethdev.com> + * @date 2015 + * + * Assertion handling. + */ + +#pragma once + +#include "Exceptions.h" +#include "debugbreak.h" + +namespace dev +{ + +#if defined(_MSC_VER) +#define ETH_FUNC __FUNCSIG__ +#elif defined(__GNUC__) +#define ETH_FUNC __PRETTY_FUNCTION__ +#else +#define ETH_FUNC __func__ +#endif + +#define asserts(A) ::dev::assertAux(A, #A, __LINE__, __FILE__, ETH_FUNC) +#define assertsEqual(A, B) ::dev::assertEqualAux(A, B, #A, #B, __LINE__, __FILE__, ETH_FUNC) + +inline bool assertAux(bool _a, char const* _aStr, unsigned _line, char const* _file, char const* _func) +{ + bool ret = _a; + if (!ret) + { + std::cerr << "Assertion failed:" << _aStr << " [func=" << _func << ", line=" << _line << ", file=" << _file << "]" << std::endl; +#if ETH_DEBUG + debug_break(); +#endif + } + return !ret; +} + +template<class A, class B> +inline bool assertEqualAux(A const& _a, B const& _b, char const* _aStr, char const* _bStr, unsigned _line, char const* _file, char const* _func) +{ + bool ret = _a == _b; + if (!ret) + { + std::cerr << "Assertion failed: " << _aStr << " == " << _bStr << " [func=" << _func << ", line=" << _line << ", file=" << _file << "]" << std::endl; + std::cerr << " Fail equality: " << _a << "==" << _b << std::endl; +#if ETH_DEBUG + debug_break(); +#endif + } + return !ret; +} + +/// Assertion that throws an exception containing the given description if it is not met. +/// Use it as assertThrow(1 == 1, ExceptionType, "Mathematics is wrong."); +/// Do NOT supply an exception object as the second parameter. +#define assertThrow(_condition, _ExceptionType, _description) \ + ::dev::assertThrowAux<_ExceptionType>(_condition, _description, __LINE__, __FILE__, ETH_FUNC) + +using errinfo_comment = boost::error_info<struct tag_comment, std::string>; + +template <class _ExceptionType> +inline void assertThrowAux( + bool _condition, + ::std::string const& _errorDescription, + unsigned _line, + char const* _file, + char const* _function +) +{ + if (!_condition) + ::boost::throw_exception( + _ExceptionType() << + ::dev::errinfo_comment(_errorDescription) << + ::boost::throw_function(_function) << + ::boost::throw_file(_file) << + ::boost::throw_line(_line) + ); +} + +template <class _ExceptionType> +inline void assertThrowAux( + void const* _pointer, + ::std::string const& _errorDescription, + unsigned _line, + char const* _file, + char const* _function +) +{ + assertThrowAux<_ExceptionType>(_pointer != nullptr, _errorDescription, _line, _file, _function); +} + +} diff --git a/libdevcore/Base64.cpp b/libdevcore/Base64.cpp new file mode 100644 index 00000000..8ee2b29f --- /dev/null +++ b/libdevcore/Base64.cpp @@ -0,0 +1,146 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch +*/ +/// Adapted from code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c +/// Originally by René Nyffenegger, modified by some other guy and then devified by Gav Wood. + +#include "Base64.h" + +using namespace std; +using namespace dev; + +static inline bool is_base64(byte c) +{ + return (isalnum(c) || (c == '+') || (c == '/')); +} + +static inline byte find_base64_char_index(byte c) +{ + if ('A' <= c && c <= 'Z') return c - 'A'; + else if ('a' <= c && c <= 'z') return c - 'a' + 1 + find_base64_char_index('Z'); + else if ('0' <= c && c <= '9') return c - '0' + 1 + find_base64_char_index('z'); + else if (c == '+') return 1 + find_base64_char_index('9'); + else if (c == '/') return 1 + find_base64_char_index('+'); + else return 1 + find_base64_char_index('/'); +} + +string dev::toBase64(bytesConstRef _in) +{ + static const char base64_chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + string ret; + int i = 0; + int j = 0; + byte char_array_3[3]; + byte char_array_4[4]; + + auto buf = _in.data(); + auto bufLen = _in.size(); + + while (bufLen--) + { + char_array_3[i++] = *(buf++); + if (i == 3) + { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (i = 0; i < 4; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for (j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; j < i + 1; j++) + ret += base64_chars[char_array_4[j]]; + + while (i++ < 3) + ret += '='; + } + + return ret; +} + +bytes dev::fromBase64(string const& encoded_string) +{ + auto in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + byte char_array_3[3]; + byte char_array_4[4]; + bytes ret; + + while (in_len-- && encoded_string[in_] != '=' && is_base64(encoded_string[in_])) + { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i == 4) + { + for (i = 0; i < 4; i++) + char_array_4[i] = find_base64_char_index(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret.push_back(char_array_3[i]); + i = 0; + } + } + + if (i) + { + for (j = i; j < 4; j++) + char_array_4[j] = 0; + + for (j = 0; j < 4; j++) + char_array_4[j] = find_base64_char_index(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; j < i - 1; j++) + ret.push_back(char_array_3[j]); + } + + return ret; +} diff --git a/libdevcore/Base64.h b/libdevcore/Base64.h new file mode 100644 index 00000000..d5481570 --- /dev/null +++ b/libdevcore/Base64.h @@ -0,0 +1,42 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch +*/ +/// Adapted from code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c +/// Originally by René Nyffenegger. +/// DEVified by Gav Wood. +#pragma once + +#include <string> +#include "Common.h" +#include "FixedHash.h" + +namespace dev +{ + +std::string toBase64(bytesConstRef _in); +bytes fromBase64(std::string const& _in); + + +} diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt new file mode 100644 index 00000000..43dd71c3 --- /dev/null +++ b/libdevcore/CMakeLists.txt @@ -0,0 +1,16 @@ +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +aux_source_directory(. SRC_LIST) + +set(EXECUTABLE devcore) + +file(GLOB HEADERS "*.h") + +include_directories(..) +add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) + +eth_use(${EXECUTABLE} REQUIRED Jsoncpp Dev::base) + +install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp new file mode 100644 index 00000000..579e0f62 --- /dev/null +++ b/libdevcore/Common.cpp @@ -0,0 +1,27 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file Common.cpp + * @author Gav Wood <i@gavwood.com> + * @date 2014 + */ + +#include "Common.h" + +namespace dev +{ +const u256 Invalid256 = ~(u256)0; +} diff --git a/libdevcore/Common.h b/libdevcore/Common.h new file mode 100644 index 00000000..7d301833 --- /dev/null +++ b/libdevcore/Common.h @@ -0,0 +1,210 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file Common.h + * @author Gav Wood <i@gavwood.com> + * @date 2014 + * + * Very common stuff (i.e. that every other header needs except vector_ref.h). + */ + +#pragma once + +// way to many unsigned to size_t warnings in 32 bit build +#ifdef _M_IX86 +#pragma warning(disable:4244) +#endif + +#if _MSC_VER && _MSC_VER < 1900 +#define _ALLOW_KEYWORD_MACROS +#define noexcept throw() +#endif + +#ifdef __INTEL_COMPILER +#pragma warning(disable:3682) //call through incomplete class +#endif + +#include <map> +#include <unordered_map> +#include <vector> +#include <set> +#include <unordered_set> +#include <functional> +#include <string> +#include <chrono> + +#if defined(__GNUC__) +#pragma warning(push) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif // defined(__GNUC__) + +// See https://github.com/ethereum/libweb3core/commit/90680a8c25bfb48b24371b4abcacde56c181517c +// See https://svn.boost.org/trac/boost/ticket/11328 +// Bob comment - perhaps we should just HARD FAIL here with Boost-1.58.00? +// It is quite old now, and requiring end-users to use a newer Boost release is probably not unreasonable. +#include <boost/version.hpp> +#if (BOOST_VERSION == 105800) + #include "boost_multiprecision_number_compare_bug_workaround.hpp" +#endif // (BOOST_VERSION == 105800) + +#include <boost/multiprecision/cpp_int.hpp> + +#if defined(__GNUC__) +#pragma warning(pop) +#pragma GCC diagnostic pop +#endif // defined(__GNUC__) + +#include "vector_ref.h" + +// CryptoPP defines byte in the global namespace, so must we. +using byte = uint8_t; + +// Quote a given token stream to turn it into a string. +#define DEV_QUOTED_HELPER(s) #s +#define DEV_QUOTED(s) DEV_QUOTED_HELPER(s) + +#define DEV_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} + +namespace dev +{ + +// Binary data types. +using bytes = std::vector<byte>; +using bytesRef = vector_ref<byte>; +using bytesConstRef = vector_ref<byte const>; + +template <class T> +class secure_vector +{ +public: + secure_vector() {} + secure_vector(secure_vector<T> const& /*_c*/) = default; // See https://github.com/ethereum/libweb3core/pull/44 + explicit secure_vector(unsigned _size): m_data(_size) {} + explicit secure_vector(unsigned _size, T _item): m_data(_size, _item) {} + explicit secure_vector(std::vector<T> const& _c): m_data(_c) {} + explicit secure_vector(vector_ref<T> _c): m_data(_c.data(), _c.data() + _c.size()) {} + explicit secure_vector(vector_ref<const T> _c): m_data(_c.data(), _c.data() + _c.size()) {} + ~secure_vector() { ref().cleanse(); } + + secure_vector<T>& operator=(secure_vector<T> const& _c) + { + if (&_c == this) + return *this; + + ref().cleanse(); + m_data = _c.m_data; + return *this; + } + std::vector<T>& writable() { clear(); return m_data; } + std::vector<T> const& makeInsecure() const { return m_data; } + + void clear() { ref().cleanse(); } + + vector_ref<T> ref() { return vector_ref<T>(&m_data); } + vector_ref<T const> ref() const { return vector_ref<T const>(&m_data); } + + size_t size() const { return m_data.size(); } + bool empty() const { return m_data.empty(); } + + void swap(secure_vector<T>& io_other) { m_data.swap(io_other.m_data); } + +private: + std::vector<T> m_data; +}; + +using bytesSec = secure_vector<byte>; + +// Numeric types. +using bigint = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<>>; +using u64 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<64, 64, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>; +using u128 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<128, 128, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>; +using u256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>; +using s256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void>>; +using u160 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<160, 160, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>; +using s160 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<160, 160, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void>>; +using u512 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<512, 512, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>; +using s512 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<512, 512, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void>>; +using u256s = std::vector<u256>; +using u160s = std::vector<u160>; +using u256Set = std::set<u256>; +using u160Set = std::set<u160>; + +// Map types. +using StringMap = std::map<std::string, std::string>; + +// Hash types. +using StringHashMap = std::unordered_map<std::string, std::string>; + +// String types. +using strings = std::vector<std::string>; +// Fixed-length string types. +using string32 = std::array<char, 32>; + +// Null/Invalid values for convenience. +static const bytes NullBytes; +extern const u256 Invalid256; + +/// Interprets @a _u as a two's complement signed number and returns the resulting s256. +inline s256 u2s(u256 _u) +{ + static const bigint c_end = bigint(1) << 256; + if (boost::multiprecision::bit_test(_u, 255)) + return s256(-(c_end - _u)); + else + return s256(_u); +} + +/// @returns the two's complement signed representation of the signed number _u. +inline u256 s2u(s256 _u) +{ + static const bigint c_end = bigint(1) << 256; + if (_u >= 0) + return u256(_u); + else + return u256(c_end + _u); +} + +template <size_t n> inline u256 exp10() +{ + return exp10<n - 1>() * u256(10); +} + +template <> inline u256 exp10<0>() +{ + return u256(1); +} + +/// RAII utility class whose destructor calls a given function. +class ScopeGuard +{ +public: + ScopeGuard(std::function<void(void)> _f): m_f(_f) {} + ~ScopeGuard() { m_f(); } + +private: + std::function<void(void)> m_f; +}; + +enum class WithExisting: int +{ + Trust = 0, + Verify, + Rescue, + Kill +}; + +} diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp new file mode 100644 index 00000000..fc438276 --- /dev/null +++ b/libdevcore/CommonData.cpp @@ -0,0 +1,107 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file CommonData.cpp + * @author Gav Wood <i@gavwood.com> + * @date 2014 + */ + +#include "CommonData.h" +#include <random> + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4724) // potential mod by 0, line 78 of boost/random/uniform_int_distribution.hpp (boost 1.55) +#endif +#include <boost/random/uniform_int_distribution.hpp> +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#include "Exceptions.h" +using namespace std; +using namespace dev; + +std::string dev::escaped(std::string const& _s, bool _all) +{ + static const map<char, char> prettyEscapes{{'\r', 'r'}, {'\n', 'n'}, {'\t', 't'}, {'\v', 'v'}}; + std::string ret; + ret.reserve(_s.size() + 2); + ret.push_back('"'); + for (auto i: _s) + if (i == '"' && !_all) + ret += "\\\""; + else if (i == '\\' && !_all) + ret += "\\\\"; + else if (prettyEscapes.count(i) && !_all) + { + ret += '\\'; + ret += prettyEscapes.find(i)->second; + } + else if (i < ' ' || _all) + { + ret += "\\x"; + ret.push_back("0123456789abcdef"[(uint8_t)i / 16]); + ret.push_back("0123456789abcdef"[(uint8_t)i % 16]); + } + else + ret.push_back(i); + ret.push_back('"'); + return ret; +} + +int dev::fromHex(char _i, WhenError _throw) +{ + if (_i >= '0' && _i <= '9') + return _i - '0'; + if (_i >= 'a' && _i <= 'f') + return _i - 'a' + 10; + if (_i >= 'A' && _i <= 'F') + return _i - 'A' + 10; + if (_throw == WhenError::Throw) + BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i)); + else + return -1; +} + +bytes dev::fromHex(std::string const& _s, WhenError _throw) +{ + unsigned s = (_s.size() >= 2 && _s[0] == '0' && _s[1] == 'x') ? 2 : 0; + std::vector<uint8_t> ret; + ret.reserve((_s.size() - s + 1) / 2); + + if (_s.size() % 2) + { + int h = fromHex(_s[s++], WhenError::DontThrow); + if (h != -1) + ret.push_back(h); + else if (_throw == WhenError::Throw) + BOOST_THROW_EXCEPTION(BadHexCharacter()); + else + return bytes(); + } + for (unsigned i = s; i < _s.size(); i += 2) + { + int h = fromHex(_s[i], WhenError::DontThrow); + int l = fromHex(_s[i + 1], WhenError::DontThrow); + if (h != -1 && l != -1) + ret.push_back((byte)(h * 16 + l)); + else if (_throw == WhenError::Throw) + BOOST_THROW_EXCEPTION(BadHexCharacter()); + else + return bytes(); + } + return ret; +} diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h new file mode 100644 index 00000000..5ffcdcca --- /dev/null +++ b/libdevcore/CommonData.h @@ -0,0 +1,182 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file CommonData.h + * @author Gav Wood <i@gavwood.com> + * @date 2014 + * + * Shared algorithms and data types. + */ + +#pragma once + +#include <vector> +#include <algorithm> +#include <unordered_set> +#include <type_traits> +#include <cstring> +#include <string> +#include "Common.h" + +namespace dev +{ + +// String conversion functions, mainly to/from hex/nibble/byte representations. + +enum class WhenError +{ + DontThrow = 0, + Throw = 1, +}; + +enum class HexPrefix +{ + DontAdd = 0, + Add = 1, +}; +/// Convert a series of bytes to the corresponding string of hex duplets. +/// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte. +/// @example toHex("A\x69") == "4169" +template <class T> +std::string toHex(T const& _data, int _w = 2, HexPrefix _prefix = HexPrefix::DontAdd) +{ + std::ostringstream ret; + unsigned ii = 0; + for (auto i: _data) + ret << std::hex << std::setfill('0') << std::setw(ii++ ? 2 : _w) << (int)(typename std::make_unsigned<decltype(i)>::type)i; + return (_prefix == HexPrefix::Add) ? "0x" + ret.str() : ret.str(); +} + +/// Converts a (printable) ASCII hex character into the correspnding integer value. +/// @example fromHex('A') == 10 && fromHex('f') == 15 && fromHex('5') == 5 +int fromHex(char _i, WhenError _throw); + +/// Converts a (printable) ASCII hex string into the corresponding byte stream. +/// @example fromHex("41626261") == asBytes("Abba") +/// If _throw = ThrowType::DontThrow, it replaces bad hex characters with 0's, otherwise it will throw an exception. +bytes fromHex(std::string const& _s, WhenError _throw = WhenError::DontThrow); +/// Converts byte array to a string containing the same (binary) data. Unless +/// the byte array happens to contain ASCII data, this won't be printable. +inline std::string asString(bytes const& _b) +{ + return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size())); +} + +/// Converts byte array ref to a string containing the same (binary) data. Unless +/// the byte array happens to contain ASCII data, this won't be printable. +inline std::string asString(bytesConstRef _b) +{ + return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size())); +} + +/// Converts a string to a byte array containing the string's (byte) data. +inline bytes asBytes(std::string const& _b) +{ + return bytes((byte const*)_b.data(), (byte const*)(_b.data() + _b.size())); +} + +// Big-endian to/from host endian conversion functions. + +/// Converts a templated integer value to the big-endian byte-stream represented on a templated collection. +/// The size of the collection object will be unchanged. If it is too small, it will not represent the +/// value properly, if too big then the additional elements will be zeroed out. +/// @a Out will typically be either std::string or bytes. +/// @a T will typically by unsigned, u160, u256 or bigint. +template <class T, class Out> +inline void toBigEndian(T _val, Out& o_out) +{ + static_assert(std::is_same<bigint, T>::value || !std::numeric_limits<T>::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift + for (auto i = o_out.size(); i != 0; _val >>= 8, i--) + { + T v = _val & (T)0xff; + o_out[i - 1] = (typename Out::value_type)(uint8_t)v; + } +} + +/// Converts a big-endian byte-stream represented on a templated collection to a templated integer value. +/// @a _In will typically be either std::string or bytes. +/// @a T will typically by unsigned, u160, u256 or bigint. +template <class T, class _In> +inline T fromBigEndian(_In const& _bytes) +{ + T ret = (T)0; + for (auto i: _bytes) + ret = (T)((ret << 8) | (byte)(typename std::make_unsigned<typename _In::value_type>::type)i); + return ret; +} +inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } +inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); return ret; } + +/// Convenience function for toBigEndian. +/// @returns a byte array just big enough to represent @a _val. +template <class T> +inline bytes toCompactBigEndian(T _val, unsigned _min = 0) +{ + static_assert(std::is_same<bigint, T>::value || !std::numeric_limits<T>::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift + int i = 0; + for (T v = _val; v; ++i, v >>= 8) {} + bytes ret(std::max<unsigned>(_min, i), 0); + toBigEndian(_val, ret); + return ret; +} +inline bytes toCompactBigEndian(byte _val, unsigned _min = 0) +{ + return (_min || _val) ? bytes{ _val } : bytes{}; +} + +/// Convenience function for conversion of a u256 to hex +inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) +{ + std::string str = toHex(toBigEndian(val)); + return (prefix == HexPrefix::Add) ? "0x" + str : str; +} + +// Algorithms for string and string-like collections. + +/// Escapes a string into the C-string representation. +/// @p _all if true will escape all characters, not just the unprintable ones. +std::string escaped(std::string const& _s, bool _all = true); +/// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero. +template <class T> +inline unsigned bytesRequired(T _i) +{ + static_assert(std::is_same<bigint, T>::value || !std::numeric_limits<T>::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift + unsigned i = 0; + for (; _i != 0; ++i, _i >>= 8) {} + return i; +} +/// Concatenate the contents of a container onto a vector +template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U const& _b) +{ + for (auto const& i: _b) + _a.push_back(i); + return _a; +} +/// Concatenate two vectors of elements. +template <class T> +inline std::vector<T> operator+(std::vector<T> const& _a, std::vector<T> const& _b) +{ + std::vector<T> ret(_a); + return ret += _b; +} + +template <class T, class V> +bool contains(T const& _t, V const& _v) +{ + return std::end(_t) != std::find(std::begin(_t), std::end(_t), _v); +} + +} diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp new file mode 100644 index 00000000..60ac518d --- /dev/null +++ b/libdevcore/CommonIO.cpp @@ -0,0 +1,90 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file CommonIO.cpp + * @author Gav Wood <i@gavwood.com> + * @date 2014 + */ + +#include "CommonIO.h" +#include <iostream> +#include <cstdlib> +#include <fstream> +#include <stdio.h> +#if defined(_WIN32) +#include <windows.h> +#else +#include <termios.h> +#endif +#include <boost/filesystem.hpp> +#include "Exceptions.h" +using namespace std; +using namespace dev; + + +template <typename _T> +inline _T contentsGeneric(std::string const& _file) +{ + _T ret; + size_t const c_elementSize = sizeof(typename _T::value_type); + std::ifstream is(_file, std::ifstream::binary); + if (!is) + return ret; + + // get length of file: + is.seekg(0, is.end); + streamoff length = is.tellg(); + if (length == 0) + return ret; // do not read empty file (MSVC does not like it) + is.seekg(0, is.beg); + + ret.resize((length + c_elementSize - 1) / c_elementSize); + is.read(const_cast<char*>(reinterpret_cast<char const*>(ret.data())), length); + return ret; +} + +string dev::contentsString(string const& _file) +{ + return contentsGeneric<string>(_file); +} + +void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename) +{ + namespace fs = boost::filesystem; + if (_writeDeleteRename) + { + fs::path tempPath = fs::unique_path(_file + "-%%%%%%"); + writeFile(tempPath.string(), _data, false); + // will delete _file if it exists + fs::rename(tempPath, _file); + } + else + { + // create directory if not existent + fs::path p(_file); + if (!fs::exists(p.parent_path())) + { + fs::create_directories(p.parent_path()); + DEV_IGNORE_EXCEPTIONS(fs::permissions(p.parent_path(), fs::owner_all)); + } + + ofstream s(_file, ios::trunc | ios::binary); + s.write(reinterpret_cast<char const*>(_data.data()), _data.size()); + if (!s) + BOOST_THROW_EXCEPTION(FileError() << errinfo_comment("Could not write to file: " + _file)); + DEV_IGNORE_EXCEPTIONS(fs::permissions(_file, fs::owner_read|fs::owner_write)); + } +} diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h new file mode 100644 index 00000000..8238fe0f --- /dev/null +++ b/libdevcore/CommonIO.h @@ -0,0 +1,54 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file CommonIO.h + * @author Gav Wood <i@gavwood.com> + * @date 2014 + * + * File & stream I/O routines. + */ + +#pragma once + +#include <sstream> +#include <string> +#include "Common.h" + +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); + +/// Write the given binary data into the given file, replacing the file if it pre-exists. +/// Throws exception on error. +/// @param _writeDeleteRename useful not to lose any data: If set, first writes to another file in +/// the same directory and then moves that file. +void writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename = false); +/// Write the given binary data into the given file, replacing the file if it pre-exists. +inline void writeFile(std::string const& _file, bytes const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(&_data), _writeDeleteRename); } +inline void writeFile(std::string const& _file, std::string const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(_data), _writeDeleteRename); } +/// Converts arbitrary value to string representation using std::stringstream. +template <class _T> +std::string toString(_T const& _t) +{ + std::ostringstream o; + o << _t; + return o.str(); +} + +} diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h new file mode 100644 index 00000000..a6c1f9ab --- /dev/null +++ b/libdevcore/Exceptions.h @@ -0,0 +1,87 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file Exceptions.h + * @author Gav Wood <i@gavwood.com> + * @date 2014 + */ + +#pragma once + +#include <exception> +#include <string> +#include <boost/exception/exception.hpp> +#include <boost/exception/info.hpp> +#include <boost/exception/info_tuple.hpp> +#include <boost/exception/diagnostic_information.hpp> +#include <boost/throw_exception.hpp> +#include <boost/tuple/tuple.hpp> +#include "CommonData.h" +#include "FixedHash.h" + +namespace dev +{ + +/// Base class for all exceptions. +struct Exception: virtual std::exception, virtual boost::exception +{ + Exception(std::string _message = std::string()): m_message(std::move(_message)) {} + const char* what() const noexcept override { return m_message.empty() ? std::exception::what() : m_message.c_str(); } + +private: + std::string m_message; +}; + +#define DEV_SIMPLE_EXCEPTION(X) struct X: virtual Exception { const char* what() const noexcept override { return #X; } } + +/// Base class for all RLP exceptions. +struct RLPException: virtual Exception { RLPException(std::string _message = std::string()): Exception(_message) {} }; +#define DEV_SIMPLE_EXCEPTION_RLP(X) struct X: virtual RLPException { const char* what() const noexcept override { return #X; } } + +DEV_SIMPLE_EXCEPTION_RLP(BadCast); +DEV_SIMPLE_EXCEPTION_RLP(BadRLP); +DEV_SIMPLE_EXCEPTION_RLP(OversizeRLP); +DEV_SIMPLE_EXCEPTION_RLP(UndersizeRLP); + +DEV_SIMPLE_EXCEPTION(BadHexCharacter); +DEV_SIMPLE_EXCEPTION(NoNetworking); +DEV_SIMPLE_EXCEPTION(NoUPnPDevice); +DEV_SIMPLE_EXCEPTION(RootNotFound); +struct BadRoot: virtual Exception { public: BadRoot(h256 const& _root): Exception("BadRoot " + _root.hex()), root(_root) {} h256 root; }; +DEV_SIMPLE_EXCEPTION(FileError); +DEV_SIMPLE_EXCEPTION(Overflow); +DEV_SIMPLE_EXCEPTION(FailedInvariant); +DEV_SIMPLE_EXCEPTION(ValueTooLarge); + +struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): Exception("Interface " + _f + " not supported.") {} }; +struct ExternalFunctionFailure: virtual Exception { public: ExternalFunctionFailure(std::string _f): Exception("Function " + _f + "() failed.") {} }; + +// error information to be added to exceptions +using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>; +using errinfo_wrongAddress = boost::error_info<struct tag_address, std::string>; +using errinfo_comment = boost::error_info<struct tag_comment, std::string>; +using errinfo_required = boost::error_info<struct tag_required, bigint>; +using errinfo_got = boost::error_info<struct tag_got, bigint>; +using errinfo_min = boost::error_info<struct tag_min, bigint>; +using errinfo_max = boost::error_info<struct tag_max, bigint>; +using RequirementError = boost::tuple<errinfo_required, errinfo_got>; +using errinfo_hash256 = boost::error_info<struct tag_hash, h256>; +using errinfo_required_h256 = boost::error_info<struct tag_required_h256, h256>; +using errinfo_got_h256 = boost::error_info<struct tag_get_h256, h256>; +using Hash256RequirementError = boost::tuple<errinfo_required_h256, errinfo_got_h256>; +using errinfo_extraData = boost::error_info<struct tag_extraData, bytes>; + +} diff --git a/libdevcore/FixedHash.cpp b/libdevcore/FixedHash.cpp new file mode 100644 index 00000000..420e2bb1 --- /dev/null +++ b/libdevcore/FixedHash.cpp @@ -0,0 +1,50 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file FixedHash.cpp + * @author Gav Wood <i@gavwood.com> + * @date 2014 + */ + +#include "FixedHash.h" +#include <ctime> +#include <boost/algorithm/string.hpp> + +using namespace std; +using namespace dev; + +boost::random_device dev::s_fixedHashEngine; + +h128 dev::fromUUID(std::string const& _uuid) +{ + try + { + return h128(boost::replace_all_copy(_uuid, "-", "")); + } + catch (...) + { + return h128(); + } +} + +std::string dev::toUUID(h128 const& _uuid) +{ + std::string ret = toHex(_uuid.ref()); + for (unsigned i: {20, 16, 12, 8}) + ret.insert(ret.begin() + i, '-'); + return ret; +} + diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h new file mode 100644 index 00000000..cf79bab0 --- /dev/null +++ b/libdevcore/FixedHash.h @@ -0,0 +1,306 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file FixedHash.h + * @author Gav Wood <i@gavwood.com> + * @date 2014 + * + * The FixedHash fixed-size "hash" container type. + */ + +#pragma once + +#include <array> +#include <cstdint> +#include <algorithm> +#include <boost/random/random_device.hpp> +#include <boost/random/uniform_int_distribution.hpp> +#include <boost/functional/hash.hpp> +#include "CommonData.h" + +namespace dev +{ + +/// Compile-time calculation of Log2 of constant values. +template <unsigned N> struct StaticLog2 { enum { result = 1 + StaticLog2<N/2>::result }; }; +template <> struct StaticLog2<1> { enum { result = 0 }; }; + +extern boost::random_device s_fixedHashEngine; + +/// Fixed-size raw-byte array container type, with an API optimised for storing hashes. +/// Transparently converts to/from the corresponding arithmetic type; this will +/// assume the data contained in the hash is big-endian. +template <unsigned N> +class FixedHash +{ +public: + /// The corresponding arithmetic type. + using Arith = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>; + + /// The size of the container. + enum { size = N }; + + /// A dummy flag to avoid accidental construction from pointer. + enum ConstructFromPointerType { ConstructFromPointer }; + + /// Method to convert from a string. + enum ConstructFromStringType { FromHex, FromBinary }; + + /// Method to convert from a string. + enum ConstructFromHashType { AlignLeft, AlignRight, FailIfDifferent }; + + /// Construct an empty hash. + FixedHash() { m_data.fill(0); } + + /// Construct from another hash, filling with zeroes or cropping as necessary. + template <unsigned M> explicit FixedHash(FixedHash<M> const& _h, ConstructFromHashType _t = AlignLeft) { m_data.fill(0); unsigned c = std::min(M, N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _h[_t == AlignRight ? M - 1 - i : i]; } + + /// Convert from the corresponding arithmetic type. + FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); } + + /// Convert from unsigned + explicit FixedHash(unsigned _u) { toBigEndian(_u, m_data); } + + /// Explicitly construct, copying from a byte array. + explicit FixedHash(bytes const& _b, ConstructFromHashType _t = FailIfDifferent) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min<unsigned>(_b.size(), N)); else { m_data.fill(0); if (_t != FailIfDifferent) { auto c = std::min<unsigned>(_b.size(), N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _b[_t == AlignRight ? _b.size() - 1 - i : i]; } } } + + /// Explicitly construct, copying from a byte array. + explicit FixedHash(bytesConstRef _b, ConstructFromHashType _t = FailIfDifferent) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min<unsigned>(_b.size(), N)); else { m_data.fill(0); if (_t != FailIfDifferent) { auto c = std::min<unsigned>(_b.size(), N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _b[_t == AlignRight ? _b.size() - 1 - i : i]; } } } + + /// Explicitly construct, copying from a bytes in memory with given pointer. + explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); } + + /// Explicitly construct, copying from a string. + explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex, ConstructFromHashType _ht = FailIfDifferent): FixedHash(_t == FromHex ? fromHex(_s, WhenError::Throw) : dev::asBytes(_s), _ht) {} + + /// Convert to arithmetic type. + operator Arith() const { return fromBigEndian<Arith>(m_data); } + + /// @returns true iff this is the empty hash. + explicit operator bool() const { return std::any_of(m_data.begin(), m_data.end(), [](byte _b) { return _b != 0; }); } + + // The obvious comparison operators. + bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; } + bool operator!=(FixedHash const& _c) const { return m_data != _c.m_data; } + bool operator<(FixedHash const& _c) const { for (unsigned i = 0; i < N; ++i) if (m_data[i] < _c.m_data[i]) return true; else if (m_data[i] > _c.m_data[i]) return false; return false; } + bool operator>=(FixedHash const& _c) const { return !operator<(_c); } + bool operator<=(FixedHash const& _c) const { return operator==(_c) || operator<(_c); } + bool operator>(FixedHash const& _c) const { return !operator<=(_c); } + + // The obvious binary operators. + FixedHash& operator^=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] ^= _c.m_data[i]; return *this; } + FixedHash operator^(FixedHash const& _c) const { return FixedHash(*this) ^= _c; } + FixedHash& operator|=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] |= _c.m_data[i]; return *this; } + FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; } + FixedHash& operator&=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; } + FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; } + FixedHash operator~() const { FixedHash ret; for (unsigned i = 0; i < N; ++i) ret[i] = ~m_data[i]; return ret; } + + // Big-endian increment. + FixedHash& operator++() { for (unsigned i = size; i > 0 && !++m_data[--i]; ) {} return *this; } + + /// @returns true if all one-bits in @a _c are set in this object. + bool contains(FixedHash const& _c) const { return (*this & _c) == _c; } + + /// @returns a particular byte from the hash. + byte& operator[](unsigned _i) { return m_data[_i]; } + /// @returns a particular byte from the hash. + byte operator[](unsigned _i) const { return m_data[_i]; } + + /// @returns an abridged version of the hash as a user-readable hex string. + std::string abridged() const { return toHex(ref().cropped(0, 4)) + "\342\200\246"; } + + /// @returns a version of the hash as a user-readable hex string that leaves out the middle part. + std::string abridgedMiddle() const { return toHex(ref().cropped(0, 4)) + "\342\200\246" + toHex(ref().cropped(N - 4)); } + + /// @returns the hash as a user-readable hex string. + std::string hex() const { return toHex(ref()); } + + /// @returns a mutable byte vector_ref to the object's data. + bytesRef ref() { return bytesRef(m_data.data(), N); } + + /// @returns a constant byte vector_ref to the object's data. + bytesConstRef ref() const { return bytesConstRef(m_data.data(), N); } + + /// @returns a mutable byte pointer to the object's data. + byte* data() { return m_data.data(); } + + /// @returns a constant byte pointer to the object's data. + byte const* data() const { return m_data.data(); } + + /// @returns a copy of the object's data as a byte vector. + bytes asBytes() const { return bytes(data(), data() + N); } + + /// @returns a mutable reference to the object's data as an STL array. + std::array<byte, N>& asArray() { return m_data; } + + /// @returns a constant reference to the object's data as an STL array. + std::array<byte, N> const& asArray() const { return m_data; } + + /// Populate with random data. + template <class Engine> + void randomize(Engine& _eng) + { + for (auto& i: m_data) + i = (uint8_t)boost::random::uniform_int_distribution<uint16_t>(0, 255)(_eng); + } + + /// @returns a random valued object. + static FixedHash random() { FixedHash ret; ret.randomize(s_fixedHashEngine); return ret; } + + struct hash + { + /// Make a hash of the object's data. + size_t operator()(FixedHash const& _value) const { return boost::hash_range(_value.m_data.cbegin(), _value.m_data.cend()); } + }; + + template <unsigned P, unsigned M> inline FixedHash& shiftBloom(FixedHash<M> const& _h) + { + return (*this |= _h.template bloomPart<P, N>()); + } + + template <unsigned P, unsigned M> inline bool containsBloom(FixedHash<M> const& _h) + { + return contains(_h.template bloomPart<P, N>()); + } + + template <unsigned P, unsigned M> inline FixedHash<M> bloomPart() const + { + unsigned const c_bloomBits = M * 8; + unsigned const c_mask = c_bloomBits - 1; + unsigned const c_bloomBytes = (StaticLog2<c_bloomBits>::result + 7) / 8; + + static_assert((M & (M - 1)) == 0, "M must be power-of-two"); + static_assert(P * c_bloomBytes <= N, "out of range"); + + FixedHash<M> ret; + byte const* p = data(); + for (unsigned i = 0; i < P; ++i) + { + unsigned index = 0; + for (unsigned j = 0; j < c_bloomBytes; ++j, ++p) + index = (index << 8) | *p; + index &= c_mask; + ret[M - 1 - index / 8] |= (1 << (index % 8)); + } + return ret; + } + + /// Returns the index of the first bit set to one, or size() * 8 if no bits are set. + inline unsigned firstBitSet() const + { + unsigned ret = 0; + for (auto d: m_data) + if (d) + for (;; ++ret, d <<= 1) + if (d & 0x80) + return ret; + else {} + else + ret += 8; + return ret; + } + + void clear() { m_data.fill(0); } + +private: + std::array<byte, N> m_data; ///< The binary data. +}; + +/// Fast equality operator for h256. +template<> inline bool FixedHash<32>::operator==(FixedHash<32> const& _other) const +{ + const uint64_t* hash1 = (const uint64_t*)data(); + const uint64_t* hash2 = (const uint64_t*)_other.data(); + return (hash1[0] == hash2[0]) && (hash1[1] == hash2[1]) && (hash1[2] == hash2[2]) && (hash1[3] == hash2[3]); +} + +/// Fast std::hash compatible hash function object for h256. +template<> inline size_t FixedHash<32>::hash::operator()(FixedHash<32> const& value) const +{ + uint64_t const* data = reinterpret_cast<uint64_t const*>(value.data()); + return boost::hash_range(data, data + 4); +} + +/// Stream I/O for the FixedHash class. +template <unsigned N> +inline std::ostream& operator<<(std::ostream& _out, FixedHash<N> const& _h) +{ + _out << std::noshowbase << std::hex << std::setfill('0'); + for (unsigned i = 0; i < N; ++i) + _out << std::setw(2) << (int)_h[i]; + _out << std::dec; + return _out; +} + +// Common types of FixedHash. +using h2048 = FixedHash<256>; +using h1024 = FixedHash<128>; +using h520 = FixedHash<65>; +using h512 = FixedHash<64>; +using h256 = FixedHash<32>; +using h160 = FixedHash<20>; +using h128 = FixedHash<16>; +using h64 = FixedHash<8>; +using h512s = std::vector<h512>; +using h256s = std::vector<h256>; +using h160s = std::vector<h160>; +using h256Set = std::set<h256>; +using h160Set = std::set<h160>; +using h256Hash = std::unordered_set<h256>; +using h160Hash = std::unordered_set<h160>; + +/// Convert the given value into h160 (160-bit unsigned integer) using the right 20 bytes. +inline h160 right160(h256 const& _t) +{ + h160 ret; + memcpy(ret.data(), _t.data() + 12, 20); + return ret; +} + +/// Convert the given value into h160 (160-bit unsigned integer) using the left 20 bytes. +inline h160 left160(h256 const& _t) +{ + h160 ret; + memcpy(&ret[0], _t.data(), 20); + return ret; +} + +h128 fromUUID(std::string const& _uuid); + +std::string toUUID(h128 const& _uuid); + +inline std::string toString(h256s const& _bs) +{ + std::ostringstream out; + out << "[ "; + for (auto i: _bs) + out << i.abridged() << ", "; + out << "]"; + return out.str(); +} + +} + +namespace std +{ + /// Forward std::hash<dev::FixedHash> to dev::FixedHash::hash. + template<> struct hash<dev::h64>: dev::h64::hash {}; + template<> struct hash<dev::h128>: dev::h128::hash {}; + template<> struct hash<dev::h160>: dev::h160::hash {}; + template<> struct hash<dev::h256>: dev::h256::hash {}; + template<> struct hash<dev::h512>: dev::h512::hash {}; +} diff --git a/libdevcore/Hash.cpp b/libdevcore/Hash.cpp new file mode 100644 index 00000000..c6b917b9 --- /dev/null +++ b/libdevcore/Hash.cpp @@ -0,0 +1,440 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file Hash.cpp + * @author Gav Wood <i@gavwood.com> + * @date 2014 + */ + +#include "Hash.h" +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include "picosha2.h" +using namespace std; +using namespace dev; + +namespace dev +{ + +h256 sha256(bytesConstRef _input) +{ + h256 ret; + picosha2::hash256(_input.begin(), _input.end(), ret.data(), ret.data() + 32); + return ret; +} + +namespace rmd160 +{ + +/********************************************************************\ + * + * FILE: rmd160.h + * FILE: rmd160.c + * + * CONTENTS: Header file for a sample C-implementation of the + * RIPEMD-160 hash-function. + * TARGET: any computer with an ANSI C compiler + * + * AUTHOR: Antoon Bosselaers, ESAT-COSIC + * DATE: 1 March 1996 + * VERSION: 1.0 + * + * Copyright (c) Katholieke Universiteit Leuven + * 1996, All Rights Reserved + * + \********************************************************************/ + +// Adapted into "header-only" format by Gav Wood. + +/* macro definitions */ + +#define RMDsize 160 + +/* collect four bytes into one word: */ +#define BYTES_TO_DWORD(strptr) \ +(((uint32_t) *((strptr)+3) << 24) | \ +((uint32_t) *((strptr)+2) << 16) | \ +((uint32_t) *((strptr)+1) << 8) | \ +((uint32_t) *(strptr))) + +/* ROL(x, n) cyclically rotates x over n bits to the left */ +/* x must be of an unsigned 32 bits type and 0 <= n < 32. */ +#define ROL(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* the five basic functions F(), G() and H() */ +#define F(x, y, z) ((x) ^ (y) ^ (z)) +#define G(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define H(x, y, z) (((x) | ~(y)) ^ (z)) +#define I(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define J(x, y, z) ((x) ^ ((y) | ~(z))) + +/* the ten basic operations FF() through III() */ +#define FF(a, b, c, d, e, x, s) {\ +(a) += F((b), (c), (d)) + (x);\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define GG(a, b, c, d, e, x, s) {\ +(a) += G((b), (c), (d)) + (x) + 0x5a827999UL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define HH(a, b, c, d, e, x, s) {\ +(a) += H((b), (c), (d)) + (x) + 0x6ed9eba1UL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define II(a, b, c, d, e, x, s) {\ +(a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcUL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define JJ(a, b, c, d, e, x, s) {\ +(a) += J((b), (c), (d)) + (x) + 0xa953fd4eUL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define FFF(a, b, c, d, e, x, s) {\ +(a) += F((b), (c), (d)) + (x);\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define GGG(a, b, c, d, e, x, s) {\ +(a) += G((b), (c), (d)) + (x) + 0x7a6d76e9UL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define HHH(a, b, c, d, e, x, s) {\ +(a) += H((b), (c), (d)) + (x) + 0x6d703ef3UL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define III(a, b, c, d, e, x, s) {\ +(a) += I((b), (c), (d)) + (x) + 0x5c4dd124UL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} +#define JJJ(a, b, c, d, e, x, s) {\ +(a) += J((b), (c), (d)) + (x) + 0x50a28be6UL;\ +(a) = ROL((a), (s)) + (e);\ +(c) = ROL((c), 10);\ +} + +void MDinit(uint32_t *MDbuf) +{ + MDbuf[0] = 0x67452301UL; + MDbuf[1] = 0xefcdab89UL; + MDbuf[2] = 0x98badcfeUL; + MDbuf[3] = 0x10325476UL; + MDbuf[4] = 0xc3d2e1f0UL; + + return; +} + +/********************************************************************/ + +void MDcompress(uint32_t *MDbuf, uint32_t *X) +{ + uint32_t aa = MDbuf[0], bb = MDbuf[1], cc = MDbuf[2], + dd = MDbuf[3], ee = MDbuf[4]; + uint32_t aaa = MDbuf[0], bbb = MDbuf[1], ccc = MDbuf[2], + ddd = MDbuf[3], eee = MDbuf[4]; + + /* round 1 */ + FF(aa, bb, cc, dd, ee, X[ 0], 11); + FF(ee, aa, bb, cc, dd, X[ 1], 14); + FF(dd, ee, aa, bb, cc, X[ 2], 15); + FF(cc, dd, ee, aa, bb, X[ 3], 12); + FF(bb, cc, dd, ee, aa, X[ 4], 5); + FF(aa, bb, cc, dd, ee, X[ 5], 8); + FF(ee, aa, bb, cc, dd, X[ 6], 7); + FF(dd, ee, aa, bb, cc, X[ 7], 9); + FF(cc, dd, ee, aa, bb, X[ 8], 11); + FF(bb, cc, dd, ee, aa, X[ 9], 13); + FF(aa, bb, cc, dd, ee, X[10], 14); + FF(ee, aa, bb, cc, dd, X[11], 15); + FF(dd, ee, aa, bb, cc, X[12], 6); + FF(cc, dd, ee, aa, bb, X[13], 7); + FF(bb, cc, dd, ee, aa, X[14], 9); + FF(aa, bb, cc, dd, ee, X[15], 8); + + /* round 2 */ + GG(ee, aa, bb, cc, dd, X[ 7], 7); + GG(dd, ee, aa, bb, cc, X[ 4], 6); + GG(cc, dd, ee, aa, bb, X[13], 8); + GG(bb, cc, dd, ee, aa, X[ 1], 13); + GG(aa, bb, cc, dd, ee, X[10], 11); + GG(ee, aa, bb, cc, dd, X[ 6], 9); + GG(dd, ee, aa, bb, cc, X[15], 7); + GG(cc, dd, ee, aa, bb, X[ 3], 15); + GG(bb, cc, dd, ee, aa, X[12], 7); + GG(aa, bb, cc, dd, ee, X[ 0], 12); + GG(ee, aa, bb, cc, dd, X[ 9], 15); + GG(dd, ee, aa, bb, cc, X[ 5], 9); + GG(cc, dd, ee, aa, bb, X[ 2], 11); + GG(bb, cc, dd, ee, aa, X[14], 7); + GG(aa, bb, cc, dd, ee, X[11], 13); + GG(ee, aa, bb, cc, dd, X[ 8], 12); + + /* round 3 */ + HH(dd, ee, aa, bb, cc, X[ 3], 11); + HH(cc, dd, ee, aa, bb, X[10], 13); + HH(bb, cc, dd, ee, aa, X[14], 6); + HH(aa, bb, cc, dd, ee, X[ 4], 7); + HH(ee, aa, bb, cc, dd, X[ 9], 14); + HH(dd, ee, aa, bb, cc, X[15], 9); + HH(cc, dd, ee, aa, bb, X[ 8], 13); + HH(bb, cc, dd, ee, aa, X[ 1], 15); + HH(aa, bb, cc, dd, ee, X[ 2], 14); + HH(ee, aa, bb, cc, dd, X[ 7], 8); + HH(dd, ee, aa, bb, cc, X[ 0], 13); + HH(cc, dd, ee, aa, bb, X[ 6], 6); + HH(bb, cc, dd, ee, aa, X[13], 5); + HH(aa, bb, cc, dd, ee, X[11], 12); + HH(ee, aa, bb, cc, dd, X[ 5], 7); + HH(dd, ee, aa, bb, cc, X[12], 5); + + /* round 4 */ + II(cc, dd, ee, aa, bb, X[ 1], 11); + II(bb, cc, dd, ee, aa, X[ 9], 12); + II(aa, bb, cc, dd, ee, X[11], 14); + II(ee, aa, bb, cc, dd, X[10], 15); + II(dd, ee, aa, bb, cc, X[ 0], 14); + II(cc, dd, ee, aa, bb, X[ 8], 15); + II(bb, cc, dd, ee, aa, X[12], 9); + II(aa, bb, cc, dd, ee, X[ 4], 8); + II(ee, aa, bb, cc, dd, X[13], 9); + II(dd, ee, aa, bb, cc, X[ 3], 14); + II(cc, dd, ee, aa, bb, X[ 7], 5); + II(bb, cc, dd, ee, aa, X[15], 6); + II(aa, bb, cc, dd, ee, X[14], 8); + II(ee, aa, bb, cc, dd, X[ 5], 6); + II(dd, ee, aa, bb, cc, X[ 6], 5); + II(cc, dd, ee, aa, bb, X[ 2], 12); + + /* round 5 */ + JJ(bb, cc, dd, ee, aa, X[ 4], 9); + JJ(aa, bb, cc, dd, ee, X[ 0], 15); + JJ(ee, aa, bb, cc, dd, X[ 5], 5); + JJ(dd, ee, aa, bb, cc, X[ 9], 11); + JJ(cc, dd, ee, aa, bb, X[ 7], 6); + JJ(bb, cc, dd, ee, aa, X[12], 8); + JJ(aa, bb, cc, dd, ee, X[ 2], 13); + JJ(ee, aa, bb, cc, dd, X[10], 12); + JJ(dd, ee, aa, bb, cc, X[14], 5); + JJ(cc, dd, ee, aa, bb, X[ 1], 12); + JJ(bb, cc, dd, ee, aa, X[ 3], 13); + JJ(aa, bb, cc, dd, ee, X[ 8], 14); + JJ(ee, aa, bb, cc, dd, X[11], 11); + JJ(dd, ee, aa, bb, cc, X[ 6], 8); + JJ(cc, dd, ee, aa, bb, X[15], 5); + JJ(bb, cc, dd, ee, aa, X[13], 6); + + /* parallel round 1 */ + JJJ(aaa, bbb, ccc, ddd, eee, X[ 5], 8); + JJJ(eee, aaa, bbb, ccc, ddd, X[14], 9); + JJJ(ddd, eee, aaa, bbb, ccc, X[ 7], 9); + JJJ(ccc, ddd, eee, aaa, bbb, X[ 0], 11); + JJJ(bbb, ccc, ddd, eee, aaa, X[ 9], 13); + JJJ(aaa, bbb, ccc, ddd, eee, X[ 2], 15); + JJJ(eee, aaa, bbb, ccc, ddd, X[11], 15); + JJJ(ddd, eee, aaa, bbb, ccc, X[ 4], 5); + JJJ(ccc, ddd, eee, aaa, bbb, X[13], 7); + JJJ(bbb, ccc, ddd, eee, aaa, X[ 6], 7); + JJJ(aaa, bbb, ccc, ddd, eee, X[15], 8); + JJJ(eee, aaa, bbb, ccc, ddd, X[ 8], 11); + JJJ(ddd, eee, aaa, bbb, ccc, X[ 1], 14); + JJJ(ccc, ddd, eee, aaa, bbb, X[10], 14); + JJJ(bbb, ccc, ddd, eee, aaa, X[ 3], 12); + JJJ(aaa, bbb, ccc, ddd, eee, X[12], 6); + + /* parallel round 2 */ + III(eee, aaa, bbb, ccc, ddd, X[ 6], 9); + III(ddd, eee, aaa, bbb, ccc, X[11], 13); + III(ccc, ddd, eee, aaa, bbb, X[ 3], 15); + III(bbb, ccc, ddd, eee, aaa, X[ 7], 7); + III(aaa, bbb, ccc, ddd, eee, X[ 0], 12); + III(eee, aaa, bbb, ccc, ddd, X[13], 8); + III(ddd, eee, aaa, bbb, ccc, X[ 5], 9); + III(ccc, ddd, eee, aaa, bbb, X[10], 11); + III(bbb, ccc, ddd, eee, aaa, X[14], 7); + III(aaa, bbb, ccc, ddd, eee, X[15], 7); + III(eee, aaa, bbb, ccc, ddd, X[ 8], 12); + III(ddd, eee, aaa, bbb, ccc, X[12], 7); + III(ccc, ddd, eee, aaa, bbb, X[ 4], 6); + III(bbb, ccc, ddd, eee, aaa, X[ 9], 15); + III(aaa, bbb, ccc, ddd, eee, X[ 1], 13); + III(eee, aaa, bbb, ccc, ddd, X[ 2], 11); + + /* parallel round 3 */ + HHH(ddd, eee, aaa, bbb, ccc, X[15], 9); + HHH(ccc, ddd, eee, aaa, bbb, X[ 5], 7); + HHH(bbb, ccc, ddd, eee, aaa, X[ 1], 15); + HHH(aaa, bbb, ccc, ddd, eee, X[ 3], 11); + HHH(eee, aaa, bbb, ccc, ddd, X[ 7], 8); + HHH(ddd, eee, aaa, bbb, ccc, X[14], 6); + HHH(ccc, ddd, eee, aaa, bbb, X[ 6], 6); + HHH(bbb, ccc, ddd, eee, aaa, X[ 9], 14); + HHH(aaa, bbb, ccc, ddd, eee, X[11], 12); + HHH(eee, aaa, bbb, ccc, ddd, X[ 8], 13); + HHH(ddd, eee, aaa, bbb, ccc, X[12], 5); + HHH(ccc, ddd, eee, aaa, bbb, X[ 2], 14); + HHH(bbb, ccc, ddd, eee, aaa, X[10], 13); + HHH(aaa, bbb, ccc, ddd, eee, X[ 0], 13); + HHH(eee, aaa, bbb, ccc, ddd, X[ 4], 7); + HHH(ddd, eee, aaa, bbb, ccc, X[13], 5); + + /* parallel round 4 */ + GGG(ccc, ddd, eee, aaa, bbb, X[ 8], 15); + GGG(bbb, ccc, ddd, eee, aaa, X[ 6], 5); + GGG(aaa, bbb, ccc, ddd, eee, X[ 4], 8); + GGG(eee, aaa, bbb, ccc, ddd, X[ 1], 11); + GGG(ddd, eee, aaa, bbb, ccc, X[ 3], 14); + GGG(ccc, ddd, eee, aaa, bbb, X[11], 14); + GGG(bbb, ccc, ddd, eee, aaa, X[15], 6); + GGG(aaa, bbb, ccc, ddd, eee, X[ 0], 14); + GGG(eee, aaa, bbb, ccc, ddd, X[ 5], 6); + GGG(ddd, eee, aaa, bbb, ccc, X[12], 9); + GGG(ccc, ddd, eee, aaa, bbb, X[ 2], 12); + GGG(bbb, ccc, ddd, eee, aaa, X[13], 9); + GGG(aaa, bbb, ccc, ddd, eee, X[ 9], 12); + GGG(eee, aaa, bbb, ccc, ddd, X[ 7], 5); + GGG(ddd, eee, aaa, bbb, ccc, X[10], 15); + GGG(ccc, ddd, eee, aaa, bbb, X[14], 8); + + /* parallel round 5 */ + FFF(bbb, ccc, ddd, eee, aaa, X[12] , 8); + FFF(aaa, bbb, ccc, ddd, eee, X[15] , 5); + FFF(eee, aaa, bbb, ccc, ddd, X[10] , 12); + FFF(ddd, eee, aaa, bbb, ccc, X[ 4] , 9); + FFF(ccc, ddd, eee, aaa, bbb, X[ 1] , 12); + FFF(bbb, ccc, ddd, eee, aaa, X[ 5] , 5); + FFF(aaa, bbb, ccc, ddd, eee, X[ 8] , 14); + FFF(eee, aaa, bbb, ccc, ddd, X[ 7] , 6); + FFF(ddd, eee, aaa, bbb, ccc, X[ 6] , 8); + FFF(ccc, ddd, eee, aaa, bbb, X[ 2] , 13); + FFF(bbb, ccc, ddd, eee, aaa, X[13] , 6); + FFF(aaa, bbb, ccc, ddd, eee, X[14] , 5); + FFF(eee, aaa, bbb, ccc, ddd, X[ 0] , 15); + FFF(ddd, eee, aaa, bbb, ccc, X[ 3] , 13); + FFF(ccc, ddd, eee, aaa, bbb, X[ 9] , 11); + FFF(bbb, ccc, ddd, eee, aaa, X[11] , 11); + + /* combine results */ + ddd += cc + MDbuf[1]; /* final result for MDbuf[0] */ + MDbuf[1] = MDbuf[2] + dd + eee; + MDbuf[2] = MDbuf[3] + ee + aaa; + MDbuf[3] = MDbuf[4] + aa + bbb; + MDbuf[4] = MDbuf[0] + bb + ccc; + MDbuf[0] = ddd; + + return; +} + +void MDfinish(uint32_t *MDbuf, byte const *strptr, uint32_t lswlen, uint32_t mswlen) +{ + unsigned int i; /* counter */ + uint32_t X[16]; /* message words */ + + memset(X, 0, 16*sizeof(uint32_t)); + + /* put bytes from strptr into X */ + for (i=0; i<(lswlen&63); i++) { + /* byte i goes into word X[i div 4] at pos. 8*(i mod 4) */ + X[i>>2] ^= (uint32_t) *strptr++ << (8 * (i&3)); + } + + /* append the bit m_n == 1 */ + X[(lswlen>>2)&15] ^= (uint32_t)1 << (8*(lswlen&3) + 7); + + if ((lswlen & 63) > 55) { + /* length goes to next block */ + MDcompress(MDbuf, X); + memset(X, 0, 16*sizeof(uint32_t)); + } + + /* append length in bits*/ + X[14] = lswlen << 3; + X[15] = (lswlen >> 29) | (mswlen << 3); + MDcompress(MDbuf, X); + + return; +} + +#undef ROL +#undef F +#undef G +#undef H +#undef I +#undef J +#undef FF +#undef GG +#undef HH +#undef II +#undef JJ +#undef FFF +#undef GGG +#undef HHH +#undef III +#undef JJJ + +} + +/* + * @returns RMD(_input) + */ +h160 ripemd160(bytesConstRef _input) +{ + h160 hashcode; + uint32_t buffer[RMDsize / 32]; // contains (A, B, C, D(, E)) + uint32_t current[16]; // current 16-word chunk + + // initialize + rmd160::MDinit(buffer); + byte const* message = _input.data(); + uint32_t remaining = _input.size(); // # of bytes not yet processed + + // process message in 16x 4-byte chunks + for (; remaining >= 64; remaining -= 64) + { + for (unsigned i = 0; i < 16; i++) + { + current[i] = BYTES_TO_DWORD(message); + message += 4; + } + rmd160::MDcompress(buffer, current); + } + // length mod 64 bytes left + + // finish: + rmd160::MDfinish(buffer, message, _input.size(), 0); + + for (unsigned i = 0; i < RMDsize / 8; i += 4) + { + hashcode[i] = buffer[i >> 2]; // implicit cast to byte + hashcode[i + 1] = (buffer[i >> 2] >> 8); //extracts the 8 least + hashcode[i + 2] = (buffer[i >> 2] >> 16); // significant bits. + hashcode[i + 3] = (buffer[i >> 2] >> 24); + } + + return hashcode; +} + +#undef BYTES_TO_DWORD +#undef RMDsize + +} diff --git a/libdevcore/Hash.h b/libdevcore/Hash.h new file mode 100644 index 00000000..d4401014 --- /dev/null +++ b/libdevcore/Hash.h @@ -0,0 +1,38 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file Hash.h + * @author Gav Wood <i@gavwood.com> + * @date 2014 + * + * The FixedHash fixed-size "hash" container type. + */ + +#pragma once + +#include <string> +#include "FixedHash.h" +#include "vector_ref.h" +#include "SHA3.h" + +namespace dev +{ + +h256 sha256(bytesConstRef _input); + +h160 ripemd160(bytesConstRef _input); + +} diff --git a/libdevcore/SHA3.cpp b/libdevcore/SHA3.cpp new file mode 100644 index 00000000..584ef07e --- /dev/null +++ b/libdevcore/SHA3.cpp @@ -0,0 +1,223 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file SHA3.cpp + * @author Gav Wood <i@gavwood.com> + * @date 2014 + */ + +#include "SHA3.h" +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include "picosha2.h" +using namespace std; +using namespace dev; + +namespace dev +{ + +namespace keccak +{ + +/** libkeccak-tiny + * + * A single-file implementation of SHA-3 and SHAKE. + * + * Implementor: David Leon Gil + * License: CC0, attribution kindly requested. Blame taken too, + * but not liability. + */ + +#define decshake(bits) \ + int shake##bits(uint8_t*, size_t, const uint8_t*, size_t); + +#define decsha3(bits) \ + int sha3_##bits(uint8_t*, size_t, const uint8_t*, size_t); + +decshake(128) +decshake(256) +decsha3(224) +decsha3(256) +decsha3(384) +decsha3(512) + +/******** The Keccak-f[1600] permutation ********/ + +/*** Constants. ***/ +static const uint8_t rho[24] = \ + { 1, 3, 6, 10, 15, 21, + 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, + 62, 18, 39, 61, 20, 44}; +static const uint8_t pi[24] = \ + {10, 7, 11, 17, 18, 3, + 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, + 20, 14, 22, 9, 6, 1}; +static const uint64_t RC[24] = \ + {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, + 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, + 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL, + 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, + 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL, + 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL}; + +/*** Helper macros to unroll the permutation. ***/ +#define rol(x, s) (((x) << s) | ((x) >> (64 - s))) +#define REPEAT6(e) e e e e e e +#define REPEAT24(e) REPEAT6(e e e e) +#define REPEAT5(e) e e e e e +#define FOR5(v, s, e) \ + v = 0; \ + REPEAT5(e; v += s;) + +/*** Keccak-f[1600] ***/ +static inline void keccakf(void* state) { + uint64_t* a = (uint64_t*)state; + uint64_t b[5] = {0}; + uint64_t t = 0; + uint8_t x, y; + + for (int i = 0; i < 24; i++) { + // Theta + FOR5(x, 1, + b[x] = 0; + FOR5(y, 5, + b[x] ^= a[x + y]; )) + FOR5(x, 1, + FOR5(y, 5, + a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) + // Rho and pi + t = a[1]; + x = 0; + REPEAT24(b[0] = a[pi[x]]; + a[pi[x]] = rol(t, rho[x]); + t = b[0]; + x++; ) + // Chi + FOR5(y, + 5, + FOR5(x, 1, + b[x] = a[y + x];) + FOR5(x, 1, + a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); )) + // Iota + a[0] ^= RC[i]; + } +} + +/******** The FIPS202-defined functions. ********/ + +/*** Some helper macros. ***/ + +#define _(S) do { S } while (0) +#define FOR(i, ST, L, S) \ + _(for (size_t i = 0; i < L; i += ST) { S; }) +#define mkapply_ds(NAME, S) \ + static inline void NAME(uint8_t* dst, \ + const uint8_t* src, \ + size_t len) { \ + FOR(i, 1, len, S); \ + } +#define mkapply_sd(NAME, S) \ + static inline void NAME(const uint8_t* src, \ + uint8_t* dst, \ + size_t len) { \ + FOR(i, 1, len, S); \ + } + +mkapply_ds(xorin, dst[i] ^= src[i]) // xorin +mkapply_sd(setout, dst[i] = src[i]) // setout + +#define P keccakf +#define Plen 200 + +// Fold P*F over the full blocks of an input. +#define foldP(I, L, F) \ + while (L >= rate) { \ + F(a, I, rate); \ + P(a); \ + I += rate; \ + L -= rate; \ + } + +/** The sponge-based hash construction. **/ +static inline int hash(uint8_t* out, size_t outlen, + const uint8_t* in, size_t inlen, + size_t rate, uint8_t delim) { + if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) { + return -1; + } + uint8_t a[Plen] = {0}; + // Absorb input. + foldP(in, inlen, xorin); + // Xor in the DS and pad frame. + a[inlen] ^= delim; + a[rate - 1] ^= 0x80; + // Xor in the last block. + xorin(a, in, inlen); + // Apply P + P(a); + // Squeeze output. + foldP(out, outlen, setout); + setout(a, out, outlen); + memset(a, 0, 200); + return 0; +} + +/*** Helper macros to define SHA3 and SHAKE instances. ***/ +#define defshake(bits) \ + int shake##bits(uint8_t* out, size_t outlen, \ + const uint8_t* in, size_t inlen) { \ + return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x1f); \ + } +#define defsha3(bits) \ + int sha3_##bits(uint8_t* out, size_t outlen, \ + const uint8_t* in, size_t inlen) { \ + if (outlen > (bits/8)) { \ + return -1; \ + } \ + return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \ + } + +/*** FIPS202 SHAKE VOFs ***/ +defshake(128) +defshake(256) + +/*** FIPS202 SHA3 FOFs ***/ +defsha3(224) +defsha3(256) +defsha3(384) +defsha3(512) + +} + +unsigned g_sha3Counter = 0; + +bool sha3(bytesConstRef _input, bytesRef o_output) +{ + // FIXME: What with unaligned memory? + if (o_output.size() != 32) + return false; + ++g_sha3Counter; + keccak::sha3_256(o_output.data(), 32, _input.data(), _input.size()); +// keccak::keccak(ret.data(), 32, (uint64_t const*)_input.data(), _input.size()); + return true; +} + +} diff --git a/libdevcore/SHA3.h b/libdevcore/SHA3.h new file mode 100644 index 00000000..5393952f --- /dev/null +++ b/libdevcore/SHA3.h @@ -0,0 +1,59 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file SHA3.h + * @author Gav Wood <i@gavwood.com> + * @date 2014 + * + * The FixedHash fixed-size "hash" container type. + */ + +#pragma once + +#include <string> +#include "FixedHash.h" +#include "vector_ref.h" + +namespace dev +{ + +// SHA-3 convenience routines. + +/// Calculate SHA3-256 hash of the given input and load it into the given output. +/// @returns false if o_output.size() != 32. +bool sha3(bytesConstRef _input, bytesRef o_output); + +/// Calculate SHA3-256 hash of the given input, returning as a 256-bit hash. +inline h256 sha3(bytesConstRef _input) { h256 ret; sha3(_input, ret.ref()); return ret; } + +/// Calculate SHA3-256 hash of the given input, returning as a 256-bit hash. +inline h256 sha3(bytes const& _input) { return sha3(bytesConstRef(&_input)); } + +/// Calculate SHA3-256 hash of the given input (presented as a binary-filled string), returning as a 256-bit hash. +inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)); } + +/// Calculate SHA3-256 hash of the given input (presented as a FixedHash), returns a 256-bit hash. +template<unsigned N> inline h256 sha3(FixedHash<N> const& _input) { return sha3(_input.ref()); } + +/// Calculate SHA3-256 hash of the given input, possibly interpreting it as nibbles, and return the hash as a string filled with binary data. +inline std::string sha3(std::string const& _input, bool _isNibbles) { return asString((_isNibbles ? sha3(fromHex(_input)) : sha3(bytesConstRef(&_input))).asBytes()); } + +/// Calculate SHA3-256 MAC +inline void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output) { sha3(_secret.toBytes() + _plain.toBytes()).ref().populate(_output); } + +extern unsigned g_sha3Counter; + +} diff --git a/libdevcore/UndefMacros.h b/libdevcore/UndefMacros.h new file mode 100644 index 00000000..91249523 --- /dev/null +++ b/libdevcore/UndefMacros.h @@ -0,0 +1,46 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file UndefMacros.h + * @author Lefteris <lefteris@ethdev.com> + * @date 2015 + * + * This header should be used to #undef some really evil macros defined by + * windows.h which result in conflict with our libsolidity/Token.h + */ +#pragma once + +#if defined(_MSC_VER) || defined(__MINGW32__) + +#undef DELETE +#undef IN +#undef VOID +#undef THIS +#undef CONST + +// Conflicting define on MinGW in windows.h +// windows.h(19): #define interface struct +#ifdef interface +#undef interface +#endif + +#elif defined(DELETE) || defined(IN) || defined(VOID) || defined(THIS) || defined(CONST) || defined(interface) + +#error "The preceding macros in this header file are reserved for V8's "\ +"TOKEN_LIST. Please add a platform specific define above to undefine "\ +"overlapping macros." + +#endif diff --git a/libdevcore/debugbreak.h b/libdevcore/debugbreak.h new file mode 100644 index 00000000..65612a34 --- /dev/null +++ b/libdevcore/debugbreak.h @@ -0,0 +1,125 @@ +/* Copyright (c) 2013, Scott Tsai + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DEBUG_BREAK_H +#define DEBUG_BREAK_H + +#if defined(_MSC_VER) || defined(__MINGW32__) + +#define debug_break __debugbreak + +#else + +#include <signal.h> +#include <unistd.h> +#include <sys/syscall.h> + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + /* gcc optimizers consider code after __builtin_trap() dead. + * Making __builtin_trap() unsuitable for breaking into the debugger */ + DEBUG_BREAK_PREFER_BUILTIN_TRAP_TO_SIGTRAP = 0, +}; + +#if defined(__i386__) || defined(__x86_64__) +enum { HAVE_TRAP_INSTRUCTION = 1, }; +__attribute__((gnu_inline, always_inline)) +static void __inline__ trap_instruction(void) +{ + __asm__ volatile("int $0x03"); +} +#elif defined(__thumb__) +enum { HAVE_TRAP_INSTRUCTION = 1, }; +/* FIXME: handle __THUMB_INTERWORK__ */ +__attribute__((gnu_inline, always_inline)) +static void __inline__ trap_instruction(void) +{ + /* See 'arm-linux-tdep.c' in GDB source. + * Both instruction sequences below works. */ +#if 1 + /* 'eabi_linux_thumb_le_breakpoint' */ + __asm__ volatile(".inst 0xde01"); +#else + /* 'eabi_linux_thumb2_le_breakpoint' */ + __asm__ volatile(".inst.w 0xf7f0a000"); +#endif + + /* Known problem: + * After a breakpoint hit, can't stepi, step, or continue in GDB. + * 'step' stuck on the same instruction. + * + * Workaround: a new GDB command, + * 'debugbreak-step' is defined in debugbreak-gdb.py + * that does: + * (gdb) set $instruction_len = 2 + * (gdb) tbreak *($pc + $instruction_len) + * (gdb) jump *($pc + $instruction_len) + */ +} +#elif defined(__arm__) && !defined(__thumb__) +enum { HAVE_TRAP_INSTRUCTION = 1, }; +__attribute__((gnu_inline, always_inline)) +static void __inline__ trap_instruction(void) +{ + /* See 'arm-linux-tdep.c' in GDB source, + * 'eabi_linux_arm_le_breakpoint' */ + __asm__ volatile(".inst 0xe7f001f0"); + /* Has same known problem and workaround + * as Thumb mode */ +} +#else +enum { HAVE_TRAP_INSTRUCTION = 0, }; +#endif + +__attribute__((gnu_inline, always_inline)) +static void __inline__ debug_break(void) +{ + if (HAVE_TRAP_INSTRUCTION) { +#if defined(ETH_EMSCRIPTEN) + asm("debugger"); +#else + trap_instruction(); +#endif + } else if (DEBUG_BREAK_PREFER_BUILTIN_TRAP_TO_SIGTRAP) { + /* raises SIGILL on Linux x86{,-64}, to continue in gdb: + * (gdb) handle SIGILL stop nopass + * */ + __builtin_trap(); + } else { + raise(SIGTRAP); + } +} + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/libdevcore/picosha2.h b/libdevcore/picosha2.h new file mode 100644 index 00000000..44b6bee5 --- /dev/null +++ b/libdevcore/picosha2.h @@ -0,0 +1,360 @@ +/* +The MIT License (MIT) + +Copyright (C) 2014 okdshin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +#ifndef PICOSHA2_H +#define PICOSHA2_H +//picosha2:20140213 +#include <cstdint> +#include <iostream> +#include <vector> +#include <iterator> +#include <cassert> +#include <sstream> +#include <algorithm> + +namespace picosha2 +{ + +namespace detail +{ + +inline uint8_t mask_8bit(uint8_t x){ + return x&0xff; +} + +inline uint32_t mask_32bit(uint32_t x){ + return x&0xffffffff; +} + +static const uint32_t add_constant[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +static const uint32_t initial_message_digest[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z){ + return (x&y)^((~x)&z); +} + +inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z){ + return (x&y)^(x&z)^(y&z); +} + +inline uint32_t rotr(uint32_t x, std::size_t n){ + assert(n < 32); + return mask_32bit((x>>n)|(x<<(32-n))); +} + +inline uint32_t bsig0(uint32_t x){ + return rotr(x, 2)^rotr(x, 13)^rotr(x, 22); +} + +inline uint32_t bsig1(uint32_t x){ + return rotr(x, 6)^rotr(x, 11)^rotr(x, 25); +} + +inline uint32_t shr(uint32_t x, std::size_t n){ + assert(n < 32); + return x >> n; +} + +inline uint32_t ssig0(uint32_t x){ + return rotr(x, 7)^rotr(x, 18)^shr(x, 3); +} + +inline uint32_t ssig1(uint32_t x){ + return rotr(x, 17)^rotr(x, 19)^shr(x, 10); +} + +template<typename RaIter1, typename RaIter2> +void hash256_block(RaIter1 message_digest, RaIter2 first, RaIter2 last){ + (void)last; // FIXME: check this is valid + uint32_t w[64]; + std::fill(w, w+64, 0); + for(std::size_t i = 0; i < 16; ++i){ + w[i] = (static_cast<uint32_t>(mask_8bit(*(first+i*4)))<<24) + |(static_cast<uint32_t>(mask_8bit(*(first+i*4+1)))<<16) + |(static_cast<uint32_t>(mask_8bit(*(first+i*4+2)))<<8) + |(static_cast<uint32_t>(mask_8bit(*(first+i*4+3)))); + } + for(std::size_t i = 16; i < 64; ++i){ + w[i] = mask_32bit(ssig1(w[i-2])+w[i-7]+ssig0(w[i-15])+w[i-16]); + } + + uint32_t a = *message_digest; + uint32_t b = *(message_digest+1); + uint32_t c = *(message_digest+2); + uint32_t d = *(message_digest+3); + uint32_t e = *(message_digest+4); + uint32_t f = *(message_digest+5); + uint32_t g = *(message_digest+6); + uint32_t h = *(message_digest+7); + + for(std::size_t i = 0; i < 64; ++i){ + uint32_t temp1 = h+bsig1(e)+ch(e,f,g)+add_constant[i]+w[i]; + uint32_t temp2 = bsig0(a)+maj(a,b,c); + h = g; + g = f; + f = e; + e = mask_32bit(d+temp1); + d = c; + c = b; + b = a; + a = mask_32bit(temp1+temp2); + } + *message_digest += a; + *(message_digest+1) += b; + *(message_digest+2) += c; + *(message_digest+3) += d; + *(message_digest+4) += e; + *(message_digest+5) += f; + *(message_digest+6) += g; + *(message_digest+7) += h; + for(std::size_t i = 0; i < 8; ++i){ + *(message_digest+i) = mask_32bit(*(message_digest+i)); + } +} + +}//namespace detail + +template<typename InIter> +void output_hex(InIter first, InIter last, std::ostream& os){ + os.setf(std::ios::hex, std::ios::basefield); + while(first != last){ + os.width(2); + os.fill('0'); + os << static_cast<unsigned int>(*first); + ++first; + } + os.setf(std::ios::dec, std::ios::basefield); +} + +template<typename InIter> +void bytes_to_hex_string(InIter first, InIter last, std::string& hex_str){ + std::ostringstream oss; + output_hex(first, last, oss); + hex_str.assign(oss.str()); +} + +template<typename InContainer> +void bytes_to_hex_string(const InContainer& bytes, std::string& hex_str){ + bytes_to_hex_string(bytes.begin(), bytes.end(), hex_str); +} + +template<typename InIter> +std::string bytes_to_hex_string(InIter first, InIter last){ + std::string hex_str; + bytes_to_hex_string(first, last, hex_str); + return hex_str; +} + +template<typename InContainer> +std::string bytes_to_hex_string(const InContainer& bytes){ + std::string hex_str; + bytes_to_hex_string(bytes, hex_str); + return hex_str; +} + +class hash256_one_by_one { +public: + hash256_one_by_one(){ + init(); + } + + void init(){ + buffer_.clear(); + std::fill(data_length_digits_, data_length_digits_+4, 0); + std::copy(detail::initial_message_digest, detail::initial_message_digest+8, h_); + } + + template<typename RaIter> + void process(RaIter first, RaIter last){ + add_to_data_length(std::distance(first, last)); + std::copy(first, last, std::back_inserter(buffer_)); + std::size_t i = 0; + for(;i+64 <= buffer_.size(); i+=64){ + detail::hash256_block(h_, buffer_.begin()+i, buffer_.begin()+i+64); + } + buffer_.erase(buffer_.begin(), buffer_.begin()+i); + } + + void finish(){ + uint8_t temp[64]; + std::fill(temp, temp+64, 0); + std::size_t remains = buffer_.size(); + std::copy(buffer_.begin(), buffer_.end(), temp); + temp[remains] = 0x80; + + if(remains > 55){ + std::fill(temp+remains+1, temp+64, 0); + detail::hash256_block(h_, temp, temp+64); + std::fill(temp, temp+64-4, 0); + } + else { + std::fill(temp+remains+1, temp+64-4, 0); + } + + write_data_bit_length(&(temp[56])); + detail::hash256_block(h_, temp, temp+64); + } + + template<typename OutIter> + void get_hash_bytes(OutIter first, OutIter last)const{ + for(const uint32_t* iter = h_; iter != h_+8; ++iter){ + for(std::size_t i = 0; i < 4 && first != last; ++i){ + *(first++) = detail::mask_8bit(static_cast<uint8_t>((*iter >> (24-8*i)))); + } + } + } + +private: + void add_to_data_length(uint32_t n) { + uint32_t carry = 0; + data_length_digits_[0] += n; + for(std::size_t i = 0; i < 4; ++i) { + data_length_digits_[i] += carry; + if(data_length_digits_[i] >= 65536u) { + data_length_digits_[i] -= 65536u; + carry = 1; + } + else { + break; + } + } + } + void write_data_bit_length(uint8_t* begin) { + uint32_t data_bit_length_digits[4]; + std::copy( + data_length_digits_, data_length_digits_+4, + data_bit_length_digits + ); + + // convert byte length to bit length (multiply 8 or shift 3 times left) + uint32_t carry = 0; + for(std::size_t i = 0; i < 4; ++i) { + uint32_t before_val = data_bit_length_digits[i]; + data_bit_length_digits[i] <<= 3; + data_bit_length_digits[i] |= carry; + data_bit_length_digits[i] &= 65535u; + carry = (before_val >> (16-3)) & 65535u; + } + + // write data_bit_length + for(int i = 3; i >= 0; --i) { + (*begin++) = static_cast<uint8_t>(data_bit_length_digits[i] >> 8); + (*begin++) = static_cast<uint8_t>(data_bit_length_digits[i]); + } + } + std::vector<uint8_t> buffer_; + uint32_t data_length_digits_[4]; //as 64bit integer (16bit x 4 integer) + uint32_t h_[8]; +}; + +inline void get_hash_hex_string(const hash256_one_by_one& hasher, std::string& hex_str){ + uint8_t hash[32]; + hasher.get_hash_bytes(hash, hash+32); + return bytes_to_hex_string(hash, hash+32, hex_str); +} + +inline std::string get_hash_hex_string(const hash256_one_by_one& hasher){ + std::string hex_str; + get_hash_hex_string(hasher, hex_str); + return hex_str; +} + +template<typename RaIter, typename OutIter> +void hash256(RaIter first, RaIter last, OutIter first2, OutIter last2){ + hash256_one_by_one hasher; + //hasher.init(); + hasher.process(first, last); + hasher.finish(); + hasher.get_hash_bytes(first2, last2); +} + +template<typename RaIter, typename OutContainer> +void hash256(RaIter first, RaIter last, OutContainer& dst){ + hash256(first, last, dst.begin(), dst.end()); +} + +template<typename RaContainer, typename OutIter> +void hash256(const RaContainer& src, OutIter first, OutIter last){ + hash256(src.begin(), src.end(), first, last); +} + +template<typename RaContainer, typename OutContainer> +void hash256(const RaContainer& src, OutContainer& dst){ + hash256(src.begin(), src.end(), dst.begin(), dst.end()); +} + + +template<typename RaIter> +void hash256_hex_string(RaIter first, RaIter last, std::string& hex_str){ + uint8_t hashed[32]; + hash256(first, last, hashed, hashed+32); + std::ostringstream oss; + output_hex(hashed, hashed+32, oss); + hex_str.assign(oss.str()); +} + +template<typename RaIter> +std::string hash256_hex_string(RaIter first, RaIter last){ + std::string hex_str; + hash256_hex_string(first, last, hex_str); + return hex_str; +} + +inline void hash256_hex_string(const std::string& src, std::string& hex_str){ + hash256_hex_string(src.begin(), src.end(), hex_str); +} + +template<typename RaContainer> +void hash256_hex_string(const RaContainer& src, std::string& hex_str){ + hash256_hex_string(src.begin(), src.end(), hex_str); +} + +template<typename RaContainer> +std::string hash256_hex_string(const RaContainer& src){ + return hash256_hex_string(src.begin(), src.end()); +} + +}//namespace picosha2 + +#endif //PICOSHA2_H diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h new file mode 100644 index 00000000..46d06946 --- /dev/null +++ b/libdevcore/vector_ref.h @@ -0,0 +1,120 @@ +#pragma once + +#include <cstring> +#include <cassert> +#include <type_traits> +#include <vector> +#include <string> + +#ifdef __INTEL_COMPILER +#pragma warning(disable:597) //will not be called for implicit or explicit conversions +#endif + +namespace dev +{ + +/** + * A modifiable reference to an existing object or vector in memory. + */ +template <class _T> +class vector_ref +{ +public: + using value_type = _T; + using element_type = _T; + using mutable_value_type = typename std::conditional<std::is_const<_T>::value, typename std::remove_const<_T>::type, _T>::type; + + static_assert(std::is_pod<value_type>::value, "vector_ref can only be used with PODs due to its low-level treatment of data."); + + vector_ref(): m_data(nullptr), m_count(0) {} + /// Creates a new vector_ref to point to @a _count elements starting at @a _data. + vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {} + /// Creates a new vector_ref pointing to the data part of a string (given as pointer). + vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const*, std::string*>::type _data): m_data(reinterpret_cast<_T*>(_data->data())), m_count(_data->size() / sizeof(_T)) {} + /// Creates a new vector_ref pointing to the data part of a vector (given as pointer). + vector_ref(typename std::conditional<std::is_const<_T>::value, std::vector<typename std::remove_const<_T>::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {} + /// Creates a new vector_ref pointing to the data part of a string (given as reference). + vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const&, std::string&>::type _data): m_data(reinterpret_cast<_T*>(_data.data())), m_count(_data.size() / sizeof(_T)) {} +#if DEV_LDB + vector_ref(ldb::Slice const& _s): m_data(reinterpret_cast<_T*>(_s.data())), m_count(_s.size() / sizeof(_T)) {} +#endif + explicit operator bool() const { return m_data && m_count; } + + bool contentsEqual(std::vector<mutable_value_type> const& _c) const { if (!m_data || m_count == 0) return _c.empty(); else return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count * sizeof(_T)); } + std::vector<mutable_value_type> toVector() const { return std::vector<mutable_value_type>(m_data, m_data + m_count); } + std::vector<unsigned char> toBytes() const { return std::vector<unsigned char>(reinterpret_cast<unsigned char const*>(m_data), reinterpret_cast<unsigned char const*>(m_data) + m_count * sizeof(_T)); } + std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(_T)); } + + template <class _T2> explicit operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>(reinterpret_cast<_T2*>(m_data), m_count * sizeof(_T) / sizeof(_T2)); } + operator vector_ref<_T const>() const { return vector_ref<_T const>(m_data, m_count); } + + _T* data() const { return m_data; } + /// @returns the number of elements referenced (not necessarily number of bytes). + size_t count() const { return m_count; } + /// @returns the number of elements referenced (not necessarily number of bytes). + size_t size() const { return m_count; } + bool empty() const { return !m_count; } + /// @returns a new vector_ref pointing at the next chunk of @a size() elements. + vector_ref<_T> next() const { if (!m_data) return *this; else return vector_ref<_T>(m_data + m_count, m_count); } + /// @returns a new vector_ref which is a shifted and shortened view of the original data. + /// If this goes out of bounds in any way, returns an empty vector_ref. + /// If @a _count is ~size_t(0), extends the view to the end of the data. + vector_ref<_T> cropped(size_t _begin, size_t _count) const { if (m_data && _begin <= m_count && _count <= m_count && _begin + _count <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); } + /// @returns a new vector_ref which is a shifted view of the original data (not going beyond it). + vector_ref<_T> cropped(size_t _begin) const { if (m_data && _begin <= m_count) return vector_ref<_T>(m_data + _begin, m_count - _begin); else return vector_ref<_T>(); } + void retarget(_T* _d, size_t _s) { m_data = _d; m_count = _s; } + void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); } + template <class T> bool overlapsWith(vector_ref<T> _t) const { void const* f1 = data(); void const* t1 = data() + size(); void const* f2 = _t.data(); void const* t2 = _t.data() + _t.size(); return f1 < t2 && t1 > f2; } + /// Copies the contents of this vector_ref to the contents of @a _t, up to the max size of @a _t. + void copyTo(vector_ref<typename std::remove_const<_T>::type> _t) const { if (overlapsWith(_t)) memmove(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); else memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); } + /// Copies the contents of this vector_ref to the contents of @a _t, and zeros further trailing elements in @a _t. + void populate(vector_ref<typename std::remove_const<_T>::type> _t) const { copyTo(_t); memset(_t.data() + m_count, 0, std::max(_t.size(), m_count) - m_count); } + /// Securely overwrite the memory. + /// @note adapted from OpenSSL's implementation. + void cleanse() + { + static unsigned char s_cleanseCounter = 0; + uint8_t* p = (uint8_t*)begin(); + size_t const len = (uint8_t*)end() - p; + size_t loop = len; + size_t count = s_cleanseCounter; + while (loop--) + { + *(p++) = (uint8_t)count; + count += (17 + ((size_t)p & 0xf)); + } + p = (uint8_t*)memchr((uint8_t*)begin(), (uint8_t)count, len); + if (p) + count += (63 + (size_t)p); + s_cleanseCounter = (uint8_t)count; + memset((uint8_t*)begin(), 0, len); + } + + _T* begin() { return m_data; } + _T* end() { return m_data + m_count; } + _T const* begin() const { return m_data; } + _T const* end() const { return m_data + m_count; } + + _T& operator[](size_t _i) { assert(m_data); assert(_i < m_count); return m_data[_i]; } + _T const& operator[](size_t _i) const { assert(m_data); assert(_i < m_count); return m_data[_i]; } + + bool operator==(vector_ref<_T> const& _cmp) const { return m_data == _cmp.m_data && m_count == _cmp.m_count; } + bool operator!=(vector_ref<_T> const& _cmp) const { return !operator==(_cmp); } + +#if DEV_LDB + operator ldb::Slice() const { return ldb::Slice((char const*)m_data, m_count * sizeof(_T)); } +#endif + + void reset() { m_data = nullptr; m_count = 0; } + +private: + _T* m_data; + size_t m_count; +}; + +template<class _T> vector_ref<_T const> ref(_T const& _t) { return vector_ref<_T const>(&_t, 1); } +template<class _T> vector_ref<_T> ref(_T& _t) { return vector_ref<_T>(&_t, 1); } +template<class _T> vector_ref<_T const> ref(std::vector<_T> const& _t) { return vector_ref<_T const>(&_t); } +template<class _T> vector_ref<_T> ref(std::vector<_T>& _t) { return vector_ref<_T>(&_t); } + +} diff --git a/libevmasm/EVMSchedule.h b/libevmasm/EVMSchedule.h new file mode 100644 index 00000000..02a34b16 --- /dev/null +++ b/libevmasm/EVMSchedule.h @@ -0,0 +1,62 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** @file EVMSchedule.h + * @author Gav <i@gavwood.com> + * @author Christian <c@ethdev.com> + * @date 2015 + */ + +#pragma once + +namespace dev +{ +namespace solidity +{ + +struct EVMSchedule +{ + unsigned stackLimit = 1024; + unsigned expGas = 10; + unsigned expByteGas = 10; + unsigned sha3Gas = 30; + unsigned sha3WordGas = 6; + unsigned sloadGas = 50; + unsigned sstoreSetGas = 20000; + unsigned sstoreResetGas = 5000; + unsigned sstoreRefundGas = 15000; + unsigned jumpdestGas = 1; + unsigned logGas = 375; + unsigned logDataGas = 8; + unsigned logTopicGas = 375; + unsigned createGas = 32000; + unsigned callGas = 40; + unsigned callStipend = 2300; + unsigned callValueTransferGas = 9000; + unsigned callNewAccountGas = 25000; + unsigned suicideRefundGas = 24000; + unsigned memoryGas = 3; + unsigned quadCoeffDiv = 512; + unsigned createDataGas = 200; + unsigned txGas = 21000; + unsigned txCreateGas = 53000; + unsigned txDataZeroGas = 4; + unsigned txDataNonZeroGas = 68; + unsigned copyGas = 3; +}; + +} +} diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 89d0bf35..6b459da4 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -42,12 +42,17 @@ void ASTJsonConverter::addKeyValue(Json::Value& _obj, string const& _key, string _obj[_key] = _val; } -void ASTJsonConverter::addJsonNode(string const& _nodeName, - initializer_list<pair<string const, string const>> _list, - bool _hasChildren = false) +void ASTJsonConverter::addJsonNode( + ASTNode const& _node, + string const& _nodeName, + initializer_list<pair<string const, string const>> _list, + bool _hasChildren = false +) { Json::Value node; + node["id"] = reinterpret_cast<Json::UInt64>(&_node); + node["src"] = sourceLocationToString(_node.location()); node["name"] = _nodeName; if (_list.size() != 0) { @@ -68,7 +73,21 @@ void ASTJsonConverter::addJsonNode(string const& _nodeName, } } -ASTJsonConverter::ASTJsonConverter(ASTNode const& _ast): m_ast(&_ast) +string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location) const +{ + int sourceIndex{-1}; + if (_location.sourceName && m_sourceIndices.count(*_location.sourceName)) + sourceIndex = m_sourceIndices.at(*_location.sourceName); + int length = -1; + if (_location.start >= 0 && _location.end >= 0) + length = _location.end - _location.start; + return std::to_string(_location.start) + ":" + std::to_string(length) + ":" + std::to_string(sourceIndex); +} + +ASTJsonConverter::ASTJsonConverter( + ASTNode const& _ast, + map<string, unsigned> const& _sourceIndices +): m_ast(&_ast), m_sourceIndices(_sourceIndices) { Json::Value children(Json::arrayValue); @@ -91,31 +110,31 @@ Json::Value const& ASTJsonConverter::json() bool ASTJsonConverter::visit(ImportDirective const& _node) { - addJsonNode("Import", { make_pair("file", _node.path())}); + addJsonNode(_node, "Import", { make_pair("file", _node.path())}); return true; } bool ASTJsonConverter::visit(ContractDefinition const& _node) { - addJsonNode("Contract", { make_pair("name", _node.name()) }, true); + addJsonNode(_node, "Contract", { make_pair("name", _node.name()) }, true); return true; } bool ASTJsonConverter::visit(StructDefinition const& _node) { - addJsonNode("Struct", { make_pair("name", _node.name()) }, true); + addJsonNode(_node, "Struct", { make_pair("name", _node.name()) }, true); return true; } -bool ASTJsonConverter::visit(ParameterList const&) +bool ASTJsonConverter::visit(ParameterList const& _node) { - addJsonNode("ParameterList", {}, true); + addJsonNode(_node, "ParameterList", {}, true); return true; } bool ASTJsonConverter::visit(FunctionDefinition const& _node) { - addJsonNode("Function", + addJsonNode(_node, "Function", { make_pair("name", _node.name()), make_pair("public", boost::lexical_cast<std::string>(_node.isPublic())), make_pair("const", boost::lexical_cast<std::string>(_node.isDeclaredConst())) }, @@ -125,7 +144,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) bool ASTJsonConverter::visit(VariableDeclaration const& _node) { - addJsonNode("VariableDeclaration", { + addJsonNode(_node, "VariableDeclaration", { make_pair("name", _node.name()), make_pair("name", _node.name()), }, true); @@ -139,114 +158,114 @@ bool ASTJsonConverter::visit(TypeName const&) bool ASTJsonConverter::visit(ElementaryTypeName const& _node) { - addJsonNode("ElementaryTypeName", { make_pair("name", _node.typeName().toString()) }); + addJsonNode(_node, "ElementaryTypeName", { make_pair("name", _node.typeName().toString()) }); return true; } bool ASTJsonConverter::visit(UserDefinedTypeName const& _node) { - addJsonNode("UserDefinedTypeName", { + addJsonNode(_node, "UserDefinedTypeName", { make_pair("name", boost::algorithm::join(_node.namePath(), ".")) }); return true; } -bool ASTJsonConverter::visit(Mapping const&) +bool ASTJsonConverter::visit(Mapping const& _node) { - addJsonNode("Mapping", {}, true); + addJsonNode(_node, "Mapping", {}, true); return true; } -bool ASTJsonConverter::visit(InlineAssembly const&) +bool ASTJsonConverter::visit(InlineAssembly const& _node) { - addJsonNode("InlineAssembly", {}, true); + addJsonNode(_node, "InlineAssembly", {}, true); return true; } -bool ASTJsonConverter::visit(Block const&) +bool ASTJsonConverter::visit(Block const& _node) { - addJsonNode("Block", {}, true); + addJsonNode(_node, "Block", {}, true); return true; } -bool ASTJsonConverter::visit(IfStatement const&) +bool ASTJsonConverter::visit(IfStatement const& _node) { - addJsonNode("IfStatement", {}, true); + addJsonNode(_node, "IfStatement", {}, true); return true; } -bool ASTJsonConverter::visit(WhileStatement const&) +bool ASTJsonConverter::visit(WhileStatement const& _node) { - addJsonNode("WhileStatement", {}, true); + addJsonNode(_node, "WhileStatement", {}, true); return true; } -bool ASTJsonConverter::visit(ForStatement const&) +bool ASTJsonConverter::visit(ForStatement const& _node) { - addJsonNode("ForStatement", {}, true); + addJsonNode(_node, "ForStatement", {}, true); return true; } -bool ASTJsonConverter::visit(Continue const&) +bool ASTJsonConverter::visit(Continue const& _node) { - addJsonNode("Continue", {}); + addJsonNode(_node, "Continue", {}); return true; } -bool ASTJsonConverter::visit(Break const&) +bool ASTJsonConverter::visit(Break const& _node) { - addJsonNode("Break", {}); + addJsonNode(_node, "Break", {}); return true; } -bool ASTJsonConverter::visit(Return const&) +bool ASTJsonConverter::visit(Return const& _node) { - addJsonNode("Return", {}, true);; + addJsonNode(_node, "Return", {}, true);; return true; } -bool ASTJsonConverter::visit(Throw const&) +bool ASTJsonConverter::visit(Throw const& _node) { - addJsonNode("Throw", {}, true);; + addJsonNode(_node, "Throw", {}, true);; return true; } -bool ASTJsonConverter::visit(VariableDeclarationStatement const&) +bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node) { - addJsonNode("VariableDefinition", {}, true); + addJsonNode(_node, "VariableDefinition", {}, true); return true; } -bool ASTJsonConverter::visit(ExpressionStatement const&) +bool ASTJsonConverter::visit(ExpressionStatement const& _node) { - addJsonNode("ExpressionStatement", {}, true); + addJsonNode(_node, "ExpressionStatement", {}, true); return true; } -bool ASTJsonConverter::visit(Conditional const&) +bool ASTJsonConverter::visit(Conditional const& _node) { - addJsonNode("Conditional", {}, true); + addJsonNode(_node, "Conditional", {}, true); return true; } bool ASTJsonConverter::visit(Assignment const& _node) { - addJsonNode("Assignment", + addJsonNode(_node, "Assignment", { make_pair("operator", Token::toString(_node.assignmentOperator())), make_pair("type", type(_node)) }, true); return true; } -bool ASTJsonConverter::visit(TupleExpression const&) +bool ASTJsonConverter::visit(TupleExpression const& _node) { - addJsonNode("TupleExpression",{}, true); + addJsonNode(_node, "TupleExpression",{}, true); return true; } bool ASTJsonConverter::visit(UnaryOperation const& _node) { - addJsonNode("UnaryOperation", + addJsonNode(_node, "UnaryOperation", { make_pair("prefix", boost::lexical_cast<std::string>(_node.isPrefixOperation())), make_pair("operator", Token::toString(_node.getOperator())), make_pair("type", type(_node)) }, @@ -256,7 +275,7 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node) bool ASTJsonConverter::visit(BinaryOperation const& _node) { - addJsonNode("BinaryOperation", { + addJsonNode(_node, "BinaryOperation", { make_pair("operator", Token::toString(_node.getOperator())), make_pair("type", type(_node)) }, true); @@ -265,7 +284,7 @@ bool ASTJsonConverter::visit(BinaryOperation const& _node) bool ASTJsonConverter::visit(FunctionCall const& _node) { - addJsonNode("FunctionCall", { + addJsonNode(_node, "FunctionCall", { make_pair("type_conversion", boost::lexical_cast<std::string>(_node.annotation().isTypeConversion)), make_pair("type", type(_node)) }, true); @@ -274,13 +293,13 @@ bool ASTJsonConverter::visit(FunctionCall const& _node) bool ASTJsonConverter::visit(NewExpression const& _node) { - addJsonNode("NewExpression", { make_pair("type", type(_node)) }, true); + addJsonNode(_node, "NewExpression", { make_pair("type", type(_node)) }, true); return true; } bool ASTJsonConverter::visit(MemberAccess const& _node) { - addJsonNode("MemberAccess", + addJsonNode(_node, "MemberAccess", { make_pair("member_name", _node.memberName()), make_pair("type", type(_node)) }, true); @@ -289,20 +308,20 @@ bool ASTJsonConverter::visit(MemberAccess const& _node) bool ASTJsonConverter::visit(IndexAccess const& _node) { - addJsonNode("IndexAccess", { make_pair("type", type(_node)) }, true); + addJsonNode(_node, "IndexAccess", { make_pair("type", type(_node)) }, true); return true; } bool ASTJsonConverter::visit(Identifier const& _node) { - addJsonNode("Identifier", + addJsonNode(_node, "Identifier", { make_pair("value", _node.name()), make_pair("type", type(_node)) }); return true; } bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node) { - addJsonNode("ElementaryTypenameExpression", + addJsonNode(_node, "ElementaryTypenameExpression", { make_pair("value", _node.typeName().toString()), make_pair("type", type(_node)) }); return true; } @@ -310,7 +329,7 @@ bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node) bool ASTJsonConverter::visit(Literal const& _node) { char const* tokenString = Token::toString(_node.token()); - addJsonNode("Literal", + addJsonNode(_node, "Literal", { make_pair("string", (tokenString) ? tokenString : "null"), make_pair("value", _node.value()), make_pair("type", type(_node)) }); diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 91ee72e1..2e3046f1 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -42,7 +42,11 @@ class ASTJsonConverter: public ASTConstVisitor { public: /// Create a converter to JSON for the given abstract syntax tree. - explicit ASTJsonConverter(ASTNode const& _ast); + /// @a _sourceIndices is used to abbreviate source names in source locations. + explicit ASTJsonConverter( + ASTNode const& _ast, + std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>() + ); /// Output the json representation of the AST to _stream. void print(std::ostream& _stream); Json::Value const& json(); @@ -118,9 +122,13 @@ public: private: void process(); void addKeyValue(Json::Value& _obj, std::string const& _key, std::string const& _val); - void addJsonNode(std::string const& _nodeName, - std::initializer_list<std::pair<std::string const, std::string const>> _list, - bool _hasChildren); + void addJsonNode( + ASTNode const& _node, + std::string const& _nodeName, + std::initializer_list<std::pair<std::string const, std::string const>> _list, + bool _hasChildren + ); + std::string sourceLocationToString(SourceLocation const& _location) const; std::string type(Expression const& _expression); std::string type(VariableDeclaration const& _varDecl); inline void goUp() @@ -132,8 +140,8 @@ private: bool processed = false; Json::Value m_astJson; std::stack<Json::Value*> m_jsonNodePtrs; - std::string m_source; ASTNode const* m_ast; + std::map<std::string, unsigned> const& m_sourceIndices; }; } diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 55254013..da74dc90 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -104,6 +104,7 @@ public: ); /// Zero-initialises (the data part of) an already allocated memory array. + /// Length has to be nonzero! /// Stack pre: <length> <memptr> /// Stack post: <updated_memptr> void zeroInitialiseMemoryArray(ArrayType const& _type); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index b973a117..80009a90 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -792,15 +792,18 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) utils().storeFreeMemoryPointer(); // Stack: memptr requested_length + // Check if length is zero + m_context << Instruction::DUP1 << Instruction::ISZERO; + auto skipInit = m_context.appendConditionalJump(); + // We only have to initialise if the base type is a not a value type. if (dynamic_cast<ReferenceType const*>(arrayType.baseType().get())) { m_context << Instruction::DUP2 << u256(32) << Instruction::ADD; utils().zeroInitialiseMemoryArray(arrayType); - m_context << Instruction::POP; } - else - m_context << Instruction::POP; + m_context << skipInit; + m_context << Instruction::POP; break; } default: diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp index c794cb24..bd0a020d 100644 --- a/libsolidity/formal/Why3Translator.cpp +++ b/libsolidity/formal/Why3Translator.cpp @@ -67,23 +67,11 @@ void Why3Translator::fatalError(ASTNode const& _node, string const& _description BOOST_THROW_EXCEPTION(FatalError()); } -void Why3Translator::appendPreface() -{ - m_lines.push_back(Line{R"( -module UInt256 - use import mach.int.Unsigned - type uint256 - constant max_uint256: int = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - clone export mach.int.Unsigned with - type t = uint256, - constant max = max_uint256 -end -)", 0}); -} - string Why3Translator::toFormalType(Type const& _type) const { - if (auto type = dynamic_cast<IntegerType const*>(&_type)) + if (_type.category() == Type::Category::Bool) + return "bool"; + else if (auto type = dynamic_cast<IntegerType const*>(&_type)) { if (!type->isAddress() && !type->isSigned() && type->numBits() == 256) return "uint256"; @@ -129,6 +117,7 @@ bool Why3Translator::visit(ContractDefinition const& _contract) if (m_seenContract) error(_contract, "More than one contract not supported."); m_seenContract = true; + m_currentContract.contract = &_contract; if (_contract.isLibrary()) error(_contract, "Libraries not supported."); @@ -141,21 +130,41 @@ bool Why3Translator::visit(ContractDefinition const& _contract) addLine("use import int.ComputerDivision"); addLine("use import mach.int.Unsigned"); addLine("use import UInt256"); - addLine("exception Ret"); + addLine("exception Revert"); + addLine("exception Return"); - addLine("type state = {"); - indent(); - m_stateVariables = _contract.stateVariables(); - for (VariableDeclaration const* variable: m_stateVariables) + if (_contract.stateVariables().empty()) + addLine("type state = ()"); + else { - string varType = toFormalType(*variable->annotation().type); - if (varType.empty()) - fatalError(*variable, "Type not supported."); - addLine("mutable _" + variable->name() + ": ref " + varType); + addLine("type state = {"); + indent(); + m_currentContract.stateVariables = _contract.stateVariables(); + for (VariableDeclaration const* variable: m_currentContract.stateVariables) + { + string varType = toFormalType(*variable->annotation().type); + if (varType.empty()) + fatalError(*variable, "Type not supported for state variable."); + addLine("mutable _" + variable->name() + ": " + varType); + } + unindent(); + addLine("}"); } + + addLine("type account = {"); + indent(); + addLine("mutable balance: uint256;"); + addLine("storage: state"); unindent(); addLine("}"); + addLine("val external_call (this: account): bool"); + indent(); + addLine("ensures { result = false -> this = (old this) }"); + addLine("writes { this }"); + addSourceFromDocStrings(m_currentContract.contract->annotation()); + unindent(); + if (!_contract.baseContracts().empty()) error(*_contract.baseContracts().front(), "Inheritance not supported."); if (!_contract.definedStructs().empty()) @@ -172,10 +181,9 @@ bool Why3Translator::visit(ContractDefinition const& _contract) return false; } -void Why3Translator::endVisit(ContractDefinition const& _contract) +void Why3Translator::endVisit(ContractDefinition const&) { - m_stateVariables.clear(); - addSourceFromDocStrings(_contract.annotation()); + m_currentContract.reset(); unindent(); addLine("end"); } @@ -207,7 +215,7 @@ bool Why3Translator::visit(FunctionDefinition const& _function) m_localVariables[var->name()] = var; add("let rec _" + _function.name()); - add(" (state: state)"); + add(" (this: account)"); for (auto const& param: _function.parameters()) { string paramType = toFormalType(*param->annotation().type); @@ -235,9 +243,20 @@ bool Why3Translator::visit(FunctionDefinition const& _function) unindent(); addSourceFromDocStrings(_function.annotation()); + if (!m_currentContract.contract) + error(_function, "Only functions inside contracts allowed."); + addSourceFromDocStrings(m_currentContract.contract->annotation()); + + if (_function.isDeclaredConst()) + addLine("ensures { (old this) = this }"); + else + addLine("writes { this }"); addLine("="); + // store the prestate in the case we need to revert + addLine("let prestate = {balance = this.balance; storage = " + copyOfStorage() + "} in "); + // initialise local variables for (auto const& variable: _function.parameters()) addLine("let _" + variable->name() + " = ref arg_" + variable->name() + " in"); @@ -259,7 +278,7 @@ bool Why3Translator::visit(FunctionDefinition const& _function) _function.body().accept(*this); add(";"); - addLine("raise Ret"); + addLine("raise Return"); string retVals; for (auto const& variable: _function.returnParameters()) @@ -268,8 +287,14 @@ bool Why3Translator::visit(FunctionDefinition const& _function) retVals += ", "; retVals += "!_" + variable->name(); } - addLine("with Ret -> (" + retVals + ")"); - newLine(); + addLine("with Return -> (" + retVals + ") |"); + string reversion = " Revert -> this.balance <- prestate.balance; "; + for (auto const* variable: m_currentContract.stateVariables) + reversion += "this.storage._" + variable->name() + " <- prestate.storage._" + variable->name() + "; "; + //@TODO in case of reversion the return values are wrong - we need to change the + // return type to include a bool to signify if an exception was thrown. + reversion += "(" + retVals + ")"; + addLine(reversion); unindent(); addLine("end"); addLine(""); @@ -349,10 +374,17 @@ bool Why3Translator::visit(Return const& _node) } add("begin _" + params.front()->name() + " := "); _node.expression()->accept(*this); - add("; raise Ret end"); + add("; raise Return end"); } else - add("raise Ret"); + add("raise Return"); + return false; +} + +bool Why3Translator::visit(Throw const& _node) +{ + addSourceFromDocStrings(_node.annotation()); + add("raise Revert"); return false; } @@ -385,7 +417,8 @@ bool Why3Translator::visit(Assignment const& _node) error(_node, "Compound assignment not supported."); _node.leftHandSide().accept(*this); - add(" := "); + + add(m_currentLValueIsRef ? " := " : " <- "); _node.rightHandSide().accept(*this); return false; @@ -402,7 +435,7 @@ bool Why3Translator::visit(TupleExpression const& _node) bool Why3Translator::visit(UnaryOperation const& _unaryOperation) { if (toFormalType(*_unaryOperation.annotation().type).empty()) - error(_unaryOperation, "Type not supported."); + error(_unaryOperation, "Type not supported in unary operation."); switch (_unaryOperation.getOperator()) { @@ -512,6 +545,42 @@ bool Why3Translator::visit(FunctionCall const& _node) add(")"); return false; } + case FunctionType::Location::Bare: + { + if (!_node.arguments().empty()) + { + error(_node, "Function calls with named arguments not supported."); + return true; + } + + add("("); + indent(); + add("let amount = 0 in "); + _node.expression().accept(*this); + addLine("if amount <= this.balance then"); + indent(); + addLine("let balance_precall = this.balance in"); + addLine("begin"); + indent(); + addLine("this.balance <- this.balance - amount;"); + addLine("if not (external_call this) then begin this.balance = balance_precall; false end else true"); + unindent(); + addLine("end"); + unindent(); + addLine("else false"); + + unindent(); + add(")"); + return false; + } + case FunctionType::Location::SetValue: + { + add("let amount = "); + solAssert(_node.arguments().size() == 1, ""); + _node.arguments()[0]->accept(*this); + add(" in "); + return false; + } default: error(_node, "Only internal function calls supported."); return true; @@ -531,8 +600,17 @@ bool Why3Translator::visit(MemberAccess const& _node) add(".length"); add(")"); } + else if ( + _node.memberName() == "call" && + *_node.expression().annotation().type == IntegerType(160, IntegerType::Modifier::Address) + ) + { + // Do nothing, do not even visit the address because this will be an external call + //@TODO ensure that the expression itself does not have side-effects + return false; + } else - error(_node, "Only read-only length access for arrays supported."); + error(_node, "Member access: Only call and array length supported."); return false; } @@ -568,13 +646,14 @@ bool Why3Translator::visit(Identifier const& _identifier) { bool isStateVar = isStateVariable(variable); bool lvalue = _identifier.annotation().lValueRequested; - if (!lvalue) - add("!("); if (isStateVar) - add("state."); + add("this.storage."); + else if (!lvalue) + add("!("); add("_" + variable->name()); - if (!lvalue) + if (!isStateVar && !lvalue) add(")"); + m_currentLValueIsRef = !isStateVar; } else error(_identifier, "Not supported."); @@ -608,12 +687,12 @@ bool Why3Translator::visit(Literal const& _literal) bool Why3Translator::isStateVariable(VariableDeclaration const* _var) const { - return contains(m_stateVariables, _var); + return contains(m_currentContract.stateVariables, _var); } bool Why3Translator::isStateVariable(string const& _name) const { - for (auto const& var: m_stateVariables) + for (auto const& var: m_currentContract.stateVariables) if (var->name() == _name) return true; return false; @@ -632,6 +711,23 @@ bool Why3Translator::isLocalVariable(string const& _name) const return m_localVariables.count(_name); } +string Why3Translator::copyOfStorage() const +{ + if (m_currentContract.stateVariables.empty()) + return "()"; + string ret = "{"; + bool first = true; + for (auto const* variable: m_currentContract.stateVariables) + { + if (first) + first = false; + else + ret += "; "; + ret += "_" + variable->name() + " = this.storage._" + variable->name(); + } + return ret + "}"; +} + void Why3Translator::visitIndentedUnlessBlock(Statement const& _statement) { bool isBlock = !!dynamic_cast<Block const*>(&_statement); @@ -674,11 +770,10 @@ string Why3Translator::transformVariableReferences(string const& _annotation) }); string varName(hash + 1, hashEnd); if (isLocalVariable(varName)) - ret += "(to_int !_" + varName + ")"; + ret += "(!_" + varName + ")"; else if (isStateVariable(varName)) - ret += "(to_int !(state._" + varName + "))"; - else if (varName == "result") //@todo actually use the name of the return parameters - ret += "(to_int result)"; + ret += "(this.storage._" + varName + ")"; + //@todo return variables else ret.append(hash, hashEnd); @@ -687,3 +782,16 @@ string Why3Translator::transformVariableReferences(string const& _annotation) return ret; } +void Why3Translator::appendPreface() +{ + m_lines.push_back(Line{R"( +module UInt256 + use import mach.int.Unsigned + type uint256 + constant max_uint256: int = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + clone export mach.int.Unsigned with + type t = uint256, + constant max = max_uint256 +end + )", 0}); +} diff --git a/libsolidity/formal/Why3Translator.h b/libsolidity/formal/Why3Translator.h index 588b6d80..e5b16844 100644 --- a/libsolidity/formal/Why3Translator.h +++ b/libsolidity/formal/Why3Translator.h @@ -80,6 +80,7 @@ private: virtual bool visit(IfStatement const& _node) override; virtual bool visit(WhileStatement const& _node) override; virtual bool visit(Return const& _node) override; + virtual bool visit(Throw const& _node) override; virtual bool visit(VariableDeclarationStatement const& _node) override; virtual bool visit(ExpressionStatement const&) override; virtual bool visit(Assignment const& _node) override; @@ -104,6 +105,9 @@ private: bool isLocalVariable(VariableDeclaration const* _var) const; bool isLocalVariable(std::string const& _name) const; + /// @returns a string representing an expression that is a copy of this.storage + std::string copyOfStorage() const; + /// Visits the givin statement and indents it unless it is a block /// (which does its own indentation). void visitIndentedUnlessBlock(Statement const& _statement); @@ -117,7 +121,17 @@ private: bool m_seenContract = false; bool m_errorOccured = false; - std::vector<VariableDeclaration const*> m_stateVariables; + /// Metadata relating to the current contract + struct ContractMetadata + { + ContractDefinition const* contract = nullptr; + std::vector<VariableDeclaration const*> stateVariables; + + void reset() { contract = nullptr; stateVariables.clear(); } + }; + + ContractMetadata m_currentContract; + bool m_currentLValueIsRef = false; std::map<std::string, VariableDeclaration const*> m_localVariables; struct Line @@ -129,6 +143,5 @@ private: ErrorList& m_errors; }; - } } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index c28e926b..f7982872 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -253,9 +253,11 @@ void CompilerStack::link(const std::map<string, h160>& _libraries) } } -bool CompilerStack::prepareFormalAnalysis() +bool CompilerStack::prepareFormalAnalysis(ErrorList* _errors) { - Why3Translator translator(m_errors); + if (!_errors) + _errors = &m_errors; + Why3Translator translator(*_errors); for (Source const* source: m_sourceOrder) if (!translator.process(*source->ast)) return false; @@ -277,6 +279,28 @@ eth::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _con return currentContract.compiler ? &contract(_contractName).compiler->runtimeAssemblyItems() : nullptr; } +string const* CompilerStack::sourceMapping(string const& _contractName) const +{ + Contract const& c = contract(_contractName); + if (!c.sourceMapping) + { + if (auto items = assemblyItems(_contractName)) + c.sourceMapping.reset(new string(computeSourceMapping(*items))); + } + return c.sourceMapping.get(); +} + +string const* CompilerStack::runtimeSourceMapping(string const& _contractName) const +{ + Contract const& c = contract(_contractName); + if (!c.runtimeSourceMapping) + { + if (auto items = runtimeAssemblyItems(_contractName)) + c.runtimeSourceMapping.reset(new string(computeSourceMapping(*items))); + } + return c.runtimeSourceMapping.get(); +} + eth::LinkerObject const& CompilerStack::object(string const& _contractName) const { return contract(_contractName).object; @@ -313,6 +337,22 @@ Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _co } } +vector<string> CompilerStack::sourceNames() const +{ + vector<string> names; + for (auto const& s: m_sources) + names.push_back(s.first); + return names; +} + +map<string, unsigned> CompilerStack::sourceIndices() const +{ + map<string, unsigned> indices; + for (auto const& s: m_sources) + indices[s.first] = indices.size(); + return indices; +} + string const& CompilerStack::interface(string const& _contractName) const { return metadata(_contractName, DocumentationType::ABIInterface); @@ -602,3 +642,76 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co return it->second; } + +string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) const +{ + string ret; + map<string, unsigned> sourceIndicesMap = sourceIndices(); + int prevStart = -1; + int prevLength = -1; + int prevSourceIndex = -1; + char prevJump = 0; + for (auto const& item: _items) + { + if (!ret.empty()) + ret += ";"; + + SourceLocation const& location = item.location(); + int length = location.start != -1 && location.end != -1 ? location.end - location.start : -1; + int sourceIndex = + location.sourceName && sourceIndicesMap.count(*location.sourceName) ? + sourceIndicesMap.at(*location.sourceName) : + -1; + char jump = '-'; + if (item.getJumpType() == eth::AssemblyItem::JumpType::IntoFunction) + jump = 'i'; + else if (item.getJumpType() == eth::AssemblyItem::JumpType::OutOfFunction) + jump = 'o'; + + unsigned components = 4; + if (jump == prevJump) + { + components--; + if (sourceIndex == prevSourceIndex) + { + components--; + if (length == prevLength) + { + components--; + if (location.start == prevStart) + components--; + } + } + } + + if (components-- > 0) + { + if (location.start != prevStart) + ret += std::to_string(location.start); + if (components-- > 0) + { + ret += ':'; + if (length != prevLength) + ret += std::to_string(length); + if (components-- > 0) + { + ret += ':'; + if (sourceIndex != prevSourceIndex) + ret += std::to_string(sourceIndex); + if (components-- > 0) + { + ret += ':'; + if (jump != prevJump) + ret += jump; + } + } + } + } + + prevStart = location.start; + prevLength = length; + prevSourceIndex = sourceIndex; + prevJump = jump; + } + return ret; +} diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index e111c982..a4b8447f 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -124,8 +124,9 @@ public: void link(std::map<std::string, h160> const& _libraries); /// Tries to translate all source files into a language suitable for formal analysis. + /// @param _errors list to store errors - defaults to the internal error list. /// @returns false on error. - bool prepareFormalAnalysis(); + bool prepareFormalAnalysis(ErrorList* _errors = nullptr); std::string const& formalTranslation() const { return m_formalTranslation; } /// @returns the assembled object for a contract. @@ -141,6 +142,12 @@ public: eth::AssemblyItems const* assemblyItems(std::string const& _contractName = "") const; /// @returns runtime contract assembly items 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; + /// @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; /// @returns hash of the runtime bytecode for the contract, i.e. the code that is /// returned by the constructor or the zero-h256 if the contract still needs to be linked or /// does not have runtime code. @@ -152,6 +159,11 @@ public: /// Prerequisite: Successful compilation. Json::Value streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const; + /// @returns the list of sources (paths) used + std::vector<std::string> sourceNames() const; + /// @returns a mapping assigning each source name its index inside the vector returned + /// by sourceNames(). + std::map<std::string, unsigned> sourceIndices() const; /// @returns a string representing the contract interface in JSON. /// Prerequisite: Successful call to parse or compile. std::string const& interface(std::string const& _contractName = "") const; @@ -195,9 +207,8 @@ private: { std::shared_ptr<Scanner> scanner; std::shared_ptr<SourceUnit> ast; - std::string interface; bool isLibrary = false; - void reset() { scanner.reset(); ast.reset(); interface.clear(); } + void reset() { scanner.reset(); ast.reset(); } }; struct Contract @@ -211,6 +222,8 @@ private: mutable std::unique_ptr<std::string const> solidityInterface; mutable std::unique_ptr<std::string const> userDocumentation; mutable std::unique_ptr<std::string const> devDocumentation; + mutable std::unique_ptr<std::string const> sourceMapping; + mutable std::unique_ptr<std::string const> runtimeSourceMapping; }; /// Loads the missing sources from @a _ast (named @a _path) using the callback @@ -235,6 +248,8 @@ private: Contract const& contract(std::string const& _contractName = "") const; Source const& source(std::string const& _sourceName = "") const; + std::string computeSourceMapping(eth::AssemblyItems const& _items) const; + struct Remapping { std::string context; diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 703e88f6..581df3a5 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -129,7 +129,6 @@ namespace solidity T(GreaterThan, ">", 7) \ T(LessThanOrEqual, "<=", 7) \ T(GreaterThanOrEqual, ">=", 7) \ - K(In, "in", 7) \ \ /* Unary operators. */ \ /* IsUnaryOp() relies on this block of enum values */ \ @@ -220,6 +219,7 @@ namespace solidity K(Case, "case", 0) \ K(Catch, "catch", 0) \ K(Final, "final", 0) \ + K(In, "in", 0) \ K(Inline, "inline", 0) \ K(Let, "let", 0) \ K(Match, "match", 0) \ @@ -267,7 +267,7 @@ public: static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd || op == Add || op == Mul || op == Equal || op == NotEqual; } static bool isArithmeticOp(Value op) { return Add <= op && op <= Exp; } - static bool isCompareOp(Value op) { return Equal <= op && op <= In; } + static bool isCompareOp(Value op) { return Equal <= op && op <= GreaterThanOrEqual; } static Value AssignmentToBinaryOp(Value op) { diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh new file mode 100755 index 00000000..53171599 --- /dev/null +++ b/scripts/build_emscripten.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# Bash script for building Solidity for emscripten. +# +# The documentation for solidity is hosted at: +# +# https://solidity.readthedocs.org +# +# ------------------------------------------------------------------------------ +# 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/> +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +if [[ "$OSTYPE" != "darwin"* ]]; then + ./scripts/travis-emscripten/install_deps.sh \ + && docker run -v $(pwd):/src trzeci/emscripten:sdk-tag-1.35.4-64bit ./scripts/travis-emscripten/build_emscripten.sh +fi diff --git a/scripts/docs.sh b/scripts/docs.sh new file mode 100644 index 00000000..a674373a --- /dev/null +++ b/scripts/docs.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# Bash script to build the Solidity Sphinx documentation locally. +# +# The documentation for solidity is hosted at: +# +# https://solidity.readthedocs.org +# +# ------------------------------------------------------------------------------ +# 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/> +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +if [[ "$OSTYPE" == "darwin"* ]]; then + # We aren't building docs locally for macOS at the moment +else + cd docs && sphinx-build -nW -b html -d _build/doctrees . _build/html && cd .. +fi diff --git a/scripts/fix_homebrew_paths_in_standalone_zip.py b/scripts/fix_homebrew_paths_in_standalone_zip.py new file mode 100755 index 00000000..38aa9685 --- /dev/null +++ b/scripts/fix_homebrew_paths_in_standalone_zip.py @@ -0,0 +1,65 @@ +# ------------------------------------------------------------------------------ +# This Python script is used within the OS X release process, to ensure +# that the standalone OS X ZIP files which we make are actually +# standalone, and not implicitly dependent on Homebrew installs for +# external libraries which we use. +# +# This implicit dependencies seem to show up only where we have +# external dependencies which are dependent on each other, and the +# path from one to another is an absolute path to "/usr/local/opt", +# the Homebrew install location. External dependencies which only +# depend on system libraries are fine. Our main applications seem +# to be fine. +# +# An example of a dependency which requires this fix-up at the time +# of writing is the following dependency edge: +# +# libjsonrpccpp-client.0.dylib +# -> /usr/local/opt/jsoncpp/lib/libjsoncpp.0.dylib +# +# See https://blogs.oracle.com/dipol/entry/dynamic_libraries_rpath_and_mac +# for a little overview of "install_name_tool" and "otool". +# +# ------------------------------------------------------------------------------ +# 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/> +# +# (c) 2016 solidity contributors. +# ----------------------------------------------------------------------------- + +import os +import subprocess +import sys + + +def readDependencies(fname): + with open(fname) as f: + o = subprocess.Popen(['otool', '-L', fname], stdout=subprocess.PIPE) + for line in o.stdout: + if line[0] == '\t': + library = line.split(' ', 1)[0][1:] + if library.startswith("/usr/local/lib") or library.startswith("/usr/local/opt") or library.startswith("/Users/"): + if (os.path.basename(library) != os.path.basename(fname)): + command = "install_name_tool -change " + \ + library + " @executable_path/./" + \ + os.path.basename(library) + " " + fname + print command + os.system("chmod +w " + fname) + os.system(command) + +root = sys.argv[1] +for (dirpath, dirnames, filenames) in os.walk(root): + for filename in filenames: + readDependencies(os.path.join(root, filename)) diff --git a/scripts/install_deps.bat b/scripts/install_deps.bat new file mode 100644 index 00000000..512a28df --- /dev/null +++ b/scripts/install_deps.bat @@ -0,0 +1,61 @@ +@ECHO OFF + +REM --------------------------------------------------------------------------- +REM Batch file for installing pre-requisite packages for solidity on +REM Windows platforms. That is implemented using CMake targets which +REM extract pre-built ZIPs hosted on GitHub into "deps\install_deps". +REM +REM See https://github.com/ethereum/cpp-dependencies +REM +REM The CMake files then point into that directory as an alternative +REM to the Homebrew, PPA or other global package server locations +REM available on Linux and UNIX platforms. +REM +REM The lack of a standard C++ packaging system for Windows is problematic +REM for us, and we have considered various options for improving the +REM situation, such as the following: +REM +REM See "Windows - Add support for Chocolatey packages" +REM https://github.com/ethereum/webthree-umbrella/issues/345 +REM +REM See "Windows - Try to use NuGet C++ packages" +REM https://github.com/ethereum/webthree-umbrella/issues/509 +REM +REM See "CM - Can we switch to NuGet delivery for our external dependencies" +REM https://github.com/ethereum/webthree-umbrella/issues/376 +REM +REM Another possible option, which would benefit build robustness on +REM multiple platforms, not just Windows, is to add dependencies as +REM git-submodules (or downloading on demand) so that we aren'targets +REM depend on platform-specific packaging systems at all. We have +REM already done just that for LLVM within evmjit. The downside of +REM that approach is that those dependencies then need to be +REM built-from-source, which adds time to the build process. It +REM gives us an unbeatable degree of control, though, because we +REM then perfectly control versioning and build flags for the binaries +REM for those packages. +REM +REM The documentation for solidity is hosted at: +REM +REM http://solidity.readthedocs.org +REM +REM --------------------------------------------------------------------------- +REM This file is part of solidity. +REM +REM solidity is free software: you can redistribute it and/or modify +REM it under the terms of the GNU General Public License as published by +REM the Free Software Foundation, either version 3 of the License, or +REM (at your option) any later version. +REM +REM solidity is distributed in the hope that it will be useful, +REM but WITHOUT ANY WARRANTY; without even the implied warranty of +REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +REM GNU General Public License for more details. +REM +REM You should have received a copy of the GNU General Public License +REM along with solidity. If not, see <http://www.gnu.org/licenses/> +REM +REM Copyright (c) 2016 solidity contributors. +REM --------------------------------------------------------------------------- + +cmake -P deps\install_deps.cmake diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh new file mode 100755 index 00000000..f1585178 --- /dev/null +++ b/scripts/install_deps.sh @@ -0,0 +1,363 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# Bash script for installing pre-requisite packages for solidity on a +# variety of Linux and other UNIX-derived platforms. +# +# This is an "infrastucture-as-code" alternative to the manual build +# instructions pages which we previously maintained at: +# http://solidity.readthedocs.io/en/latest/installing-solidity.html +# +# The aim of this script is to simplify things down to the following basic +# flow for all supported operating systems: +# +# - git clone --recursive +# - ./install_deps.sh +# - cmake && make +# +# At the time of writing we are assuming that 'lsb_release' is present for all +# Linux distros, which is not a valid assumption. We will need a variety of +# approaches to actually get this working across all the distros which people +# are using. +# +# See http://unix.stackexchange.com/questions/92199/how-can-i-reliably-get-the-operating-systems-name +# for some more background on this common problem. +# +# TODO - There is no support here yet for cross-builds in any form, only +# native builds. Expanding the functionality here to cover the mobile, +# wearable and SBC platforms covered by doublethink and EthEmbedded would +# also bring in support for Android, iOS, watchOS, tvOS, Tizen, Sailfish, +# Maemo, MeeGo and Yocto. +# +# The documentation for solidity is hosted at: +# +# http://solidity.readthedocs.io/ +# +# ------------------------------------------------------------------------------ +# 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/> +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +# Check for 'uname' and abort if it is not available. +uname -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires 'uname' to identify the platform."; exit 1; } + +case $(uname -s) in + +#------------------------------------------------------------------------------ +# macOS +#------------------------------------------------------------------------------ + + Darwin) + case $(sw_vers -productVersion | awk -F . '{print $1"."$2}') in + 10.9) + echo "Installing solidity dependencies on OS X 10.9 Mavericks." + ;; + 10.10) + echo "Installing solidity dependencies on OS X 10.10 Yosemite." + ;; + 10.11) + echo "Installing solidity dependencies on OS X 10.11 El Capitan." + ;; + 10.12) + echo "Installing solidity dependencies on macOS 10.12 Sierra." + echo "" + echo "NOTE - You are in unknown territory with this preview OS." + echo "Even Homebrew doesn't have official support yet, and there are" + echo "known issues (see https://github.com/ethereum/webthree-umbrella/issues/614)." + echo "If you would like to partner with us to work through these issues, that" + echo "would be fantastic. Please just comment on that issue. Thanks!" + ;; + *) + echo "Unsupported macOS version." + echo "We only support Mavericks, Yosemite and El Capitan, with work-in-progress on Sierra." + exit 1 + ;; + esac + + # Check for Homebrew install and abort if it is not installed. + brew -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires a Homebrew install. See http://brew.sh."; exit 1; } + + brew update + brew upgrade + + brew install boost + brew install cmake + brew install jsoncpp + + # We should really 'brew install' our eth client here, but at the time of writing + # the bottle is known broken, so we will just cheat and use a hardcoded ZIP for + # the time being, which is good enough. The cause of the breaks will go away + # when we commit the repository reorg changes anyway. + curl -L -O https://github.com/bobsummerwill/cpp-ethereum/releases/download/v1.3.0/cpp-ethereum-osx-mavericks-v1.3.0.zip + unzip cpp-ethereum-osx-mavericks-v1.3.0.zip + + ;; + +#------------------------------------------------------------------------------ +# FreeBSD +#------------------------------------------------------------------------------ + + FreeBSD) + echo "Installing solidity dependencies on FreeBSD." + echo "ERROR - 'install_deps.sh' doesn't have FreeBSD support yet." + echo "Please let us know if you see this error message, and we can work out what is missing." + echo "Drop us a message at https://gitter.im/ethereum/solidity." + exit 1 + ;; + +#------------------------------------------------------------------------------ +# Linux +#------------------------------------------------------------------------------ + + Linux) + case $(lsb_release -is) in + +#------------------------------------------------------------------------------ +# Arch Linux +#------------------------------------------------------------------------------ + + Arch) + #Arch + echo "Installing solidity dependencies on Arch Linux." + + # All our dependencies can be found in the Arch Linux official repositories. + # See https://wiki.archlinux.org/index.php/Official_repositories + sudo pacman -Sy \ + base-devel \ + boost \ + cmake \ + git \ + ;; + +#------------------------------------------------------------------------------ +# Alpine Linux +#------------------------------------------------------------------------------ + + Alpine) + #Alpine + echo "Installing solidity dependencies on Alpine Linux." + echo "ERROR - 'install_deps.sh' doesn't have Alpine Linux support yet." + echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "If you would like to get 'install_deps.sh' working for Alpine Linux, that would be fantastic." + echo "Drop us a message at https://gitter.im/ethereum/solidity." + echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support." + exit 1 + ;; + +#------------------------------------------------------------------------------ +# Debian +#------------------------------------------------------------------------------ + + Debian) + #Debian + case $(lsb_release -cs) in + wheezy) + #wheezy + echo "Installing solidity dependencies on Debian Wheezy (7.x)." + echo "ERROR - 'install_deps.sh' doesn't have Debian Wheezy support yet." + echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "If you would like to get 'install_deps.sh' working for Debian Wheezy, that would be fantastic." + echo "Drop us a message at https://gitter.im/ethereum/solidity." + echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support." + exit 1 + ;; + jessie) + #jessie + echo "Installing solidity dependencies on Debian Jesse (8.x)." + ;; + stretch) + #stretch + echo "Installing solidity dependencies on Debian Stretch (9.x)." + echo "ERROR - 'install_deps.sh' doesn't have Debian Stretch support yet." + echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "If you would like to get 'install_deps.sh' working for Debian Stretch, that would be fantastic." + echo "Drop us a message at https://gitter.im/ethereum/solidity." + exit 1 + ;; + *) + #other Debian + echo "Installing solidity dependencies on unknown Debian version." + echo "ERROR - Debian Jessie is the only Debian version which solidity has been tested on." + echo "If you are using a different release and would like to get 'install_deps.sh'" + echo "working for that release that would be fantastic." + echo "Drop us a message at https://gitter.im/ethereum/solidity." + exit 1 + ;; + esac + + # Install "normal packages" + sudo apt-get -y update + sudo apt-get -y install \ + build-essential \ + cmake \ + g++ \ + gcc \ + git \ + libboost-all-dev \ + libjsoncpp-dev \ + unzip + + ;; + +#------------------------------------------------------------------------------ +# Fedora +#------------------------------------------------------------------------------ + + Fedora) + #Fedora + echo "Installing solidity dependencies on Fedora." + + # Install "normal packages" + # See https://fedoraproject.org/wiki/Package_management_system. + dnf install \ + autoconf \ + automake \ + boost-devel \ + cmake \ + gcc \ + gcc-c++ \ + git \ + libtool + + ;; + +#------------------------------------------------------------------------------ +# OpenSUSE +#------------------------------------------------------------------------------ + + "openSUSE project") + #openSUSE + echo "Installing solidity dependencies on openSUSE." + echo "ERROR - 'install_deps.sh' doesn't have openSUSE support yet." + echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "If you would like to get 'install_deps.sh' working for openSUSE, that would be fantastic." + echo "See https://github.com/ethereum/webthree-umbrella/issues/552." + exit 1 + ;; + +#------------------------------------------------------------------------------ +# Ubuntu +# +# TODO - I wonder whether all of the Ubuntu-variants need some special +# treatment? +# +# TODO - We should also test this code on Ubuntu Server, Ubuntu Snappy Core +# and Ubuntu Phone. +# +# TODO - Our Ubuntu build is only working for amd64 and i386 processors. +# It would be good to add armel, armhf and arm64. +# See https://github.com/ethereum/webthree-umbrella/issues/228. +#------------------------------------------------------------------------------ + + Ubuntu) + #Ubuntu + case $(lsb_release -cs) in + trusty) + #trusty + echo "Installing solidity dependencies on Ubuntu Trusty Tahr (14.04)." + ;; + utopic) + #utopic + echo "Installing solidity dependencies on Ubuntu Utopic Unicorn (14.10)." + ;; + vivid) + #vivid + echo "Installing solidity dependencies on Ubuntu Vivid Vervet (15.04)." + ;; + wily) + #wily + echo "Installing solidity dependencies on Ubuntu Wily Werewolf (15.10)." + ;; + xenial) + #xenial + echo "Installing solidity dependencies on Ubuntu Xenial Xerus (16.04)." + ;; + yakkety) + #yakkety + echo "Installing solidity dependencies on Ubuntu Yakkety Yak (16.10)." + echo "" + echo "NOTE - You are in unknown territory with this preview OS." + echo "We will need to update the Ethereum PPAs, work through build and runtime breaks, etc." + echo "See https://github.com/ethereum/webthree-umbrella/issues/624." + echo "If you would like to partner with us to work through these, that" + echo "would be fantastic. Please just comment on that issue. Thanks!" + ;; + *) + #other Ubuntu + echo "ERROR - Unknown or unsupported Ubuntu version." + echo "We only support Trusty, Utopic, Vivid, Wily and Xenial, with work-in-progress on Yakkety." + exit 1 + ;; + esac + + sudo apt-get -y update + sudo apt-get -y install \ + build-essential \ + cmake \ + git \ + libboost-all-dev \ + libjsoncpp-dev + + # Install 'eth', for use in the Solidity Tests-over-IPC. + sudo add-apt-repository -y ppa:ethereum/ethereum + sudo add-apt-repository -y ppa:ethereum/ethereum-dev + sudo apt-get -y update + sudo apt-get -y install eth + + # And install the English language package and reconfigure the locales. + # We really shouldn't need to do this, and should instead force our locales to "C" + # within our application runtimes, because this issue shows up on multiple Linux distros, + # and each will need fixing in the install steps, where we should really just fix it once + # in the code. + # + # See https://github.com/ethereum/webthree-umbrella/issues/169 + sudo apt-get -y install language-pack-en-base + sudo dpkg-reconfigure locales + + ;; + *) + +#------------------------------------------------------------------------------ +# Other (unknown) Linux +# Major and medium distros which we are missing would include Mint, CentOS, +# RHEL, Raspbian, Cygwin, OpenWrt, gNewSense, Trisquel and SteamOS. +#------------------------------------------------------------------------------ + + #other Linux + echo "ERROR - Unsupported or unidentified Linux distro." + echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "If you would like to get your distro working, that would be fantastic." + echo "Drop us a message at https://gitter.im/ethereum/solidity." + exit 1 + ;; + esac + ;; + +#------------------------------------------------------------------------------ +# Other platform (not Linux, FreeBSD or macOS). +# Not sure what might end up here? +# Maybe OpenBSD, NetBSD, AIX, Solaris, HP-UX? +#------------------------------------------------------------------------------ + + *) + #other + echo "ERROR - Unsupported or unidentified operating system." + echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "If you would like to get your operating system working, that would be fantastic." + echo "Drop us a message at https://gitter.im/ethereum/solidity." + ;; +esac diff --git a/scripts/release.bat b/scripts/release.bat new file mode 100644 index 00000000..6f0ef6b0 --- /dev/null +++ b/scripts/release.bat @@ -0,0 +1,34 @@ +@ECHO OFF + +REM --------------------------------------------------------------------------- +REM Batch file for implementing release flow for solidity for Windows. +REM +REM The documentation for solidity is hosted at: +REM +REM https://solidity.readthedocs.org +REM +REM --------------------------------------------------------------------------- +REM This file is part of solidity. +REM +REM solidity is free software: you can redistribute it and/or modify +REM it under the terms of the GNU General Public License as published by +REM the Free Software Foundation, either version 3 of the License, or +REM (at your option) any later version. +REM +REM solidity is distributed in the hope that it will be useful, +REM but WITHOUT ANY WARRANTY; without even the implied warranty of +REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +REM GNU General Public License for more details. +REM +REM You should have received a copy of the GNU General Public License +REM along with solidity. If not, see <http://www.gnu.org/licenses/> +REM +REM Copyright (c) 2016 solidity contributors. +REM --------------------------------------------------------------------------- + +set CONFIGURATION=%1 + +REM TODO - Add soltest\%CONFIGURATION%\soltest.exe, when that is buildable. +7z a solidity-develop-windows.zip ^ + .\build\solc\%CONFIGURATION%\solc.exe ^ + "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll" diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 00000000..275e53bb --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# Bash script implementing release flow for solidity for Linux and macOS. +# +# TODO - At the time of writing, we only have ZIPs working. Need to hook up +# support for Homebrew and PPAs. +# +# The documentation for solidity is hosted at: +# +# https://solidity.readthedocs.org +# +# ------------------------------------------------------------------------------ +# 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/> +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +ZIP_SUFFIX=$1 +ZIP_TEMP_DIR=$(pwd)/build/zip/ + +# There is an implicit assumption here that we HAVE to run from root directory. +REPO_ROOT=$(pwd) + +if [[ "$OSTYPE" == "darwin"* ]]; then + DLL_EXT=dylib +else + DLL_EXT=so +fi + +mkdir -p $ZIP_TEMP_DIR + +# Copy all the solidity executables into a temporary directory prior to ZIP creation + +cp $REPO_ROOT/build/lllc/lllc $ZIP_TEMP_DIR +cp $REPO_ROOT/build/solc/solc $ZIP_TEMP_DIR +cp $REPO_ROOT/build/soltest/soltest $ZIP_TEMP_DIR + +# Copy all the dynamic libraries into a temporary directory prior to ZIP creation. +# There are a lot of these, and it would be great if we didn't have to worry about them. +# There is work-in-progress to support static-linkage on the UNIX platforms, which +# is most promising on Alpine Linux using musl. macOS doesn't support statically +# linked binaries (ie. executables which make direct system calls to the kernel. +# +# See https://developer.apple.com/library/mac/qa/qa1118/_index.html. +# See https://github.com/ethereum/webthree-umbrella/issues/495. + +cp $REPO_ROOT/build/libdevcore/*.$DLL_EXT $ZIP_TEMP_DIR +cp $REPO_ROOT/build/libevmasm/*.$DLL_EXT $ZIP_TEMP_DIR +cp $REPO_ROOT/build/libsolidity/*.$DLL_EXT $ZIP_TEMP_DIR + +# For macOS, we also copy the dynamic libraries for our external dependencies. +# When building from source on your own machine, these libraries will be installed +# globally, using Homebrew, but we don't want to rely on that for these ZIPs, so +# we copy these into the ZIP temporary directory too. +# +# TODO - So what happens for Linux and other UNIX distros in this case? +# There will be runtime dependencies on equivalent SO files being present, likely in +# a completely analogous way. Does that mean that ZIPs are actually useless on such +# distros, because there will be symbol links to global install locations (distro-specific) +# and those files will just be missing on the target machines? + +if [[ "$OSTYPE" == "darwin"* ]]; then + cp /usr/local/opt/jsoncpp/lib/libjsoncpp.1.dylib $ZIP_TEMP_DIR +fi + +# For macOS, we run a fix-up script which alters all of the symbolic links within +# the executables and dynamic libraries such that the ZIP becomes self-contained, by +# revectoring all the dylib references to be relative to the directory containing the +# application, so that the ZIPs are self-contained, with the only external references +# being for kernel-level dylibs. + +if [[ "$OSTYPE" == "darwin"* ]]; then + python $REPO_ROOT/scripts/fix_homebrew_paths_in_standalone_zip.py $ZIP_TEMP_DIR +fi + +# And ZIP it all up, with a filename suffix passed in on the command-line. + +zip -j $REPO_ROOT/solidity-develop-$ZIP_SUFFIX.zip $ZIP_TEMP_DIR/* diff --git a/scripts/tests.sh b/scripts/tests.sh new file mode 100755 index 00000000..c342c65f --- /dev/null +++ b/scripts/tests.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# Bash script to execute the Solidity tests. +# +# The documentation for solidity is hosted at: +# +# https://solidity.readthedocs.org +# +# ------------------------------------------------------------------------------ +# 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/> +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +# There is an implicit assumption here that we HAVE to run from root directory. +REPO_ROOT=$(pwd) + +# This conditional is only needed because we don't have a working Homebrew +# install for `eth` at the time of writing, so we unzip the ZIP file locally +# instead. This will go away soon. +if [[ "$OSTYPE" == "darwin"* ]]; then + ETH_PATH="$REPO_ROOT/eth" +else + ETH_PATH="eth" +fi + +# This trailing ampersand directs the shell to run the command in the background, +# that is, it is forked and run in a separate sub-shell, as a job, +# asynchronously. The shell will immediately return the return status of 0 for +# true and continue as normal, either processing further commands in a script +# or returning the cursor focus back to the user in a Linux terminal. +$ETH_PATH --test -d /tmp/test & + +# Wait until the IPC endpoint is available. That won't be available instantly. +# The node needs to get a little way into its startup sequence before the IPC +# is available and is ready for the unit-tests to start talking to it. +while [ ! -S /tmp/test/geth.ipc ]; do sleep 2; done + +# TODO - It should be possible to set the IPC path with explicit parameters: +# +# ./test/soltest --ipc /tmp/test/geth.ipc +# +# But that doesn't work on macOS, so we're just using the cruder approach of +# using an environment variable. That works on Linux and macOS. We will +# need to check if this command-line support works for Windows too, when we +# have implemented IPC Sockets support at all for Windows. +export ETH_TEST_IPC=/tmp/test/geth.ipc +$REPO_ROOT/build/test/soltest +ERROR_CODE=$? +pkill eth +exit $ERROR_CODE diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh new file mode 100755 index 00000000..db409455 --- /dev/null +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# This script builds the solidity binary using Emscripten. +# Emscripten is a way to compile C/C++ to JavaScript. +# +# http://kripken.github.io/emscripten-site/ +# +# First run install_dep.sh OUTSIDE of docker and then +# run this script inside a docker image trzeci/emscripten +# +# The documentation for solidity is hosted at: +# +# http://solidity.readthedocs.io/ +# +# ------------------------------------------------------------------------------ +# 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/> +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +set -ev + +# We need git for extracting the commit hash +apt-get update +apt-get -y install git-core + +export WORKSPACE=/src + +# CryptoPP +echo -en 'travis_fold:start:compiling_cryptopp\\r' +cd "$WORKSPACE/cryptopp" +# if .git exists, it is a fresh checkout, otherwise it comes from the cache +# and is already compiled +test -e .git && ( +emcmake cmake -DCRYPTOPP_LIBRARY_TYPE=STATIC -DCRYPTOPP_RUNTIME_TYPE=STATIC && emmake make -j 4 +ln -s . src/cryptopp || true +rm -rf .git +) +echo -en 'travis_fold:end:compiling_cryptopp\\r' + +# Json-CPP +echo -en 'travis_fold:start:compiling_jsoncpp\\r' +cd "$WORKSPACE/jsoncpp" +# if .git exists, it is a fresh checkout, otherwise it comes from the cache +# and is already compiled +test -e .git && ( +emcmake cmake -DJSONCPP_LIB_BUILD_STATIC=ON -DJSONCPP_LIB_BUILD_SHARED=OFF \ + -DJSONCPP_WITH_TESTS=OFF -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF \ + -G "Unix Makefiles" . +emmake make -j 4 +rm -rf .git +) +echo -en 'travis_fold:end:compiling_jsoncpp\\r' + +# Boost +echo -en 'travis_fold:start:compiling_boost\\r' +cd "$WORKSPACE"/boost_1_57_0 +# if b2 exists, it is a fresh checkout, otherwise it comes from the cache +# and is already compiled +test -e b2 && ( +sed -i 's|using gcc ;|using gcc : : /usr/local/bin/em++ ;|g' ./project-config.jam +sed -i 's|$(archiver\[1\])|/usr/local/bin/emar|g' ./tools/build/src/tools/gcc.jam +sed -i 's|$(ranlib\[1\])|/usr/local/bin/emranlib|g' ./tools/build/src/tools/gcc.jam +./b2 link=static variant=release threading=single runtime-link=static \ + thread system regex date_time chrono filesystem unit_test_framework program_options random +find . -name 'libboost*.a' -exec cp {} . \; +rm -rf b2 libs doc tools more bin.v2 status +) +echo -en 'travis_fold:end:compiling_boost\\r' + +# Build dependent components and solidity itself +echo -en 'travis_fold:start:compiling_solidity\\r' +cd $WORKSPACE +mkdir -p build +cd build +emcmake cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DEMSCRIPTEN=1 \ + -DCMAKE_CXX_COMPILER=em++ \ + -DCMAKE_C_COMPILER=emcc \ + -DBoost_FOUND=1 \ + -DBoost_USE_STATIC_LIBS=1 \ + -DBoost_USE_STATIC_RUNTIME=1 \ + -DBoost_INCLUDE_DIR="$WORKSPACE"/boost_1_57_0/ \ + -DBoost_CHRONO_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_chrono.a \ + -DBoost_CHRONO_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_chrono.a \ + -DBoost_DATE_TIME_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_date_time.a \ + -DBoost_DATE_TIME_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_date_time.a \ + -DBoost_FILESYSTEM_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_filesystem.a \ + -DBoost_FILESYSTEM_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_filesystem.a \ + -DBoost_PROGRAM_OPTIONS_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_program_options.a \ + -DBoost_PROGRAM_OPTIONS_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_program_options.a \ + -DBoost_RANDOM_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_random.a \ + -DBoost_RANDOM_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_random.a \ + -DBoost_REGEX_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_regex.a \ + -DBoost_REGEX_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_regex.a \ + -DBoost_SYSTEM_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_system.a \ + -DBoost_SYSTEM_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_system.a \ + -DBoost_THREAD_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_thread.a \ + -DBoost_THREAD_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_thread.a \ + -DBoost_UNIT_TEST_FRAMEWORK_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_unit_test_framework.a \ + -DBoost_UNIT_TEST_FRAMEWORK_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_unit_test_framework.a \ + -DJSONCPP_LIBRARY="$WORKSPACE"/jsoncpp/src/lib_json/libjsoncpp.a \ + -DJSONCPP_INCLUDE_DIR="$WORKSPACE"/jsoncpp/include/ \ + -DCRYPTOPP_LIBRARY="$WORKSPACE"/cryptopp/src/libcryptlib.a \ + -DCRYPTOPP_INCLUDE_DIR="$WORKSPACE"/cryptopp/src/ \ + -DDev_DEVCORE_LIBRARY="$WORKSPACE"/solidity/build/libdevcore/libdevcore.a \ + -DEth_EVMASM_LIBRARY="$WORKSPACE"/solidity/build/libevmasm/libevmasm.a \ + -DETHASHCL=0 -DEVMJIT=0 -DETH_STATIC=1 -DSOLIDITY=1 -DFATDB=0 -DTESTS=0 -DTOOLS=0 \ + .. +emmake make -j 4 + +# TODO - This is a temporary solution to the permissions issue which we are seeing in TravisCI, +# where this Emscripten build generates files which the main build then cannot delete. +# Presumably different accounts being used? This needs wrapping in some conditional, so we +# can choose to build, or build-and-clean. +cd .. +rm -rf build + +echo -en 'travis_fold:end:compiling_solidity\\r' diff --git a/scripts/travis-emscripten/deploy_key.enc b/scripts/travis-emscripten/deploy_key.enc Binary files differnew file mode 100644 index 00000000..d613c32e --- /dev/null +++ b/scripts/travis-emscripten/deploy_key.enc diff --git a/scripts/travis-emscripten/install_deps.sh b/scripts/travis-emscripten/install_deps.sh new file mode 100755 index 00000000..2c0e9f26 --- /dev/null +++ b/scripts/travis-emscripten/install_deps.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# Bash script for installing pre-requisite packages for building solidity +# using Emscripten on Ubuntu Trusty. +# +# The documentation for solidity is hosted at: +# +# http://solidity.readthedocs.io/ +# +# ------------------------------------------------------------------------------ +# 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/> +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +set -ev + +echo -en 'travis_fold:start:installing_dependencies\\r' +test -e cryptopp -a -e cryptopp/src || git clone https://github.com/mmoss/cryptopp.git +test -e jsoncpp -a -e jsoncpp/include || git clone https://github.com/open-source-parsers/jsoncpp.git +test -e boost_1_57_0 -a -e boost_1_57_0/boost || ( +wget 'http://downloads.sourceforge.net/project/boost/boost/'\ +'1.57.0/boost_1_57_0.tar.bz2?r=http%3A%2F%2Fsourceforge.net%2F'\ +'projects%2Fboost%2Ffiles%2Fboost%2F1.57.0%2F&ts=1421887207'\ + -O - | tar xj +cd boost_1_57_0 +./bootstrap.sh --with-toolset=gcc --with-libraries=thread,system,regex,date_time,chrono,filesystem,program_options,random +) +cd .. +echo -en 'travis_fold:end:installing_dependencies\\r' diff --git a/scripts/travis-emscripten/publish_binary.sh b/scripts/travis-emscripten/publish_binary.sh new file mode 100755 index 00000000..f4139539 --- /dev/null +++ b/scripts/travis-emscripten/publish_binary.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# Bash script for publishing Solidity Emscripten binaries to Github. +# +# The results are committed to https://github.com/ethereum/solc-bin. +# +# The documentation for solidity is hosted at: +# +# http://solidity.readthedocs.io/ +# +# ------------------------------------------------------------------------------ +# 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/> +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +set -e + +cd solidity +VER=$(cat CMakeLists.txt | grep 'set(PROJECT_VERSION' | sed -e 's/.*set(PROJECT_VERSION "\(.*\)".*/\1/') +test -n "$VER" +VER="v$VER" +COMMIT=$(git rev-parse --short HEAD) +DATE=$(date --date="$(git log -1 --date=iso --format=%ad HEAD)" --utc +%F) +cp build/solc/soljson.js "../soljson-$VER-$DATE-$COMMIT.js" +cd .. + + +ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key" +ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv" +ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR} +ENCRYPTED_IV=${!ENCRYPTED_IV_VAR} +openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in scripts/travis-emscripten/deploy_key.enc -out deploy_key -d +chmod 600 deploy_key +eval `ssh-agent -s` +ssh-add deploy_key + +git clone --depth 2 git@github.com:ethereum/solc-bin.git +cd solc-bin +git config user.name "travis" +git config user.email "chris@ethereum.org" +git checkout -B gh-pages origin/gh-pages +git clean -f -d -x +# We only want one release per day and we do not want to push the same commit twice. +if ls ./bin/soljson-"$VER-$DATE"-*.js ./bin/soljson-*-"$COMMIT.js" > /dev/null +then + true +else + cp ../soljson-*.js ./bin/ + ./update-index.sh + cd bin + LATEST=$(ls -r soljson-v* | head -n 1) + cp "$LATEST" soljson-latest.js + cp soljson-latest.js ../soljson.js + git add . + git add ../soljson.js + git commit -m "Added compiler version $LATEST" + git push origin gh-pages +fi diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 09c7c8e8..ac8db160 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -86,6 +86,8 @@ static set<string> const g_combinedJsonArgs{ "bin", "bin-runtime", "clone-bin", + "srcmap", + "srcmap-runtime", "opcodes", "abi", "interface", @@ -658,6 +660,16 @@ void CommandLineInterface::handleCombinedJSON() ostringstream unused; contractData["asm"] = m_compiler->streamAssembly(unused, contractName, m_sourceCodes, true); } + if (requests.count("srcmap")) + { + auto map = m_compiler->sourceMapping(contractName); + contractData["srcmap"] = map ? *map : ""; + } + if (requests.count("srcmap-runtime")) + { + auto map = m_compiler->runtimeSourceMapping(contractName); + contractData["srcmap"] = map ? *map : ""; + } if (requests.count("devdoc")) contractData["devdoc"] = m_compiler->metadata(contractName, DocumentationType::NatspecDev); if (requests.count("userdoc")) @@ -665,12 +677,22 @@ void CommandLineInterface::handleCombinedJSON() output["contracts"][contractName] = contractData; } + bool needsSourceList = requests.count("ast") || requests.count("srcmap") || requests.count("srcmap-runtime"); + if (needsSourceList) + { + // Indices into this array are used to abbreviate source names in source locations. + output["sourceList"] = Json::Value(Json::arrayValue); + + for (auto const& source: m_compiler->sourceNames()) + output["sourceList"].append(source); + } + if (requests.count("ast")) { output["sources"] = Json::Value(Json::objectValue); for (auto const& sourceCode: m_sourceCodes) { - ASTJsonConverter converter(m_compiler->ast(sourceCode.first)); + ASTJsonConverter converter(m_compiler->ast(sourceCode.first), m_compiler->sourceIndices()); output["sources"][sourceCode.first] = Json::Value(Json::objectValue); output["sources"][sourceCode.first]["AST"] = converter.json(); } diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index e8f674a0..8fc42d73 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -214,17 +214,38 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback contractData["opcodes"] = solidity::disassemble(compiler.object(contractName).bytecode); contractData["functionHashes"] = functionHashes(compiler.contractDefinition(contractName)); contractData["gasEstimates"] = estimateGas(compiler, contractName); + auto sourceMap = compiler.sourceMapping(contractName); + contractData["srcmap"] = sourceMap ? *sourceMap : ""; + auto runtimeSourceMap = compiler.sourceMapping(contractName); + contractData["srcmap-runtime"] = runtimeSourceMap ? *runtimeSourceMap : ""; ostringstream unused; contractData["assembly"] = compiler.streamAssembly(unused, contractName, _sources, true); output["contracts"][contractName] = contractData; } - output["sources"] = Json::Value(Json::objectValue); - for (auto const& source: _sources) + // Do not taint the internal error list + ErrorList formalErrors; + if (compiler.prepareFormalAnalysis(&formalErrors)) + output["formal"]["why3"] = compiler.formalTranslation(); + if (!formalErrors.empty()) { - output["sources"][source.first] = Json::Value(Json::objectValue); - output["sources"][source.first]["AST"] = ASTJsonConverter(compiler.ast(source.first)).json(); + Json::Value errors(Json::arrayValue); + for (auto const& error: formalErrors) + errors.append(formatError( + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error", + scannerFromSourceName + )); + output["formal"]["errors"] = errors; } + + // Indices into this array are used to abbreviate source names in source locations. + output["sourceList"] = Json::Value(Json::arrayValue); + for (auto const& source: compiler.sourceNames()) + output["sourceList"].append(source); + output["sources"] = Json::Value(Json::objectValue); + for (auto const& source: compiler.sourceNames()) + output["sources"][source]["AST"] = ASTJsonConverter(compiler.ast(source), compiler.sourceIndices()).json(); } return Json::FastWriter().write(output); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index efcbb82e..2f001b21 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,7 +26,7 @@ file(GLOB HEADERS "*.h" "*/*.h") set(EXECUTABLE soltest) eth_simple_add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) -eth_use(${EXECUTABLE} REQUIRED Solidity::solidity Eth::ethcore) +eth_use(${EXECUTABLE} REQUIRED Solidity::solidity) include_directories(BEFORE ..) target_link_libraries(${EXECUTABLE} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 54e1675f..e9df9031 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -36,15 +36,25 @@ IPCSocket::IPCSocket(string const& _path): m_path(_path) BOOST_FAIL("Error opening IPC: socket path is too long!"); struct sockaddr_un saun; + memset(&saun, 0, sizeof(sockaddr_un)); saun.sun_family = AF_UNIX; strcpy(saun.sun_path, _path.c_str()); +// http://idletechnology.blogspot.ca/2011/12/unix-domain-sockets-on-osx.html +// +// SUN_LEN() might be optimal, but it seemingly affects the portability, +// with at least Android missing this macro. Just using the sizeof() for +// structure seemingly works, and would only have the side-effect of +// sending larger-than-required packets over the socket. Given that this +// code is only used for unit-tests, that approach seems simpler. +#if defined(__APPLE__) + saun.sun_len = sizeof(struct sockaddr_un); +#endif // defined(__APPLE__) + if ((m_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) BOOST_FAIL("Error creating IPC socket object"); - int len = sizeof(saun.sun_family) + strlen(saun.sun_path); - - if (connect(m_socket, reinterpret_cast<struct sockaddr const*>(&saun), len) < 0) + if (connect(m_socket, reinterpret_cast<struct sockaddr const*>(&saun), sizeof(struct sockaddr_un)) < 0) BOOST_FAIL("Error connecting to IPC socket: " << _path); m_fp = fdopen(m_socket, "r"); diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index 9aa47189..8860727d 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -23,8 +23,8 @@ #include <string> #include <tuple> #include <boost/test/unit_test.hpp> +#include <libdevcore/ABI.h> #include <libdevcore/Hash.h> -#include <libethcore/ABI.h> #include <test/libsolidity/SolidityExecutionFramework.h> using namespace std; @@ -333,7 +333,7 @@ BOOST_AUTO_TEST_CASE(double_reserve_long) registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), m_sender); - sendEther(account(1), u256(10) * eth::ether); + sendEther(account(1), u256(10) * ether); m_sender = account(1); registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), account(0)); @@ -350,7 +350,7 @@ BOOST_AUTO_TEST_CASE(properties) for (string const& name: names) { m_sender = account(0); - sendEther(account(count), u256(20) * eth::ether); + sendEther(account(count), u256(20) * ether); m_sender = account(count); auto sender = m_sender; addr += count; @@ -402,7 +402,7 @@ BOOST_AUTO_TEST_CASE(disown) BOOST_CHECK_EQUAL(registrar.name(u160(124)), name); // someone else tries disowning - sendEther(account(1), u256(10) * eth::ether); + sendEther(account(1), u256(10) * ether); m_sender = account(1); registrar.disown(name); BOOST_CHECK_EQUAL(registrar.owner(name), account(0)); @@ -451,7 +451,7 @@ BOOST_AUTO_TEST_CASE(auction_bidding) registrar.setNextValue(12); registrar.reserve(name); // another bid by someone else - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); m_rpc.test_modifyTimestamp(startTime + 2 * m_biddingTime - 50); registrar.setNextValue(13); @@ -480,7 +480,7 @@ BOOST_AUTO_TEST_CASE(auction_renewal) BOOST_CHECK_EQUAL(registrar.owner(name), m_sender); // try to re-register before interval end - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); m_rpc.test_modifyTimestamp(currentTimestamp() + m_renewalInterval - 1); registrar.setNextValue(80); diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index 8cb8257a..63db0531 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -174,7 +174,7 @@ BOOST_AUTO_TEST_CASE(double_reserve) BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs()); BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(h256(account(0), h256::AlignRight))); - sendEther(account(1), 100 * eth::ether); + sendEther(account(1), 100 * ether); m_sender = account(1); BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs()); BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(h256(account(0), h256::AlignRight))); @@ -191,7 +191,7 @@ BOOST_AUTO_TEST_CASE(properties) { addr++; m_sender = account(0); - sendEther(account(count), 100 * eth::ether); + sendEther(account(count), 100 * ether); m_sender = account(count); Address owner = m_sender; // setting by sender works @@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(properties) count++; // but not by someone else m_sender = account(0); - sendEther(account(count), 100 * eth::ether); + sendEther(account(count), 100 * ether); m_sender = account(count); BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(h256(owner, h256::AlignRight))); BOOST_CHECK(callContractFunction("setAddr(string,address)", u256(0x40), addr + 1, u256(name.length()), name) == encodeArgs()); diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index fbab2404..27bdb396 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -474,13 +474,13 @@ BOOST_AUTO_TEST_CASE(add_owners) BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(1), h256::AlignRight)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(account(1), h256::AlignRight)) == encodeArgs(true)); // now let the new owner add someone - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true)); // and check that a non-owner cannot add a new owner m_sender = account(0); - sendEther(account(2), 10 * eth::ether); + sendEther(account(2), 10 * ether); m_sender = account(2); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x20)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x20)) == encodeArgs(false)); @@ -559,17 +559,17 @@ BOOST_AUTO_TEST_CASE(multisig_value_transfer) // check that balance is and stays zero at destination address BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); auto ophash = callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00); BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(2), 10 * eth::ether); + sendEther(account(2), 10 * ether); m_sender = account(2); callContractFunction("confirm(bytes32)", ophash); BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(3), 10 * eth::ether); + sendEther(account(3), 10 * ether); m_sender = account(3); callContractFunction("confirm(bytes32)", ophash); // now it should go through @@ -590,7 +590,7 @@ BOOST_AUTO_TEST_CASE(revoke_addOwner) BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false)); m_sender = account(0); - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false)); @@ -598,12 +598,12 @@ BOOST_AUTO_TEST_CASE(revoke_addOwner) m_sender = deployer; BOOST_REQUIRE(callContractFunction("revoke(bytes32)", opHash) == encodeArgs()); m_sender = account(0); - sendEther(account(2), 10 * eth::ether); + sendEther(account(2), 10 * ether); m_sender = account(2); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false)); m_sender = account(0); - sendEther(account(3), 10 * eth::ether); + sendEther(account(3), 10 * ether); m_sender = account(3); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(true)); @@ -621,24 +621,24 @@ BOOST_AUTO_TEST_CASE(revoke_transaction) Address deployer = m_sender; BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); auto opHash = callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00); BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(2), 10 * eth::ether); + sendEther(account(2), 10 * ether); m_sender = account(2); callContractFunction("confirm(bytes32)", opHash); BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); BOOST_REQUIRE(callContractFunction("revoke(bytes32)", opHash) == encodeArgs()); m_sender = deployer; callContractFunction("confirm(bytes32)", opHash); BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); m_sender = account(0); - sendEther(account(3), 10 * eth::ether); + sendEther(account(3), 10 * ether); m_sender = account(3); callContractFunction("confirm(bytes32)", opHash); // now it should go through @@ -659,7 +659,7 @@ BOOST_AUTO_TEST_CASE(daylimit) // try to send tx over daylimit BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); m_sender = account(1); BOOST_REQUIRE( callContractFunction("execute(address,uint256,bytes)", h256(0x05), 150, 0x60, 0x00) != @@ -668,7 +668,7 @@ BOOST_AUTO_TEST_CASE(daylimit) BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); // try to send tx under daylimit by stranger m_sender = account(0); - sendEther(account(4), 10 * eth::ether); + sendEther(account(4), 10 * ether); m_sender = account(4); BOOST_REQUIRE( callContractFunction("execute(address,uint256,bytes)", h256(0x05), 90, 0x60, 0x00) == @@ -677,7 +677,7 @@ BOOST_AUTO_TEST_CASE(daylimit) BOOST_CHECK_EQUAL(balanceAt(Address(0x05)), 0); // now send below limit by owner m_sender = account(0); - sendEther(account(1), 10 * eth::ether); + sendEther(account(1), 10 * ether); BOOST_REQUIRE( callContractFunction("execute(address,uint256,bytes)", h256(0x05), 90, 0x60, 0x00) == encodeArgs(u256(0)) diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp new file mode 100644 index 00000000..6d914391 --- /dev/null +++ b/test/libsolidity/ASTJSON.cpp @@ -0,0 +1,69 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2016 + * Tests for the json ast output. + */ + +#include <string> +#include <boost/test/unit_test.hpp> +#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/interface/CompilerStack.h> +#include <libsolidity/ast/ASTJsonConverter.h> + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(SolidityASTJSON) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CompilerStack c; + c.addSource("a", "contract C {}"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + BOOST_CHECK_EQUAL(astJson["name"], "root"); +} + +BOOST_AUTO_TEST_CASE(source_location) +{ + CompilerStack c; + c.addSource("a", "contract C { function f() { var x = 2; x++; } }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + BOOST_CHECK_EQUAL(astJson["name"], "root"); + BOOST_CHECK_EQUAL(astJson["children"][0]["name"], "Contract"); + BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["name"], "Function"); + BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["src"], "13:32:1"); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index fc7a033f..41204a0a 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -21,6 +21,7 @@ */ #include <test/libsolidity/SolidityExecutionFramework.h> +#include <libevmasm/EVMSchedule.h> #include <libevmasm/GasMeter.h> #include <libevmasm/KnownState.h> #include <libevmasm/PathGasMeter.h> diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 8dcc878e..1b7c5ea4 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -21,6 +21,7 @@ * Unit tests for the solidity expression compiler, testing the behaviour of the code. */ +#include <functional> #include <string> #include <tuple> #include <boost/test/unit_test.hpp> @@ -29,6 +30,7 @@ #include <test/libsolidity/SolidityExecutionFramework.h> using namespace std; +using namespace std::placeholders; namespace dev { @@ -2462,7 +2464,7 @@ BOOST_AUTO_TEST_CASE(use_std_lib) contract Icarus is mortal { } )"; m_addStandardSources = true; - u256 amount(130 * eth::ether); + u256 amount(130 * ether); compileAndRun(sourceCode, amount, "Icarus"); u256 balanceBefore = balanceAt(m_sender); BOOST_CHECK(callContractFunction("kill()") == bytes()); @@ -5921,9 +5923,9 @@ BOOST_AUTO_TEST_CASE(version_stamp_for_libraries) m_optimize = true; bytes runtimeCode = compileAndRun(sourceCode, 0, "lib"); BOOST_CHECK(runtimeCode.size() >= 8); - BOOST_CHECK_EQUAL(runtimeCode[0], int(eth::Instruction::PUSH6)); // might change once we switch to 1.x.x + BOOST_CHECK_EQUAL(runtimeCode[0], int(Instruction::PUSH6)); // might change once we switch to 1.x.x BOOST_CHECK_EQUAL(runtimeCode[1], 3); // might change once we switch away from x.3.x - BOOST_CHECK_EQUAL(runtimeCode[7], int(eth::Instruction::POP)); + BOOST_CHECK_EQUAL(runtimeCode[7], int(Instruction::POP)); } BOOST_AUTO_TEST_CASE(contract_binary_dependencies) @@ -6837,6 +6839,20 @@ BOOST_AUTO_TEST_CASE(skip_dynamic_types_for_structs) BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(2), u256(6))); } +BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length) +{ + char const* sourceCode = R"( + contract C { + function f() returns (uint) { + var a = new uint[][](0); + return 7; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(7))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index da9cf5f1..a33f4caf 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -22,13 +22,21 @@ #include <cstdlib> #include <boost/test/framework.hpp> +#include <libdevcore/CommonIO.h> #include <test/libsolidity/SolidityExecutionFramework.h> + + using namespace std; using namespace dev; using namespace dev::solidity; using namespace dev::solidity::test; +namespace // anonymous +{ + h256 const EmptyTrie("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); +} + string getIPCSocketPath() { string ipcPath; @@ -56,9 +64,6 @@ ExecutionFramework::ExecutionFramework() : m_rpc(RPCSession::instance(getIPCSocketPath())), m_sender(m_rpc.account(0)) { - if (g_logVerbosity != -1) - g_logVerbosity = 0; - m_rpc.test_rewindToBlock(0); } diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 2b589498..5764784a 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -22,25 +22,33 @@ #pragma once -#include <string> -#include <tuple> -#include <fstream> +#include <functional> + #include "../TestHelper.h" #include "../RPCSession.h" -#include <libethcore/ABI.h> -#include <libethcore/SealEngine.h> -#include <libethereum/State.h> -#include <libethereum/Executive.h> -#include <libethereum/ChainParams.h> + +#include <libdevcore/ABI.h> +#include <libdevcore/FixedHash.h> +#include <libevmasm/Instruction.h> + #include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/Exceptions.h> -#include <libethcore/BasicAuthority.h> -#include <libethcore/SealEngine.h> namespace dev { namespace solidity { + /// An Ethereum address: 20 bytes. + /// @NOTE This is not endian-specific; it's just a bunch of bytes. + using Address = h160; + + // The various denominations; here for ease of use where needed within code. + static const u256 ether = exp10<18>(); + static const u256 finney = exp10<15>(); + static const u256 szabo = exp10<12>(); + static const u256 shannon = exp10<9>(); + static const u256 wei = exp10<0>(); + namespace test { @@ -275,8 +283,7 @@ protected: dev::solidity::CompilerStack m_compiler; Address m_sender; Address m_contractAddress; - eth::EnvInfo m_envInfo; - u256 const m_gasPrice = 100 * eth::szabo; + u256 const m_gasPrice = 100 * szabo; u256 const m_gas = 100000000; bytes m_output; std::vector<LogEntry> m_logs; |