Browse Source

Merge remote-tracking branch 'up/develop' into deploydialog

cl-refactor
yann300 10 years ago
parent
commit
6a5b524eba
  1. 35
      CMakeLists.txt
  2. 6
      cmake/EthExecutableHelper.cmake
  3. 3
      cmake/FindMHD.cmake
  4. 18
      cmake/FindOpenCL.cmake
  5. 6
      cmake/scripts/copydlls.cmake
  6. 3
      eth/CMakeLists.txt
  7. 2
      eth/main.cpp
  8. 4
      ethminer/CMakeLists.txt
  9. 8
      ethminer/main.cpp
  10. 20
      evmjit/CMakeLists.txt
  11. 3
      exp/CMakeLists.txt
  12. 1
      extdep/getstuff.bat
  13. 15
      libdevcore/Worker.cpp
  14. 5
      libethash-cl/CMakeLists.txt
  15. 88
      libethash-cl/ethash_cl_miner.cpp
  16. 1
      libethash-cl/ethash_cl_miner.h
  17. 4
      libethcore/CMakeLists.txt
  18. 40
      libethereum/BlockChainSync.cpp
  19. 17
      libethereum/BlockChainSync.h
  20. 8
      libethereum/TransactionQueue.cpp
  21. 41
      libp2p/Common.h
  22. 119
      libp2p/Host.cpp
  23. 2
      libp2p/Host.h
  24. 103
      libp2p/NodeTable.cpp
  25. 26
      libp2p/NodeTable.h
  26. 53
      libsolidity/AST.cpp
  27. 10
      libsolidity/AST.h
  28. 1
      libsolidity/CMakeLists.txt
  29. 10
      libsolidity/CompilerStack.cpp
  30. 17
      libsolidity/ExpressionCompiler.cpp
  31. 26
      libsolidity/InterfaceHandler.cpp
  32. 25
      libsolidity/InterfaceHandler.h
  33. 2
      libsolidity/LValue.cpp
  34. 1
      libsolidity/Parser.cpp
  35. 26
      libsolidity/Types.cpp
  36. 9
      libsolidity/Types.h
  37. 67
      libwhisper/WhisperDB.cpp
  38. 3
      libwhisper/WhisperDB.h
  39. 74
      libwhisper/WhisperHost.cpp
  40. 12
      libwhisper/WhisperHost.h
  41. 2
      mix/CodeModel.cpp
  42. 4
      neth/main.cpp
  43. 5
      solc/CommandLineInterface.cpp
  44. 4
      solc/jsonCompiler.cpp
  45. 2
      test/TestHelper.h
  46. 2
      test/libdevcore/rlp.cpp
  47. 40
      test/libp2p/net.cpp
  48. 7
      test/libp2p/peer.cpp
  49. 65
      test/libsolidity/SolidityEndToEndTest.cpp
  50. 15
      test/libsolidity/SolidityNameAndTypeResolution.cpp
  51. 93
      test/libsolidity/SolidityNatspecJSON.cpp
  52. 41
      test/libsolidity/SolidityWallet.cpp
  53. 9
      test/libsolidity/solidityExecutionFramework.h
  54. 89
      test/libwhisper/whisperDB.cpp
  55. 24
      test/libwhisper/whisperTopic.cpp

35
CMakeLists.txt

@ -44,7 +44,7 @@ option(TESTS "Build the tests." ON)
option(NOBOOST "No use of boost macros in test functions" OFF) option(NOBOOST "No use of boost macros in test functions" OFF)
option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF) option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF)
option(ETHASHCL "Build in support for GPU mining via OpenCL" OFF) option(ETHASHCL "Build in support for GPU mining via OpenCL" OFF)
option(JSCONSOLE "Build in javascript console" OFF) option(JSCONSOLE "Build in javascript console" ON)
# propagates CMake configuration options to the compiler # propagates CMake configuration options to the compiler
function(configureProject) function(configureProject)
@ -131,7 +131,7 @@ function(createBuildInfo)
add_custom_target(BuildInfo.h ALL add_custom_target(BuildInfo.h ALL
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -DETH_SOURCE_DIR="${CMAKE_SOURCE_DIR}" -DETH_DST_DIR="${CMAKE_BINARY_DIR}" COMMAND ${CMAKE_COMMAND} -DETH_SOURCE_DIR="${CMAKE_SOURCE_DIR}" -DETH_DST_DIR="${CMAKE_BINARY_DIR}"
-DETH_BUILD_TYPE="${_cmake_build_type}" -DETH_BUILD_PLATFORM="${ETH_BUILD_PLATFORM}" -DETH_BUILD_TYPE="${_cmake_build_type}" -DETH_BUILD_PLATFORM="${ETH_BUILD_PLATFORM}"
-P "${ETH_SCRIPTS_DIR}/buildinfo.cmake" -P "${ETH_SCRIPTS_DIR}/buildinfo.cmake"
) )
include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR})
@ -175,9 +175,9 @@ endif ()
macro(eth_format_option O) macro(eth_format_option O)
if (${${O}}) if (${${O}})
set(${O} ON) set(${O} ON)
else() else()
set(${O} OFF) set(${O} OFF)
endif() endif()
endmacro() endmacro()
@ -219,6 +219,9 @@ if (GUI)
set(JSONRPC ON) set(JSONRPC ON)
endif() endif()
# note: The value "default" which provides the defaults is just a fake value
# which lets us keep the default values of all build options and is set at
# the beginning of this file.
if (BUNDLE STREQUAL "minimal") if (BUNDLE STREQUAL "minimal")
set(SERPENT OFF) set(SERPENT OFF)
set(SOLIDITY OFF) set(SOLIDITY OFF)
@ -506,26 +509,26 @@ endif ()
if (WIN32) if (WIN32)
# packaging stuff # packaging stuff
include(InstallRequiredSystemLibraries) include(InstallRequiredSystemLibraries)
set(CPACK_PACKAGE_NAME "Ethereum") set(CPACK_PACKAGE_NAME "Ethereum (++)")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Ethereum") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The Ethereum (++) Toolset")
set(CPACK_PACKAGE_VENDOR "ethereum.org") set(CPACK_PACKAGE_VENDOR "ethereum.org")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_VERSION "0.9") set(CPACK_PACKAGE_VERSION "0.9.29")
set(CPACK_GENERATOR "NSIS") set(CPACK_GENERATOR "NSIS")
# seems to be not working # seems to be not working
# set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/alethzero/alethzero.bmp") # set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/alethzero/alethzero.bmp")
# our stuff # our stuff
set(CPACK_COMPONENT_ALETHZERO_GROUP "Applications") #set(CPACK_COMPONENT_ALETHZERO_GROUP "Applications")
set(CPACK_COMPONENT_MIX_GROUP "Applications") #set(CPACK_COMPONENT_MIX_GROUP "Applications")
set(CPACK_COMPONENT_SOLC_GROUP "CLI") #set(CPACK_COMPONENT_SOLC_GROUP "CLI")
set(CPACK_COMPONENT_ETH_GROUP "CLI") #set(CPACK_COMPONENT_ETH_GROUP "CLI")
set(CPACK_COMPONENT_ETHMINER_GROUP "CLI") #set(CPACK_COMPONENT_ETHMINER_GROUP "CLI")
set(CPACK_COMPONENT_RLP_GROUP "CLI") #set(CPACK_COMPONENT_RLP_GROUP "CLI")
set(CPACK_COMPONENT_ABI_GROUP "CLI") #set(CPACK_COMPONENT_ABI_GROUP "CLI")
set(CPACK_COMPONENTS_ALL alethzero mix solc eth ethminer rlp abi) #set(CPACK_COMPONENTS_ALL alethzero mix solc eth ethminer rlp abi)
# nsis specific stuff # nsis specific stuff
if (CMAKE_CL_64) if (CMAKE_CL_64)
@ -536,7 +539,7 @@ if (WIN32)
set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION}") set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION}")
endif() endif()
set(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} Ethereum") set(CPACK_NSIS_DISPLAY_NAME "Ethereum (++)")
set(CPACK_NSIS_HELP_LINK "https://github.com/ethereum/cpp-ethereum") set(CPACK_NSIS_HELP_LINK "https://github.com/ethereum/cpp-ethereum")
set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/ethereum/cpp-ethereum") set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/ethereum/cpp-ethereum")
set(CPACK_NSIS_CONTACT "ethereum.org") set(CPACK_NSIS_CONTACT "ethereum.org")

6
cmake/EthExecutableHelper.cmake

@ -139,6 +139,12 @@ macro(eth_install_executable EXECUTABLE)
COMPONENT ${EXECUTABLE} COMPONENT ${EXECUTABLE}
) )
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/"
DESTINATION bin
CONFIGURATIONS RelWithDebInfo
COMPONENT ${EXECUTABLE}
)
else() else()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin)
endif () endif ()

3
cmake/FindMHD.cmake

@ -38,7 +38,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG}) set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG})
# prepare dlls # prepare dlls
string(REPLACE ".lib" ".dll" MHD_DLL ${MHD_LIBRARY}) string(REPLACE ".lib" ".dll" MHD_DLL ${MHD_LIBRARY})
string(REPLACE "/lib/" "/bin/" MHD_DLL ${MHD_DLL}) string(REPLACE "/lib/" "/bin/" MHD_DLL ${MHD_DLL})
string(REPLACE ".lib" ".dll" MHD_DLL_DEBUG ${MHD_LIBRARY_DEBUG}) string(REPLACE ".lib" ".dll" MHD_DLL_DEBUG ${MHD_LIBRARY_DEBUG})
@ -52,4 +52,3 @@ find_package_handle_standard_args(mhd DEFAULT_MSG
MHD_INCLUDE_DIR MHD_LIBRARY) MHD_INCLUDE_DIR MHD_LIBRARY)
mark_as_advanced(MHD_INCLUDE_DIR MHD_LIBRARY) mark_as_advanced(MHD_INCLUDE_DIR MHD_LIBRARY)

18
cmake/FindOpenCL.cmake

@ -124,6 +124,24 @@ endif()
set(OpenCL_LIBRARIES ${OpenCL_LIBRARY}) set(OpenCL_LIBRARIES ${OpenCL_LIBRARY})
set(OpenCL_INCLUDE_DIRS ${OpenCL_INCLUDE_DIR}) set(OpenCL_INCLUDE_DIRS ${OpenCL_INCLUDE_DIR})
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
find_library(
OpenCL_LIBRARY_DEBUG
NAMES OpenCL_d
)
set(OpenCL_LIBRARIES optimized ${OpenCL_LIBRARY} debug ${OpenCL_LIBRARY_DEBUG})
# prepare dlls
string(REPLACE ".lib" ".dll" OpenCL_DLL ${OpenCL_LIBRARY})
string(REPLACE "/lib/" "/bin/" OpenCL_DLL ${OpenCL_DLL})
string(REPLACE ".lib" ".dll" OpenCL_DLL_DEBUG ${OpenCL_LIBRARY_DEBUG})
string(REPLACE "/lib/" "/bin/" OpenCL_DLL_DEBUG ${OpenCL_DLL_DEBUG})
set(OpenCL_DLLS optimized ${OpenCL_DLL} debug ${OpenCL_DLL_DEBUG})
endif()
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
find_package_handle_standard_args( find_package_handle_standard_args(
OpenCL OpenCL

6
cmake/scripts/copydlls.cmake

@ -8,10 +8,10 @@
# this script is created cause we do not know configuration in multiconfiguration generators at cmake configure phase ;) # this script is created cause we do not know configuration in multiconfiguration generators at cmake configure phase ;)
if ("${CONF}" STREQUAL "Release") if ("${CONF}" STREQUAL "Debug")
set(DLL ${DLL_RELEASE})
else () # Debug
set(DLL ${DLL_DEBUG}) set(DLL ${DLL_DEBUG})
else ()
set(DLL ${DLL_RELEASE})
endif() endif()
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${DLL}" "${DESTINATION}") execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${DLL}" "${DESTINATION}")

3
eth/CMakeLists.txt

@ -30,7 +30,7 @@ if (JSONRPC)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls(${EXECUTABLE} CURL_DLLS) eth_copy_dlls("${EXECUTABLE}" CURL_DLLS)
endif() endif()
endif() endif()
@ -43,6 +43,7 @@ endif()
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls("${EXECUTABLE}" MHD_DLLS) eth_copy_dlls("${EXECUTABLE}" MHD_DLLS)
eth_copy_dlls("${EXECUTABLE}" OpenCL_DLLS)
endif() endif()
if (APPLE) if (APPLE)

2
eth/main.cpp

@ -1090,7 +1090,6 @@ int main(int argc, char** argv)
string jsonAdmin; string jsonAdmin;
bool upnp = true; bool upnp = true;
WithExisting withExisting = WithExisting::Trust; WithExisting withExisting = WithExisting::Trust;
bool jit = false;
string sentinel; string sentinel;
/// Networking params. /// Networking params.
@ -1530,7 +1529,6 @@ int main(int argc, char** argv)
}; };
StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL);
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp);
netPrefs.discovery = !disableDiscovery; netPrefs.discovery = !disableDiscovery;
netPrefs.pin = pinning; netPrefs.pin = pinning;

4
ethminer/CMakeLists.txt

@ -9,9 +9,6 @@ if (JSONRPC)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
endif() endif()
if (ETHASHCL)
include_directories(${OpenCL_INCLUDE_DIRS})
endif ()
set(EXECUTABLE ethminer) set(EXECUTABLE ethminer)
@ -36,6 +33,7 @@ target_link_libraries(${EXECUTABLE} ethash)
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls("${EXECUTABLE}" MHD_DLLS) eth_copy_dlls("${EXECUTABLE}" MHD_DLLS)
eth_copy_dlls("${EXECUTABLE}" OpenCL_DLLS)
endif() endif()
if (APPLE) if (APPLE)

8
ethminer/main.cpp

@ -20,6 +20,14 @@
* Ethereum client. * Ethereum client.
*/ */
// Solves the problem of including windows.h before including winsock.h
// as detailed here:
// http://stackoverflow.com/questions/1372480/c-redefinition-header-files-winsock2-h
#if defined(_WIN32)
#define _WINSOCKAPI_
#include <windows.h>
#endif
#include <thread> #include <thread>
#include <chrono> #include <chrono>
#include <fstream> #include <fstream>

20
evmjit/CMakeLists.txt

@ -16,11 +16,21 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "D
endif() endif()
# LLVM # LLVM
find_package(LLVM 3.7 REQUIRED CONFIG) if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT LLVM_DIR)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") # Workaround for Ubuntu broken LLVM package
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") message(STATUS "Using llvm-3.7-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake")
add_definitions(${LLVM_DEFINITIONS}) execute_process(COMMAND llvm-config-3.7 --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS)
llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen ipo) message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}")
set(LLVM_LIBS "-lLLVMipo -lLLVMVectorize -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMX86Desc -lLLVMX86Info -lLLVMMCDisassembler -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMProfileData -lLLVMInstCombine -lLLVMInstrumentation -lLLVMTransformUtils -lLLVMipa -lLLVMMCJIT -lLLVMExecutionEngine -lLLVMTarget -lLLVMAnalysis -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm")
add_definitions(-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS)
link_directories(/usr/lib/llvm-3.7/lib)
else()
find_package(LLVM 3.7 REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
add_definitions(${LLVM_DEFINITIONS})
llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen ipo)
endif()
get_filename_component(EVMJIT_INCLUDE_DIR include ABSOLUTE) get_filename_component(EVMJIT_INCLUDE_DIR include ABSOLUTE)

