Browse Source

Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop

cl-refactor
Gav Wood 10 years ago
parent
commit
9302eb109a
  1. 4
      cmake/EthCompilerSettings.cmake
  2. 7
      cmake/EthDependencies.cmake
  3. 19
      cmake/FindMiniupnpc.cmake
  4. 322
      cmake/FindWindowsSDK.cmake
  5. 1
      extdep/getstuff.bat
  6. 35
      libdevcore/Base64.cpp
  7. 1
      libdevcore/Base64.h
  8. 2
      libethcore/Common.cpp
  9. 2
      libethereum/BlockChain.cpp
  10. 1
      libethereum/CanonBlockChain.h
  11. 28
      libethereum/Client.cpp
  12. 5
      libethereum/CommonNet.h
  13. 50
      libethereum/DownloadMan.cpp
  14. 113
      libethereum/DownloadMan.h
  15. 593
      libethereum/EthereumHost.cpp
  16. 43
      libethereum/EthereumHost.h
  17. 542
      libethereum/EthereumPeer.cpp
  18. 50
      libethereum/EthereumPeer.h
  19. 27
      libevmasm/ExpressionClasses.cpp
  20. 5
      libevmasm/ExpressionClasses.h
  21. 9
      libevmasm/GasMeter.cpp
  22. 14
      libevmasm/GasMeter.h
  23. 128
      libevmasm/PathGasMeter.cpp
  24. 66
      libevmasm/PathGasMeter.h
  25. 2
      libevmasm/SemanticInformation.cpp
  26. 6
      libp2p/Host.cpp
  27. 1
      libp2p/Host.h
  28. 7
      libp2p/HostCapability.cpp
  29. 5
      libp2p/HostCapability.h
  30. 2
      libp2p/Session.h
  31. 2
      libsolidity/ASTPrinter.cpp
  32. 6
      libsolidity/ASTPrinter.h
  33. 5
      libsolidity/Compiler.cpp
  34. 4
      libsolidity/Compiler.h
  35. 6
      libsolidity/CompilerContext.cpp
  36. 4
      libsolidity/CompilerContext.h
  37. 18
      libsolidity/CompilerStack.cpp
  38. 8
      libsolidity/CompilerStack.h
  39. 62
      libsolidity/GasEstimator.cpp
  40. 29
      libsolidity/GasEstimator.h
  41. 22
      libsolidity/Token.h
  42. 2
      libtestutils/BlockChainLoader.h
  43. 8
      libwhisper/Message.h
  44. 2
      libwhisper/WhisperPeer.cpp
  45. 3
      libwhisper/WhisperPeer.h
  46. 3
      mix/ClientModel.cpp
  47. 5
      mix/CodeModel.cpp
  48. 2
      mix/MixClient.h
  49. 2
      mix/qml/DebuggerPaneStyle.qml
  50. 14
      mix/qml/QIntTypeView.qml
  51. 15
      mix/qml/StructView.qml
  52. 2
      mix/qml/VariablesView.qml
  53. 59
      solc/CommandLineInterface.cpp
  54. 1
      solc/CommandLineInterface.h
  55. 39
      test/libdevcrypto/hexPrefix.cpp
  56. 7748
      test/libethereum/BlockTestsFiller/bcWalletTestFiller.json
  57. 2
      test/libethereum/StateTestsFiller/stBlockHashTestFiller.json
  58. 516
      test/libethereum/StateTestsFiller/stCallCreateCallCodeTestFiller.json
  59. 433
      test/libethereum/StateTestsFiller/stWalletTestFiller.json
  60. 2
      test/libp2p/capability.cpp
  61. 99
      test/libsolidity/GasMeter.cpp

4
cmake/EthCompilerSettings.cmake

@ -37,13 +37,15 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# disable warning C4535: calling _set_se_translator() requires /EHa (for boost tests)
# declare Windows XP requirement
# undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions
add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501 /DNOMINMAX)
# define miniupnp static library
add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501 /DNOMINMAX /DMINIUPNP_STATICLIB)
# 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.")

7
cmake/EthDependencies.cmake

@ -23,10 +23,11 @@ set(ETH_SCRIPTS_DIR ${CMAKE_SOURCE_DIR}/cmake/scripts)
# TODO use proper version of windows SDK (32 vs 64)
# TODO make it possible to use older versions of windows SDK (7.0+ should also work)
# TODO it windows SDK is NOT FOUND, throw ERROR
# from https://github.com/rpavlik/cmake-modules/blob/master/FindWindowsSDK.cmake
if (WIN32)
set (CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:/Program Files/Windows Kits/8.1/Lib/winv6.3/um/x86")
message(" - Found windows 8.1 SDK")
#set (CMAKE_PREFIX_PATH "C:/Program Files/Windows Kits/8.1/Lib/winv6.3/um/x64")
find_package(WINDOWSSDK REQUIRED)
message(" - WindowsSDK dirs: ${WINDOWSSDK_DIRS}")
set (CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WINDOWSSDK_DIRS})
endif()
# homebrew installs qts in opt

19
cmake/FindMiniupnpc.cmake

@ -14,17 +14,32 @@ find_path(
MINIUPNPC_INCLUDE_DIR
NAMES miniupnpc/miniupnpc.h
DOC "miniupnpc include dir"
)
)
find_library(
MINIUPNPC_LIBRARY
NAMES miniupnpc
DOC "miniupnpc library"
)
)
set(MINIUPNPC_INCLUDE_DIRS ${MINIUPNPC_INCLUDE_DIR})
set(MINIUPNPC_LIBRARIES ${MINIUPNPC_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(
MINIUPNPC_LIBRARY_DEBUG
NAMES miniupnpcd
DOC "miniupnpc debug library"
)
set(MINIUPNPC_LIBRARIES "iphlpapi" optimized ${MINIUPNPC_LIBRARIES} debug ${MINIUPNPC_LIBRARY_DEBUG})
endif()
# handle the QUIETLY and REQUIRED arguments and set MINIUPNPC_FOUND to TRUE
# if all listed variables are TRUE, hide their existence from configuration view
include(FindPackageHandleStandardArgs)

322
cmake/FindWindowsSDK.cmake

@ -0,0 +1,322 @@
# - Find the Windows SDK aka Platform SDK
#
# Relevant Wikipedia article: http://en.wikipedia.org/wiki/Microsoft_Windows_SDK
#
# Variables:
# WINDOWSSDK_FOUND - if any version of the windows or platform SDK was found that is usable with the current version of visual studio
# WINDOWSSDK_LATEST_DIR
# WINDOWSSDK_LATEST_NAME
# WINDOWSSDK_FOUND_PREFERENCE - if we found an entry indicating a "preferred" SDK listed for this visual studio version
# WINDOWSSDK_PREFERRED_DIR
# WINDOWSSDK_PREFERRED_NAME
#
# WINDOWSSDK_DIRS - contains no duplicates, ordered most recent first.
# WINDOWSSDK_PREFERRED_FIRST_DIRS - contains no duplicates, ordered with preferred first, followed by the rest in descending recency
#
# Functions:
# windowssdk_name_lookup(<directory> <output variable>) - Find the name corresponding with the SDK directory you pass in, or
# NOTFOUND if not recognized. Your directory must be one of WINDOWSSDK_DIRS for this to work.
#
# get_windowssdk_from_component(<file or dir> <output variable>) - Given a library or include dir,
# find the Windows SDK root dir corresponding to it, or NOTFOUND if unrecognized.
#
# get_windowssdk_library_dirs(<directory> <output variable>) - Find the architecture-appropriate
# library directories corresponding to the SDK directory you pass in (or NOTFOUND if none)
#
# get_windowssdk_include_dirs(<directory> <output variable>) - Find the
# include directories corresponding to the SDK directory you pass in (or NOTFOUND if none)
#
# Requires these CMake modules:
# FindPackageHandleStandardArgs (known included with CMake >=2.6.2)
#
# Original Author:
# 2012 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2012.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
set(_preferred_sdk_dirs)
set(_win_sdk_dirs)
set(_win_sdk_versanddirs)
if(MSVC_VERSION GREATER 1310) # Newer than VS .NET/VS Toolkit 2003
# Environment variable for SDK dir
if(EXISTS "$ENV{WindowsSDKDir}" AND (NOT "$ENV{WindowsSDKDir}" STREQUAL ""))
message(STATUS "Got $ENV{WindowsSDKDir} - Windows/Platform SDK directories: ${_win_sdk_dirs}")
list(APPEND _preferred_sdk_dirs "$ENV{WindowsSDKDir}")
endif()
if(MSVC_VERSION LESS 1600)
# Per-user current Windows SDK for VS2005/2008
get_filename_component(_sdkdir
"[HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
ABSOLUTE)
if(EXISTS "${_sdkdir}")
list(APPEND _preferred_sdk_dirs "${_sdkdir}")
endif()
# System-wide current Windows SDK for VS2005/2008
get_filename_component(_sdkdir
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
ABSOLUTE)
if(EXISTS "${_sdkdir}")
list(APPEND _preferred_sdk_dirs "${_sdkdir}")
endif()
endif()
if(MSVC_VERSION LESS 1700)
# VC 10 and older has broad target support
set(_winsdk_vistaonly)
else()
# VC 11 by default targets Vista and later only, so we can add a few more SDKs that (might?) only work on vista+
if("${CMAKE_VS_PLATFORM_TOOLSET}" MATCHES "_xp")
# This is the XP-compatible v110 toolset
elseif("${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v100")
# This is the VS2010 toolset
else()
if(NOT WINDOWSSDK_FOUND AND NOT WindowsSDK_FIND_QUIETLY)
message(STATUS "FindWindowsSDK: Detected Visual Studio 2012 or newer, not using the _xp toolset variant: including SDK versions that drop XP support in search!")
endif()
# These versions have no XP (and possibly Vista pre-SP1) support
set(_winsdk_vistaonly)
if(NOT MSVC_VERSION LESS 1800)
list(APPEND _winsdk_vistaonly
# Windows Software Development Kit (SDK) for Windows 8.1
# http://msdn.microsoft.com/en-gb/windows/desktop/bg162891
v8.1)
endif()
list(APPEND _winsdk_vistaonly
# Included in Visual Studio 2012
v8.0A
# Microsoft Windows SDK for Windows 8 and .NET Framework 4.5
# This is the first version to also include the DirectX SDK
# http://msdn.microsoft.com/en-US/windows/desktop/hh852363.aspx
v8.0
# Microsoft Windows SDK for Windows 7 and .NET Framework 4
# http://www.microsoft.com/downloads/en/details.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b
v7.1
)
endif()
endif()
foreach(_winsdkver
${_winsdk_vistaonly}
# Included in Visual Studio 2013
# Includes the v120_xp toolset
v8.1A
# Included with VS 2012 Update 1 or later
# Introduces v110_xp toolset
v7.1A
# Included with VS 2010
v7.0A
# Windows SDK for Windows 7 and .NET Framework 3.5 SP1
# Works with VC9
#http://www.microsoft.com/en-us/download/details.aspx?id=18950
v7.0
# Two versions call themselves "v6.1":
# Older:
# Windows Vista Update & .NET 3.0 SDK
# http://www.microsoft.com/en-us/download/details.aspx?id=14477
# Newer:
# Windows Server 2008 & .NET 3.5 SDK
# may have broken VS9SP1? they recommend v7.0 instead, or a KB...
# http://www.microsoft.com/en-us/download/details.aspx?id=24826
v6.1
# Included in VS 2008
v6.0A
# Microsoft Windows Software Development Kit for Windows Vista and .NET Framework 3.0 Runtime Components
# http://blogs.msdn.com/b/stanley/archive/2006/11/08/microsoft-windows-software-development-kit-for-windows-vista-and-net-framework-3-0-runtime-components.aspx
v6.0)
get_filename_component(_sdkdir
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\${_winsdkver};InstallationFolder]"
ABSOLUTE)
if(EXISTS "${_sdkdir}")
list(APPEND _win_sdk_dirs "${_sdkdir}")
list(APPEND
_win_sdk_versanddirs
"Windows SDK ${_winsdkver}"
"${_sdkdir}")
endif()
endforeach()
endif()
if(MSVC_VERSION GREATER 1200)
foreach(_platformsdkinfo
"D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1_Microsoft Platform SDK for Windows Server 2003 R2"
"8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3_Microsoft Platform SDK for Windows Server 2003 SP1")
string(SUBSTRING "${_platformsdkinfo}" 0 36 _platformsdkguid)
string(SUBSTRING "${_platformsdkinfo}" 37 -1 _platformsdkname)
foreach(HIVE HKEY_LOCAL_MACHINE HKEY_CURRENT_USER)
get_filename_component(_sdkdir
"[${HIVE}\\SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\${_platformsdkguid};Install Dir]"
ABSOLUTE)
if(EXISTS "${_sdkdir}")
list(APPEND _win_sdk_dirs "${_sdkdir}")
list(APPEND _win_sdk_versanddirs "${_platformsdkname}" "${_sdkdir}")
endif()
endforeach()
endforeach()
endif()
set(_win_sdk_versanddirs
"${_win_sdk_versanddirs}"
CACHE
INTERNAL
"mapping between windows sdk version locations and names"
FORCE)
function(windowssdk_name_lookup _dir _outvar)
list(FIND _win_sdk_versanddirs "${_dir}" _diridx)
math(EXPR _nameidx "${_diridx} - 1")
if(${_nameidx} GREATER -1)
list(GET _win_sdk_versanddirs ${_nameidx} _sdkname)
else()
set(_sdkname "NOTFOUND")
endif()
set(${_outvar} "${_sdkname}" PARENT_SCOPE)
endfunction()
if(_win_sdk_dirs)
# Remove duplicates
list(REMOVE_DUPLICATES _win_sdk_dirs)
list(GET _win_sdk_dirs 0 WINDOWSSDK_LATEST_DIR)
windowssdk_name_lookup("${WINDOWSSDK_LATEST_DIR}"
WINDOWSSDK_LATEST_NAME)
set(WINDOWSSDK_DIRS ${_win_sdk_dirs})
endif()
if(_preferred_sdk_dirs)
list(GET _preferred_sdk_dirs 0 WINDOWSSDK_PREFERRED_DIR)
windowssdk_name_lookup("${WINDOWSSDK_LATEST_DIR}"
WINDOWSSDK_PREFERRED_NAME)
set(WINDOWSSDK_PREFERRED_FIRST_DIRS
${_preferred_sdk_dirs}
${_win_sdk_dirs})
list(REMOVE_DUPLICATES WINDOWSSDK_PREFERRED_FIRST_DIRS)
set(WINDOWSSDK_FOUND_PREFERENCE ON)
# In case a preferred dir was found that isn't found otherwise
#set(WINDOWSSDK_DIRS ${WINDOWSSDK_DIRS} ${WINDOWSSDK_PREFERRED_FIRST_DIRS})
#list(REMOVE_DUPLICATES WINDOWSSDK_DIRS)
else()
set(WINDOWSSDK_PREFERRED_DIR "${WINDOWSSDK_LATEST_DIR}")
set(WINDOWSSDK_PREFERRED_NAME "${WINDOWSSDK_LATEST_NAME}")
set(WINDOWSSDK_PREFERRED_FIRST_DIRS ${WINDOWSSDK_DIRS})
set(WINDOWSSDK_FOUND_PREFERENCE OFF)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WindowsSDK
"No compatible version of the Windows SDK or Platform SDK found."
WINDOWSSDK_DIRS)
if(WINDOWSSDK_FOUND)
if(NOT _winsdk_remembered_dirs STREQUAL WINDOWSSDK_DIRS)
set(_winsdk_remembered_dirs
"${WINDOWSSDK_DIRS}"
CACHE
INTERNAL
""
FORCE)
if(NOT WindowsSDK_FIND_QUIETLY)
foreach(_sdkdir ${WINDOWSSDK_DIRS})
windowssdk_name_lookup("${_sdkdir}" _sdkname)
message(STATUS " - Found ${_sdkname} at ${_sdkdir}")
endforeach()
endif()
endif()
# Internal: Architecture-appropriate library directory names.
if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM")
set(_winsdk_archbare /arm) # what the architecture used to be called in oldest SDKs
set(_winsdk_arch arm) # what the architecture used to be called
set(_winsdk_arch8 arm) # what the WDK for Win8+ calls this architecture
else()
if(CMAKE_SIZEOF_VOID_P MATCHES "8")
set(_winsdk_archbare /x64) # what the architecture used to be called in oldest SDKs
set(_winsdk_arch amd64) # what the architecture used to be called
set(_winsdk_arch8 x64) # what the WDK for Win8+ calls this architecture
else()
set(_winsdk_archbare ) # what the architecture used to be called in oldest SDKs
set(_winsdk_arch i386) # what the architecture used to be called
set(_winsdk_arch8 x86) # what the WDK for Win8+ calls this architecture
endif()
endif()
function(get_windowssdk_from_component _component _var)
get_filename_component(_component "${_component}" ABSOLUTE)
file(TO_CMAKE_PATH "${_component}" _component)
foreach(_sdkdir ${WINDOWSSDK_DIRS})
get_filename_component(_sdkdir "${_sdkdir}" ABSOLUTE)
string(LENGTH "${_sdkdir}" _sdklen)
file(RELATIVE_PATH _rel "${_sdkdir}" "${_component}")
# If we don't have any "parent directory" items...
if(NOT "${_rel}" MATCHES "[.][.]")
set(${_var} "${_sdkdir}" PARENT_SCOPE)
return()
endif()
endforeach()
# Fail.
set(${_var} "NOTFOUND" PARENT_SCOPE)
endfunction()
function(get_windowssdk_library_dirs _winsdk_dir _var)
set(_result)
foreach(_suffix
"lib${_winsdk_archbare}" # SDKs like 7.1A
"lib/w2k/${_winsdk_arch}" # Win2k min requirement
"lib/wxp/${_winsdk_arch}" # WinXP min requirement
"lib/wnet/${_winsdk_arch}" # Win Server 2003 min requirement
"lib/wlh/${_winsdk_arch}" # Win Vista ("Long Horn") min requirement
"lib/wlh/um/${_winsdk_arch8}" # Win Vista ("Long Horn") min requirement
"lib/win7/${_winsdk_arch}" # Win 7 min requirement
"lib/win7/um/${_winsdk_arch8}" # Win 7 min requirement
"lib/win8/um/${_winsdk_arch8}" # Win 8 min requirement
"lib/win8/km/${_winsdk_arch8}" # Win 8 min requirement
"lib/winv6.3/km/${_winsdk_arch8}" # Win 8.1 min requirement
"lib/winv6.3/um/${_winsdk_arch8}" # Win 8.1 min requirement
)
# Check to see if a library actually exists here.
file(GLOB _libs "${_winsdk_dir}/${_suffix}/*.lib")
if(_libs)
list(APPEND _result "${_winsdk_dir}/${_suffix}")
endif()
endforeach()
if(NOT _result)
set(_result NOTFOUND)
endif()
set(${_var} ${_result} PARENT_SCOPE)
endfunction()
function(get_windowssdk_include_dirs _winsdk_dir _var)
set(_result)
foreach(_suffix
"Include"
"Include/shared"
"Include/um"
"Include/winrt"
"Include/km"
"Include/wdf"
)
# Check to see if a header file actually exists here.
file(GLOB _headers "${_winsdk_dir}/${_suffix}/*.h")
if(_headers)
list(APPEND _result "${_winsdk_dir}/${_suffix}")
endif()
endforeach()
if(NOT _result)
set(_result NOTFOUND)
endif()
set(${_var} ${_result} PARENT_SCOPE)
endfunction()
endif()