3
exp/CMakeLists.txt

@ -6,9 +6,6 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${DB_INCLUDE_DIRS}) include_directories(${DB_INCLUDE_DIRS})
if (ETHASHCL)
include_directories(${OpenCL_INCLUDE_DIRS})
endif ()
set(EXECUTABLE exp) set(EXECUTABLE exp)

1
extdep/getstuff.bat

@ -13,6 +13,7 @@ call :download json-rpc-cpp 0.5.0
call :download leveldb 1.2 call :download leveldb 1.2
call :download llvm 3.7svn call :download llvm 3.7svn
call :download microhttpd 0.9.2 call :download microhttpd 0.9.2
call :download OpenCL_ICD 1
call :download qt 5.4.1 call :download qt 5.4.1
call :download miniupnpc 1.9 call :download miniupnpc 1.9
call :download v8 3.15.9 call :download v8 3.15.9

15
libdevcore/Worker.cpp

@ -50,11 +50,16 @@ void Worker::startWorking()
// cnote << "Trying to set Started: Thread was" << (unsigned)ex << "; " << ok; // cnote << "Trying to set Started: Thread was" << (unsigned)ex << "; " << ok;
(void)ok; (void)ok;
startedWorking(); try
// cnote << "Entering work loop..."; {
workLoop(); startedWorking();
// cnote << "Finishing up worker thread..."; workLoop();
doneWorking(); doneWorking();
}
catch (std::exception const& _e)
{
clog(WarnChannel) << "Exception thrown in Worker thread: " << _e.what();
}
// ex = WorkerState::Stopping; // ex = WorkerState::Stopping;
// m_state.compare_exchange_strong(ex, WorkerState::Stopped); // m_state.compare_exchange_strong(ex, WorkerState::Stopped);

5
libethash-cl/CMakeLists.txt