1
extdep/getstuff.bat

@ -13,6 +13,7 @@ call :download json-rpc-cpp 0.5.0
call :download leveldb 1.2
call :download microhttpd 0.9.2
call :download qt 5.4.1
call :download miniupnpc 1.9
goto :EOF

35
libdevcore/Base64.cpp

@ -27,20 +27,27 @@
/// Originally by René Nyffenegger, modified by some other guy and then devified by Gav Wood.
#include "Base64.h"
#include <iostream>
using namespace std;
using namespace dev;
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
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('/');
}
std::string dev::toBase64(bytesConstRef _in) {
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string ret;
int i = 0;
int j = 0;
@ -85,7 +92,7 @@ std::string dev::toBase64(bytesConstRef _in) {
}
bytes dev::fromBase64(std::string const& encoded_string) {
int in_len = encoded_string.size();
auto in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
@ -94,9 +101,9 @@ bytes dev::fromBase64(std::string const& encoded_string) {
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] = base64_chars.find(char_array_4[i]);
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);
@ -109,11 +116,11 @@ bytes dev::fromBase64(std::string const& encoded_string) {
}
if (i) {
for (j = i; j <4; j++)
for (j = i; j < 4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
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);

1
libdevcore/Base64.h

@ -28,7 +28,6 @@
/// DEVified by Gav Wood.
#pragma once
#include <vector>
#include <string>
#include "Common.h"
#include "FixedHash.h"

2
libethcore/Common.cpp

@ -35,7 +35,7 @@ namespace dev
namespace eth
{
const unsigned c_protocolVersion = 60;
const unsigned c_protocolVersion = 61;
#if ETH_FATDB
const unsigned c_minorProtocolVersion = 3;
const unsigned c_databaseBaseVersion = 9;

2
libethereum/BlockChain.cpp

@ -317,7 +317,7 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
{
try
{
// Nonce & uncle nonces already verified thread at this point.
// Nonce & uncle nonces already verified in verification thread at this point.
ImportRoute r;
DEV_TIMED_ABOVE(Block import, 500)
r = import(block.first, block.second, _stateDB, ImportRequirements::Default & ~ImportRequirements::ValidNonce & ~ImportRequirements::CheckUncles);

1
libethereum/CanonBlockChain.h

@ -34,7 +34,6 @@
#include <libdevcore/Guards.h>
#include "BlockDetails.h"
#include "Account.h"
#include "BlockQueue.h"
#include "BlockChain.h"
namespace ldb = leveldb;

28
libethereum/Client.cpp

@ -164,28 +164,8 @@ const char* ClientDetail::name() { return EthTeal "⧫" EthCoal " ●"; }
#endif
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId):
Worker("eth", 0),
m_vc(_dbPath),
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
m_gp(new TrivialGasPricer),
m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))),
m_preMine(m_stateDB, BaseState::CanonGenesis),
m_postMine(m_stateDB)
Client(_extNet, make_shared<TrivialGasPricer>(), _dbPath, _forceAction, _networkId)
{
m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30);
m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue);
m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue);
m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); });
m_gp->update(m_bc);
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
m_vc.setOk();
doWork();
startWorking();
}
@ -195,7 +175,7 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string c
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
m_gp(_gp),
m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))),
m_preMine(m_stateDB),
m_preMine(m_stateDB, BaseState::CanonGenesis),
m_postMine(m_stateDB)
{
m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30);
@ -205,7 +185,9 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string c
m_gp->update(m_bc);
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
auto host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
m_host = host;
_extNet->addCapability(host, EthereumHost::staticName(), EthereumHost::c_oldProtocolVersion); //TODO: remove this one v61+ protocol is common
if (_dbPath.size())
Defaults::setDBPath(_dbPath);

5
libethereum/CommonNet.h

@ -38,12 +38,12 @@ namespace eth
#if ETH_DEBUG
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
#else
static const unsigned c_maxHashes = 2048; ///< Maximum number of hashes BlockHashes will ever send.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxHashesAsk = 2048; ///< Maximum number of hashes GetBlockHashes will ever ask for.
static const unsigned c_maxBlocks = 128; ///< Maximum number of blocks Blocks will ever send.
static const unsigned c_maxBlocksAsk = 128; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain).
#endif
@ -63,6 +63,7 @@ enum
GetBlocksPacket,
BlocksPacket,
NewBlockPacket,
GetBlockHashesByNumberPacket,
PacketCount
};

50
libethereum/DownloadMan.cpp

@ -75,3 +75,53 @@ bool DownloadSub::noteBlock(h256 _hash)
m_remaining.erase(_hash);
return ret;
}
HashDownloadSub::HashDownloadSub(HashDownloadMan& _man): m_man(&_man)
{
WriteGuard l(m_man->x_subs);
m_asked = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
m_attempted = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
m_man->m_subs.insert(this);
}
HashDownloadSub::~HashDownloadSub()
{
if (m_man)
{
WriteGuard l(m_man->x_subs);
m_man->m_subs.erase(this);
}
}
void HashDownloadSub::resetFetch()
{
Guard l(m_fetch);
m_remaining = 0;
m_asked = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
m_attempted = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
}
unsigned HashDownloadSub::nextFetch(unsigned _n)
{
Guard l(m_fetch);
m_asked = RangeMask<unsigned>(m_man->m_chainStart, m_man->m_chainStart + m_man->m_chainCount);
if (!m_man || m_man->chainEmpty())
return 0;
m_asked = (~(m_man->taken() + m_attempted)).lowest(_n);
if (m_asked.empty())
m_asked = (~(m_man->taken(true) + m_attempted)).lowest(_n);
m_attempted += m_asked;
return *m_asked.begin();
}
void HashDownloadSub::noteHash(unsigned _index, unsigned _size)
{
Guard l(m_fetch);
if (m_man)
for(unsigned i = _index; i < _index + _size; ++i)
if (i >= m_man->m_got.all().first && i < m_man->m_got.all().second)
m_man->m_got += i;
}

113
libethereum/DownloadMan.h

@ -88,6 +88,13 @@ public:
i->m_man = nullptr;
}
void appendToChain(h256s const& _hashes)
{
WriteGuard l(m_lock);
m_chain.insert(m_chain.end(), _hashes.cbegin(), _hashes.cend());
m_blocksGot = RangeMask<unsigned>(0, m_chain.size());
}
void resetToChain(h256s const& _chain)
{
{
@ -158,6 +165,112 @@ private:
std::unordered_set<DownloadSub*> m_subs;
};
class HashDownloadMan;
class HashDownloadSub
{
friend class HashDownloadMan;
public:
HashDownloadSub(HashDownloadMan& _man);
~HashDownloadSub();
/// Finished last fetch - grab the next hash index to download
unsigned nextFetch(unsigned _n);
/// Note that we've received a particular hash range.
void noteHash(unsigned _index, unsigned count);
/// Nothing doing here.
void doneFetch() { resetFetch(); }
bool askedContains(unsigned _i) const { Guard l(m_fetch); return m_asked.contains(_i); }
RangeMask<unsigned> const& asked() const { return m_asked; }
RangeMask<unsigned> const& attemped() const { return m_attempted; }
private:
void resetFetch(); // Called by DownloadMan when we need to reset the download.
HashDownloadMan* m_man = nullptr;
mutable Mutex m_fetch;
unsigned m_remaining;
RangeMask<unsigned> m_asked;
RangeMask<unsigned> m_attempted;
};
class HashDownloadMan
{
friend class HashDownloadSub;
public:
~HashDownloadMan()
{
for (auto i: m_subs)
i->m_man = nullptr;
}
void resetToRange(unsigned _start, unsigned _count)
{
{
ReadGuard l(x_subs);
for (auto i: m_subs)
i->resetFetch();
}
WriteGuard l(m_lock);
m_chainStart = _start;
m_chainCount = _count;
m_got += RangeMask<unsigned>(_start, _start + _count);
{
ReadGuard l(x_subs);
for (auto i: m_subs)
i->resetFetch();
}
}
void reset(unsigned _start)
{
WriteGuard l(m_lock);
m_chainStart = _start;
m_chainCount = 0;
m_got = RangeMask<unsigned>(_start, _start);
}
RangeMask<unsigned> taken(bool _desperate = false) const
{
ReadGuard l(m_lock);
auto ret = m_got;
if (!_desperate)
{
ReadGuard l(x_subs);
for (auto i: m_subs)
ret += i->m_asked;
}
return ret;
}
bool isComplete() const
{
ReadGuard l(m_lock);
return m_got.full();
}
size_t chainSize() const { ReadGuard l(m_lock); return m_chainCount; }
size_t chainEmpty() const { ReadGuard l(m_lock); return m_chainCount == 0; }
void foreachSub(std::function<void(HashDownloadSub const&)> const& _f) const { ReadGuard l(x_subs); for(auto i: m_subs) _f(*i); }
unsigned subCount() const { ReadGuard l(x_subs); return m_subs.size(); }
RangeMask<unsigned> hashesGot() const { ReadGuard l(m_lock); return m_got; }
private:
mutable SharedMutex m_lock;
unsigned m_chainStart = 0;
unsigned m_chainCount = 0;
RangeMask<unsigned> m_got;
mutable SharedMutex x_subs;
std::unordered_set<HashDownloadSub*> m_subs;
};
}
}

593
libethereum/EthereumHost.cpp