@ -21,11 +21,10 @@ set(HEADERS ${OUR_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories(${OpenCL_INCLUDE_DIRS})
include_directories(..) include_directories(..)
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
TARGET_LINK_LIBRARIES(${EXECUTABLE} ${OpenCL_LIBRARIES} ethash) target_include_directories(${EXECUTABLE} PUBLIC ${OpenCL_INCLUDE_DIR})
target_link_libraries(${EXECUTABLE} ${OpenCL_LIBRARIES} ethash)
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

88
libethash-cl/ethash_cl_miner.cpp

@ -78,23 +78,37 @@ ethash_cl_miner::~ethash_cl_miner()
finish(); finish();
} }
string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId) std::vector<cl::Platform> ethash_cl_miner::getPlatforms()
{ {
vector<cl::Platform> platforms; vector<cl::Platform> platforms;
cl::Platform::get(&platforms); try
if (platforms.empty())
{ {
ETHCL_LOG("No OpenCL platforms found."); cl::Platform::get(&platforms);
return string();
} }
catch(cl::Error const& err)
{
#if defined(CL_PLATFORM_NOT_FOUND_KHR)
if (err.err() == CL_PLATFORM_NOT_FOUND_KHR)
ETHCL_LOG("No OpenCL platforms found");
else
#endif
throw err;
}
return platforms;
}
string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId)
{
vector<cl::Platform> platforms = getPlatforms();
if (platforms.empty())
return {};
// get GPU device of the selected platform // get GPU device of the selected platform
unsigned platform_num = min<unsigned>(_platformId, platforms.size() - 1); unsigned platform_num = min<unsigned>(_platformId, platforms.size() - 1);
vector<cl::Device> devices = getDevices(platforms, _platformId); vector<cl::Device> devices = getDevices(platforms, _platformId);
if (devices.empty()) if (devices.empty())
{ {
ETHCL_LOG("No OpenCL devices found."); ETHCL_LOG("No OpenCL devices found.");
return string(); return {};
} }
// use selected default device // use selected default device
@ -109,29 +123,35 @@ std::vector<cl::Device> ethash_cl_miner::getDevices(std::vector<cl::Platform> co
{ {
vector<cl::Device> devices; vector<cl::Device> devices;
unsigned platform_num = min<unsigned>(_platformId, _platforms.size() - 1); unsigned platform_num = min<unsigned>(_platformId, _platforms.size() - 1);
_platforms[platform_num].getDevices( try
s_allowCPU ? CL_DEVICE_TYPE_ALL : ETHCL_QUERIED_DEVICE_TYPES, {
&devices _platforms[platform_num].getDevices(
); s_allowCPU ? CL_DEVICE_TYPE_ALL : ETHCL_QUERIED_DEVICE_TYPES,
&devices
);
}
catch (cl::Error const& err)
{
// if simply no devices found return empty vector
if (err.err() != CL_DEVICE_NOT_FOUND)
throw err;
}
return devices; return devices;
} }
unsigned ethash_cl_miner::getNumPlatforms() unsigned ethash_cl_miner::getNumPlatforms()
{ {
vector<cl::Platform> platforms; vector<cl::Platform> platforms = getPlatforms();
cl::Platform::get(&platforms); if (platforms.empty())
return 0;
return platforms.size(); return platforms.size();
} }
unsigned ethash_cl_miner::getNumDevices(unsigned _platformId) unsigned ethash_cl_miner::getNumDevices(unsigned _platformId)
{ {
vector<cl::Platform> platforms; vector<cl::Platform> platforms = getPlatforms();
cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{
ETHCL_LOG("No OpenCL platforms found.");
return 0; return 0;
}
vector<cl::Device> devices = getDevices(platforms, _platformId); vector<cl::Device> devices = getDevices(platforms, _platformId);
if (devices.empty()) if (devices.empty())
@ -160,7 +180,7 @@ bool ethash_cl_miner::configureGPU(
// by default let's only consider the DAG of the first epoch // by default let's only consider the DAG of the first epoch
uint64_t dagSize = ethash_get_datasize(_currentBlock); uint64_t dagSize = ethash_get_datasize(_currentBlock);
uint64_t requiredSize = dagSize + _extraGPUMemory; uint64_t requiredSize = dagSize + _extraGPUMemory;
return searchForAllDevices(_platformId, [&requiredSize](cl::Device const _device) -> bool return searchForAllDevices(_platformId, [&requiredSize](cl::Device const& _device) -> bool
{ {
cl_ulong result; cl_ulong result;
_device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result); _device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result);
@ -172,7 +192,7 @@ bool ethash_cl_miner::configureGPU(
); );
return true; return true;
} }
ETHCL_LOG( ETHCL_LOG(
"OpenCL device " << _device.getInfo<CL_DEVICE_NAME>() "OpenCL device " << _device.getInfo<CL_DEVICE_NAME>()
<< " has insufficient GPU memory." << result << << " has insufficient GPU memory." << result <<
@ -191,13 +211,9 @@ unsigned ethash_cl_miner::s_initialGlobalWorkSize = ethash_cl_miner::c_defaultGl
bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _callback) bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _callback)
{ {
vector<cl::Platform> platforms; vector<cl::Platform> platforms = getPlatforms();
cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{
ETHCL_LOG("No OpenCL platforms found.");
return false; return false;
}
for (unsigned i = 0; i < platforms.size(); ++i) for (unsigned i = 0; i < platforms.size(); ++i)
if (searchForAllDevices(i, _callback)) if (searchForAllDevices(i, _callback))
return true; return true;
@ -207,8 +223,9 @@ bool ethash_cl_miner::searchForAllDevices(function<bool(cl::Device const&)> _cal
bool ethash_cl_miner::searchForAllDevices(unsigned _platformId, function<bool(cl::Device const&)> _callback) bool ethash_cl_miner::searchForAllDevices(unsigned _platformId, function<bool(cl::Device const&)> _callback)
{ {
vector<cl::Platform> platforms; vector<cl::Platform> platforms = getPlatforms();
cl::Platform::get(&platforms); if (platforms.empty())
return false;
if (_platformId >= platforms.size()) if (_platformId >= platforms.size())
return false; return false;
@ -216,27 +233,24 @@ bool ethash_cl_miner::searchForAllDevices(unsigned _platformId, function<bool(cl
for (cl::Device const& device: devices) for (cl::Device const& device: devices)
if (_callback(device)) if (_callback(device))
return true; return true;
return false; return false;
} }
void ethash_cl_miner::doForAllDevices(function<void(cl::Device const&)> _callback) void ethash_cl_miner::doForAllDevices(function<void(cl::Device const&)> _callback)
{ {
vector<cl::Platform> platforms; vector<cl::Platform> platforms = getPlatforms();
cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{
ETHCL_LOG("No OpenCL platforms found.");
return; return;
}
for (unsigned i = 0; i < platforms.size(); ++i) for (unsigned i = 0; i < platforms.size(); ++i)
doForAllDevices(i, _callback); doForAllDevices(i, _callback);
} }
void ethash_cl_miner::doForAllDevices(unsigned _platformId, function<void(cl::Device const&)> _callback) void ethash_cl_miner::doForAllDevices(unsigned _platformId, function<void(cl::Device const&)> _callback)
{ {
vector<cl::Platform> platforms; vector<cl::Platform> platforms = getPlatforms();
cl::Platform::get(&platforms); if (platforms.empty())
return;
if (_platformId >= platforms.size()) if (_platformId >= platforms.size())
return; return;
@ -274,13 +288,9 @@ bool ethash_cl_miner::init(
// get all platforms // get all platforms
try try
{ {
vector<cl::Platform> platforms; vector<cl::Platform> platforms = getPlatforms();
cl::Platform::get(&platforms);
if (platforms.empty()) if (platforms.empty())
{
ETHCL_LOG("No OpenCL platforms found.");
return false; return false;
}
// use selected platform // use selected platform
_platformId = min<unsigned>(_platformId, platforms.size() - 1); _platformId = min<unsigned>(_platformId, platforms.size() - 1);

1
libethash-cl/ethash_cl_miner.h

@ -75,6 +75,7 @@ public:
private: private:
static std::vector<cl::Device> getDevices(std::vector<cl::Platform> const& _platforms, unsigned _platformId); static std::vector<cl::Device> getDevices(std::vector<cl::Platform> const& _platforms, unsigned _platformId);
static std::vector<cl::Platform> getPlatforms();
cl::Context m_context; cl::Context m_context;
cl::CommandQueue m_queue; cl::CommandQueue m_queue;

4
libethcore/CMakeLists.txt

@ -12,10 +12,6 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
if (ETHASHCL)
include_directories(${OpenCL_INCLUDE_DIRS})
endif ()
if (CPUID_FOUND) if (CPUID_FOUND)
include_directories(${Cpuid_INCLUDE_DIRS}) include_directories(${Cpuid_INCLUDE_DIRS})
endif () endif ()

40
libethereum/BlockChainSync.cpp

@ -916,26 +916,23 @@ void PV61Sync::requestSubchain(std::shared_ptr<EthereumPeer> _peer)
if (syncPeer != m_chainSyncPeers.end()) if (syncPeer != m_chainSyncPeers.end())
{ {
// Already downoading, request next batch // Already downoading, request next batch
h256s& d = m_downloadingChainMap.at(syncPeer->second); SubChain const& s = m_downloadingChainMap.at(syncPeer->second);
_peer->requestHashes(d.back()); _peer->requestHashes(s.lastHash);
} }
else if (needsSyncing(_peer)) else if (needsSyncing(_peer))
{ {
if (!m_readyChainMap.empty()) if (!m_readyChainMap.empty())
{ {
clog(NetAllDetail) << "Helping with hashchin download"; clog(NetAllDetail) << "Helping with hashchin download";
h256s& d = m_readyChainMap.begin()->second; SubChain& s = m_readyChainMap.begin()->second;
_peer->requestHashes(d.back()); _peer->requestHashes(s.lastHash);
m_downloadingChainMap[m_readyChainMap.begin()->first] = move(d); m_downloadingChainMap[m_readyChainMap.begin()->first] = move(s);
m_chainSyncPeers[_peer] = m_readyChainMap.begin()->first; m_chainSyncPeers[_peer] = m_readyChainMap.begin()->first;
m_readyChainMap.erase(m_readyChainMap.begin()); m_readyChainMap.erase(m_readyChainMap.begin());
} }
else if (!m_downloadingChainMap.empty() && m_hashScanComplete) else if (!m_downloadingChainMap.empty() && m_hashScanComplete)
{
// Lead syncer is done, just grab whatever we can // Lead syncer is done, just grab whatever we can
h256s& d = m_downloadingChainMap.begin()->second; _peer->requestHashes(m_downloadingChainMap.begin()->second.lastHash);
_peer->requestHashes(d.back());
}
} }
} }
@ -974,7 +971,7 @@ void PV61Sync::completeSubchain(std::shared_ptr<EthereumPeer> _peer, unsigned _n
//Done chain-get //Done chain-get
m_syncingNeededBlocks.clear(); m_syncingNeededBlocks.clear();
for (auto h = m_completeChainMap.rbegin(); h != m_completeChainMap.rend(); ++h) for (auto h = m_completeChainMap.rbegin(); h != m_completeChainMap.rend(); ++h)
m_syncingNeededBlocks.insert(m_syncingNeededBlocks.end(), h->second.begin(), h->second.end()); m_syncingNeededBlocks.insert(m_syncingNeededBlocks.end(), h->second.hashes.begin(), h->second.hashes.end());
m_completeChainMap.clear(); m_completeChainMap.clear();
m_knownHashes.clear(); m_knownHashes.clear();
m_syncingBlockNumber = 0; m_syncingBlockNumber = 0;
@ -1010,7 +1007,7 @@ void PV61Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
if (isSyncing(_peer) && _peer->m_syncHashNumber == m_syncingBlockNumber) if (isSyncing(_peer) && _peer->m_syncHashNumber == m_syncingBlockNumber)
{ {
// End of hash chain, add last chunk to download // End of hash chain, add last chunk to download
m_readyChainMap.insert(make_pair(m_syncingBlockNumber, h256s { _peer->m_latestHash })); m_readyChainMap.insert(make_pair(m_syncingBlockNumber, SubChain{ h256s{ _peer->m_latestHash }, _peer->m_latestHash }));
m_hashScanComplete = true; m_hashScanComplete = true;
_peer->m_syncHashNumber = 0; _peer->m_syncHashNumber = 0;
requestSubchain(_peer); requestSubchain(_peer);
@ -1038,7 +1035,7 @@ void PV61Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
// Got new subchain marker // Got new subchain marker
assert(_hashes.size() == 1); assert(_hashes.size() == 1);
m_knownHashes.insert(_hashes[0]); m_knownHashes.insert(_hashes[0]);
m_readyChainMap.insert(make_pair(m_syncingBlockNumber, h256s { _hashes[0] })); m_readyChainMap.insert(make_pair(m_syncingBlockNumber, SubChain{ h256s{ _hashes[0] }, _hashes[0] }));
if ((m_readyChainMap.size() + m_downloadingChainMap.size() + m_completeChainMap.size()) * c_hashSubchainSize > _peer->m_expectedHashes) if ((m_readyChainMap.size() + m_downloadingChainMap.size() + m_completeChainMap.size()) * c_hashSubchainSize > _peer->m_expectedHashes)
{ {
_peer->disable("Too many hashes from lead peer"); _peer->disable("Too many hashes from lead peer");
@ -1056,7 +1053,7 @@ void PV61Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
{ {
//check downlading peers //check downlading peers
for (auto const& downloader: m_downloadingChainMap) for (auto const& downloader: m_downloadingChainMap)
if (downloader.second.back() == _peer->m_syncHash) if (downloader.second.lastHash == _peer->m_syncHash)
{ {
number = downloader.first; number = downloader.first;
break; break;
@ -1071,7 +1068,7 @@ void PV61Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
} }
auto downloadingPeer = m_downloadingChainMap.find(number); auto downloadingPeer = m_downloadingChainMap.find(number);
if (downloadingPeer == m_downloadingChainMap.end() || downloadingPeer->second.back() != _peer->m_syncHash) if (downloadingPeer == m_downloadingChainMap.end() || downloadingPeer->second.lastHash != _peer->m_syncHash)
{ {
// Too late, other peer has already downloaded our hashes // Too late, other peer has already downloaded our hashes
m_chainSyncPeers.erase(_peer); m_chainSyncPeers.erase(_peer);
@ -1079,7 +1076,7 @@ void PV61Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
return; return;
} }
h256s& hashes = downloadingPeer->second; SubChain& subChain = downloadingPeer->second;
unsigned knowns = 0; unsigned knowns = 0;
unsigned unknowns = 0; unsigned unknowns = 0;
for (unsigned i = 0; i < _hashes.size(); ++i) for (unsigned i = 0; i < _hashes.size(); ++i)
@ -1111,13 +1108,14 @@ void PV61Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
else if (status == QueueStatus::Unknown) else if (status == QueueStatus::Unknown)
{ {
unknowns++; unknowns++;
hashes.push_back(h); subChain.hashes.push_back(h);
} }
else else
knowns++; knowns++;
subChain.lastHash = h;
} }
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << hashes.back(); clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << subChain.lastHash;
if (hashes.size() > c_hashSubchainSize) if (subChain.hashes.size() > c_hashSubchainSize)
{ {
_peer->disable("Too many subchain hashes"); _peer->disable("Too many subchain hashes");
restartSync(); restartSync();
@ -1175,11 +1173,11 @@ SyncStatus PV61Sync::status() const
{ {
res.hashesReceived = 0; res.hashesReceived = 0;
for (auto const& d : m_readyChainMap) for (auto const& d : m_readyChainMap)
res.hashesReceived += d.second.size(); res.hashesReceived += d.second.hashes.size();
for (auto const& d : m_downloadingChainMap) for (auto const& d : m_downloadingChainMap)
res.hashesReceived += d.second.size(); res.hashesReceived += d.second.hashes.size();
for (auto const& d : m_completeChainMap) for (auto const& d : m_completeChainMap)
res.hashesReceived += d.second.size(); res.hashesReceived += d.second.hashes.size();
} }
return res; return res;
} }

17
libethereum/BlockChainSync.h

@ -126,7 +126,6 @@ private:
void logNewBlock(h256 const& _h); void logNewBlock(h256 const& _h);
EthereumHost& m_host; EthereumHost& m_host;
HashDownloadMan m_hashMan;
}; };
@ -308,12 +307,18 @@ private:
/// Check if downloading hashes in parallel /// Check if downloading hashes in parallel
bool isPV61Syncing() const; bool isPV61Syncing() const;
std::map<unsigned, h256s> m_completeChainMap; ///< Fully downloaded subchains struct SubChain
std::map<unsigned, h256s> m_readyChainMap; ///< Subchains ready for download {
std::map<unsigned, h256s> m_downloadingChainMap; ///< Subchains currently being downloading. In sync with m_chainSyncPeers h256s hashes; ///< List of subchain hashes
h256 lastHash; ///< Last requested subchain hash
};
std::map<unsigned, SubChain> m_completeChainMap; ///< Fully downloaded subchains
std::map<unsigned, SubChain> m_readyChainMap; ///< Subchains ready for download
std::map<unsigned, SubChain> m_downloadingChainMap; ///< Subchains currently being downloading. In sync with m_chainSyncPeers
std::map<std::weak_ptr<EthereumPeer>, unsigned, std::owner_less<std::weak_ptr<EthereumPeer>>> m_chainSyncPeers; ///< Peers to m_downloadingSubchain number map std::map<std::weak_ptr<EthereumPeer>, unsigned, std::owner_less<std::weak_ptr<EthereumPeer>>> m_chainSyncPeers; ///< Peers to m_downloadingSubchain number map
h256Hash m_knownHashes; ///< Subchain start markers. Used to track suchain completion h256Hash m_knownHashes; ///< Subchain start markers. Used to track suchain completion
unsigned m_syncingBlockNumber = 0; ///< Current subchain marker unsigned m_syncingBlockNumber = 0; ///< Current subchain marker
bool m_hashScanComplete = false; ///< True if leading peer completed hashchain scan and we have a list of subchains ready bool m_hashScanComplete = false; ///< True if leading peer completed hashchain scan and we have a list of subchains ready
}; };

8
libethereum/TransactionQueue.cpp

@ -161,6 +161,8 @@ ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transactio
{ {
fs->second.erase(t); fs->second.erase(t);
--m_futureSize; --m_futureSize;
if (fs->second.empty())
m_future.erase(fs);
} }
} }
} }
@ -201,11 +203,11 @@ u256 TransactionQueue::maxNonce_WITH_LOCK(Address const& _a) const
u256 ret = 0; u256 ret = 0;
auto cs = m_currentByAddressAndNonce.find(_a); auto cs = m_currentByAddressAndNonce.find(_a);
if (cs != m_currentByAddressAndNonce.end() && !cs->second.empty()) if (cs != m_currentByAddressAndNonce.end() && !cs->second.empty())
ret = cs->second.rbegin()->first; ret = cs->second.rbegin()->first + 1;
auto fs = m_future.find(_a); auto fs = m_future.find(_a);
if (fs != m_future.end() && !fs->second.empty()) if (fs != m_future.end() && !fs->second.empty())
ret = std::max(ret, fs->second.rbegin()->first); ret = std::max(ret, fs->second.rbegin()->first + 1);
return ret + 1; return ret;
} }
void TransactionQueue::insertCurrent_WITH_LOCK(std::pair<h256, Transaction> const& _p) void TransactionQueue::insertCurrent_WITH_LOCK(std::pair<h256, Transaction> const& _p)

41
libp2p/Common.h

@ -37,6 +37,7 @@
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include <libdevcore/Guards.h>
namespace ba = boost::asio; namespace ba = boost::asio;
namespace bi = boost::asio::ip; namespace bi = boost::asio::ip;
@ -214,6 +215,46 @@ struct Node
virtual operator bool() const { return (bool)id; } virtual operator bool() const { return (bool)id; }
}; };
class DeadlineOps
{
class DeadlineOp
{
public:
DeadlineOp(ba::io_service& _io, unsigned _msInFuture, std::function<void(boost::system::error_code const&)> const& _f): m_timer(new ba::deadline_timer(_io)) { m_timer->expires_from_now(boost::posix_time::milliseconds(_msInFuture)); m_timer->async_wait(_f); }
~DeadlineOp() {}
DeadlineOp(DeadlineOp&& _s): m_timer(_s.m_timer.release()) {}
DeadlineOp& operator=(DeadlineOp&& _s) { m_timer.reset(_s.m_timer.release()); return *this; }
bool expired() { Guard l(x_timer); return m_timer->expires_from_now().total_nanoseconds() <= 0; }
void wait() { Guard l(x_timer); m_timer->wait(); }
private:
std::unique_ptr<ba::deadline_timer> m_timer;
Mutex x_timer;
};
public:
DeadlineOps(ba::io_service& _io, unsigned _reapIntervalMs = 100): m_io(_io), m_reapIntervalMs(_reapIntervalMs), m_stopped({false}) { reap(); }
~DeadlineOps() { stop(); }
void schedule(unsigned _msInFuture, std::function<void(boost::system::error_code const&)> const& _f) { if (m_stopped) return; DEV_GUARDED(x_timers) m_timers.emplace_back(m_io, _msInFuture, _f); }
void stop() { m_stopped = true; DEV_GUARDED(x_timers) m_timers.clear(); }
protected:
void reap() { Guard l(x_timers); auto t = m_timers.begin(); while (t != m_timers.end()) if (t->expired()) { t->wait(); m_timers.erase(t); } else t++; m_timers.emplace_back(m_io, m_reapIntervalMs, [this](boost::system::error_code const& ec){ if (!ec) reap(); }); }
private:
ba::io_service& m_io;
unsigned m_reapIntervalMs;
std::vector<DeadlineOp> m_timers;
Mutex x_timers;
std::atomic<bool> m_stopped;
};
} }
/// Simple stream output for a NodeIPEndpoint. /// Simple stream output for a NodeIPEndpoint.

119
libp2p/Host.cpp

@ -120,6 +120,15 @@ Host::~Host()
void Host::start() void Host::start()
{ {
startWorking(); startWorking();
while (isWorking() && !haveNetwork())
this_thread::sleep_for(chrono::milliseconds(10));
// network start failed!
if (isWorking())
return;
clog(NetWarn) << "Network start failed!";
doneWorking();
} }
void Host::stop() void Host::stop()
@ -130,11 +139,12 @@ void Host::stop()
{ {
// Although m_run is set by stop() or start(), it effects m_runTimer so x_runTimer is used instead of a mutex for m_run. // Although m_run is set by stop() or start(), it effects m_runTimer so x_runTimer is used instead of a mutex for m_run.
// when m_run == false, run() will cause this::run() to stop() ioservice
Guard l(x_runTimer); Guard l(x_runTimer);
// ignore if already stopped/stopping // ignore if already stopped/stopping
if (!m_run) if (!m_run)
return; return;
// signal run() to prepare for shutdown and reset m_timer
m_run = false; m_run = false;
} }
@ -143,14 +153,18 @@ void Host::stop()
this_thread::sleep_for(chrono::milliseconds(50)); this_thread::sleep_for(chrono::milliseconds(50));
// stop worker thread // stop worker thread
stopWorking(); if (isWorking())
stopWorking();
} }
void Host::doneWorking() void Host::doneWorking()
{ {
// reset ioservice (allows manually polling network, below) // reset ioservice (cancels all timers and allows manually polling network, below)
m_ioService.reset(); m_ioService.reset();
DEV_GUARDED(x_timers)
m_timers.clear();
// shutdown acceptor // shutdown acceptor
m_tcp4Acceptor.cancel(); m_tcp4Acceptor.cancel();
if (m_tcp4Acceptor.is_open()) if (m_tcp4Acceptor.is_open())
@ -170,15 +184,13 @@ void Host::doneWorking()
// disconnect pending handshake, before peers, as a handshake may create a peer // disconnect pending handshake, before peers, as a handshake may create a peer
for (unsigned n = 0;; n = 0) for (unsigned n = 0;; n = 0)
{ {
{ DEV_GUARDED(x_connecting)
Guard l(x_connecting); for (auto const& i: m_connecting)
for (auto i: m_connecting)
if (auto h = i.lock()) if (auto h = i.lock())
{ {
h->cancel(); h->cancel();
n++; n++;
} }
}
if (!n) if (!n)
break; break;
m_ioService.poll(); m_ioService.poll();
@ -187,8 +199,7 @@ void Host::doneWorking()
// disconnect peers // disconnect peers
for (unsigned n = 0;; n = 0) for (unsigned n = 0;; n = 0)
{ {
{ DEV_RECURSIVE_GUARDED(x_sessions)
RecursiveGuard l(x_sessions);
for (auto i: m_sessions) for (auto i: m_sessions)
if (auto p = i.second.lock()) if (auto p = i.second.lock())
if (p->isConnected()) if (p->isConnected())
@ -196,7 +207,6 @@ void Host::doneWorking()
p->disconnect(ClientQuit); p->disconnect(ClientQuit);
n++; n++;
} }
}
if (!n) if (!n)
break; break;
@ -389,43 +399,42 @@ void Host::runAcceptor()
auto socket = make_shared<RLPXSocket>(new bi::tcp::socket(m_ioService)); auto socket = make_shared<RLPXSocket>(new bi::tcp::socket(m_ioService));
m_tcp4Acceptor.async_accept(socket->ref(), [=](boost::system::error_code ec) m_tcp4Acceptor.async_accept(socket->ref(), [=](boost::system::error_code ec)
{ {
if (peerCount() > 9 * m_idealPeerCount) m_accepting = false;
if (ec || !m_run)
{ {
clog(NetConnect) << "Dropping incoming connect due to maximum peer count (9 * ideal peer count): " << socket->remoteEndpoint(); socket->close();
return;
}
if (peerCount() > Ingress * m_idealPeerCount)
{
clog(NetConnect) << "Dropping incoming connect due to maximum peer count (" << Ingress << " * ideal peer count): " << socket->remoteEndpoint();
socket->close(); socket->close();
if (ec.value() < 1) if (ec.value() < 1)
runAcceptor(); runAcceptor();
return; return;
} }
// if no error code
bool success = false; bool success = false;
if (!ec) try
{ {
try // incoming connection; we don't yet know nodeid
{ auto handshake = make_shared<RLPXHandshake>(this, socket);
// incoming connection; we don't yet know nodeid m_connecting.push_back(handshake);
auto handshake = make_shared<RLPXHandshake>(this, socket); handshake->start();
m_connecting.push_back(handshake); success = true;
handshake->start(); }
success = true; catch (Exception const& _e)
} {
catch (Exception const& _e) clog(NetWarn) << "ERROR: " << diagnostic_information(_e);
{ }
clog(NetWarn) << "ERROR: " << diagnostic_information(_e); catch (std::exception const& _e)
} {
catch (std::exception const& _e) clog(NetWarn) << "ERROR: " << _e.what();
{
clog(NetWarn) << "ERROR: " << _e.what();
}
} }
if (!success) if (!success)
socket->ref().close(); socket->ref().close();
runAcceptor();
m_accepting = false;
if (ec.value() < 1)
runAcceptor();
}); });
} }
} }
@ -465,6 +474,9 @@ void Host::addNode(NodeId const& _node, NodeIPEndpoint const& _endpoint)
void Host::requirePeer(NodeId const& _n, NodeIPEndpoint const& _endpoint) void Host::requirePeer(NodeId const& _n, NodeIPEndpoint const& _endpoint)
{ {
if (!m_run)
return;
Node node(_n, _endpoint, true); Node node(_n, _endpoint, true);
if (_n) if (_n)
{ {
@ -482,22 +494,21 @@ void Host::requirePeer(NodeId const& _n, NodeIPEndpoint const& _endpoint)
p.reset(new Peer(node)); p.reset(new Peer(node));
m_peers[_n] = p; m_peers[_n] = p;
} }
connect(p);
} }
else if (m_nodeTable) else if (m_nodeTable)
{ {
shared_ptr<boost::asio::deadline_timer> t(new boost::asio::deadline_timer(m_ioService));
m_timers.push_back(t);
m_nodeTable->addNode(node); m_nodeTable->addNode(node);
shared_ptr<boost::asio::deadline_timer> t(new boost::asio::deadline_timer(m_ioService));
t->expires_from_now(boost::posix_time::milliseconds(600)); t->expires_from_now(boost::posix_time::milliseconds(600));
t->async_wait([this, _n](boost::system::error_code const& _ec) t->async_wait([this, _n](boost::system::error_code const& _ec)
{ {
if (!_ec && m_nodeTable) if (!_ec)
// FIXME RACE CONDITION (use weak_ptr or mutex). if (m_nodeTable)
if (auto n = m_nodeTable->node(_n)) if (auto n = m_nodeTable->node(_n))
requirePeer(n.id, n.endpoint); requirePeer(n.id, n.endpoint);
}); });
DEV_GUARDED(x_timers)
m_timers.push_back(t);
} }
} }
@ -512,8 +523,6 @@ void Host::connect(std::shared_ptr<Peer> const& _p)
{ {
if (!m_run) if (!m_run)
return; return;
_p->m_lastAttempted = std::chrono::system_clock::now();
if (havePeerSession(_p->id)) if (havePeerSession(_p->id))
{ {
@ -539,6 +548,8 @@ void Host::connect(std::shared_ptr<Peer> const& _p)
m_pendingPeerConns.insert(nptr); m_pendingPeerConns.insert(nptr);
} }
_p->m_lastAttempted = std::chrono::system_clock::now();
bi::tcp::endpoint ep(_p->endpoint); bi::tcp::endpoint ep(_p->endpoint);
clog(NetConnect) << "Attempting connection to node" << _p->id << "@" << ep << "from" << id(); clog(NetConnect) << "Attempting connection to node" << _p->id << "@" << ep << "from" << id();
auto socket = make_shared<RLPXSocket>(new bi::tcp::socket(m_ioService)); auto socket = make_shared<RLPXSocket>(new bi::tcp::socket(m_ioService));
@ -615,21 +626,13 @@ void Host::run(boost::system::error_code const&)
m_nodeTable->processEvents(); m_nodeTable->processEvents();
// cleanup zombies // cleanup zombies
{ DEV_GUARDED(x_connecting)
Guard l(x_connecting); m_connecting.remove_if([](std::weak_ptr<RLPXHandshake> h){ return h.expired(); });
m_connecting.remove_if([](std::weak_ptr<RLPXHandshake> h){ return h.lock(); }); DEV_GUARDED(x_timers)
}
{
Guard l(x_timers);
m_timers.remove_if([](std::shared_ptr<boost::asio::deadline_timer> t) m_timers.remove_if([](std::shared_ptr<boost::asio::deadline_timer> t)
{ {
return t->expires_from_now().total_milliseconds() > 0; return t->expires_from_now().total_milliseconds() < 0;
}); });
}
for (auto p: m_sessions)
if (auto pp = p.second.lock())
pp->serviceNodesRequest();
keepAlivePeers(); keepAlivePeers();
@ -666,13 +669,9 @@ void Host::run(boost::system::error_code const&)
pendingCount = m_pendingPeerConns.size(); pendingCount = m_pendingPeerConns.size();
int openSlots = m_idealPeerCount - peerCount() - pendingCount + reqConn; int openSlots = m_idealPeerCount - peerCount() - pendingCount + reqConn;
if (openSlots > 0) if (openSlots > 0)
{
for (auto p: toConnect) for (auto p: toConnect)
if (!p->required && openSlots--) if (!p->required && openSlots--)
connect(p); connect(p);
m_nodeTable->discover();
}
} }
auto runcb = [this](boost::system::error_code const& error) { run(error); }; auto runcb = [this](boost::system::error_code const& error) { run(error); };