@ -27,6 +27,7 @@
#include <libp2p/Host.h>
#include <libp2p/Session.h>
#include <libethcore/Exceptions.h>
#include <libethcore/Params.h>
#include "BlockChain.h"
#include "TransactionQueue.h"
#include "BlockQueue.h"
@ -37,6 +38,8 @@ using namespace dev;
using namespace dev::eth;
using namespace p2p;
unsigned const EthereumHost::c_oldProtocolVersion = 60; //TODO: remove this once v61+ is common
EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId):
HostCapability<EthereumPeer>(),
Worker ("ethsync"),
@ -46,12 +49,12 @@ EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQu
m_networkId (_networkId)
{
m_latestBlockSent = _ch.currentHash();
m_hashMan.reset(m_chain.number() + 1);
}
EthereumHost::~EthereumHost()
{
for (auto i: peerSessions())
i.first->cap<EthereumPeer>().get()->abortSync();
forEachPeer([](EthereumPeer* _p) { _p->abortSync(); });
}
bool EthereumHost::ensureInitialised()
@ -69,88 +72,18 @@ bool EthereumHost::ensureInitialised()
return false;
}
void EthereumHost::noteNeedsSyncing(EthereumPeer* _who)
{
// if already downloading hash-chain, ignore.
if (isSyncing())
{
clog(NetAllDetail) << "Sync in progress: Just set to help out.";
if (m_syncer->m_asking == Asking::Blocks)
_who->transition(Asking::Blocks);
}
else
// otherwise check to see if we should be downloading...
_who->attemptSync();
}
void EthereumHost::changeSyncer(EthereumPeer* _syncer, bool _needHelp)
{
if (_syncer)
clog(NetAllDetail) << "Changing syncer to" << _syncer->session()->socketId();
else
clog(NetAllDetail) << "Clearing syncer.";
m_syncer = _syncer;
if (isSyncing())
{
if (_needHelp && _syncer->m_asking == Asking::Blocks)
for (auto j: peerSessions())
{
clog(NetNote) << "Getting help with downloading blocks";
auto e = j.first->cap<EthereumPeer>().get();
if (e != _syncer && e->m_asking == Asking::Nothing)
e->transition(Asking::Blocks);
}
}
else
{
// start grabbing next hash chain if there is one.
for (auto j: peerSessions())
{
j.first->cap<EthereumPeer>()->attemptSync();
if (isSyncing())
return;
}
clog(NetNote) << "No more peers to sync with.";
}
}
void EthereumHost::noteDoneBlocks(EthereumPeer* _who, bool _clemency)
{
if (m_man.isComplete())
{
// Done our chain-get.
clog(NetNote) << "Chain download complete.";
// 1/100th for each useful block hash.
_who->addRating(m_man.chainSize() / 100);
m_man.reset();
}
else if (_who->isSyncing())
{
if (_clemency)
clog(NetNote) << "Chain download failed. Aborted while incomplete.";
else
{
// Done our chain-get.
clog(NetWarn) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished.";
clog(NetWarn) << m_man.remaining();
clog(NetWarn) << "WOULD BAN.";
// m_banned.insert(_who->session()->id()); // We know who you are!
// _who->disable("Peer sent hashes but was unable to provide the blocks.");
}
m_man.reset();
}
}
void EthereumHost::reset()
{
if (m_syncer)
m_syncer->abortSync();
forEachPeer([](EthereumPeer* _p) { _p->abortSync(); });
m_man.resetToChain(h256s());
m_hashMan.reset(m_chain.number() + 1);
m_needSyncBlocks = true;
m_needSyncHashes = true;
m_syncingLatestHash = h256();
m_syncingTotalDifficulty = 0;
m_latestBlockSent = h256();
m_transactionsSent.clear();
m_hashes.clear();
}
void EthereumHost::doWork()
@ -172,9 +105,7 @@ void EthereumHost::doWork()
}
}
for (auto p: peerSessions())
if (shared_ptr<EthereumPeer> const& ep = p.first->cap<EthereumPeer>())
ep->tick();
forEachPeer([](EthereumPeer* _p) { _p->tick(); });
// return netChange;
// TODO: Figure out what to do with netChange.
@ -194,43 +125,58 @@ void EthereumHost::maintainTransactions()
}
for (auto const& t: ts)
m_transactionsSent.insert(t.first);
for (auto p: peerSessions())
if (auto ep = p.first->cap<EthereumPeer>())
forEachPeerPtr([&](shared_ptr<EthereumPeer> _p)
{
bytes b;
unsigned n = 0;
for (auto const& h: peerTransactions[_p])
{
bytes b;
unsigned n = 0;
for (auto const& h: peerTransactions[ep])
{
ep->m_knownTransactions.insert(h);
b += ts[h].rlp();
++n;
}
_p->m_knownTransactions.insert(h);
b += ts[h].rlp();
++n;
}
ep->clearKnownTransactions();
_p->clearKnownTransactions();
if (n || ep->m_requireTransactions)
{
RLPStream ts;
ep->prep(ts, TransactionsPacket, n).appendRaw(b, n);
ep->sealAndSend(ts);
}
ep->m_requireTransactions = false;
if (n || _p->m_requireTransactions)
{
RLPStream ts;
_p->prep(ts, TransactionsPacket, n).appendRaw(b, n);
_p->sealAndSend(ts);
}
_p->m_requireTransactions = false;
});
}
void EthereumHost::forEachPeer(std::function<void(EthereumPeer*)> const& _f) const
{
forEachPeerPtr([&](std::shared_ptr<EthereumPeer> _p)
{
if (_p)
_f(_p.get());
});
}
void EthereumHost::forEachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const
{
for (auto s: peerSessions())
_f(s.first->cap<EthereumPeer>());
for (auto s: peerSessions(c_oldProtocolVersion)) //TODO: remove once v61+ is common
_f(s.first->cap<EthereumPeer>(c_oldProtocolVersion));
}
pair<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>> EthereumHost::randomSelection(unsigned _percent, std::function<bool(EthereumPeer*)> const& _allow)
{
pair<vector<shared_ptr<EthereumPeer>>, vector<shared_ptr<EthereumPeer>>> ret;
ret.second.reserve(peerSessions().size());
for (auto const& j: peerSessions())
forEachPeerPtr([&](shared_ptr<EthereumPeer> _p)
{
auto pp = j.first->cap<EthereumPeer>();
if (_allow(pp.get()))
ret.second.push_back(pp);
}
if (_p && _allow(_p.get()))
ret.second.push_back(_p);
});
ret.second.reserve((peerSessions().size() * _percent + 99) / 100);
for (unsigned i = (peerSessions().size() * _percent + 99) / 100; i-- && ret.second.size();)
size_t size = (ret.second.size() * _percent + 99) / 100;
ret.second.reserve(size);
for (unsigned i = size; i-- && ret.second.size();)
{
unsigned n = rand() % ret.second.size();
ret.first.push_back(std::move(ret.second[n]));
@ -279,3 +225,434 @@ void EthereumHost::maintainBlocks(h256 const& _currentHash)
m_latestBlockSent = _currentHash;
}
}
void EthereumHost::onPeerStatus(EthereumPeer* _peer)
{
Guard l(x_sync);
if (_peer->m_genesisHash != m_chain.genesisHash())
_peer->disable("Invalid genesis hash");
else if (_peer->m_protocolVersion != protocolVersion() && _peer->m_protocolVersion != c_oldProtocolVersion)
_peer->disable("Invalid protocol version.");
else if (_peer->m_networkId != networkId())
_peer->disable("Invalid network identifier.");
else if (_peer->session()->info().clientVersion.find("/v0.7.0/") != string::npos)
_peer->disable("Blacklisted client version.");
else if (isBanned(_peer->session()->id()))
_peer->disable("Peer banned for previous bad behaviour.");
else
{
if (_peer->m_protocolVersion != protocolVersion())
estimatePeerHashes(_peer);
else if (_peer->m_latestBlockNumber > m_chain.number())
_peer->m_expectedHashes = (unsigned)_peer->m_latestBlockNumber - m_chain.number();
if (m_hashMan.chainSize() < _peer->m_expectedHashes)
m_hashMan.resetToRange(m_chain.number() + 1, _peer->m_expectedHashes);
continueSync(_peer);
}
}
void EthereumHost::estimatePeerHashes(EthereumPeer* _peer)
{
BlockInfo block = m_chain.info();
time_t lastBlockTime = (block.hash() == m_chain.genesisHash()) ? 1428192000 : (time_t)block.timestamp;
time_t now = time(0);
unsigned blockCount = 1000;
if (lastBlockTime > now)
clog(NetWarn) << "Clock skew? Latest block is in the future";
else
blockCount += (now - lastBlockTime) / (unsigned)c_durationLimit;
clog(NetAllDetail) << "Estimated hashes: " << blockCount;
_peer->m_expectedHashes = blockCount;
}
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes)
{
Guard l(x_sync);
assert(_peer->m_asking == Asking::Nothing);
onPeerHashes(_peer, _hashes, false);
}
void EthereumHost::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete)
{
if (_hashes.empty())
{
onPeerDoneHashes(_peer, true);
return;
}
unsigned knowns = 0;
unsigned unknowns = 0;
h256s neededBlocks;
for (unsigned i = 0; i < _hashes.size(); ++i)
{
_peer->addRating(1);
auto h = _hashes[i];
auto status = m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || m_chain.isKnown(h))
{
clog(NetMessageSummary) << "block hash ready:" << h << ". Start blocks download...";
m_hashes += neededBlocks;
onPeerDoneHashes(_peer, true);
return;
}
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
_peer->setIdle();
return;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
neededBlocks.push_back(h);
}
else
knowns++;
m_syncingLatestHash = h;
}
m_hashes += neededBlocks;
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << m_syncingLatestHash;
if (_complete)
{
m_needSyncBlocks = true;
continueSync(_peer);
}
else if (m_hashes.size() > _peer->m_expectedHashes)
{
_peer->disable("Too many hashes");
m_hashes.clear();
m_syncingLatestHash = h256();
continueSync(); ///Try with some other peer, keep the chain
}
else
continueSync(_peer); /// Grab next hashes
}
void EthereumHost::onPeerHashes(EthereumPeer* _peer, unsigned /*_index*/, h256s const& _hashes)
{
Guard l(x_sync);
assert(_peer->m_asking == Asking::Nothing);
if (_hashes.empty())
{
onPeerDoneHashes(_peer, true);
return;
}
unsigned knowns = 0;
unsigned unknowns = 0;
h256s neededBlocks;
for (unsigned i = 0; i < _hashes.size(); ++i)
{
_peer->addRating(1);
auto h = _hashes[i];
auto status = m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || m_chain.isKnown(h))
{
clog(NetWarn) << "block hash already known:" << h;
}
else if (status == QueueStatus::Bad)
{
clog(NetWarn) << "block hash bad!" << h << ". Bailing...";
_peer->setIdle();
return;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
neededBlocks.push_back(h);
}
else
knowns++;
}
m_man.appendToChain(neededBlocks);
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns";
if (m_hashMan.isComplete())
{
// Done our chain-get.
m_needSyncHashes = false;
clog(NetNote) << "Hashes download complete.";
// 1/100th for each useful block hash.
_peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
m_hashMan.reset(m_chain.number() + 1);
continueSync();
}
else
continueSync(_peer);
}
void EthereumHost::onPeerDoneHashes(EthereumPeer* _peer, bool _localChain)
{
assert(_peer->m_asking == Asking::Nothing);
m_needSyncHashes = false;
if (_peer->m_protocolVersion != protocolVersion() || _localChain)
{
m_man.resetToChain(m_hashes);
m_hashes.clear();
}
continueSync();
}
void EthereumHost::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
{
Guard l(x_sync);
assert(_peer->m_asking == Asking::Nothing);
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
if (itemCount == 0)
{
// Got to this peer's latest block - just give up.
clog(NetNote) << "Finishing blocks fetch...";
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
_peer->setIdle();
return;
}
unsigned success = 0;
unsigned future = 0;
unsigned unknown = 0;
unsigned got = 0;
unsigned repeated = 0;
for (unsigned i = 0; i < itemCount; ++i)
{
auto h = BlockInfo::headerHash(_r[i].data());
if (_peer->m_sub.noteBlock(h))
{
_peer->addRating(10);
switch (m_bq.import(_r[i].data(), m_chain))
{
case ImportResult::Success:
success++;
break;
case ImportResult::Malformed:
case ImportResult::BadChain:
_peer->disable("Malformed block received.");
return;
case ImportResult::FutureTime:
future++;
break;
case ImportResult::AlreadyInChain:
case ImportResult::AlreadyKnown:
got++;
break;
case ImportResult::UnknownParent:
unknown++;
break;
default:;
}
}
else
{
_peer->addRating(0); // -1?
repeated++;
}
}
clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received.";
if (m_man.isComplete() && !m_needSyncHashes)
{
// Done our chain-get.
m_needSyncBlocks = false;
clog(NetNote) << "Chain download complete.";
// 1/100th for each useful block hash.
_peer->addRating(m_man.chainSize() / 100); //TODO: what about other peers?
m_man.reset();
}
continueSync(_peer);
}
void EthereumHost::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
{
Guard l(x_sync);
if (_peer->m_asking != Asking::Nothing)
{
clog(NetMessageSummary) << "Ignoring new hashes since we're already downloading.";
return;
}
clog(NetNote) << "New block hash discovered: syncing without help.";
onPeerHashes(_peer, _hashes, true);
}
void EthereumHost::onPeerNewBlock(EthereumPeer* _peer, RLP const& _r)
{
Guard l(x_sync);
if (_peer->m_asking != Asking::Nothing)
{
clog(NetMessageSummary) << "Ignoring new blocks since we're already downloading.";
return;
}
auto h = BlockInfo::headerHash(_r[0].data());
clog(NetMessageSummary) << "NewBlock: " << h;
if (_r.itemCount() != 2)
_peer->disable("NewBlock without 2 data fields.");
else
{
bool sync = false;
switch (m_bq.import(_r[0].data(), m_chain))
{
case ImportResult::Success:
_peer->addRating(100);
break;
case ImportResult::FutureTime:
//TODO: Rating dependent on how far in future it is.
break;
case ImportResult::Malformed:
case ImportResult::BadChain:
_peer->disable("Malformed block received.");
return;
case ImportResult::AlreadyInChain:
case ImportResult::AlreadyKnown:
break;
case ImportResult::UnknownParent:
if (h)
{
u256 difficulty = _r[1].toInt<u256>();
if (m_syncingTotalDifficulty < difficulty)
{
clog(NetMessageSummary) << "Received block with no known parent. Resyncing...";
_peer->m_latestHash = h;
_peer->m_totalDifficulty = difficulty;
m_needSyncHashes = true;
m_needSyncBlocks = true;
m_syncingLatestHash = _peer->m_latestHash;
sync = true;
}
}
break;
default:;
}
DEV_GUARDED(_peer->x_knownBlocks)
_peer->m_knownBlocks.insert(h);
if (sync)
continueSync(_peer);
}
}
void EthereumHost::onPeerTransactions(EthereumPeer* _peer, RLP const& _r)
{
unsigned itemCount = _r.itemCount();
clog(NetAllDetail) << "Transactions (" << dec << itemCount << "entries)";
Guard l(_peer->x_knownTransactions);
for (unsigned i = 0; i < itemCount; ++i)
{
auto h = sha3(_r[i].data());
_peer->m_knownTransactions.insert(h);
ImportResult ir = m_tq.import(_r[i].data());
switch (ir)
{
case ImportResult::Malformed:
_peer->addRating(-100);
break;
case ImportResult::AlreadyKnown:
// if we already had the transaction, then don't bother sending it on.
m_transactionsSent.insert(h);
_peer->addRating(0);
break;
case ImportResult::Success:
_peer->addRating(100);
break;
default:;
}
}
}
void EthereumHost::continueSync()
{
clog(NetAllDetail) << "Getting help with downloading hashes and blocks";
forEachPeer([&](EthereumPeer* _p)
{
if (_p->m_asking == Asking::Nothing)
continueSync(_p);
});
}
void EthereumHost::continueSync(EthereumPeer* _peer)
{
assert(_peer->m_asking == Asking::Nothing);
bool otherPeerSync = false;
if (m_needSyncHashes && peerShouldGrabChain(_peer))
{
forEachPeer([&](EthereumPeer* _p)
{
if (_p != _peer && _p->m_asking == Asking::Hashes && _p->m_protocolVersion != protocolVersion())
otherPeerSync = true; // Already have a peer downloading hash chain with old protocol, do nothing
});
if (otherPeerSync)
{
/// Downloading from other peer with v60 protocol, nothing ese we can do
_peer->setIdle();
return;
}
if (_peer->m_protocolVersion == protocolVersion() && !m_syncingLatestHash)
_peer->requestHashes(); /// v61+ and not catching up to a particular hash
else
{
// Restart/continue sync in single peer mode
if (!m_syncingLatestHash)
{
m_syncingLatestHash =_peer->m_latestHash;
m_syncingTotalDifficulty = _peer->m_totalDifficulty;
}
_peer->requestHashes(m_syncingLatestHash);
}
}
else if (m_needSyncBlocks && peerShouldGrabBlocks(_peer)) // Check if this peer can help with downloading blocks
_peer->requestBlocks();
else
_peer->setIdle();
}
bool EthereumHost::peerShouldGrabBlocks(EthereumPeer* _peer) const
{
auto td = _peer->m_totalDifficulty;
auto lh = m_syncingLatestHash;
auto ctd = m_chain.details().totalDifficulty;
clog(NetAllDetail) << "Should grab blocks? " << td << "vs" << ctd;
if (td < ctd || (td == ctd && m_chain.currentHash() == lh))
return false;
return true;
}
bool EthereumHost::peerShouldGrabChain(EthereumPeer* _peer) const
{
h256 c = m_chain.currentHash();
unsigned n = m_chain.number();
u256 td = m_chain.details().totalDifficulty;
clog(NetAllDetail) << "Attempt chain-grab? Latest:" << c << ", number:" << n << ", TD:" << td << " versus " << _peer->m_totalDifficulty;
if (td >= _peer->m_totalDifficulty)
{
clog(NetAllDetail) << "No. Our chain is better.";
return false;
}
else
{
clog(NetAllDetail) << "Yes. Their chain is better.";
return true;
}
}
bool EthereumHost::isSyncing() const
{
Guard l(x_sync);
bool syncing = false;
forEachPeer([&](EthereumPeer* _p)
{
if (_p->m_asking != Asking::Nothing)
syncing = true;
});
return syncing;
}