2
libp2p/Host.h

@ -212,7 +212,7 @@ protected:
void restoreNetwork(bytesConstRef _b); void restoreNetwork(bytesConstRef _b);
private: private:
enum PeerSlotRatio { Egress = 2, Ingress = 9 }; enum PeerSlotRatio { Egress = 1, Ingress = 4 };
bool havePeerSession(NodeId const& _id) { return !!peerSession(_id); } bool havePeerSession(NodeId const& _id) { return !!peerSession(_id); }

103
libp2p/NodeTable.cpp

@ -43,33 +43,31 @@ NodeEntry::NodeEntry(NodeId const& _src, Public const& _pubk, NodeIPEndpoint con
NodeTable::NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint, bool _enabled): NodeTable::NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint, bool _enabled):
m_node(Node(_alias.pub(), _endpoint)), m_node(Node(_alias.pub(), _endpoint)),
m_secret(_alias.sec()), m_secret(_alias.sec()),
m_io(_io), m_socket(new NodeSocket(_io, *this, (bi::udp::endpoint)m_node.endpoint)),
m_socket(new NodeSocket(m_io, *this, (bi::udp::endpoint)m_node.endpoint)),
m_socketPointer(m_socket.get()), m_socketPointer(m_socket.get()),
m_bucketRefreshTimer(m_io), m_timers(_io)
m_evictionCheckTimer(m_io),
m_disabled(!_enabled)
{ {
for (unsigned i = 0; i < s_bins; i++) for (unsigned i = 0; i < s_bins; i++)
{
m_state[i].distance = i; m_state[i].distance = i;
m_state[i].modified = chrono::steady_clock::now() - chrono::seconds(1);
}
if (!m_disabled) if (!_enabled)
return;
try
{ {
m_socketPointer->connect(); m_socketPointer->connect();
doRefreshBuckets(boost::system::error_code()); doDiscovery();
}
catch (std::exception const& _e)
{
clog(NetWarn) << "Exception connecting NodeTable socket: " << _e.what();
clog(NetWarn) << "Discovery disabled.";
} }
} }
NodeTable::~NodeTable() NodeTable::~NodeTable()
{ {
// Cancel scheduled tasks to ensure. m_timers.stop();
m_evictionCheckTimer.cancel();
m_bucketRefreshTimer.cancel();
// Disconnect socket so that deallocation is safe.
m_socketPointer->disconnect(); m_socketPointer->disconnect();
} }
@ -117,16 +115,6 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node, NodeRelation _relati
return ret; return ret;
} }
void NodeTable::discover()
{
static chrono::steady_clock::time_point s_lastDiscover = chrono::steady_clock::now() - std::chrono::seconds(30);
if (chrono::steady_clock::now() > s_lastDiscover + std::chrono::seconds(30))
{
s_lastDiscover = chrono::steady_clock::now();
discover(m_node.id);
}
}
list<NodeId> NodeTable::nodes() const list<NodeId> NodeTable::nodes() const
{ {
list<NodeId> nodes; list<NodeId> nodes;
@ -164,14 +152,17 @@ shared_ptr<NodeEntry> NodeTable::nodeEntry(NodeId _id)
return m_nodes.count(_id) ? m_nodes[_id] : shared_ptr<NodeEntry>(); return m_nodes.count(_id) ? m_nodes[_id] : shared_ptr<NodeEntry>();
} }
void NodeTable::discover(NodeId _node, unsigned _round, shared_ptr<set<shared_ptr<NodeEntry>>> _tried) void NodeTable::doDiscover(NodeId _node, unsigned _round, shared_ptr<set<shared_ptr<NodeEntry>>> _tried)
{ {
if (!m_socketPointer->isOpen() || _round == s_maxSteps) // NOTE: ONLY called by doDiscovery!
if (!m_socketPointer->isOpen())
return; return;
if (_round == s_maxSteps) if (_round == s_maxSteps)
{ {
clog(NodeTableEvent) << "Terminating discover after " << _round << " rounds."; clog(NodeTableEvent) << "Terminating discover after " << _round << " rounds.";
doDiscovery();
return; return;
} }
else if (!_round && !_tried) else if (!_round && !_tried)
@ -195,6 +186,7 @@ void NodeTable::discover(NodeId _node, unsigned _round, shared_ptr<set<shared_pt
if (tried.empty()) if (tried.empty())
{ {
clog(NodeTableEvent) << "Terminating discover after " << _round << " rounds."; clog(NodeTableEvent) << "Terminating discover after " << _round << " rounds.";
doDiscovery();
return; return;
} }
@ -203,14 +195,12 @@ void NodeTable::discover(NodeId _node, unsigned _round, shared_ptr<set<shared_pt
_tried->insert(tried.front()); _tried->insert(tried.front());
tried.pop_front(); tried.pop_front();
} }
auto self(shared_from_this()); m_timers.schedule(c_reqTimeout.count() * 2, [this, _node, _round, _tried](boost::system::error_code const& _ec)
m_evictionCheckTimer.expires_from_now(boost::posix_time::milliseconds(c_reqTimeout.count() * 2));
m_evictionCheckTimer.async_wait([this, self, _node, _round, _tried](boost::system::error_code const& _ec)
{ {
if (_ec) if (_ec)
return; clog(NodeTableWarn) << "Discovery timer canceled!";
discover(_node, _round + 1, _tried); doDiscover(_node, _round + 1, _tried);
}); });
} }
@ -310,15 +300,15 @@ void NodeTable::evict(shared_ptr<NodeEntry> _leastSeen, shared_ptr<NodeEntry> _n
if (!m_socketPointer->isOpen()) if (!m_socketPointer->isOpen())
return; return;
unsigned ec; unsigned evicts;
DEV_GUARDED(x_evictions) DEV_GUARDED(x_evictions)
{ {
m_evictions.push_back(EvictionTimeout(make_pair(_leastSeen->id,chrono::steady_clock::now()), _new->id)); m_evictions.push_back(EvictionTimeout(make_pair(_leastSeen->id,chrono::steady_clock::now()), _new->id));
ec = m_evictions.size(); evicts = m_evictions.size();
} }
if (ec == 1) if (evicts == 1)
doCheckEvictions(boost::system::error_code()); doCheckEvictions();
ping(_leastSeen.get()); ping(_leastSeen.get());
} }
@ -348,14 +338,15 @@ void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _en
if (s.nodes.size() >= s_bucketSize) if (s.nodes.size() >= s_bucketSize)
{ {
if (removed)
clog(NodeTableWarn) << "DANGER: Bucket overflow when swapping node position.";
// It's only contested iff nodeentry exists // It's only contested iff nodeentry exists
contested = s.nodes.front().lock(); contested = s.nodes.front().lock();
if (!contested) if (!contested)
{ {
s.nodes.pop_front(); s.nodes.pop_front();
s.nodes.push_back(node); s.nodes.push_back(node);
s.touch();
if (!removed && m_nodeEventHandler) if (!removed && m_nodeEventHandler)
m_nodeEventHandler->appendEvent(node->id, NodeEntryAdded); m_nodeEventHandler->appendEvent(node->id, NodeEntryAdded);
} }
@ -363,8 +354,6 @@ void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _en
else else
{ {
s.nodes.push_back(node); s.nodes.push_back(node);
s.touch();
if (!removed && m_nodeEventHandler) if (!removed && m_nodeEventHandler)
m_nodeEventHandler->appendEvent(node->id, NodeEntryAdded); m_nodeEventHandler->appendEvent(node->id, NodeEntryAdded);
} }
@ -576,14 +565,9 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
} }
} }
void NodeTable::doCheckEvictions(boost::system::error_code const& _ec) void NodeTable::doCheckEvictions()
{ {
if (_ec || !m_socketPointer->isOpen()) m_timers.schedule(c_evictionCheckInterval.count(), [this](boost::system::error_code const& _ec)
return;
auto self(shared_from_this());
m_evictionCheckTimer.expires_from_now(c_evictionCheckInterval);
m_evictionCheckTimer.async_wait([this, self](boost::system::error_code const& _ec)
{ {
if (_ec) if (_ec)
return; return;
@ -605,28 +589,23 @@ void NodeTable::doCheckEvictions(boost::system::error_code const& _ec)
dropNode(n); dropNode(n);
if (evictionsRemain) if (evictionsRemain)
doCheckEvictions(boost::system::error_code()); doCheckEvictions();
}); });
} }
void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec) void NodeTable::doDiscovery()
{ {
if (_ec) m_timers.schedule(c_bucketRefresh.count(), [this](boost::system::error_code const& ec)
return;
clog(NodeTableEvent) << "refreshing buckets";
bool connected = m_socketPointer->isOpen();
if (connected)
{ {
if (ec)
return;
clog(NodeTableEvent) << "performing random discovery";
NodeId randNodeId; NodeId randNodeId;
crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(0, h256::size)); crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(0, h256::size));
crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(h256::size, h256::size)); crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(h256::size, h256::size));
discover(randNodeId); doDiscover(randNodeId);
} });
auto runcb = [this](boost::system::error_code const& error) { doRefreshBuckets(error); };
m_bucketRefreshTimer.expires_from_now(boost::posix_time::milliseconds(c_bucketRefresh.count()));
m_bucketRefreshTimer.async_wait(runcb);
} }
void PingNode::streamRLP(RLPStream& _s) const void PingNode::streamRLP(RLPStream& _s) const

26
libp2p/NodeTable.h

@ -128,6 +128,7 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this<NodeTable>
public: public:
enum NodeRelation { Unknown = 0, Known }; enum NodeRelation { Unknown = 0, Known };
enum DiscoverType { Random = 0 };
/// Constructor requiring host for I/O, credentials, and IP Address and port to listen on. /// Constructor requiring host for I/O, credentials, and IP Address and port to listen on.
NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint, bool _enabled = true); NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint, bool _enabled = true);
@ -145,9 +146,6 @@ public:
/// Add node. Node will be pinged and empty shared_ptr is returned if node has never been seen or NodeId is empty. /// Add node. Node will be pinged and empty shared_ptr is returned if node has never been seen or NodeId is empty.
std::shared_ptr<NodeEntry> addNode(Node const& _node, NodeRelation _relation = NodeRelation::Unknown); std::shared_ptr<NodeEntry> addNode(Node const& _node, NodeRelation _relation = NodeRelation::Unknown);
/// To be called when node table is empty. Runs node discovery with m_node.id as the target in order to populate node-table.
void discover();
/// Returns list of node ids active in node table. /// Returns list of node ids active in node table.
std::list<NodeId> nodes() const; std::list<NodeId> nodes() const;
@ -184,16 +182,14 @@ private:
/// Intervals /// Intervals
/* todo: replace boost::posix_time; change constants to upper camelcase */ /* todo: replace boost::posix_time; change constants to upper camelcase */
boost::posix_time::milliseconds const c_evictionCheckInterval = boost::posix_time::milliseconds(75); ///< Interval at which eviction timeouts are checked. std::chrono::milliseconds const c_evictionCheckInterval = std::chrono::milliseconds(75); ///< Interval at which eviction timeouts are checked.
std::chrono::milliseconds const c_reqTimeout = std::chrono::milliseconds(300); ///< How long to wait for requests (evict, find iterations). std::chrono::milliseconds const c_reqTimeout = std::chrono::milliseconds(300); ///< How long to wait for requests (evict, find iterations).
std::chrono::milliseconds const c_bucketRefresh = std::chrono::milliseconds(7200); ///< Refresh interval prevents bucket from becoming stale. [Kademlia] std::chrono::milliseconds const c_bucketRefresh = std::chrono::milliseconds(7200); ///< Refresh interval prevents bucket from becoming stale. [Kademlia]
struct NodeBucket struct NodeBucket
{ {
unsigned distance; unsigned distance;
TimePoint modified;
std::list<std::weak_ptr<NodeEntry>> nodes; std::list<std::weak_ptr<NodeEntry>> nodes;
void touch() { modified = std::chrono::steady_clock::now(); }
}; };
/// Used to ping endpoint. /// Used to ping endpoint.
@ -210,7 +206,7 @@ private:
/// Used to discovery nodes on network which are close to the given target. /// Used to discovery nodes on network which are close to the given target.
/// Sends s_alpha concurrent requests to nodes nearest to target, for nodes nearest to target, up to s_maxSteps rounds. /// Sends s_alpha concurrent requests to nodes nearest to target, for nodes nearest to target, up to s_maxSteps rounds.
void discover(NodeId _target, unsigned _round = 0, std::shared_ptr<std::set<std::shared_ptr<NodeEntry>>> _tried = std::shared_ptr<std::set<std::shared_ptr<NodeEntry>>>()); void doDiscover(NodeId _target, unsigned _round = 0, std::shared_ptr<std::set<std::shared_ptr<NodeEntry>>> _tried = std::shared_ptr<std::set<std::shared_ptr<NodeEntry>>>());
/// Returns nodes from node table which are closest to target. /// Returns nodes from node table which are closest to target.
std::vector<std::shared_ptr<NodeEntry>> nearestNodeEntries(NodeId _target); std::vector<std::shared_ptr<NodeEntry>> nearestNodeEntries(NodeId _target);
@ -240,10 +236,10 @@ private:
/// Tasks /// Tasks
/// Called by evict() to ensure eviction check is scheduled to run and terminates when no evictions remain. Asynchronous. /// Called by evict() to ensure eviction check is scheduled to run and terminates when no evictions remain. Asynchronous.
void doCheckEvictions(boost::system::error_code const& _ec); void doCheckEvictions();
/// Purges and pings nodes for any buckets which haven't been touched for c_bucketRefresh seconds. /// Looks up a random node at @c_bucketRefresh interval.
void doRefreshBuckets(boost::system::error_code const& _ec); void doDiscovery();
std::unique_ptr<NodeTableEventHandler> m_nodeEventHandler; ///< Event handler for node events. std::unique_ptr<NodeTableEventHandler> m_nodeEventHandler; ///< Event handler for node events.
@ -251,7 +247,7 @@ private:
Secret m_secret; ///< This nodes secret key. Secret m_secret; ///< This nodes secret key.
mutable Mutex x_nodes; ///< LOCK x_state first if both locks are required. Mutable for thread-safe copy in nodes() const. mutable Mutex x_nodes; ///< LOCK x_state first if both locks are required. Mutable for thread-safe copy in nodes() const.
std::unordered_map<NodeId, std::shared_ptr<NodeEntry>> m_nodes; ///< Nodes std::unordered_map<NodeId, std::shared_ptr<NodeEntry>> m_nodes; ///< Known Node Endpoints
mutable Mutex x_state; ///< LOCK x_state first if both x_nodes and x_state locks are required. mutable Mutex x_state; ///< LOCK x_state first if both x_nodes and x_state locks are required.
std::array<NodeBucket, s_bins> m_state; ///< State of p2p node network. std::array<NodeBucket, s_bins> m_state; ///< State of p2p node network.
@ -264,15 +260,11 @@ private:
Mutex x_findNodeTimeout; Mutex x_findNodeTimeout;
std::list<NodeIdTimePoint> m_findNodeTimeout; ///< Timeouts for pending Ping and FindNode requests. std::list<NodeIdTimePoint> m_findNodeTimeout; ///< Timeouts for pending Ping and FindNode requests.
ba::io_service& m_io; ///< Used by bucket refresh timer.
std::shared_ptr<NodeSocket> m_socket; ///< Shared pointer for our UDPSocket; ASIO requires shared_ptr. std::shared_ptr<NodeSocket> m_socket; ///< Shared pointer for our UDPSocket; ASIO requires shared_ptr.
NodeSocket* m_socketPointer; ///< Set to m_socket.get(). Socket is created in constructor and disconnected in destructor to ensure access to pointer is safe. NodeSocket* m_socketPointer; ///< Set to m_socket.get(). Socket is created in constructor and disconnected in destructor to ensure access to pointer is safe.
boost::asio::deadline_timer m_bucketRefreshTimer; ///< Timer which schedules and enacts bucket refresh. DeadlineOps m_timers;
boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions.
bool m_disabled; ///< Disable discovery.
}; };
inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable) inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable)

53
libsolidity/AST.cpp

@ -382,6 +382,27 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getIn
return *m_interfaceFunctionList; return *m_interfaceFunctionList;
} }
string const& ContractDefinition::devDocumentation() const
{
return m_devDocumentation;
}
string const& ContractDefinition::userDocumentation() const
{
return m_userDocumentation;
}
void ContractDefinition::setDevDocumentation(string const& _devDocumentation)
{
m_devDocumentation = _devDocumentation;
}
void ContractDefinition::setUserDocumentation(string const& _userDocumentation)
{
m_userDocumentation = _userDocumentation;
}
vector<Declaration const*> const& ContractDefinition::getInheritableMembers() const vector<Declaration const*> const& ContractDefinition::getInheritableMembers() const
{ {
if (!m_inheritableMembers) if (!m_inheritableMembers)
@ -482,7 +503,7 @@ void StructDefinition::checkRecursion() const
); );
} }
}; };
check(this, {}); check(this, StructPointersSet{});
} }
TypePointer EnumDefinition::getType(ContractDefinition const*) const TypePointer EnumDefinition::getType(ContractDefinition const*) const
@ -830,11 +851,14 @@ void FunctionCall::checkTypeRequirements(TypePointers const*)
return; return;
} }
/// For error message: Struct members that were removed during conversion to memory.
set<string> membersRemovedForStructConstructor;
if (isStructConstructorCall()) if (isStructConstructorCall())
{ {
TypeType const& type = dynamic_cast<TypeType const&>(*expressionType); TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
auto const& structType = dynamic_cast<StructType const&>(*type.getActualType()); auto const& structType = dynamic_cast<StructType const&>(*type.getActualType());
functionType = structType.constructorType(); functionType = structType.constructorType();
membersRemovedForStructConstructor = structType.membersMissingInMemory();
} }
else else
functionType = dynamic_pointer_cast<FunctionType const>(expressionType); functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
@ -847,13 +871,22 @@ void FunctionCall::checkTypeRequirements(TypePointers const*)
// function parameters // function parameters
TypePointers const& parameterTypes = functionType->getParameterTypes(); TypePointers const& parameterTypes = functionType->getParameterTypes();
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size()) if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError( {
string msg =
"Wrong argument count for function call: " + "Wrong argument count for function call: " +
toString(m_arguments.size()) + toString(m_arguments.size()) +
" arguments given but expected " + " arguments given but expected " +
toString(parameterTypes.size()) + toString(parameterTypes.size()) +
"." ".";
)); // Extend error message in case we try to construct a struct with mapping member.
if (isStructConstructorCall() && !membersRemovedForStructConstructor.empty())
{
msg += " Members that have to be skipped in memory:";
for (auto const& member: membersRemovedForStructConstructor)
msg += " " + member;
}
BOOST_THROW_EXCEPTION(createTypeError(msg));
}
if (isPositionalCall) if (isPositionalCall)
{ {
@ -972,10 +1005,22 @@ void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes)
++it; ++it;
} }
if (possibleMembers.size() == 0) if (possibleMembers.size() == 0)
{
auto storageType = ReferenceType::copyForLocationIfReference(
DataLocation::Storage,
m_expression->getType()
);
if (!storageType->getMembers().membersByName(*m_memberName).empty())
BOOST_THROW_EXCEPTION(createTypeError(
"Member \"" + *m_memberName + "\" is not available in " +
type.toString() +
" outside of storage."
));
BOOST_THROW_EXCEPTION(createTypeError( BOOST_THROW_EXCEPTION(createTypeError(
"Member \"" + *m_memberName + "\" not found or not visible " "Member \"" + *m_memberName + "\" not found or not visible "
"after argument-dependent lookup in " + type.toString() "after argument-dependent lookup in " + type.toString()
)); ));
}
else if (possibleMembers.size() > 1) else if (possibleMembers.size() > 1)
BOOST_THROW_EXCEPTION(createTypeError( BOOST_THROW_EXCEPTION(createTypeError(
"Member \"" + *m_memberName + "\" not unique " "Member \"" + *m_memberName + "\" not unique "

10
libsolidity/AST.h

@ -281,6 +281,12 @@ public:
/// Returns the fallback function or nullptr if no fallback function was specified. /// Returns the fallback function or nullptr if no fallback function was specified.
FunctionDefinition const* getFallbackFunction() const; FunctionDefinition const* getFallbackFunction() const;
std::string const& userDocumentation() const;
void setUserDocumentation(std::string const& _userDocumentation);
std::string const& devDocumentation() const;
void setDevDocumentation(std::string const& _devDocumentation);
private: private:
/// Checks that two functions defined in this contract with the same name have different /// Checks that two functions defined in this contract with the same name have different
/// arguments and that there is at most one constructor. /// arguments and that there is at most one constructor.
@ -302,6 +308,10 @@ private:
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers; std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
std::vector<ASTPointer<EventDefinition>> m_events; std::vector<ASTPointer<EventDefinition>> m_events;
// parsed Natspec documentation of the contract.
std::string m_userDocumentation;
std::string m_devDocumentation;
std::vector<ContractDefinition const*> m_linearizedBaseContracts; std::vector<ContractDefinition const*> m_linearizedBaseContracts;
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList; mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
mutable std::unique_ptr<std::vector<ASTPointer<EventDefinition>>> m_interfaceEvents; mutable std::unique_ptr<std::vector<ASTPointer<EventDefinition>>> m_interfaceEvents;

1
libsolidity/CMakeLists.txt

@ -20,6 +20,7 @@ set(EXECUTABLE solidity)
file(GLOB HEADERS "*.h") file(GLOB HEADERS "*.h")
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
add_dependencies(${EXECUTABLE} BuildInfo.h)
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} evmasm) target_link_libraries(${EXECUTABLE} evmasm)

10
libsolidity/CompilerStack.cpp

@ -116,6 +116,7 @@ void CompilerStack::parse()
resolver.resolveNamesAndTypes(*contract); resolver.resolveNamesAndTypes(*contract);
m_contracts[contract->getName()].contract = contract; m_contracts[contract->getName()].contract = contract;
} }
InterfaceHandler interfaceHandler;
for (Source const* source: m_sourceOrder) for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes()) for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
@ -123,6 +124,8 @@ void CompilerStack::parse()
m_globalContext->setCurrentContract(*contract); m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->getCurrentThis()); resolver.updateDeclaration(*m_globalContext->getCurrentThis());
resolver.checkTypeRequirements(*contract); resolver.checkTypeRequirements(*contract);
contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract));
contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract));
m_contracts[contract->getName()].contract = contract; m_contracts[contract->getName()].contract = contract;
} }
m_parseSuccessful = true; m_parseSuccessful = true;
@ -231,6 +234,8 @@ string const& CompilerStack::getMetadata(string const& _contractName, Documentat
Contract const& contract = getContract(_contractName); Contract const& contract = getContract(_contractName);
std::unique_ptr<string const>* doc; std::unique_ptr<string const>* doc;
// checks wheather we already have the documentation
switch (_type) switch (_type)
{ {
case DocumentationType::NatspecUser: case DocumentationType::NatspecUser:
@ -248,8 +253,11 @@ string const& CompilerStack::getMetadata(string const& _contractName, Documentat
default: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
} }
// caches the result
if (!*doc) if (!*doc)
*doc = contract.interfaceHandler->getDocumentation(*contract.contract, _type); doc->reset(new string(contract.interfaceHandler->getDocumentation(*contract.contract, _type)));
return *(*doc); return *(*doc);
} }

17
libsolidity/ExpressionCompiler.cpp

@ -48,12 +48,23 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
{ {
if (!_varDecl.getValue()) if (!_varDecl.getValue())
return; return;
solAssert(!!_varDecl.getValue()->getType(), "Type information not available."); TypePointer type = _varDecl.getValue()->getType();
solAssert(!!type, "Type information not available.");
CompilerContext::LocationSetter locationSetter(m_context, _varDecl); CompilerContext::LocationSetter locationSetter(m_context, _varDecl);
_varDecl.getValue()->accept(*this); _varDecl.getValue()->accept(*this);
utils().convertType(*_varDecl.getValue()->getType(), *_varDecl.getType(), true);
StorageItem(m_context, _varDecl).storeValue(*_varDecl.getType(), _varDecl.getLocation(), true); if (_varDecl.getType()->dataStoredIn(DataLocation::Storage))
{
// reference type, only convert value to mobile type and do final conversion in storeValue.
utils().convertType(*type, *type->mobileType());
type = type->mobileType();
}
else
{
utils().convertType(*type, *_varDecl.getType());
type = _varDecl.getType();
}
StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.getLocation(), true);
} }
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)

26
libsolidity/InterfaceHandler.cpp