43
libethereum/EthereumHost.h

@ -56,8 +56,6 @@ class BlockQueue;
*/
class EthereumHost: public p2p::HostCapability<EthereumPeer>, Worker
{
friend class EthereumPeer;
public:
/// Start server, but don't listen.
EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId);
@ -72,21 +70,30 @@ public:
void reset();
DownloadMan const& downloadMan() const { return m_man; }
bool isSyncing() const { return !!m_syncer; }
bool isSyncing() const;
bool isBanned(p2p::NodeId _id) const { return !!m_banned.count(_id); }
void noteNewTransactions() { m_newTransactions = true; }
void noteNewBlocks() { m_newBlocks = true; }
private:
std::pair<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; });
void onPeerStatus(EthereumPeer* _peer); ///< Called by peer to report status
void onPeerBlocks(EthereumPeer* _peer, RLP const& _r); ///< Called by peer once it has new blocks during syn
void onPeerNewBlock(EthereumPeer* _peer, RLP const& _r); ///< Called by peer once it has new blocks
void onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes); ///< Called by peer once it has new hashes
void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes); ///< Called by peer once it has another sequential block of hashes during sync
void onPeerHashes(EthereumPeer* _peer, unsigned _index, h256s const& _hashes); ///< Called by peer once it has a new ordered block of hashes starting with a particular number
void onPeerTransactions(EthereumPeer* _peer, RLP const& _r); ///< Called by peer when it has new transactions
/// Session is tell us that we may need (re-)syncing with the peer.
void noteNeedsSyncing(EthereumPeer* _who);
DownloadMan& downloadMan() { return m_man; }
HashDownloadMan& hashDownloadMan() { return m_hashMan; }
BlockChain const& chain() { return m_chain; }
/// Called when the peer can no longer provide us with any needed blocks.
void noteDoneBlocks(EthereumPeer* _who, bool _clemency);
static unsigned const c_oldProtocolVersion;
private:
std::pair<std::vector<std::shared_ptr<EthereumPeer>>, std::vector<std::shared_ptr<EthereumPeer>>> randomSelection(unsigned _percent = 25, std::function<bool(EthereumPeer*)> const& _allow = [](EthereumPeer const*){ return true; });
void forEachPeerPtr(std::function<void(std::shared_ptr<EthereumPeer>)> const& _f) const;
void forEachPeer(std::function<void(EthereumPeer*)> const& _f) const;
/// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network.
void doWork();
@ -108,7 +115,13 @@ private:
virtual void onStarting() { startWorking(); }
virtual void onStopping() { stopWorking(); }
void changeSyncer(EthereumPeer* _ignore, bool _needHelp = true);
void continueSync(); /// Find something to do for all peers
void continueSync(EthereumPeer* _peer); /// Find some work to do for a peer
void onPeerDoneHashes(EthereumPeer* _peer, bool _new); /// Called when done downloading hashes from peer
void onPeerHashes(EthereumPeer* _peer, h256s const& _hashes, bool _complete);
bool peerShouldGrabBlocks(EthereumPeer* _peer) const;
bool peerShouldGrabChain(EthereumPeer* _peer) const;
void estimatePeerHashes(EthereumPeer* _peer);
BlockChain const& m_chain;
TransactionQueue& m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
@ -116,9 +129,8 @@ private:
u256 m_networkId;
EthereumPeer* m_syncer = nullptr; // TODO: switch to weak_ptr
DownloadMan m_man;
HashDownloadMan m_hashMan;
h256 m_latestBlockSent;
h256Hash m_transactionsSent;
@ -127,6 +139,13 @@ private:
bool m_newTransactions = false;
bool m_newBlocks = false;
mutable Mutex x_sync;
bool m_needSyncHashes = true; ///< Indicates if need to downlad hashes
bool m_needSyncBlocks = true; ///< Indicates if we still need to download some blocks
h256 m_syncingLatestHash; ///< Latest block's hash, as of the current sync.
u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty, as of the current sync.
h256s m_hashes; ///< List of hashes with unknown block numbers. Used for v60 chain downloading and catching up to a particular unknown
};
}

542
libethereum/EthereumPeer.cpp

@ -34,11 +34,14 @@ using namespace dev;
using namespace dev::eth;
using namespace p2p;
EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i):
EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, CapDesc const& _cap):
Capability(_s, _h, _i),
m_sub(host()->m_man)
m_sub(host()->downloadMan()),
m_hashSub(host()->hashDownloadMan()),
m_peerCapabilityVersion(_cap.second)
{
transition(Asking::State);
m_syncHashNumber = host()->chain().number() + 1;
requestStatus();
}
EthereumPeer::~EthereumPeer()
@ -50,7 +53,7 @@ EthereumPeer::~EthereumPeer()
void EthereumPeer::abortSync()
{
if (isSyncing())
transition(Asking::Nothing, true);
setIdle();
}
EthereumHost* EthereumPeer::host() const
@ -74,161 +77,76 @@ string toString(Asking _a)
return "?";
}
void EthereumPeer::transition(Asking _a, bool _force, bool _needHelp)
{
clog(NetMessageSummary) << "Transition!" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
if (m_asking == Asking::State && _a != Asking::State)
m_requireTransactions = true;
void EthereumPeer::setIdle()
{
m_sub.doneFetch();
m_hashSub.doneFetch();
setAsking(Asking::Nothing);
}
void EthereumPeer::requestStatus()
{
if (m_asking != Asking::Nothing)
clog(NetWarn) << "Bad state: requesting state should be the first action";
setAsking(Asking::State);
RLPStream s;
bool latest = m_peerCapabilityVersion == host()->protocolVersion();
prep(s, StatusPacket, latest ? 6 : 5)
<< (latest ? host()->protocolVersion() : EthereumHost::c_oldProtocolVersion)
<< host()->networkId()
<< host()->chain().details().totalDifficulty
<< host()->chain().currentHash()
<< host()->chain().genesisHash();
if (latest)
s << u256(host()->chain().number());
sealAndSend(s);
}
if (_a == Asking::State)
{
if (m_asking == Asking::Nothing)
{
setAsking(Asking::State, false);
prep(s, StatusPacket, 5)
<< host()->protocolVersion()
<< host()->networkId()
<< host()->m_chain.details().totalDifficulty
<< host()->m_chain.currentHash()
<< host()->m_chain.genesisHash();
sealAndSend(s);
return;
}
}
else if (_a == Asking::Hashes)
{
if (m_asking == Asking::State || m_asking == Asking::Nothing)
{
if (isSyncing())
clog(NetWarn) << "Bad state: not asking for Hashes, yet syncing!";
m_syncingLatestHash = m_latestHash;
m_syncingTotalDifficulty = m_totalDifficulty;
resetNeedsSyncing();
setAsking(_a, true);
prep(s, GetBlockHashesPacket, 2) << m_syncingLatestHash << c_maxHashesAsk;
m_syncingNeededBlocks = h256s(1, m_syncingLatestHash);
sealAndSend(s);
return;
}
else if (m_asking == Asking::Hashes)
{
if (!isSyncing())
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
setAsking(_a, true);
prep(s, GetBlockHashesPacket, 2) << m_syncingLastReceivedHash << c_maxHashesAsk;
sealAndSend(s);
return;
}
}
else if (_a == Asking::Blocks)
{
if (m_asking == Asking::Hashes)
{
if (!isSyncing())
clog(NetWarn) << "Bad state: asking for Hashes yet not syncing!";
if (shouldGrabBlocks())
{
clog(NetNote) << "Difficulty of hashchain HIGHER. Grabbing" << m_syncingNeededBlocks.size() << "blocks [latest now" << m_syncingLatestHash << ", was" << host()->m_latestBlockSent << "]";
host()->m_man.resetToChain(m_syncingNeededBlocks);
// host()->m_latestBlockSent = m_syncingLatestHash;
}
else
{
clog(NetNote) << "Difficulty of hashchain not HIGHER. Ignoring.";
m_syncingLatestHash = h256();
setAsking(Asking::Nothing, false);
return;
}
}
// run through into...
if (m_asking == Asking::Nothing || m_asking == Asking::Hashes || m_asking == Asking::Blocks)
{
// Looks like it's the best yet for total difficulty. Set to download.
setAsking(Asking::Blocks, isSyncing(), _needHelp); // will kick off other peers to help if available.
auto blocks = m_sub.nextFetch(c_maxBlocksAsk);
if (blocks.size())
{
prep(s, GetBlocksPacket, blocks.size());
for (auto const& i: blocks)
s << i;
sealAndSend(s);
}
else
transition(Asking::Nothing);
return;
}
}
else if (_a == Asking::Nothing)
{
if (m_asking == Asking::Blocks)
{
clog(NetNote) << "Finishing blocks fetch...";
// a bit overkill given that the other nodes may yet have the needed blocks, but better to be safe than sorry.
if (isSyncing())
host()->noteDoneBlocks(this, _force);
// NOTE: need to notify of giving up on chain-hashes, too, altering state as necessary.
m_sub.doneFetch();
setAsking(Asking::Nothing, false);
}
else if (m_asking == Asking::Hashes)
{
clog(NetNote) << "Finishing hashes fetch...";
setAsking(Asking::Nothing, false);
}
else if (m_asking == Asking::State)
{
setAsking(Asking::Nothing, false);
// Just got the state - should check to see if we can be of help downloading the chain if any.
// Otherwise, should put ourselves up for sync.
setNeedsSyncing(m_latestHash, m_totalDifficulty);
}
// Otherwise it's fine. We don't care if it's Nothing->Nothing.
void EthereumPeer::requestHashes()
{
if (m_asking == Asking::Blocks)
return;
}
clog(NetWarn) << "Invalid state transition:" << ::toString(_a) << "from" << ::toString(m_asking) << ", " << (isSyncing() ? "syncing" : "holding") << (needsSyncing() ? "& needed" : "");
m_syncHashNumber = m_hashSub.nextFetch(c_maxHashesAsk);
setAsking(Asking::Hashes);
RLPStream s;
prep(s, GetBlockHashesByNumberPacket, 2) << m_syncHashNumber << c_maxHashesAsk;
sealAndSend(s);
}
void EthereumPeer::setAsking(Asking _a, bool _isSyncing, bool _needHelp)
void EthereumPeer::requestHashes(h256 const& _lastHash)
{
bool changedAsking = (m_asking != _a);
m_asking = _a;
if (_isSyncing != (host()->m_syncer == this) || (_isSyncing && changedAsking))
host()->changeSyncer(_isSyncing ? this : nullptr, _needHelp);
if (m_asking == Asking::Blocks)
return;
setAsking(Asking::Hashes);
RLPStream s;
prep(s, GetBlockHashesPacket, 2) << _lastHash << c_maxHashesAsk;
sealAndSend(s);
}
if (!_isSyncing)
void EthereumPeer::requestBlocks()
{
setAsking(Asking::Blocks);
auto blocks = m_sub.nextFetch(c_maxBlocksAsk);
if (blocks.size())
{
m_syncingLatestHash = h256();
m_syncingTotalDifficulty = 0;
m_syncingNeededBlocks.clear();
RLPStream s;
prep(s, GetBlocksPacket, blocks.size());
for (auto const& i: blocks)
s << i;
sealAndSend(s);
}
m_lastAsk = chrono::system_clock::now();
session()->addNote("ask", _a == Asking::Nothing ? "nothing" : _a == Asking::State ? "state" : _a == Asking::Hashes ? "hashes" : _a == Asking::Blocks ? "blocks" : "?");
session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? " & needed" : ""));
else
setIdle();
return;
}
void EthereumPeer::setNeedsSyncing(h256 _latestHash, u256 _td)
void EthereumPeer::setAsking(Asking _a)
{
m_latestHash = _latestHash;
m_totalDifficulty = _td;
if (m_latestHash)
host()->noteNeedsSyncing(this);
m_asking = _a;
m_lastAsk = chrono::system_clock::now();
session()->addNote("ask", _a == Asking::Nothing ? "nothing" : _a == Asking::State ? "state" : _a == Asking::Hashes ? "hashes" : _a == Asking::Blocks ? "blocks" : "?");
session()->addNote("sync", string(isSyncing() ? "ongoing" : "holding") + (needsSyncing() ? " & needed" : ""));
}
@ -241,57 +159,7 @@ void EthereumPeer::tick()
bool EthereumPeer::isSyncing() const
{
return host()->m_syncer == this;
}
bool EthereumPeer::shouldGrabBlocks() const
{
auto td = m_syncingTotalDifficulty;
auto lh = m_syncingLatestHash;
auto ctd = host()->m_chain.details().totalDifficulty;
if (m_syncingNeededBlocks.empty())
return false;
clog(NetNote) << "Should grab blocks? " << td << "vs" << ctd << ";" << m_syncingNeededBlocks.size() << " blocks, ends" << m_syncingNeededBlocks.back();
if (td < ctd || (td == ctd && host()->m_chain.currentHash() == lh))
return false;
return true;
}
void EthereumPeer::attemptSync()
{
if (m_asking != Asking::Nothing)
{
clog(NetAllDetail) << "Can't synced with this peer - outstanding asks.";
return;
}
// if already done this, then ignore.
if (!needsSyncing())
{
clog(NetAllDetail) << "Already synced with this peer.";
return;
}
h256 c = host()->m_chain.currentHash();
unsigned n = host()->m_chain.number();
u256 td = host()->m_chain.details().totalDifficulty;
clog(NetAllDetail) << "Attempt chain-grab? Latest:" << c << ", number:" << n << ", TD:" << td << " versus " << m_totalDifficulty;
if (td >= m_totalDifficulty)
{
clog(NetAllDetail) << "No. Our chain is better.";
resetNeedsSyncing();
transition(Asking::Nothing);
}
else
{
clog(NetAllDetail) << "Yes. Their chain is better.";
transition(Asking::Hashes);
}
return m_asking != Asking::Nothing;
}
bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
@ -304,54 +172,23 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
{
m_protocolVersion = _r[0].toInt<unsigned>();
m_networkId = _r[1].toInt<u256>();
// a bit dirty as we're misusing these to communicate the values to transition, but harmless.
m_totalDifficulty = _r[2].toInt<u256>();
m_latestHash = _r[3].toHash<h256>();
auto genesisHash = _r[4].toHash<h256>();
clog(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash << ", TD:" << m_totalDifficulty << "=" << m_latestHash;
m_genesisHash = _r[4].toHash<h256>();
if (m_peerCapabilityVersion == host()->protocolVersion())
{
m_protocolVersion = host()->protocolVersion();
m_latestBlockNumber = _r[5].toInt<u256>();
}
if (genesisHash != host()->m_chain.genesisHash())
disable("Invalid genesis hash");
else if (m_protocolVersion != host()->protocolVersion())
disable("Invalid protocol version.");
else if (m_networkId != host()->networkId())
disable("Invalid network identifier.");
else if (session()->info().clientVersion.find("/v0.7.0/") != string::npos)
disable("Blacklisted client version.");
else if (host()->isBanned(session()->id()))
disable("Peer banned for previous bad behaviour.");
else
transition(Asking::Nothing);
clog(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << m_genesisHash << "/" << m_latestBlockNumber << ", TD:" << m_totalDifficulty << "=" << m_latestHash;
setAsking(Asking::Nothing);
host()->onPeerStatus(this);
break;
}
case TransactionsPacket:
{
unsigned itemCount = _r.itemCount();
clog(NetAllDetail) << "Transactions (" << dec << itemCount << "entries)";
Guard l(x_knownTransactions);
for (unsigned i = 0; i < itemCount; ++i)
{
auto h = sha3(_r[i].data());
m_knownTransactions.insert(h);
ImportResult ir = host()->m_tq.import(_r[i].data());
switch (ir)
{
case ImportResult::Malformed:
addRating(-100);
break;
case ImportResult::AlreadyKnown:
// if we already had the transaction, then don't bother sending it on.
host()->m_transactionsSent.insert(h);
addRating(0);
break;
case ImportResult::Success:
addRating(100);
break;
default:;
}
}
host()->onPeerTransactions(this, _r);
break;
}
case GetBlockHashesPacket:
@ -359,18 +196,39 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
h256 later = _r[0].toHash<h256>();
unsigned limit = _r[1].toInt<unsigned>();
clog(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later << ")";
unsigned c = min<unsigned>(host()->m_chain.number(later), limit);
unsigned c = min<unsigned>(host()->chain().number(later), limit);
RLPStream s;
prep(s, BlockHashesPacket, c);
h256 p = host()->m_chain.details(later).parent;
for (unsigned i = 0; i < c && p; ++i, p = host()->m_chain.details(p).parent)
h256 p = host()->chain().details(later).parent;
for (unsigned i = 0; i < c && p; ++i, p = host()->chain().details(p).parent)
s << p;
sealAndSend(s);
addRating(0);
break;
}
case GetBlockHashesByNumberPacket:
{
u256 number256 = _r[0].toInt<u256>();
unsigned number = (unsigned) number256;
unsigned limit = _r[1].toInt<unsigned>();
clog(NetMessageSummary) << "GetBlockHashesByNumber (" << number << "-" << number + limit << ")";
RLPStream s;
if (number <= host()->chain().number())
{
unsigned c = min<unsigned>(host()->chain().number() - number + 1, limit);
prep(s, BlockHashesPacket, c);
for (unsigned n = number; n < number + c; n++)
{
h256 p = host()->chain().numberHash(n);
s << p;
}
}
else
prep(s, BlockHashesPacket, 0);
sealAndSend(s);
addRating(0);
break;
}
case BlockHashesPacket:
{
unsigned itemCount = _r.itemCount();
@ -378,45 +236,22 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
if (m_asking != Asking::Hashes)
{
cwarn << "Peer giving us hashes when we didn't ask for them.";
clog(NetWarn) << "Peer giving us hashes when we didn't ask for them.";
break;
}
if (itemCount == 0)
{
transition(Asking::Blocks);
return true;
}
unsigned knowns = 0;
unsigned unknowns = 0;
setAsking(Asking::Nothing);
h256s hashes(itemCount);
for (unsigned i = 0; i < itemCount; ++i)
{
addRating(1);
auto h = _r[i].toHash<h256>();
auto status = host()->m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host()->m_chain.isKnown(h))
{
clog(NetMessageSummary) << "block hash ready:" << h << ". Start blocks download...";
transition(Asking::Blocks);
return true;
}
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
transition(Asking::Nothing);
return true;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
m_syncingNeededBlocks.push_back(h);
}
else
knowns++;
m_syncingLastReceivedHash = h;
hashes[i] = _r[i].toHash<h256>();
m_hashSub.noteHash(m_syncHashNumber + i, 1);
}
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << m_syncingLastReceivedHash;
// run through - ask for more.
transition(Asking::Hashes);
if (m_protocolVersion == host()->protocolVersion())
host()->onPeerHashes(this, m_syncHashNumber, hashes); // V61+, report hashes by number
else
host()->onPeerHashes(this, hashes);
m_syncHashNumber += itemCount;
break;
}
case GetBlocksPacket:
@ -436,9 +271,9 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
for (unsigned i = 0; i < min(count, c_maxBlocks); ++i)
{
auto h = _r[i].toHash<h256>();
if (host()->m_chain.isKnown(h))
if (host()->chain().isKnown(h))
{
rlp += host()->m_chain.block(_r[i].toHash<h256>());
rlp += host()->chain().block(_r[i].toHash<h256>());
++n;
}
}
@ -455,157 +290,30 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r)
}
case BlocksPacket:
{
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "Blocks (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreBlocks");
if (m_asking != Asking::Blocks)
clog(NetWarn) << "Unexpected Blocks received!";
if (itemCount == 0)
{
// Got to this peer's latest block - just give up.
transition(Asking::Nothing);
break;
}
unsigned success = 0;
unsigned future = 0;
unsigned unknown = 0;
unsigned got = 0;
unsigned repeated = 0;
for (unsigned i = 0; i < itemCount; ++i)
{
auto h = BlockInfo::headerHash(_r[i].data());
if (m_sub.noteBlock(h))
{
addRating(10);
switch (host()->m_bq.import(_r[i].data(), host()->m_chain))
{
case ImportResult::Success:
success++;
break;
case ImportResult::Malformed:
case ImportResult::BadChain:
disable("Malformed block received.");
return true;
case ImportResult::FutureTime:
future++;
break;
case ImportResult::AlreadyInChain:
case ImportResult::AlreadyKnown:
got++;
break;
case ImportResult::UnknownParent:
unknown++;
break;
default:;
}
}
else
{
addRating(0); // -1?
repeated++;
}
}
clog(NetMessageSummary) << dec << success << "imported OK," << unknown << "with unknown parents," << future << "with future timestamps," << got << " already known," << repeated << " repeats received.";
if (m_asking == Asking::Blocks)
clog(NetWarn) << "Peer giving us blocks when we didn't ask for them.";
else
{
if (!got)
transition(Asking::Blocks);
else
transition(Asking::Nothing);
setAsking(Asking::Nothing);
host()->onPeerBlocks(this, _r);
}
break;
}
case NewBlockPacket:
{
auto h = BlockInfo::headerHash(_r[0].data());
clog(NetMessageSummary) << "NewBlock: " << h;
if (_r.itemCount() != 2)
disable("NewBlock without 2 data fields.");
else
{
switch (host()->m_bq.import(_r[0].data(), host()->m_chain))
{
case ImportResult::Success:
addRating(100);
break;
case ImportResult::FutureTime:
//TODO: Rating dependent on how far in future it is.
break;
case ImportResult::Malformed:
case ImportResult::BadChain:
disable("Malformed block received.");
return true;
case ImportResult::AlreadyInChain:
case ImportResult::AlreadyKnown:
break;
case ImportResult::UnknownParent:
clog(NetMessageSummary) << "Received block with no known parent. Resyncing...";
setNeedsSyncing(h, _r[1].toInt<u256>());
break;
default:;
}
DEV_GUARDED(x_knownBlocks)
m_knownBlocks.insert(h);
}
host()->onPeerNewBlock(this, _r);
break;
}
case NewBlockHashesPacket:
{
clog(NetMessageSummary) << "NewBlockHashes";
if (host()->isSyncing())
clog(NetMessageSummary) << "Ignoring since we're already downloading.";
else
{
unsigned knowns = 0;
unsigned unknowns = 0;
unsigned itemCount = _r.itemCount();
for (unsigned i = 0; i < itemCount; ++i)
{
addRating(1);
auto h = _r[i].toHash<h256>();
DEV_GUARDED(x_knownBlocks)
m_knownBlocks.insert(h);
auto status = host()->m_bq.blockStatus(h);
if (status == QueueStatus::Importing || status == QueueStatus::Ready || host()->m_chain.isKnown(h))
knowns++;
else if (status == QueueStatus::Bad)
{
cwarn << "block hash bad!" << h << ". Bailing...";
return true;
}
else if (status == QueueStatus::Unknown)
{
unknowns++;
m_syncingNeededBlocks.push_back(h);
}
else
knowns++;
}
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns";
if (unknowns > 0)
{
clog(NetNote) << "Not syncing and new block hash discovered: syncing without help.";
host()->m_man.resetToChain(m_syncingNeededBlocks);
host()->changeSyncer(this, false);
transition(Asking::Blocks, false, false); // TODO: transaction(Asking::NewBlocks, false)
}
return true;
}
unsigned itemCount = _r.itemCount();
clog(NetMessageSummary) << "BlockHashes (" << dec << itemCount << "entries)" << (itemCount ? "" : ": NoMoreHashes");
h256s hashes(itemCount);
for (unsigned i = 0; i < itemCount; ++i)
hashes[i] = _r[i].toHash<h256>();
host()->onPeerNewHashes(this, hashes);
break;
}
default:

50
libethereum/EthereumPeer.h

@ -49,11 +49,11 @@ namespace eth
*/
class EthereumPeer: public p2p::Capability
{
friend class EthereumHost;
friend class EthereumHost; //TODO: remove this
public:
/// Basic constructor.
EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h, unsigned _i);
EthereumPeer(p2p::Session* _s, p2p::HostCapabilityFace* _h, unsigned _i, p2p::CapDesc const& _cap);
/// Basic destructor.
virtual ~EthereumPeer();
@ -70,17 +70,26 @@ public:
/// What is the ethereum subprotocol host object.
EthereumHost* host() const;
/// Abort sync and reset fetch
void setIdle();
/// Request hashes. Uses hash download manager to get hash number. v61+ protocol version only
void requestHashes();
/// Request hashes for given parent hash.
void requestHashes(h256 const& _lastHash);
/// Request blocks. Uses block download manager.
void requestBlocks();
private:
using p2p::Capability::sealAndSend;
/// Interpret an incoming message.
virtual bool interpret(unsigned _id, RLP const& _r);
/// Transition state in a particular direction.
void transition(Asking _wantState, bool _force = false, bool _needHelp = true);
/// Attempt to begin syncing with this peer; first check the peer has a more difficlult chain to download, then start asking for hashes, then move to blocks.
void attemptSync();
/// Request status. Called from constructor
void requestStatus();
/// Abort the sync operation.
void abortSync();
@ -89,11 +98,7 @@ private:
void clearKnownTransactions() { std::lock_guard<std::mutex> l(x_knownTransactions); m_knownTransactions.clear(); }
/// Update our asking state.
void setAsking(Asking _g, bool _isSyncing, bool _needHelp = true);
/// Update our syncing requirements state.
void setNeedsSyncing(h256 _latestHash, u256 _td);
void resetNeedsSyncing() { setNeedsSyncing(h256(), 0); }
void setAsking(Asking _g);
/// Do we presently need syncing with this peer?
bool needsSyncing() const { return !!m_latestHash; }
@ -101,14 +106,12 @@ private:
/// Are we presently syncing with this peer?
bool isSyncing() const;
/// Check whether the session should bother grabbing the peer's blocks.
bool shouldGrabBlocks() const;
/// Runs period checks to check up on the peer.
void tick();
/// Peer's protocol version.
unsigned m_protocolVersion;
/// Peer's network id.
u256 m_networkId;
@ -117,24 +120,24 @@ private:
/// When we asked for it. Allows a time out.
std::chrono::system_clock::time_point m_lastAsk;
/// Whether this peer is in the process of syncing or not. Only one peer can be syncing at once.
bool m_isSyncing = false;
/// These are determined through either a Status message or from NewBlock.
h256 m_latestHash; ///< Peer's latest block's hash that we know about or default null value if no need to sync.
u256 m_totalDifficulty; ///< Peer's latest block's total difficulty.
/// Once a sync is started on this peer, they are cleared and moved into m_syncing*.
h256 m_genesisHash; ///< Peer's genesis hash
u256 m_latestBlockNumber; ///< Number of the latest block this peer has
/// This is built as we ask for hashes. Once no more hashes are given, we present this to the
/// host who initialises the DownloadMan and m_sub becomes active for us to begin asking for blocks.
h256s m_syncingNeededBlocks; ///< The blocks that we should download from this peer.
h256 m_syncingLastReceivedHash; ///< Hash most recently received from peer.
h256 m_syncingLatestHash; ///< Peer's latest block's hash, as of the current sync.
u256 m_syncingTotalDifficulty; ///< Peer's latest block's total difficulty, as of the current sync.
unsigned m_expectedHashes = 0; ///< Estimated upper bound of hashes to expect from this peer.
unsigned m_syncHashNumber = 0; ///< Number of latest hash we sync to
/// Once we're asking for blocks, this becomes in use.
DownloadSub m_sub;
/// Once we're asking for hashes, this becomes in use.
HashDownloadSub m_hashSub;
u256 m_peerCapabilityVersion; ///< Protocol version this peer supports received as capability
/// Have we received a GetTransactions packet that we haven't yet answered?
bool m_requireTransactions = false;
@ -142,7 +145,6 @@ private:
h256Hash m_knownBlocks; ///< Blocks that the peer already knows about (that don't need to be sent to them).
Mutex x_knownTransactions;
h256Hash m_knownTransactions; ///< Transactions that the peer already knows of.
};
}

27
libevmasm/ExpressionClasses.cpp

@ -57,11 +57,11 @@ ExpressionClasses::Id ExpressionClasses::find(
exp.arguments = _arguments;
exp.sequenceNumber = _sequenceNumber;
if (SemanticInformation::isCommutativeOperation(_item))
sort(exp.arguments.begin(), exp.arguments.end());
if (SemanticInformation::isDeterministic(_item))
{
if (SemanticInformation::isCommutativeOperation(_item))
sort(exp.arguments.begin(), exp.arguments.end());
auto it = m_expressions.find(exp);
if (it != m_expressions.end())
return it->id;
@ -82,6 +82,27 @@ ExpressionClasses::Id ExpressionClasses::find(
return exp.id;
}
void ExpressionClasses::forceEqual(
ExpressionClasses::Id _id,
AssemblyItem const& _item,
ExpressionClasses::Ids const& _arguments,
bool _copyItem
)
{
Expression exp;
exp.id = _id;
exp.item = &_item;
exp.arguments = _arguments;
if (SemanticInformation::isCommutativeOperation(_item))
sort(exp.arguments.begin(), exp.arguments.end());
if (_copyItem)
exp.item = storeItem(_item);
m_expressions.insert(exp);
}
ExpressionClasses::Id ExpressionClasses::newClass(SourceLocation const& _location)
{
Expression exp;

5
libevmasm/ExpressionClasses.h

@ -74,6 +74,11 @@ public:
/// @returns the number of classes.
Id size() const { return m_representatives.size(); }
/// Forces the given @a _item with @a _arguments to the class @a _id. This can be used to
/// add prior knowledge e.g. about CALLDATA, but has to be used with caution. Will not work as
/// expected if @a _item applied to @a _arguments already exists.
void forceEqual(Id _id, AssemblyItem const& _item, Ids const& _arguments, bool _copyItem = true);
/// @returns the id of a new class which is different to all other classes.
Id newClass(SourceLocation const& _location);

9
libevmasm/GasMeter.cpp

@ -29,12 +29,13 @@ using namespace dev::eth;
GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption const& _other)
{
isInfinite = isInfinite || _other.isInfinite;
if (_other.isInfinite && !isInfinite)
*this = infinite();
if (isInfinite)
return *this;
bigint v = bigint(value) + _other.value;
if (v > std::numeric_limits<u256>::max())
isInfinite = true;
if (v > numeric_limits<u256>::max())
*this = infinite();
else
value = u256(v);
return *this;
@ -147,7 +148,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item)
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
gas += c_expByteGas * (32 - (h256(*value).firstBitSet() / 8));
else
gas = GasConsumption::infinite();
gas += c_expByteGas * 32;
break;
default:
break;

14
libevmasm/GasMeter.h

@ -22,6 +22,7 @@
#pragma once
#include <ostream>
#include <tuple>
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/AssemblyItem.h>
@ -46,20 +47,25 @@ public:
GasConsumption(u256 _value = 0, bool _infinite = false): value(_value), isInfinite(_infinite) {}
static GasConsumption infinite() { return GasConsumption(0, true); }
GasConsumption& operator+=(GasConsumption const& _otherS);
std::ostream& operator<<(std::ostream& _str) const;
GasConsumption& operator+=(GasConsumption const& _other);
bool operator<(GasConsumption const& _other) const { return this->tuple() < _other.tuple(); }
std::tuple<bool const&, u256 const&> tuple() const { return std::tie(isInfinite, value); }
u256 value;
bool isInfinite;
};
/// Constructs a new gas meter given the current state.
GasMeter(std::shared_ptr<KnownState> const& _state): m_state(_state) {}
explicit GasMeter(std::shared_ptr<KnownState> const& _state, u256 const& _largestMemoryAccess = 0):
m_state(_state), m_largestMemoryAccess(_largestMemoryAccess) {}
/// @returns an upper bound on the gas consumed by the given instruction and updates
/// the state.
GasConsumption estimateMax(AssemblyItem const& _item);
u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; }
private:
/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.
GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value);
@ -80,7 +86,7 @@ private:
inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption const& _consumption)
{
if (_consumption.isInfinite)
return _str << "inf";
return _str << "[???]";
else
return _str << std::dec << _consumption.value;
}