@ -16,7 +16,7 @@ InterfaceHandler::InterfaceHandler()
m_lastTag = DocTagType::None; m_lastTag = DocTagType::None;
} }
unique_ptr<string> InterfaceHandler::getDocumentation( string InterfaceHandler::getDocumentation(
ContractDefinition const& _contractDef, ContractDefinition const& _contractDef,
DocumentationType _type DocumentationType _type
) )
@ -24,9 +24,9 @@ unique_ptr<string> InterfaceHandler::getDocumentation(
switch(_type) switch(_type)
{ {
case DocumentationType::NatspecUser: case DocumentationType::NatspecUser:
return getUserDocumentation(_contractDef); return userDocumentation(_contractDef);
case DocumentationType::NatspecDev: case DocumentationType::NatspecDev:
return getDevDocumentation(_contractDef); return devDocumentation(_contractDef);
case DocumentationType::ABIInterface: case DocumentationType::ABIInterface:
return getABIInterface(_contractDef); return getABIInterface(_contractDef);
case DocumentationType::ABISolidityInterface: case DocumentationType::ABISolidityInterface:
@ -34,10 +34,10 @@ unique_ptr<string> InterfaceHandler::getDocumentation(
} }
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
return nullptr; return "";
} }
unique_ptr<string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef) string InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
{ {
Json::Value abi(Json::arrayValue); Json::Value abi(Json::arrayValue);
@ -103,10 +103,10 @@ unique_ptr<string> InterfaceHandler::getABIInterface(ContractDefinition const& _
event["inputs"] = params; event["inputs"] = params;
abi.append(event); abi.append(event);
} }
return unique_ptr<string>(new string(Json::FastWriter().write(abi))); return Json::FastWriter().write(abi);
} }
unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef) string InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef)
{ {
string ret = "contract " + _contractDef.getName() + "{"; string ret = "contract " + _contractDef.getName() + "{";
@ -140,10 +140,10 @@ unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition
ret += ";"; ret += ";";
} }
return unique_ptr<string>(new string(ret + "}")); return ret + "}";
} }
unique_ptr<string> InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef) string InterfaceHandler::userDocumentation(ContractDefinition const& _contractDef)
{ {
Json::Value doc; Json::Value doc;
Json::Value methods(Json::objectValue); Json::Value methods(Json::objectValue);
@ -165,10 +165,10 @@ unique_ptr<string> InterfaceHandler::getUserDocumentation(ContractDefinition con
} }
doc["methods"] = methods; doc["methods"] = methods;
return unique_ptr<string>(new string(Json::FastWriter().write(doc))); return Json::StyledWriter().write(doc);
} }
unique_ptr<string> InterfaceHandler::getDevDocumentation(ContractDefinition const& _contractDef) string InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef)
{ {
// LTODO: Somewhere in this function warnings for mismatch of param names // LTODO: Somewhere in this function warnings for mismatch of param names
// should be thrown // should be thrown
@ -212,7 +212,7 @@ unique_ptr<string> InterfaceHandler::getDevDocumentation(ContractDefinition cons
// LTODO: mismatching parameter name, throw some form of warning and not just an exception // LTODO: mismatching parameter name, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
DocstringParsingError() << DocstringParsingError() <<
errinfo_comment("documented parameter \"" + pair.first + "\" not found found in the function") errinfo_comment("documented parameter \"" + pair.first + "\" not found in the parameter list of the function.")
); );
params[pair.first] = pair.second; params[pair.first] = pair.second;
} }
@ -229,7 +229,7 @@ unique_ptr<string> InterfaceHandler::getDevDocumentation(ContractDefinition cons
} }
doc["methods"] = methods; doc["methods"] = methods;
return unique_ptr<string>(new string(Json::FastWriter().write(doc))); return Json::StyledWriter().write(doc);
} }
/* -- private -- */ /* -- private -- */

25
libsolidity/InterfaceHandler.h

@ -65,28 +65,25 @@ public:
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @param _type The type of the documentation. Can be one of the /// @param _type The type of the documentation. Can be one of the
/// types provided by @c DocumentationType /// types provided by @c DocumentationType
/// @return A unique pointer contained string with the json /// @return A string with the json representation of provided type
/// representation of provided type std::string getDocumentation(
std::unique_ptr<std::string> getDocumentation(
ContractDefinition const& _contractDef, ContractDefinition const& _contractDef,
DocumentationType _type DocumentationType _type
); );
/// Get the ABI Interface of the contract /// Get the ABI Interface of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A string with the json representation of the contract's ABI Interface
/// representation of the contract's ABI Interface std::string getABIInterface(ContractDefinition const& _contractDef);
std::unique_ptr<std::string> getABIInterface(ContractDefinition const& _contractDef); std::string getABISolidityInterface(ContractDefinition const& _contractDef);
std::unique_ptr<std::string> getABISolidityInterface(ContractDefinition const& _contractDef);
/// Get the User documentation of the contract /// Get the User documentation of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A string with the json representation of the contract's user documentation
/// representation of the contract's user documentation std::string userDocumentation(ContractDefinition const& _contractDef);
std::unique_ptr<std::string> getUserDocumentation(ContractDefinition const& _contractDef); /// Genereates the Developer's documentation of the contract
/// Get the Developer's documentation of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A string with the json representation
/// representation of the contract's developer documentation /// of the contract's developer documentation
std::unique_ptr<std::string> getDevDocumentation(ContractDefinition const& _contractDef); std::string devDocumentation(ContractDefinition const& _contractDef);
private: private:
void resetUser(); void resetUser();

2
libsolidity/LValue.cpp

@ -41,7 +41,7 @@ StackVariable::StackVariable(CompilerContext& _compilerContext, Declaration cons
void StackVariable::retrieveValue(SourceLocation const& _location, bool) const void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
{ {
unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset); unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset);
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory if (stackPos + 1 > 16) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
CompilerError() << CompilerError() <<
errinfo_sourceLocation(_location) << errinfo_sourceLocation(_location) <<

1
libsolidity/Parser.cpp

@ -26,6 +26,7 @@
#include <libsolidity/Parser.h> #include <libsolidity/Parser.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <libsolidity/InterfaceHandler.h>
using namespace std; using namespace std;

26
libsolidity/Types.cpp

@ -1040,14 +1040,6 @@ u256 StructType::getStorageSize() const
return max<u256>(1, getMembers().getStorageSize()); return max<u256>(1, getMembers().getStorageSize());
} }
bool StructType::canLiveOutsideStorage() const
{
for (auto const& member: getMembers())
if (!member.type->canLiveOutsideStorage())
return false;
return true;
}
string StructType::toString(bool _short) const string StructType::toString(bool _short) const
{ {
string ret = "struct " + m_struct.getName(); string ret = "struct " + m_struct.getName();
@ -1064,9 +1056,13 @@ MemberList const& StructType::getMembers() const
MemberList::MemberMap members; MemberList::MemberMap members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers()) for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
{ {
TypePointer type = variable->getType();
// Skip all mapping members if we are not in storage.
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
continue;
members.push_back(MemberList::Member( members.push_back(MemberList::Member(
variable->getName(), variable->getName(),
copyForLocationIfReference(variable->getType()), copyForLocationIfReference(type),
variable.get()) variable.get())
); );
} }
@ -1077,8 +1073,7 @@ MemberList const& StructType::getMembers() const
TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const
{ {
auto copy = make_shared<StructType>(m_struct); auto copy = make_shared<StructType>(m_struct, _location);
copy->m_location = _location;
copy->m_isPointer = _isPointer; copy->m_isPointer = _isPointer;
return copy; return copy;
} }
@ -1122,6 +1117,15 @@ u256 StructType::memoryOffsetOfMember(string const& _name) const
return 0; return 0;
} }
set<string> StructType::membersMissingInMemory() const
{
set<string> missing;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
if (!variable->getType()->canLiveOutsideStorage())
missing.insert(variable->getName());
return missing;
}
TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
{ {
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();

9
libsolidity/Types.h

@ -574,14 +574,14 @@ class StructType: public ReferenceType
{ {
public: public:
virtual Category getCategory() const override { return Category::Struct; } virtual Category getCategory() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct): explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage):
ReferenceType(DataLocation::Storage), m_struct(_struct) {} ReferenceType(_location), m_struct(_struct) {}
virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize(bool _padded) const override; virtual unsigned getCalldataEncodedSize(bool _padded) const override;
u256 memorySize() const; u256 memorySize() const;
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override; virtual bool canLiveOutsideStorage() const override { return true; }
virtual std::string toString(bool _short) const override; virtual std::string toString(bool _short) const override;
virtual MemberList const& getMembers() const override; virtual MemberList const& getMembers() const override;
@ -597,6 +597,9 @@ public:
StructDefinition const& structDefinition() const { return m_struct; } StructDefinition const& structDefinition() const { return m_struct; }
/// @returns the set of all members that are removed in the memory version (typically mappings).
std::set<std::string> membersMissingInMemory() const;
private: private:
StructDefinition const& m_struct; StructDefinition const& m_struct;
/// List of member types, will be lazy-initialized because of recursive references. /// List of member types, will be lazy-initialized because of recursive references.

67
libwhisper/WhisperDB.cpp

@ -29,6 +29,7 @@ using namespace dev::shh;
WhisperDB::WhisperDB() WhisperDB::WhisperDB()
{ {
m_readOptions.verify_checksums = true;
string path = dev::getDataDir("shh"); string path = dev::getDataDir("shh");
boost::filesystem::create_directories(path); boost::filesystem::create_directories(path);
leveldb::Options op; leveldb::Options op;
@ -60,6 +61,15 @@ void WhisperDB::insert(dev::h256 const& _key, string const& _value)
BOOST_THROW_EXCEPTION(FailedInsertInLevelDB(status.ToString())); BOOST_THROW_EXCEPTION(FailedInsertInLevelDB(status.ToString()));
} }
void WhisperDB::insert(dev::h256 const& _key, bytes const& _value)
{
leveldb::Slice k((char const*)_key.data(), _key.size);
leveldb::Slice v((char const*)_value.data(), _value.size());
leveldb::Status status = m_db->Put(m_writeOptions, k, v);
if (!status.ok())
BOOST_THROW_EXCEPTION(FailedInsertInLevelDB(status.ToString()));
}
void WhisperDB::kill(dev::h256 const& _key) void WhisperDB::kill(dev::h256 const& _key)
{ {
leveldb::Slice slice((char const*)_key.data(), _key.size); leveldb::Slice slice((char const*)_key.data(), _key.size);
@ -67,3 +77,60 @@ void WhisperDB::kill(dev::h256 const& _key)
if (!status.ok()) if (!status.ok())
BOOST_THROW_EXCEPTION(FailedDeleteInLevelDB(status.ToString())); BOOST_THROW_EXCEPTION(FailedDeleteInLevelDB(status.ToString()));
} }
void WhisperDB::loadAll(std::map<h256, Envelope>& o_dst)
{
leveldb::ReadOptions op;
op.fill_cache = false;
op.verify_checksums = true;
vector<string> wasted;
unsigned now = (unsigned)time(0);
unique_ptr<leveldb::Iterator> it(m_db->NewIterator(op));
for (it->SeekToFirst(); it->Valid(); it->Next())
{
leveldb::Slice const k = it->key();
leveldb::Slice const v = it->value();
bool useless = true;
try
{
RLP rlp((byte const*)v.data(), v.size());
Envelope e(rlp);
h256 h2 = e.sha3();
h256 h1;
if (k.size() == h256::size)
h1 = h256((byte const*)k.data(), h256::ConstructFromPointer);
if (h1 != h2)
cwarn << "Corrupted data in Level DB:" << h1.hex() << "versus" << h2.hex();
else if (e.expiry() > now)
{
o_dst[h1] = e;
useless = false;
}
}
catch(RLPException const& ex)
{
cwarn << "RLPException in WhisperDB::loadAll():" << ex.what();
}
catch(Exception const& ex)
{
cwarn << "Exception in WhisperDB::loadAll():" << ex.what();
}
if (useless)
wasted.push_back(k.ToString());
}
cdebug << "WhisperDB::loadAll(): loaded " << o_dst.size() << ", deleted " << wasted.size() << "messages";
for (auto const& k: wasted)
{
leveldb::Status status = m_db->Delete(m_writeOptions, k);
if (!status.ok())
cwarn << "Failed to delete an entry from Level DB:" << k;
}
}

3
libwhisper/WhisperDB.h

@ -24,6 +24,7 @@
#include <libdevcore/db.h> #include <libdevcore/db.h>
#include <libdevcore/FixedHash.h> #include <libdevcore/FixedHash.h>
#include "Common.h" #include "Common.h"
#include "Message.h"
namespace dev namespace dev
{ {
@ -43,7 +44,9 @@ class WhisperDB
std::string lookup(dev::h256 const& _key) const; std::string lookup(dev::h256 const& _key) const;
void insert(dev::h256 const& _key, std::string const& _value); void insert(dev::h256 const& _key, std::string const& _value);
void insert(dev::h256 const& _key, bytes const& _value);
void kill(dev::h256 const& _key); void kill(dev::h256 const& _key);
void loadAll(std::map<h256, Envelope>& o_dst);
private: private:
leveldb::ReadOptions m_readOptions; leveldb::ReadOptions m_readOptions;

74
libwhisper/WhisperHost.cpp

@ -20,21 +20,24 @@
*/ */
#include "WhisperHost.h" #include "WhisperHost.h"
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libp2p/All.h> #include <libp2p/All.h>
#include "WhisperDB.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
using namespace dev::shh; using namespace dev::shh;
WhisperHost::WhisperHost(): Worker("shh") WhisperHost::WhisperHost(bool _useDB): Worker("shh"), m_useDB(_useDB)
{ {
loadMessagesFromBD();
} }
WhisperHost::~WhisperHost() WhisperHost::~WhisperHost()
{ {
saveMessagesToBD();
} }
void WhisperHost::streamMessage(h256 _m, RLPStream& _s) const void WhisperHost::streamMessage(h256 _m, RLPStream& _s) const
@ -200,3 +203,70 @@ void WhisperHost::noteAdvertiseTopicsOfInterest()
for (auto i: peerSessions()) for (auto i: peerSessions())
i.first->cap<WhisperPeer>().get()->noteAdvertiseTopicsOfInterest(); i.first->cap<WhisperPeer>().get()->noteAdvertiseTopicsOfInterest();
} }
void WhisperHost::saveMessagesToBD()
{
if (!m_useDB)
return;
try
{
WhisperDB db;
ReadGuard g(x_messages);
for (auto const& m: m_messages)
{
RLPStream rlp;
m.second.streamRLP(rlp);
bytes b;
rlp.swapOut(b);
db.insert(m.first, b);
}
}
catch(FailedToOpenLevelDB const& ex)
{
cwarn << "Exception in WhisperHost::saveMessagesToBD() - failed to open DB:" << ex.what();
}
catch(FailedInsertInLevelDB const& ex)
{
cwarn << "Exception in WhisperHost::saveMessagesToBD() - failed to insert:" << ex.what();
}
catch(FailedLookupInLevelDB const& ex)
{
cwarn << "Exception in WhisperHost::saveMessagesToBD() - failed lookup:" << ex.what();
}
catch(FailedDeleteInLevelDB const& ex)
{
cwarn << "Exception in WhisperHost::saveMessagesToBD() - failed to delete:" << ex.what();
}
catch(Exception const& ex)
{
cwarn << "Exception in WhisperHost::saveMessagesToBD():" << ex.what();
}
catch(...)
{
cwarn << "Unknown Exception in WhisperHost::saveMessagesToBD()";
}
}
void WhisperHost::loadMessagesFromBD()
{
if (!m_useDB)
return;
try
{
map<h256, Envelope> m;
WhisperDB db;
db.loadAll(m);
WriteGuard g(x_messages);
m_messages.swap(m);
}
catch(Exception const& ex)
{
cwarn << "Exception in WhisperHost::loadMessagesFromBD():" << ex.what();
}
catch(...)
{
cwarn << "Unknown Exception in WhisperHost::loadMessagesFromBD()";
}
}

12
libwhisper/WhisperHost.h

@ -48,11 +48,10 @@ class WhisperHost: public HostCapability<WhisperPeer>, public Interface, public
friend class WhisperPeer; friend class WhisperPeer;
public: public:
WhisperHost(); WhisperHost(bool _useDB = false);
virtual ~WhisperHost(); virtual ~WhisperHost();
unsigned protocolVersion() const { return WhisperProtocolVersion; } unsigned protocolVersion() const { return WhisperProtocolVersion; }
/// remove old messages void cleanup(); ///< remove old messages
void cleanup();
std::map<h256, Envelope> all() const { dev::ReadGuard l(x_messages); return m_messages; } std::map<h256, Envelope> all() const { dev::ReadGuard l(x_messages); return m_messages; }
TopicBloomFilterHash bloom() const { dev::Guard l(m_filterLock); return m_bloom; } TopicBloomFilterHash bloom() const { dev::Guard l(m_filterLock); return m_bloom; }
@ -62,8 +61,7 @@ public:
virtual void uninstallWatch(unsigned _watchId) override; virtual void uninstallWatch(unsigned _watchId) override;
virtual h256s peekWatch(unsigned _watchId) const override { dev::Guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return h256s(); } } virtual h256s peekWatch(unsigned _watchId) const override { dev::Guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return h256s(); } }
virtual h256s checkWatch(unsigned _watchId) override { cleanup(); dev::Guard l(m_filterLock); h256s ret; try { ret = m_watches.at(_watchId).changes; m_watches.at(_watchId).changes.clear(); } catch (...) {} return ret; } virtual h256s checkWatch(unsigned _watchId) override { cleanup(); dev::Guard l(m_filterLock); h256s ret; try { ret = m_watches.at(_watchId).changes; m_watches.at(_watchId).changes.clear(); } catch (...) {} return ret; }
/// returns IDs of messages, which match specific watch criteria virtual h256s watchMessages(unsigned _watchId) override; ///< returns IDs of messages, which match specific watch criteria
virtual h256s watchMessages(unsigned _watchId) override;
virtual Envelope envelope(h256 _m) const override { try { dev::ReadGuard l(x_messages); return m_messages.at(_m); } catch (...) { return Envelope(); } } virtual Envelope envelope(h256 _m) const override { try { dev::ReadGuard l(x_messages); return m_messages.at(_m); } catch (...) { return Envelope(); } }
protected: protected:
@ -74,6 +72,8 @@ private:
virtual void onStarting() override { startWorking(); } virtual void onStarting() override { startWorking(); }
virtual void onStopping() override { stopWorking(); } virtual void onStopping() override { stopWorking(); }
void streamMessage(h256 _m, RLPStream& _s) const; void streamMessage(h256 _m, RLPStream& _s) const;
void saveMessagesToBD();
void loadMessagesFromBD();
mutable dev::SharedMutex x_messages; mutable dev::SharedMutex x_messages;
std::map<h256, Envelope> m_messages; std::map<h256, Envelope> m_messages;
@ -83,6 +83,8 @@ private:
std::map<h256, InstalledFilter> m_filters; std::map<h256, InstalledFilter> m_filters;
std::map<unsigned, ClientWatch> m_watches; std::map<unsigned, ClientWatch> m_watches;
TopicBloomFilter m_bloom; TopicBloomFilter m_bloom;
bool m_useDB; ///< needed for tests and other special cases
}; };
} }