128
libevmasm/PathGasMeter.cpp

@ -0,0 +1,128 @@
/*
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 PathGasMeter.cpp
* @author Christian <c@ethdev.com>
* @date 2015
*/
#include "PathGasMeter.h"
#include <libevmasm/KnownState.h>
#include <libevmasm/SemanticInformation.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
PathGasMeter::PathGasMeter(AssemblyItems const& _items):
m_items(_items)
{
for (size_t i = 0; i < m_items.size(); ++i)
if (m_items[i].type() == Tag)
m_tagPositions[m_items[i].data()] = i;
}
GasMeter::GasConsumption PathGasMeter::estimateMax(
size_t _startIndex,
shared_ptr<KnownState> const& _state
)
{
auto path = unique_ptr<GasPath>(new GasPath());
path->index = _startIndex;
path->state = _state->copy();
m_queue.push_back(move(path));
GasMeter::GasConsumption gas;
while (!m_queue.empty() && !gas.isInfinite)
gas = max(gas, handleQueueItem());
return gas;
}
GasMeter::GasConsumption PathGasMeter::handleQueueItem()
{
assertThrow(!m_queue.empty(), OptimizerException, "");
unique_ptr<GasPath> path = move(m_queue.back());
m_queue.pop_back();
shared_ptr<KnownState> state = path->state;
GasMeter meter(state, path->largestMemoryAccess);
ExpressionClasses& classes = state->expressionClasses();
GasMeter::GasConsumption gas = path->gas;
size_t index = path->index;
if (index >= m_items.size() || (index > 0 && m_items.at(index).type() != Tag))
// Invalid jump usually provokes an out-of-gas exception, but we want to give an upper
// bound on the gas that is needed without changing the behaviour, so it is fine to
// return the current gas value.
return gas;
set<u256> jumpTags;
for (; index < m_items.size() && !gas.isInfinite; ++index)
{
bool branchStops = false;
jumpTags.clear();
AssemblyItem const& item = m_items.at(index);
if (item.type() == Tag || item == AssemblyItem(eth::Instruction::JUMPDEST))
{
// Do not allow any backwards jump. This is quite restrictive but should work for
// the simplest things.
if (path->visitedJumpdests.count(index))
return GasMeter::GasConsumption::infinite();
path->visitedJumpdests.insert(index);
}
else if (item == AssemblyItem(eth::Instruction::JUMP))
{
branchStops = true;
jumpTags = state->tagsInExpression(state->relativeStackElement(0));
if (jumpTags.empty()) // unknown jump destination
return GasMeter::GasConsumption::infinite();
}
else if (item == AssemblyItem(eth::Instruction::JUMPI))
{
ExpressionClasses::Id condition = state->relativeStackElement(-1);
if (classes.knownNonZero(condition) || !classes.knownZero(condition))
{
jumpTags = state->tagsInExpression(state->relativeStackElement(0));
if (jumpTags.empty()) // unknown jump destination
return GasMeter::GasConsumption::infinite();
}
branchStops = classes.knownNonZero(condition);
}
else if (SemanticInformation::altersControlFlow(item))
branchStops = true;
gas += meter.estimateMax(item);
for (u256 const& tag: jumpTags)
{
auto newPath = unique_ptr<GasPath>(new GasPath());
newPath->index = m_items.size();
if (m_tagPositions.count(tag))
newPath->index = m_tagPositions.at(tag);
newPath->gas = gas;
newPath->largestMemoryAccess = meter.largestMemoryAccess();
newPath->state = state->copy();
newPath->visitedJumpdests = path->visitedJumpdests;
m_queue.push_back(move(newPath));
}
if (branchStops)
break;
}
return gas;
}

66
libevmasm/PathGasMeter.h

@ -0,0 +1,66 @@
/*
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 PathGasMeter.cpp
* @author Christian <c@ethdev.com>
* @date 2015
*/
#pragma once
#include <set>
#include <vector>
#include <memory>
#include <libevmasm/GasMeter.h>
namespace dev
{
namespace eth
{
class KnownState;
struct GasPath
{
size_t index = 0;
std::shared_ptr<KnownState> state;
u256 largestMemoryAccess;
GasMeter::GasConsumption gas;
std::set<size_t> visitedJumpdests;
};
/**
* Computes an upper bound on the gas usage of a computation starting at a certain position in
* a list of AssemblyItems in a given state until the computation stops.
* Can be used to estimate the gas usage of functions on any given input.
*/
class PathGasMeter
{
public:
PathGasMeter(AssemblyItems const& _items);
GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr<KnownState> const& _state);
private:
GasMeter::GasConsumption handleQueueItem();
std::vector<std::unique_ptr<GasPath>> m_queue;
std::map<u256, size_t> m_tagPositions;
AssemblyItems const& m_items;
};
}
}

2
libevmasm/SemanticInformation.cpp

@ -111,7 +111,7 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
switch (_item.instruction())
{
// note that CALL, CALLCODE and CREATE do not really alter the control flow, because we
// continue on the next instruction (unless an exception happens which can always happen)
// continue on the next instruction
case Instruction::JUMP:
case Instruction::JUMPI:
case Instruction::RETURN:

6
libp2p/Host.cpp

@ -202,6 +202,10 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
// clang error (previously: ... << hex << caps ...)
// "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments"
stringstream capslog;
if (caps.size() > 1)
caps.erase(remove_if(caps.begin(), caps.end(), [&](CapDesc const& _r){ return any_of(caps.begin(), caps.end(), [&](CapDesc const& _o){ return _r.first == _o.first && _o.second > _r.second; }); }), caps.end());
for (auto cap: caps)
capslog << "(" << cap.first << "," << dec << cap.second << ")";
clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id << showbase << capslog.str() << dec << listenPort;
@ -237,7 +241,7 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
for (auto const& i: caps)
if (haveCapability(i))
{
ps->m_capabilities[i] = shared_ptr<Capability>(m_capabilities[i]->newPeerCapability(ps.get(), o));
ps->m_capabilities[i] = shared_ptr<Capability>(m_capabilities[i]->newPeerCapability(ps.get(), o, i));
o += m_capabilities[i]->messageCount();
}
ps->start();

1
libp2p/Host.h

@ -99,6 +99,7 @@ public:
/// Register a peer-capability; all new peer connections will have this capability.
template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; std::shared_ptr<T> ret(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; }
template <class T> void addCapability(std::shared_ptr<T> const & _p, std::string const& _name, u256 const& _version) { m_capabilities[std::make_pair(_name, _version)] = _p; }
bool haveCapability(CapDesc const& _name) const { return m_capabilities.count(_name) != 0; }
CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; }

7
libp2p/HostCapability.cpp

@ -28,12 +28,17 @@ using namespace dev;
using namespace dev::p2p;
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions() const
{
return peerSessions(version());
}
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> HostCapabilityFace::peerSessions(u256 const& _version) const
{
RecursiveGuard l(m_host->x_sessions);
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> ret;
for (auto const& i: m_host->m_sessions)
if (std::shared_ptr<Session> s = i.second.lock())
if (s->m_capabilities.count(capDesc()))
if (s->m_capabilities.count(std::make_pair(name(), _version)))
ret.push_back(make_pair(s,s->m_peer));
return ret;
}

5
libp2p/HostCapability.h

@ -46,13 +46,14 @@ public:
Host* host() const { return m_host; }
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> peerSessions() const;
std::vector<std::pair<std::shared_ptr<Session>,std::shared_ptr<Peer>>> peerSessions(u256 const& _version) const;
protected:
virtual std::string name() const = 0;
virtual u256 version() const = 0;
CapDesc capDesc() const { return std::make_pair(name(), version()); }
virtual unsigned messageCount() const = 0;
virtual Capability* newPeerCapability(Session* _s, unsigned _idOffset) = 0;
virtual Capability* newPeerCapability(Session* _s, unsigned _idOffset, CapDesc const& _cap) = 0;
virtual void onStarting() {}
virtual void onStopping() {}
@ -76,7 +77,7 @@ protected:
virtual std::string name() const { return PeerCap::name(); }
virtual u256 version() const { return PeerCap::version(); }
virtual unsigned messageCount() const { return PeerCap::messageCount(); }
virtual Capability* newPeerCapability(Session* _s, unsigned _idOffset) { return new PeerCap(_s, this, _idOffset); }
virtual Capability* newPeerCapability(Session* _s, unsigned _idOffset, CapDesc const& _cap) { return new PeerCap(_s, this, _idOffset, _cap); }
};
}

2
libp2p/Session.h

@ -69,6 +69,8 @@ public:
template <class PeerCap>
std::shared_ptr<PeerCap> cap() const { try { return std::static_pointer_cast<PeerCap>(m_capabilities.at(std::make_pair(PeerCap::name(), PeerCap::version()))); } catch (...) { return nullptr; } }
template <class PeerCap>
std::shared_ptr<PeerCap> cap(u256 const& _version) const { try { return std::static_pointer_cast<PeerCap>(m_capabilities.at(std::make_pair(PeerCap::name(), _version))); } catch (...) { return nullptr; } }
static RLPStream& prep(RLPStream& _s, PacketType _t, unsigned _args = 0);
void sealAndSend(RLPStream& _s);

2
libsolidity/ASTPrinter.cpp

@ -33,7 +33,7 @@ namespace solidity
ASTPrinter::ASTPrinter(
ASTNode const& _ast,
string const& _source,
StructuralGasEstimator::ASTGasConsumption const& _gasCosts
GasEstimator::ASTGasConsumption const& _gasCosts
): m_indentation(0), m_source(_source), m_ast(&_ast), m_gasCosts(_gasCosts)
{
}

6
libsolidity/ASTPrinter.h

@ -24,7 +24,7 @@
#include <ostream>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/StructuralGasEstimator.h>
#include <libsolidity/GasEstimator.h>
namespace dev
{
@ -42,7 +42,7 @@ public:
ASTPrinter(
ASTNode const& _ast,
std::string const& _source = std::string(),
StructuralGasEstimator::ASTGasConsumption const& _gasCosts = StructuralGasEstimator::ASTGasConsumption()
GasEstimator::ASTGasConsumption const& _gasCosts = GasEstimator::ASTGasConsumption()
);
/// Output the string representation of the AST to _stream.
void print(std::ostream& _stream);
@ -133,7 +133,7 @@ private:
int m_indentation;
std::string m_source;
ASTNode const* m_ast;
StructuralGasEstimator::ASTGasConsumption m_gasCosts;
GasEstimator::ASTGasConsumption m_gasCosts;
std::ostream* m_ostream;
};

5
libsolidity/Compiler.cpp

@ -71,6 +71,11 @@ void Compiler::compileContract(ContractDefinition const& _contract,
packIntoContractCreator(_contract, m_runtimeContext);
}
eth::AssemblyItem Compiler::getFunctionEntryLabel(FunctionDefinition const& _function) const
{
return m_runtimeContext.getFunctionEntryLabelIfExists(_function);
}
void Compiler::initializeContext(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts)
{

4
libsolidity/Compiler.h

@ -52,6 +52,10 @@ public:
/// @returns Assembly items of the runtime compiler context
eth::AssemblyItems const& getRuntimeAssemblyItems() const { return m_runtimeContext.getAssembly().getItems(); }
/// @returns the entry label of the given function. Might return an AssemblyItem of type
/// UndefinedItem if it does not exist yet.
eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const;
private:
/// Registers the non-function objects inside the contract with the context.
void initializeContext(ContractDefinition const& _contract,

6
libsolidity/CompilerContext.cpp

@ -99,6 +99,12 @@ eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _dec
return res->second.tag();
}
eth::AssemblyItem CompilerContext::getFunctionEntryLabelIfExists(Declaration const& _declaration) const
{
auto res = m_functionEntryLabels.find(&_declaration);
return res == m_functionEntryLabels.end() ? eth::AssemblyItem(eth::UndefinedItem) : res->second.tag();
}
eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function)
{
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");

4
libsolidity/CompilerContext.h

@ -59,7 +59,11 @@ public:
bool isLocalVariable(Declaration const* _declaration) const;
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
/// @returns the entry label of the given function and creates it if it does not exist yet.
eth::AssemblyItem getFunctionEntryLabel(Declaration const& _declaration);
/// @returns the entry label of the given function. Might return an AssemblyItem of type
/// UndefinedItem if it does not exist yet.
eth::AssemblyItem getFunctionEntryLabelIfExists(Declaration const& _declaration) const;
void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; }
/// @returns the entry label of the given function and takes overrides into account.
eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function);

18
libsolidity/CompilerStack.cpp

@ -268,6 +268,24 @@ ContractDefinition const& CompilerStack::getContractDefinition(string const& _co
return *getContract(_contractName).contract;
}
size_t CompilerStack::getFunctionEntryPoint(
std::string const& _contractName,
FunctionDefinition const& _function
) const
{
shared_ptr<Compiler> const& compiler = getContract(_contractName).compiler;
if (!compiler)
return 0;
eth::AssemblyItem tag = compiler->getFunctionEntryLabel(_function);
if (tag.type() == eth::UndefinedItem)
return 0;
eth::AssemblyItems const& items = compiler->getRuntimeAssemblyItems();
for (size_t i = 0; i < items.size(); ++i)
if (items.at(i).type() == eth::Tag && items.at(i).data() == tag.data())
return i;
return 0;
}
bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
{
CompilerStack stack;

8
libsolidity/CompilerStack.h

@ -48,6 +48,7 @@ namespace solidity
// forward declarations
class Scanner;
class ContractDefinition;
class FunctionDefinition;
class SourceUnit;
class Compiler;
class GlobalContext;
@ -131,6 +132,13 @@ public:
/// does not exist.
ContractDefinition const& getContractDefinition(std::string const& _contractName) const;
/// @returns the offset of the entry point of the given function into the list of assembly items
/// or zero if it is not found or does not exist.
size_t getFunctionEntryPoint(
std::string const& _contractName,
FunctionDefinition const& _function
) const;
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
/// scanning the source code - this is useful for printing exception information.
static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false);

62
libsolidity/StructuralGasEstimator.cpp → libsolidity/GasEstimator.cpp

@ -20,27 +20,30 @@
* Gas consumption estimator working alongside the AST.
*/
#include "StructuralGasEstimator.h"
#include "GasEstimator.h"
#include <map>
#include <functional>
#include <memory>
#include <libdevcore/SHA3.h>
#include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/KnownState.h>
#include <libevmasm/PathGasMeter.h>
#include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/CompilerUtils.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace dev::solidity;
StructuralGasEstimator::ASTGasConsumptionSelfAccumulated StructuralGasEstimator::performEstimation(
GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation(
AssemblyItems const& _items,
vector<ASTNode const*> const& _ast
)
{
solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, "");
map<SourceLocation, GasMeter::GasConsumption> particularCosts;
map<SourceLocation, GasConsumption> particularCosts;
ControlFlowGraph cfg(_items);
for (BasicBlock const& block: cfg.optimisedBlocks())
@ -72,7 +75,7 @@ StructuralGasEstimator::ASTGasConsumptionSelfAccumulated StructuralGasEstimator:
return gasCosts;
}
map<ASTNode const*, GasMeter::GasConsumption> StructuralGasEstimator::breakToStatementLevel(
map<ASTNode const*, GasMeter::GasConsumption> GasEstimator::breakToStatementLevel(
ASTGasConsumptionSelfAccumulated const& _gasCosts,
vector<ASTNode const*> const& _roots
)
@ -99,7 +102,7 @@ map<ASTNode const*, GasMeter::GasConsumption> StructuralGasEstimator::breakToSta
// we use the location of a node if
// - its statement depth is 0 or
// - its statement depth is undefined but the parent's statement depth is at least 1
map<ASTNode const*, GasMeter::GasConsumption> gasCosts;
map<ASTNode const*, GasConsumption> gasCosts;
auto onNodeSecondPass = [&](ASTNode const& _node)
{
return statementDepth.count(&_node);
@ -121,7 +124,53 @@ map<ASTNode const*, GasMeter::GasConsumption> StructuralGasEstimator::breakToSta
return gasCosts;
}
set<ASTNode const*> StructuralGasEstimator::finestNodesAtLocation(
GasEstimator::GasConsumption GasEstimator::functionalEstimation(
AssemblyItems const& _items,
string const& _signature
)
{
auto state = make_shared<KnownState>();
if (!_signature.empty())
{
ExpressionClasses& classes = state->expressionClasses();
using Id = ExpressionClasses::Id;
using Ids = vector<Id>;
Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::sha3(_signature)))));
Id calldata = classes.find(eth::Instruction::CALLDATALOAD, Ids{classes.find(u256(0))});
classes.forceEqual(hashValue, eth::Instruction::DIV, Ids{
calldata,
classes.find(u256(1) << (8 * 28))
});
}
PathGasMeter meter(_items);
return meter.estimateMax(0, state);
}
GasEstimator::GasConsumption GasEstimator::functionalEstimation(
AssemblyItems const& _items,
size_t const& _offset,
FunctionDefinition const& _function
)
{
auto state = make_shared<KnownState>();
unsigned parametersSize = CompilerUtils::getSizeOnStack(_function.getParameters());
if (parametersSize > 16)
return GasConsumption::infinite();
// Store an invalid return value on the stack, so that the path estimator breaks upon reaching
// the return jump.
AssemblyItem invalidTag(PushTag, u256(-0x10));
state->feedItem(invalidTag, true);
if (parametersSize > 0)
state->feedItem(eth::swapInstruction(parametersSize));
return PathGasMeter(_items).estimateMax(_offset, state);
}
set<ASTNode const*> GasEstimator::finestNodesAtLocation(
vector<ASTNode const*> const& _roots
)
{
@ -140,4 +189,3 @@ set<ASTNode const*> StructuralGasEstimator::finestNodesAtLocation(
root->accept(visitor);
return nodes;
}

29
libsolidity/StructuralGasEstimator.h → libsolidity/GasEstimator.h

@ -34,17 +34,18 @@ namespace dev
namespace solidity
{
class StructuralGasEstimator
struct GasEstimator
{
public:
using ASTGasConsumption = std::map<ASTNode const*, eth::GasMeter::GasConsumption>;
using GasConsumption = eth::GasMeter::GasConsumption;
using ASTGasConsumption = std::map<ASTNode const*, GasConsumption>;
using ASTGasConsumptionSelfAccumulated =
std::map<ASTNode const*, std::array<eth::GasMeter::GasConsumption, 2>>;
std::map<ASTNode const*, std::array<GasConsumption, 2>>;
/// Estimates the gas consumption for every assembly item in the given assembly and stores
/// it by source location.
/// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs.
ASTGasConsumptionSelfAccumulated performEstimation(
static ASTGasConsumptionSelfAccumulated structuralEstimation(
eth::AssemblyItems const& _items,
std::vector<ASTNode const*> const& _ast
);
@ -52,14 +53,30 @@ public:
/// the following source locations are part of the mapping:
/// 1. source locations of statements that do not contain other statements
/// 2. maximal source locations that do not overlap locations coming from the first rule
ASTGasConsumption breakToStatementLevel(
static ASTGasConsumption breakToStatementLevel(
ASTGasConsumptionSelfAccumulated const& _gasCosts,
std::vector<ASTNode const*> const& _roots
);
/// @returns the estimated gas consumption by the (public or external) function with the
/// given signature. If no signature is given, estimates the maximum gas usage.
static GasConsumption functionalEstimation(
eth::AssemblyItems const& _items,
std::string const& _signature = ""
);
/// @returns the estimated gas consumption by the given function which starts at the given
/// offset into the list of assembly items.
/// @note this does not work correctly for recursive functions.
static GasConsumption functionalEstimation(
eth::AssemblyItems const& _items,
size_t const& _offset,
FunctionDefinition const& _function
);
private:
/// @returns the set of AST nodes which are the finest nodes at their location.
std::set<ASTNode const*> finestNodesAtLocation(std::vector<ASTNode const*> const& _roots);
static std::set<ASTNode const*> finestNodesAtLocation(std::vector<ASTNode const*> const& _roots);
};
}

22
libsolidity/Token.h

@ -142,34 +142,34 @@ namespace solidity
K(Delete, "delete", 0) \
\
/* Keywords */ \
K(Anonymous, "anonymous", 0) \
K(Break, "break", 0) \
K(Const, "constant", 0) \
K(Anonymous, "anonymous", 0) \
K(Continue, "continue", 0) \
K(Contract, "contract", 0) \
K(Default, "default", 0) \
K(Do, "do", 0) \
K(Else, "else", 0) \
K(Enum, "enum", 0) \
K(Event, "event", 0) \
K(External, "external", 0) \
K(Is, "is", 0) \
K(Indexed, "indexed", 0) \
K(For, "for", 0) \
K(Function, "function", 0) \
K(If, "if", 0) \
K(Indexed, "indexed", 0) \
K(Internal, "internal", 0) \
K(Import, "import", 0) \
K(Is, "is", 0) \
K(Mapping, "mapping", 0) \
K(Modifier, "modifier", 0) \
K(New, "new", 0) \
K(Public, "public", 0) \
K(Private, "private", 0) \
K(Internal, "internal", 0) \
K(Return, "return", 0) \
K(Returns, "returns", 0) \
K(Struct, "struct", 0) \
K(Var, "var", 0) \
K(While, "while", 0) \
K(Enum, "enum", 0) \
\
/* Ether subdenominations */ \
K(SubWei, "wei", 0) \
@ -304,15 +304,21 @@ namespace solidity
T(Identifier, NULL, 0) \
\
/* Keywords reserved for future. use*/ \
T(String, "string", 0) \
K(As, "as", 0) \
K(Case, "case", 0) \
K(Catch, "catch", 0) \
K(Final, "final", 0) \
K(Let, "let", 0) \
K(Match, "match", 0) \
K(Of, "of", 0) \
K(Relocatable, "relocatable", 0) \
T(String, "string", 0) \
K(Switch, "switch", 0) \
K(Throw, "throw", 0) \
K(Try, "try", 0) \
K(Catch, "catch", 0) \
K(Using, "using", 0) \
K(Type, "type", 0) \
K(TypeOf, "typeof", 0) \
K(Using, "using", 0) \
/* Illegal token - not able to scan. */ \
T(Illegal, "ILLEGAL", 0) \
\

2
libtestutils/BlockChainLoader.h

@ -44,7 +44,7 @@ public:
private:
TransientDirectory m_dir;
std::auto_ptr<eth::BlockChain> m_bc;
std::unique_ptr<eth::BlockChain> m_bc;
eth::State m_state;
};

8
libwhisper/Message.h

@ -119,11 +119,11 @@ public:
operator bool() const { return !!m_payload.size() || m_from || m_to; }
/// Turn this message into a ditributable Envelope.
Envelope seal(Secret _from, FullTopic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) const;
Envelope seal(Secret _from, FullTopic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const;
// Overloads for skipping _from or specifying _to.
Envelope seal(FullTopic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const { return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope sealTo(Public _to, FullTopic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope sealTo(Secret _from, Public _to, FullTopic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(_from, _topic, _workToProve, _ttl); }
Envelope seal(FullTopic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const { return seal(Secret(), _topic, _ttl, _workToProve); }
Envelope sealTo(Public _to, FullTopic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { m_to = _to; return seal(Secret(), _topic, _ttl, _workToProve); }
Envelope sealTo(Secret _from, Public _to, FullTopic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { m_to = _to; return seal(_from, _topic, _ttl, _workToProve); }
private:
bool populate(bytes const& _data);

2
libwhisper/WhisperPeer.cpp

@ -29,7 +29,7 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
WhisperPeer::WhisperPeer(Session* _s, HostCapabilityFace* _h, unsigned _i): Capability(_s, _h, _i)
WhisperPeer::WhisperPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, CapDesc const&): Capability(_s, _h, _i)
{
RLPStream s;
sealAndSend(prep(s, StatusPacket, 1) << version());

3
libwhisper/WhisperPeer.h

@ -42,6 +42,7 @@ using p2p::Session;
using p2p::HostCapabilityFace;
using p2p::HostCapability;
using p2p::Capability;
using p2p::CapDesc;
/**
*/
@ -50,7 +51,7 @@ class WhisperPeer: public Capability
friend class WhisperHost;
public:
WhisperPeer(Session* _s, HostCapabilityFace* _h, unsigned _i);
WhisperPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, CapDesc const& _cap);
virtual ~WhisperPeer();
static std::string name() { return "shh"; }

3
mix/ClientModel.cpp

@ -484,7 +484,10 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
if (!functionName.isEmpty() && ((prevInstruction.getJumpType() == AssemblyItem::JumpType::IntoFunction) || solCallStack.empty()))
solCallStack.push_front(QVariant::fromValue(functionName));
else if (prevInstruction.getJumpType() == AssemblyItem::JumpType::OutOfFunction && !solCallStack.empty())
{
solCallStack.pop_front();
solLocals.clear();
}
}
//format solidity context values

5
mix/CodeModel.cpp

@ -33,7 +33,7 @@
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include <libsolidity/InterfaceHandler.h>
#include <libsolidity/StructuralGasEstimator.h>
#include <libsolidity/GasEstimator.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include <libevmcore/Instruction.h>
#include <libethcore/CommonJS.h>
@ -373,8 +373,7 @@ void CodeModel::gasEstimation(solidity::CompilerStack const& _cs)
continue;
dev::solidity::SourceUnit const& sourceUnit = _cs.getAST(*contractDefinition.getLocation().sourceName);
AssemblyItems const* items = _cs.getRuntimeAssemblyItems(n);
StructuralGasEstimator estimator;
std::map<ASTNode const*, GasMeter::GasConsumption> gasCosts = estimator.breakToStatementLevel(estimator.performEstimation(*items, std::vector<ASTNode const*>({&sourceUnit})), {&sourceUnit});
std::map<ASTNode const*, GasMeter::GasConsumption> gasCosts = GasEstimator::breakToStatementLevel(GasEstimator::structuralEstimation(*items, std::vector<ASTNode const*>({&sourceUnit})), {&sourceUnit});
for (auto gasItem = gasCosts.begin(); gasItem != gasCosts.end(); ++gasItem)
{
SourceLocation const& location = gasItem->first->getLocation();

2
mix/MixClient.h

@ -94,7 +94,7 @@ private:
eth::State m_state;
eth::State m_startState;
OverlayDB m_stateDB;
std::auto_ptr<MixBlockChain> m_bc;
std::unique_ptr<MixBlockChain> m_bc;
mutable boost::shared_mutex x_state;
mutable boost::shared_mutex x_executions;
ExecutionResults m_executions;

2
mix/qml/DebuggerPaneStyle.qml

@ -9,6 +9,8 @@ QtObject {
property QtObject general: QtObject {
property int basicFontSize: absoluteSize(-2)
property string basicColor: "#4a4a4a"
property string basicFont: "monospace"
property int dataDumpFontSize: absoluteSize(-3)
}
}

14
mix/qml/QIntTypeView.qml

@ -5,24 +5,24 @@ Item
property alias value: textinput.text
property alias readOnly: textinput.readOnly
id: editRoot
height: 20
width: readOnly ? textinput.implicitWidth : 150
SourceSansProBold
{
id: boldFont
DebuggerPaneStyle {
id: dbgStyle
}
Rectangle {
anchors.fill: parent
radius: 4
TextInput {
anchors.verticalCenter: parent.verticalCenter
id: textinput
text: value
anchors.fill: parent
font.family: boldFont.name
font.family: dbgStyle.general.basicFont
clip: true
selectByMouse: true
text: value
font.pointSize: dbgStyle.general.basicFontSize
color: dbgStyle.general.basicColor
}
}
}

15
mix/qml/StructView.qml

@ -13,6 +13,11 @@ Column
property string context
Layout.fillWidth: true
spacing: 0
DebuggerPaneStyle {
id: dbgStyle
}
Repeater
{
id: repeater
@ -29,6 +34,9 @@ Column
id: typeLabel
text: modelData.type.name
anchors.verticalCenter: parent.verticalCenter
font.family: dbgStyle.general.basicFont
color: dbgStyle.general.basicColor
font.pointSize: dbgStyle.general.basicFontSize
}
DefaultLabel {
@ -36,6 +44,9 @@ Column
id: nameLabel
text: modelData.name
anchors.verticalCenter: parent.verticalCenter
font.family: dbgStyle.general.basicFont
color: dbgStyle.general.basicColor
font.pointSize: dbgStyle.general.basicFontSize
}
DefaultLabel {
@ -43,7 +54,11 @@ Column
id: equalLabel
text: "="
anchors.verticalCenter: parent.verticalCenter
font.family: dbgStyle.general.basicFont
color: dbgStyle.general.basicColor
font.pointSize: dbgStyle.general.basicFontSize
}
Loader
{
id: typeLoader

2
mix/qml/VariablesView.qml

@ -18,7 +18,7 @@ DebugInfoList
property alias members: typeLoader.members;
property alias value: typeLoader.value;
anchors.fill: parent
anchors.rightMargin: 8
anchors.leftMargin: 10
StructView
{
id: typeLoader

59
solc/CommandLineInterface.cpp

@ -34,6 +34,7 @@
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libevmcore/Params.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/ASTPrinter.h>
@ -42,7 +43,7 @@
#include <libsolidity/Exceptions.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include <libsolidity/StructuralGasEstimator.h>
#include <libsolidity/GasEstimator.h>
using namespace std;
namespace po = boost::program_options;
@ -55,6 +56,7 @@ namespace solidity
static string const g_argAbiStr = "json-abi";
static string const g_argSolAbiStr = "sol-abi";
static string const g_argSignatureHashes = "hashes";
static string const g_argGas = "gas";
static string const g_argAsmStr = "asm";
static string const g_argAsmJsonStr = "asm-json";
static string const g_argAstStr = "ast";
@ -94,6 +96,7 @@ static bool needsHumanTargetedStdout(po::variables_map const& _args)
{
return
_args.count(g_argGas) ||
humanTargetedStdout(_args, g_argAbiStr) ||
humanTargetedStdout(_args, g_argSolAbiStr) ||
humanTargetedStdout(_args, g_argSignatureHashes) ||
@ -245,6 +248,50 @@ void CommandLineInterface::handleMeta(DocumentationType _type, string const& _co
}
}
void CommandLineInterface::handleGasEstimation(string const& _contract)
{
using Gas = GasEstimator::GasConsumption;
if (!m_compiler->getAssemblyItems(_contract) && !m_compiler->getRuntimeAssemblyItems(_contract))
return;
cout << "Gas estimation:" << endl;
if (eth::AssemblyItems const* items = m_compiler->getAssemblyItems(_contract))
{
Gas gas = GasEstimator::functionalEstimation(*items);
u256 bytecodeSize(m_compiler->getRuntimeBytecode(_contract).size());
cout << "construction:" << endl;
cout << " " << gas << " + " << (bytecodeSize * eth::c_createDataGas) << " = ";
gas += bytecodeSize * eth::c_createDataGas;
cout << gas << endl;
}
if (eth::AssemblyItems const* items = m_compiler->getRuntimeAssemblyItems(_contract))
{
ContractDefinition const& contract = m_compiler->getContractDefinition(_contract);
cout << "external:" << endl;
for (auto it: contract.getInterfaceFunctions())
{
string sig = it.second->externalSignature();
GasEstimator::GasConsumption gas = GasEstimator::functionalEstimation(*items, sig);
cout << " " << sig << ":\t" << gas << endl;
}
cout << "internal:" << endl;
for (auto const& it: contract.getDefinedFunctions())
{
if (it->isPartOfExternalInterface() || it->isConstructor())
continue;
size_t entry = m_compiler->getFunctionEntryPoint(_contract, *it);
GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
if (entry > 0)
gas = GasEstimator::functionalEstimation(*items, entry, *it);
FunctionType type(*it);
cout << " " << it->getName() << "(";
auto end = type.getParameterTypes().end();
for (auto it = type.getParameterTypes().begin(); it != end; ++it)
cout << (*it)->toString() << (it + 1 == end ? "" : ",");
cout << "):\t" << gas << endl;
}
}
}
bool CommandLineInterface::parseArguments(int argc, char** argv)
{
// Declare the supported options.
@ -278,6 +325,8 @@ bool CommandLineInterface::parseArguments(int argc, char** argv)
"Request to output the contract's Solidity ABI interface.")
(g_argSignatureHashes.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the contract's functions' signature hashes.")
(g_argGas.c_str(),
"Request to output an estimate for each function's maximal gas usage.")
(g_argNatspecUserStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
"Request to output the contract's Natspec user documentation.")
(g_argNatspecDevStr.c_str(), po::value<OutputType>()->value_name("stdout|file|both"),
@ -465,14 +514,13 @@ void CommandLineInterface::handleAst(string const& _argStr)
// do we need AST output?
if (m_args.count(_argStr))
{
StructuralGasEstimator gasEstimator;
vector<ASTNode const*> asts;
for (auto const& sourceCode: m_sourceCodes)
asts.push_back(&m_compiler->getAST(sourceCode.first));
map<ASTNode const*, eth::GasMeter::GasConsumption> gasCosts;
if (m_compiler->getRuntimeAssemblyItems())
gasCosts = gasEstimator.breakToStatementLevel(
gasEstimator.performEstimation(*m_compiler->getRuntimeAssemblyItems(), asts),
gasCosts = GasEstimator::breakToStatementLevel(
GasEstimator::structuralEstimation(*m_compiler->getRuntimeAssemblyItems(), asts),
asts
);
@ -554,6 +602,9 @@ void CommandLineInterface::actOnInput()
}
}
if (m_args.count(g_argGas))
handleGasEstimation(contract);
handleBytecode(contract);
handleSignatureHashes(contract);
handleMeta(DocumentationType::ABIInterface, contract);

1
solc/CommandLineInterface.h

@ -61,6 +61,7 @@ private:
void handleSignatureHashes(std::string const& _contract);
void handleMeta(DocumentationType _type,
std::string const& _contract);
void handleGasEstimation(std::string const& _contract);
/// Compiler arguments variable map
boost::program_options::variables_map m_args;

39
test/libdevcrypto/hexPrefix.cpp

@ -27,6 +27,7 @@
#include "../JsonSpiritHeaders.h"
#include <libdevcore/Log.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Base64.h>
#include <libdevcore/TrieCommon.h>
#include "../TestHelper.h"
@ -60,4 +61,42 @@ BOOST_AUTO_TEST_CASE(hexPrefix_test)
}
}
BOOST_AUTO_TEST_CASE(base64)
{
static char const* const s_tests[][2] =
{
{"", ""},
{"f", "Zg=="},
{"fo", "Zm8="},
{"foo", "Zm9v"},
{"foob", "Zm9vYg=="},
{"fooba", "Zm9vYmE="},
{"foobar", "Zm9vYmFy"},
{
"So?<p>"
"This 4, 5, 6, 7, 8, 9, z, {, |, } tests Base64 encoder. "
"Show me: @, A, B, C, D, E, F, G, H, I, J, K, L, M, "
"N, O, P, Q, R, S, T, U, V, W, X, Y, Z, [, \\, ], ^, _, `, "
"a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s.",
"U28/PHA+VGhpcyA0LCA1LCA2LCA3LCA4LCA5LCB6LCB7LCB8LCB9IHRlc3RzIEJhc2U2NCBlbmNv"
"ZGVyLiBTaG93IG1lOiBALCBBLCBCLCBDLCBELCBFLCBGLCBHLCBILCBJLCBKLCBLLCBMLCBNLCBO"
"LCBPLCBQLCBRLCBSLCBTLCBULCBVLCBWLCBXLCBYLCBZLCBaLCBbLCBcLCBdLCBeLCBfLCBgLCBh"
"LCBiLCBjLCBkLCBlLCBmLCBnLCBoLCBpLCBqLCBrLCBsLCBtLCBuLCBvLCBwLCBxLCByLCBzLg=="
}
};
static const auto c_numTests = sizeof(s_tests) / sizeof(s_tests[0]);
for (size_t i = 0; i < c_numTests; ++i)
{
auto expectedDecoded = std::string{s_tests[i][0]};
auto expectedEncoded = std::string{s_tests[i][1]};
auto encoded = toBase64(expectedDecoded);
BOOST_CHECK_EQUAL(expectedEncoded, encoded);
auto decodedBytes = fromBase64(expectedEncoded);
auto decoded = bytesConstRef{decodedBytes.data(), decodedBytes.size()}.toString();
BOOST_CHECK_EQUAL(decoded, expectedDecoded);
}
}
BOOST_AUTO_TEST_SUITE_END()

7748
test/libethereum/BlockTestsFiller/bcWalletTestFiller.json

File diff suppressed because one or more lines are too long

2
test/libethereum/StateTestsFiller/stBlockHashTestFiller.json

@ -64,7 +64,7 @@
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000",
"balance" : "3850000",
"nonce" : "0",
"code" : "",
"storage": {}

516
test/libethereum/StateTestsFiller/stCallCreateCallCodeTestFiller.json

@ -1025,5 +1025,521 @@
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"callOutput1" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "{ (MSTORE 0 0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6) (CALL 50000 0xaaae7baea6a6c7c4c2dfeb977efac326af552d87 0 0 0 0 0) [[ 0 ]] (MLOAD 0) }",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"aaae7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "1000000",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
},
"callOutput2" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "{ (MSTORE 0 0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6) (CALL 50000 0xaaae7baea6a6c7c4c2dfeb977efac326af552d87 0 0 32 0 0) [[ 0 ]] (MLOAD 0)}",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"aaae7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "1000000",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
},
"callOutput3" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "{ (MSTORE 0 0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6) (CALL 50000 0xaaae7baea6a6c7c4c2dfeb977efac326af552d87 0 0 0 0 32) [[ 0 ]] (MLOAD 0)}",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"aaae7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "1000000",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
},
"callOutput3Fail" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "{ (MSTORE 0 0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6) (CALL 50000 0xaaae7baea6a6c7c4c2dfeb977efac326af552d87 0 0 0 0 32) [[ 0 ]] (MLOAD 0)}",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"aaae7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x016001600101600055",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "1000000",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
},
"callOutput3partial" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "{ (MSTORE 0 0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6) (CALL 50000 0xaaae7baea6a6c7c4c2dfeb977efac326af552d87 0 0 0 0 10) [[ 0 ]] (MLOAD 0)}",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"aaae7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "1000000",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
},
"callOutput3partialFail" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "{ (MSTORE 0 0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6) (CALL 50000 0xaaae7baea6a6c7c4c2dfeb977efac326af552d87 0 0 0 0 10) [[ 0 ]] (MLOAD 0)}",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"aaae7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x016001600101600055",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "1000000",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
},
"callcodeOutput1" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "{ (MSTORE 0 0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6) (CALLCODE 50000 0xaaae7baea6a6c7c4c2dfeb977efac326af552d87 0 0 0 0 0) [[ 0 ]] (MLOAD 0) }",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"aaae7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "1000000",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
},
"callcodeOutput2" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "{ (MSTORE 0 0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6) (CALLCODE 50000 0xaaae7baea6a6c7c4c2dfeb977efac326af552d87 0 0 32 0 0) [[ 0 ]] (MLOAD 0)}",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"aaae7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "1000000",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
},
"callcodeOutput3" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "{ (MSTORE 0 0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6) (CALLCODE 50000 0xaaae7baea6a6c7c4c2dfeb977efac326af552d87 0 0 0 0 32) [[ 0 ]] (MLOAD 0)}",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"aaae7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "1000000",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
},
"callcodeOutput3Fail" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "{ (MSTORE 0 0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6) (CALLCODE 50000 0xaaae7baea6a6c7c4c2dfeb977efac326af552d87 0 0 0 0 32) [[ 0 ]] (MLOAD 0)}",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"aaae7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x016001600101600055",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "1000000",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
},
"callcodeOutput3partial" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "{ (MSTORE 0 0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6) (CALLCODE 50000 0xaaae7baea6a6c7c4c2dfeb977efac326af552d87 0 0 0 0 10) [[ 0 ]] (MLOAD 0)}",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"aaae7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "1000000",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
},
"callcodeOutput3partialFail" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "{ (MSTORE 0 0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6) (CALLCODE 50000 0xaaae7baea6a6c7c4c2dfeb977efac326af552d87 0 0 0 0 10) [[ 0 ]] (MLOAD 0)}",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"aaae7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x016001600101600055",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "1000000",
"gasPrice" : "0",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
}
}