2
mix/CodeModel.cpp

@ -157,7 +157,7 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler
m_bytes = _compiler.getBytecode(_contractName.toStdString()); m_bytes = _compiler.getBytecode(_contractName.toStdString());
dev::solidity::InterfaceHandler interfaceHandler; dev::solidity::InterfaceHandler interfaceHandler;
m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition)); m_contractInterface = QString::fromStdString(interfaceHandler.getABIInterface(contractDefinition));
if (m_contractInterface.isEmpty()) if (m_contractInterface.isEmpty())
m_contractInterface = "[]"; m_contractInterface = "[]";
if (contractDefinition.getLocation().sourceName.get()) if (contractDefinition.getLocation().sourceName.get())

4
neth/main.cpp

@ -339,7 +339,6 @@ int main(int argc, char** argv)
bool upnp = true; bool upnp = true;
bool forceMining = false; bool forceMining = false;
bool killChain = false; bool killChain = false;
bool jit = false;
bool structuredLogging = false; bool structuredLogging = false;
string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S"; string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S";
string clientName; string clientName;
@ -544,10 +543,9 @@ int main(int argc, char** argv)
cout << credits(); cout << credits();
StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat); StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat);
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp);
auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp");
std::string clientImplString = "N++eth/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); std::string clientImplString = "N++eth/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM));
dev::WebThreeDirect web3( dev::WebThreeDirect web3(
clientImplString, clientImplString,
dbPath, dbPath,

5
solc/CommandLineInterface.cpp

@ -449,6 +449,11 @@ bool CommandLineInterface::processInput()
<< boost::diagnostic_information(_exception); << boost::diagnostic_information(_exception);
return false; return false;
} }
catch (DocstringParsingError const& _exception)
{
cerr << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_exception) << endl;
return false;
}
catch (Exception const& _exception) catch (Exception const& _exception)
{ {
cerr << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl; cerr << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl;

4
solc/jsonCompiler.cpp

@ -147,6 +147,10 @@ string compile(string _input, bool _optimize)
{ {
return formatError(exception, "Internal compiler error", compiler); return formatError(exception, "Internal compiler error", compiler);
} }
catch (DocstringParsingError const& exception)
{
return formatError(exception, "Documentation parsing error", compiler);
}
catch (Exception const& exception) catch (Exception const& exception)
{ {
output["error"] = "Exception during compilation: " + boost::diagnostic_information(exception); output["error"] = "Exception during compilation: " + boost::diagnostic_information(exception);

2
test/TestHelper.h

@ -224,7 +224,7 @@ public:
bool inputLimits = false; bool inputLimits = false;
bool bigData = false; bool bigData = false;
bool wallet = false; bool wallet = false;
bool nonetwork = true; bool nonetwork = false;
bool nodag = true; bool nodag = true;
/// @} /// @}

2
test/libdevcore/rlp.cpp

@ -71,6 +71,7 @@ namespace dev
{ {
bytes payloadToDecode = fromHex(o["out"].get_str()); bytes payloadToDecode = fromHex(o["out"].get_str());
RLP payload(payloadToDecode); RLP payload(payloadToDecode);
ostringstream() << payload;
if (payload.isEmpty()) if (payload.isEmpty())
BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("Decoded Empty RLP!")); BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("Decoded Empty RLP!"));
o["in"] = "VALID"; o["in"] = "VALID";
@ -128,6 +129,7 @@ namespace dev
{ {
bytes payloadToDecode = fromHex(o["out"].get_str()); bytes payloadToDecode = fromHex(o["out"].get_str());
RLP payload(payloadToDecode); RLP payload(payloadToDecode);
ostringstream() << payload;
if (rlpType == RlpType::Test) if (rlpType == RlpType::Test)
dev::test::checkRLPAgainstJson(inputData, payload); dev::test::checkRLPAgainstJson(inputData, payload);

40
test/libp2p/net.cpp

@ -308,35 +308,17 @@ BOOST_AUTO_TEST_CASE(kademlia)
if (test::Options::get().nonetwork) if (test::Options::get().nonetwork)
return; return;
// Not yet a 'real' test.
TestNodeTableHost node(8); TestNodeTableHost node(8);
node.start(); node.start();
node.nodeTable->discover(); // ideally, joining with empty node table logs warning we can check for
node.setup(); node.setup();
node.populate(); node.populate();
// clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
node.populateAll(); node.populateAll();
// clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
auto nodes = node.nodeTable->nodes(); auto nodes = node.nodeTable->nodes();
nodes.sort(); nodes.sort();
node.nodeTable->reset(); node.nodeTable->reset();
// clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
node.populate(1); node.populate(1);
// clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
node.nodeTable->discover();
this_thread::sleep_for(chrono::milliseconds(2000)); this_thread::sleep_for(chrono::milliseconds(2000));
// clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
BOOST_REQUIRE_EQUAL(node.nodeTable->count(), 8); BOOST_REQUIRE_EQUAL(node.nodeTable->count(), 8);
auto netNodes = node.nodeTable->nodes();
netNodes.sort();
} }
BOOST_AUTO_TEST_CASE(udpOnce) BOOST_AUTO_TEST_CASE(udpOnce)
@ -355,6 +337,28 @@ BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(netTypes) BOOST_AUTO_TEST_SUITE(netTypes)
BOOST_AUTO_TEST_CASE(deadlineTimer)
{
ba::io_service io;
ba::deadline_timer t(io);
bool start = false;
boost::system::error_code ec;
std::atomic<unsigned> fired(0);
thread thread([&](){ while(!start) this_thread::sleep_for(chrono::milliseconds(10)); io.run(); });
t.expires_from_now(boost::posix_time::milliseconds(200));
start = true;
t.async_wait([&](boost::system::error_code const& _ec){ ec = _ec; fired++; });
BOOST_REQUIRE_NO_THROW(t.wait());
this_thread::sleep_for(chrono::milliseconds(250));
auto expire = t.expires_from_now().total_milliseconds();
BOOST_REQUIRE(expire <= 0);
BOOST_REQUIRE(fired == 1);
BOOST_REQUIRE(!ec);
io.stop();
thread.join();
}
BOOST_AUTO_TEST_CASE(unspecifiedNode) BOOST_AUTO_TEST_CASE(unspecifiedNode)
{ {
if (test::Options::get().nonetwork) if (test::Options::get().nonetwork)

7
test/libp2p/peer.cpp

@ -57,18 +57,19 @@ BOOST_AUTO_TEST_CASE(host)
this_thread::sleep_for(chrono::milliseconds(step)); this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(host1.isStarted() && host2.isStarted()); BOOST_REQUIRE(host1.isStarted() && host2.isStarted());
host1.addNode(node2, NodeIPEndpoint(bi::address::from_string("127.0.0.1"), host2prefs.listenPort, host2prefs.listenPort));
for (int i = 0; i < 3000 && (!host1.haveNetwork() || !host2.haveNetwork()); i += step) for (int i = 0; i < 3000 && (!host1.haveNetwork() || !host2.haveNetwork()); i += step)
this_thread::sleep_for(chrono::milliseconds(step)); this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(host1.haveNetwork() && host2.haveNetwork()); BOOST_REQUIRE(host1.haveNetwork() && host2.haveNetwork());
host1.addNode(node2, NodeIPEndpoint(bi::address::from_string("127.0.0.1"), host2prefs.listenPort, host2prefs.listenPort));
for (int i = 0; i < 3000 && (!host1.peerCount() || !host2.peerCount()); i += step) for (int i = 0; i < 3000 && (!host1.peerCount() || !host2.peerCount()); i += step)
this_thread::sleep_for(chrono::milliseconds(step)); this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE_EQUAL(host1.peerCount(), 1); //Temporary disabled
BOOST_REQUIRE_EQUAL(host2.peerCount(), 1); //BOOST_REQUIRE_EQUAL(host1.peerCount(), 1);
//BOOST_REQUIRE_EQUAL(host2.peerCount(), 1);
} }
BOOST_AUTO_TEST_CASE(networkConfig) BOOST_AUTO_TEST_CASE(networkConfig)

65
test/libsolidity/SolidityEndToEndTest.cpp

@ -25,6 +25,7 @@
#include <tuple> #include <tuple>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <libdevcore/Hash.h> #include <libdevcore/Hash.h>
#include <libsolidity/Exceptions.h>
#include <test/libsolidity/solidityExecutionFramework.h> #include <test/libsolidity/solidityExecutionFramework.h>
using namespace std; using namespace std;
@ -4657,6 +4658,32 @@ BOOST_AUTO_TEST_CASE(bytes_memory_index_access)
) == encodeArgs(u256(data.size()), string("d"))); ) == encodeArgs(u256(data.size()), string("d")));
} }
BOOST_AUTO_TEST_CASE(dev_title_at_function_error)
{
char const* sourceCode = " /// @author Lefteris\n"
" /// @title Just a test contract\n"
"contract test {\n"
" /// @dev Mul function\n"
" /// @title I really should not be here\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
compileRequireThrow<DocstringParsingError>(sourceCode);
}
BOOST_AUTO_TEST_CASE(dev_documenting_nonexistant_param)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter\n"
" /// @param not_existing Documentation for the second parameter\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
compileRequireThrow<DocstringParsingError>(sourceCode);
}
BOOST_AUTO_TEST_CASE(storage_array_ref) BOOST_AUTO_TEST_CASE(storage_array_ref)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
@ -5033,6 +5060,44 @@ BOOST_AUTO_TEST_CASE(literal_strings)
BOOST_CHECK(callContractFunction("empty()") == encodeDyn(string())); BOOST_CHECK(callContractFunction("empty()") == encodeDyn(string()));
} }
BOOST_AUTO_TEST_CASE(initialise_string_constant)
{
char const* sourceCode = R"(
contract Test {
string public short = "abcdef";
string public long = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
}
)";
compileAndRun(sourceCode, 0, "Test");
string longStr = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
string shortStr = "abcdef";
BOOST_CHECK(callContractFunction("long()") == encodeDyn(longStr));
BOOST_CHECK(callContractFunction("short()") == encodeDyn(shortStr));
}
BOOST_AUTO_TEST_CASE(memory_structs_with_mappings)
{
char const* sourceCode = R"(
contract Test {
struct S { uint8 a; mapping(uint => uint) b; uint8 c; }
S s;
function f() returns (uint) {
S memory x;
if (x.a != 0 || x.c != 0) return 1;
x.a = 4; x.c = 5;
s = x;
if (s.a != 4 || s.c != 5) return 2;
x = S(2, 3);
if (x.a != 2 || x.c != 3) return 3;
x = s;
if (s.a != 4 || s.c != 5) return 4;
}
}
)";
compileAndRun(sourceCode, 0, "Test");
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0)));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

15
test/libsolidity/SolidityNameAndTypeResolution.cpp

@ -2134,6 +2134,21 @@ BOOST_AUTO_TEST_CASE(invalid_integer_literal_exp)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
} }
BOOST_AUTO_TEST_CASE(memory_structs_with_mappings)
{
char const* text = R"(
contract Test {
struct S { uint8 a; mapping(uint => uint) b; uint8 c; }
S s;
function f() {
S memory x;
x.b[1];
}
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

93
test/libsolidity/SolidityNatspecJSON.cpp

@ -21,6 +21,7 @@
*/ */
#include "../TestHelper.h" #include "../TestHelper.h"
#include <string>
#include <json/json.h> #include <json/json.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
@ -38,9 +39,11 @@ class DocumentationChecker
public: public:
DocumentationChecker(): m_compilerStack(false) {} DocumentationChecker(): m_compilerStack(false) {}
void checkNatspec(std::string const& _code, void checkNatspec(
std::string const& _expectedDocumentationString, std::string const& _code,
bool _userDocumentation) std::string const& _expectedDocumentationString,
bool _userDocumentation
)
{ {
std::string generatedDocumentationString; std::string generatedDocumentationString;
ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing failed"); ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing failed");
@ -53,9 +56,11 @@ public:
m_reader.parse(generatedDocumentationString, generatedDocumentation); m_reader.parse(generatedDocumentationString, generatedDocumentation);
Json::Value expectedDocumentation; Json::Value expectedDocumentation;
m_reader.parse(_expectedDocumentationString, expectedDocumentation); m_reader.parse(_expectedDocumentationString, expectedDocumentation);
BOOST_CHECK_MESSAGE(expectedDocumentation == generatedDocumentation, BOOST_CHECK_MESSAGE(
"Expected " << _expectedDocumentationString << expectedDocumentation == generatedDocumentation,
"\n but got:\n" << generatedDocumentationString); "Expected " << _expectedDocumentationString <<
"\n but got:\n" << generatedDocumentationString
);
} }
private: private:
@ -229,18 +234,6 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params)
checkNatspec(sourceCode, natspec, false); checkNatspec(sourceCode, natspec, false);
} }
BOOST_AUTO_TEST_CASE(dev_documenting_nonexistant_param)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter\n"
" /// @param not_existing Documentation for the second parameter\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
BOOST_CHECK_THROW(checkNatspec(sourceCode, "", false), DocstringParsingError);
}
BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) BOOST_AUTO_TEST_CASE(dev_mutiline_param_description)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = "contract test {\n"
@ -488,46 +481,48 @@ BOOST_AUTO_TEST_CASE(dev_author_at_function)
checkNatspec(sourceCode, natspec, false); checkNatspec(sourceCode, natspec, false);
} }
BOOST_AUTO_TEST_CASE(dev_title_at_function_error)
{
char const* sourceCode = " /// @author Lefteris\n"
" /// @title Just a test contract\n"
"contract test {\n"
" /// @dev Mul function\n"
" /// @title I really should not be here\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
BOOST_CHECK_THROW(checkNatspec(sourceCode, "", false), DocstringParsingError);
}
BOOST_AUTO_TEST_CASE(natspec_notice_without_tag) BOOST_AUTO_TEST_CASE(natspec_notice_without_tag)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = R"(
" /// I do something awesome\n" contract test {
" function mul(uint a) returns(uint d) { return a * 7; }\n" /// I do something awesome
"}\n"; function mul(uint a) returns(uint d) { return a * 7; }
}
)";
char const* natspec = "{"
"\"methods\":{" char const* natspec = R"ABCDEF(
" \"mul(uint256)\":{ \"notice\": \"I do something awesome\"}" {
"}}"; "methods" : {
"mul(uint256)" : {
"notice" : "I do something awesome"
}
}
}
)ABCDEF";
checkNatspec(sourceCode, natspec, true); checkNatspec(sourceCode, natspec, true);
} }
BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag) BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = R"(
" /// I do something awesome\n" contract test {
" /// which requires two lines to explain\n" /// I do something awesome
" function mul(uint a) returns(uint d) { return a * 7; }\n" /// which requires two lines to explain
"}\n"; function mul(uint a) returns(uint d) { return a * 7; }
}
char const* natspec = "{" )";
"\"methods\":{"
" \"mul(uint256)\":{ \"notice\": \"I do something awesome which requires two lines to explain\"}" char const* natspec = R"ABCDEF(
"}}"; {
"methods" : {
"mul(uint256)" : {
"notice" : "I do something awesome which requires two lines to explain"
}
}
}
)ABCDEF";
checkNatspec(sourceCode, natspec, true); checkNatspec(sourceCode, natspec, true);
} }

41
test/libsolidity/SolidityWallet.cpp

@ -89,11 +89,16 @@ contract multiowned {
// constructor is given number of sigs required to do protected "onlymanyowners" transactions // constructor is given number of sigs required to do protected "onlymanyowners" transactions
// as well as the selection of addresses capable of confirming them. // as well as the selection of addresses capable of confirming them.
function multiowned() { function multiowned(address[] _owners, uint _required) {
m_required = 1; m_numOwners = _owners.length + 1;
m_numOwners = 1; m_owners[1] = uint(msg.sender);
m_owners[m_numOwners] = uint(msg.sender); m_ownerIndex[uint(msg.sender)] = 1;
m_ownerIndex[uint(msg.sender)] = m_numOwners; for (uint i = 0; i < _owners.length; ++i)
{
m_owners[2 + i] = uint(_owners[i]);
m_ownerIndex[uint(_owners[i])] = 2 + i;
}
m_required = _required;
} }
// Revokes a prior confirmation of the given operation // Revokes a prior confirmation of the given operation
@ -122,6 +127,7 @@ contract multiowned {
m_ownerIndex[uint(_to)] = ownerIndex; m_ownerIndex[uint(_to)] = ownerIndex;
OwnerChanged(_from, _to); OwnerChanged(_from, _to);
} }
function addOwner(address _owner) onlymanyowners(sha3(msg.data, block.number)) external { function addOwner(address _owner) onlymanyowners(sha3(msg.data, block.number)) external {
if (isOwner(_owner)) return; if (isOwner(_owner)) return;
@ -346,15 +352,10 @@ contract Wallet is multisig, multiowned, daylimit {
bytes data; bytes data;
} }
// EVENTS
event Created(bytes32 indexed identifier);
// METHODS // METHODS
// constructor - just pass on the owner arra to the multiowned. // constructor - just pass on the owner array to the multiowned.
function Wallet(bytes32 identifier) { function Wallet(address[] _owners, uint _required) multiowned(_owners, _required) {
Created(identifier);
} }
// kills the contract sending everything to `_to`. // kills the contract sending everything to `_to`.
@ -423,7 +424,7 @@ static unique_ptr<bytes> s_compiledWallet;
class WalletTestFramework: public ExecutionFramework class WalletTestFramework: public ExecutionFramework
{ {
protected: protected:
void deployWallet(u256 const& _value = 0) void deployWallet(u256 const& _value = 0, vector<u256> const& _owners = vector<u256>{}, u256 _required = 1)
{ {
if (!s_compiledWallet) if (!s_compiledWallet)
{ {
@ -433,7 +434,8 @@ protected:
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
s_compiledWallet.reset(new bytes(m_compiler.getBytecode("Wallet"))); s_compiledWallet.reset(new bytes(m_compiler.getBytecode("Wallet")));
} }
sendMessage(*s_compiledWallet, true, _value); bytes args = encodeArgs(u256(0x40), _required, u256(_owners.size()), _owners);
sendMessage(*s_compiledWallet + args, true, _value);
BOOST_REQUIRE(!m_output.empty()); BOOST_REQUIRE(!m_output.empty());
} }
}; };
@ -506,6 +508,17 @@ BOOST_AUTO_TEST_CASE(remove_owner)
BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true)); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true));
} }
BOOST_AUTO_TEST_CASE(initial_owners)
{
deployWallet(200, vector<u256>{5, 6, 7}, 2);
BOOST_CHECK(callContractFunction("m_numOwners()") == encodeArgs(u256(4)));
BOOST_CHECK(callContractFunction("isOwner(address)", h256(m_sender, h256::AlignRight)) == encodeArgs(true));
BOOST_CHECK(callContractFunction("isOwner(address)", u256(5)) == encodeArgs(true));
BOOST_CHECK(callContractFunction("isOwner(address)", u256(6)) == encodeArgs(true));
BOOST_CHECK(callContractFunction("isOwner(address)", u256(7)) == encodeArgs(true));
BOOST_CHECK(callContractFunction("isOwner(address)", u256(8)) == encodeArgs(false));
}
BOOST_AUTO_TEST_CASE(multisig_value_transfer) BOOST_AUTO_TEST_CASE(multisig_value_transfer)
{ {
deployWallet(200); deployWallet(200);

9
test/libsolidity/solidityExecutionFramework.h

@ -28,6 +28,7 @@
#include <libethereum/State.h> #include <libethereum/State.h>
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/Exceptions.h>
namespace dev namespace dev
{ {
@ -57,6 +58,14 @@ public:
return m_output; return m_output;
} }
template <class Exceptiontype>
void compileRequireThrow(std::string const& _sourceCode)
{
m_compiler.reset(false, m_addStandardSources);
m_compiler.addSource("", _sourceCode);
BOOST_REQUIRE_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), Exceptiontype);
}
bytes const& compileAndRun( bytes const& compileAndRun(
std::string const& _sourceCode, std::string const& _sourceCode,
u256 const& _value = 0, u256 const& _value = 0,

89
test/libwhisper/whisperDB.cpp

@ -21,13 +21,21 @@ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
#include <thread> #include <thread>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <libp2p/Host.h>
#include <libwhisper/WhisperDB.h> #include <libwhisper/WhisperDB.h>
#include <libwhisper/WhisperHost.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::shh; using namespace dev::shh;
BOOST_AUTO_TEST_SUITE(whisperDB) struct P2PFixture
{
P2PFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = true; }
~P2PFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = false; }
};
BOOST_FIXTURE_TEST_SUITE(whisperDB, P2PFixture)
BOOST_AUTO_TEST_CASE(basic) BOOST_AUTO_TEST_CASE(basic)
{ {
@ -119,4 +127,83 @@ BOOST_AUTO_TEST_CASE(persistence)
} }
} }
BOOST_AUTO_TEST_CASE(messages)
{
cnote << "Testing load/save Whisper messages...";
VerbosityHolder setTemporaryLevel(2);
unsigned const TestSize = 3;
map<h256, Envelope> m1;
map<h256, Envelope> preexisting;
KeyPair us = KeyPair::create();
{
p2p::Host h("Test");
auto wh = h.registerCapability(new WhisperHost(true));
preexisting = wh->all();
cnote << preexisting.size() << "preexisting messages in DB";
for (unsigned i = 0; i < TestSize; ++i)
wh->post(us.sec(), RLPStream().append(i).out(), BuildTopic("test"), 0xFFFFF);
m1 = wh->all();
}
{
p2p::Host h("Test");
auto wh = h.registerCapability(new WhisperHost(true));
map<h256, Envelope> m2 = wh->all();
BOOST_REQUIRE_EQUAL(m1.size(), m2.size());
BOOST_REQUIRE_EQUAL(m1.size() - preexisting.size(), TestSize);
for (auto i: m1)
{
RLPStream rlp1;
RLPStream rlp2;
i.second.streamRLP(rlp1);
m2[i.first].streamRLP(rlp2);
BOOST_REQUIRE_EQUAL(rlp1.out().size(), rlp2.out().size());
for (unsigned j = 0; j < rlp1.out().size(); ++j)
BOOST_REQUIRE_EQUAL(rlp1.out()[j], rlp2.out()[j]);
}
}
WhisperDB db;
unsigned x = 0;
for (auto i: m1)
if (preexisting.find(i.first) == preexisting.end())
{
db.kill(i.first);
++x;
}
BOOST_REQUIRE_EQUAL(x, TestSize);
}
BOOST_AUTO_TEST_CASE(corruptedData)
{
cnote << "Testing corrupted data...";
VerbosityHolder setTemporaryLevel(2);
map<h256, Envelope> m;
h256 x = h256::random();
{
WhisperDB db;
db.insert(x, "this is a test input, representing corrupt data");
}
{
p2p::Host h("Test");
auto wh = h.registerCapability(new WhisperHost(true));
m = wh->all();
BOOST_REQUIRE(m.end() == m.find(x));
}
{
WhisperDB db;
string s = db.lookup(x);
BOOST_REQUIRE(s.empty());
}
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

24
test/libwhisper/whisperTopic.cpp

@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
cnote << "Testing Whisper async forwarding..."; cnote << "Testing Whisper async forwarding...";
VerbosityHolder setTemporaryLevel(2); VerbosityHolder setTemporaryLevel(2);
unsigned const TestValue = 8456;
unsigned result = 0; unsigned result = 0;
bool done = false; bool done = false;
@ -228,18 +228,13 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
while (!host1.haveNetwork()) while (!host1.haveNetwork())
this_thread::sleep_for(chrono::milliseconds(2)); this_thread::sleep_for(chrono::milliseconds(2));
auto w = whost1->installWatch(BuildTopicMask("test")); // only interested in odd packets
bool startedForwarder = false; bool startedForwarder = false;
std::thread forwarder([&]() std::thread forwarder([&]()
{ {
setThreadName("forwarder"); setThreadName("forwarder");
this_thread::sleep_for(chrono::milliseconds(50));
this_thread::sleep_for(chrono::milliseconds(500));
startedForwarder = true; startedForwarder = true;
/// Only interested in odd packets
auto w = whost1->installWatch(BuildTopicMask("test"));
while (!done) while (!done)
{ {
for (auto i: whost1->checkWatch(w)) for (auto i: whost1->checkWatch(w))
@ -261,13 +256,13 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
host2.start(); host2.start();
while (!host2.haveNetwork()) while (!host2.haveNetwork())
this_thread::sleep_for(chrono::milliseconds(2)); this_thread::sleep_for(chrono::milliseconds(2));
host2.addNode(host1.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30305, 30305));
while (!host2.peerCount()) host2.requirePeer(host1.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30305, 30305));
while (!host2.peerCount() || !host1.peerCount())
this_thread::sleep_for(chrono::milliseconds(5)); this_thread::sleep_for(chrono::milliseconds(5));
KeyPair us = KeyPair::create(); KeyPair us = KeyPair::create();
whost2->post(us.sec(), RLPStream().append(1).out(), BuildTopic("test")); whost2->post(us.sec(), RLPStream().append(TestValue).out(), BuildTopic("test"), 777000);
this_thread::sleep_for(chrono::milliseconds(250)); this_thread::sleep_for(chrono::milliseconds(250));
} }
@ -278,10 +273,9 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
ph.start(); ph.start();
while (!ph.haveNetwork()) while (!ph.haveNetwork())
this_thread::sleep_for(chrono::milliseconds(2)); this_thread::sleep_for(chrono::milliseconds(2));
ph.addNode(host1.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30305, 30305));
/// Only interested in odd packets auto w = wh->installWatch(BuildTopicMask("test")); // only interested in odd packets
auto w = wh->installWatch(BuildTopicMask("test")); ph.requirePeer(host1.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), 30305, 30305));
for (int i = 0; i < 200 && !result; ++i) for (int i = 0; i < 200 && !result; ++i)
{ {
@ -298,7 +292,7 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
done = true; done = true;
forwarder.join(); forwarder.join();
BOOST_REQUIRE_EQUAL(result, 1); BOOST_REQUIRE_EQUAL(result, TestValue);
} }
BOOST_AUTO_TEST_CASE(topicAdvertising) BOOST_AUTO_TEST_CASE(topicAdvertising)

Loading…
Cancel
Save