433
test/libethereum/StateTestsFiller/stWalletTestFiller.json

File diff suppressed because one or more lines are too long

2
test/libp2p/capability.cpp

@ -49,7 +49,7 @@ struct VerbosityHolder
class TestCapability: public Capability
{
public:
TestCapability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset): Capability(_s, _h, _idOffset), m_cntReceivedMessages(0), m_testSum(0) {}
TestCapability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset, CapDesc const&): Capability(_s, _h, _idOffset), m_cntReceivedMessages(0), m_testSum(0) {}
virtual ~TestCapability() {}
int countReceivedMessages() { return m_cntReceivedMessages; }
int testSum() { return m_testSum; }

99
test/libsolidity/GasMeter.cpp

@ -23,8 +23,9 @@
#include <test/libsolidity/solidityExecutionFramework.h>
#include <libevmasm/GasMeter.h>
#include <libevmasm/KnownState.h>
#include <libevmasm/PathGasMeter.h>
#include <libsolidity/AST.h>
#include <libsolidity/StructuralGasEstimator.h>
#include <libsolidity/GasEstimator.h>
#include <libsolidity/SourceReferenceFormatter.h>
using namespace std;
@ -47,30 +48,47 @@ public:
m_compiler.setSource(_sourceCode);
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(), "Compiling contract failed");
StructuralGasEstimator estimator;
AssemblyItems const* items = m_compiler.getRuntimeAssemblyItems("");
ASTNode const& sourceUnit = m_compiler.getAST();
BOOST_REQUIRE(items != nullptr);
m_gasCosts = estimator.breakToStatementLevel(
estimator.performEstimation(*items, vector<ASTNode const*>({&sourceUnit})),
m_gasCosts = GasEstimator::breakToStatementLevel(
GasEstimator::structuralEstimation(*items, vector<ASTNode const*>({&sourceUnit})),
{&sourceUnit}
);
}
void testCreationTimeGas(string const& _sourceCode, string const& _contractName = "")
void testCreationTimeGas(string const& _sourceCode)
{
compileAndRun(_sourceCode);
auto state = make_shared<KnownState>();
GasMeter meter(state);
GasMeter::GasConsumption gas;
for (AssemblyItem const& item: *m_compiler.getAssemblyItems(_contractName))
gas += meter.estimateMax(item);
u256 bytecodeSize(m_compiler.getRuntimeBytecode(_contractName).size());
PathGasMeter meter(*m_compiler.getAssemblyItems());
GasMeter::GasConsumption gas = meter.estimateMax(0, state);
u256 bytecodeSize(m_compiler.getRuntimeBytecode().size());
gas += bytecodeSize * c_createDataGas;
BOOST_REQUIRE(!gas.isInfinite);
BOOST_CHECK(gas.value == m_gasUsed);
}
/// Compares the gas computed by PathGasMeter for the given signature (but unknown arguments)
/// against the actual gas usage computed by the VM on the given set of argument variants.
void testRunTimeGas(string const& _sig, vector<bytes> _argumentVariants)
{
u256 gasUsed = 0;
FixedHash<4> hash(dev::sha3(_sig));
for (bytes const& arguments: _argumentVariants)
{
sendMessage(hash.asBytes() + arguments, false, 0);
gasUsed = max(gasUsed, m_gasUsed);
}
GasMeter::GasConsumption gas = GasEstimator::functionalEstimation(
*m_compiler.getRuntimeAssemblyItems(),
_sig
);
BOOST_REQUIRE(!gas.isInfinite);
BOOST_CHECK(gas.value == m_gasUsed);
}
protected:
map<ASTNode const*, eth::GasMeter::GasConsumption> m_gasCosts;
};
@ -149,6 +167,67 @@ BOOST_AUTO_TEST_CASE(updating_store)
testCreationTimeGas(sourceCode);
}
BOOST_AUTO_TEST_CASE(branches)
{
char const* sourceCode = R"(
contract test {
uint data;
uint data2;
function f(uint x) {
if (x > 7)
data2 = 1;
else
data = 1;
}
}
)";
testCreationTimeGas(sourceCode);
testRunTimeGas("f(uint256)", vector<bytes>{encodeArgs(2), encodeArgs(8)});
}
BOOST_AUTO_TEST_CASE(function_calls)
{
char const* sourceCode = R"(
contract test {
uint data;
uint data2;
function f(uint x) {
if (x > 7)
data2 = g(x**8) + 1;
else
data = 1;
}
function g(uint x) internal returns (uint) {
return data2;
}
}
)";
testCreationTimeGas(sourceCode);
testRunTimeGas("f(uint256)", vector<bytes>{encodeArgs(2), encodeArgs(8)});
}
BOOST_AUTO_TEST_CASE(multiple_external_functions)
{
char const* sourceCode = R"(
contract test {
uint data;
uint data2;
function f(uint x) {
if (x > 7)
data2 = g(x**8) + 1;
else
data = 1;
}
function g(uint x) returns (uint) {
return data2;
}
}
)";
testCreationTimeGas(sourceCode);
testRunTimeGas("f(uint256)", vector<bytes>{encodeArgs(2), encodeArgs(8)});
testRunTimeGas("g(uint256)", vector<bytes>{encodeArgs(2)});
}
BOOST_AUTO_TEST_SUITE_END()
}

Loading…
Cancel
Save