Browse Source

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

win-jit-dll

Conflicts:
	eth/CMakeLists.txt
cl-refactor
arkpar 10 years ago
parent
commit
bd28d0fde5
  1. 29
      CMakeLists.txt
  2. 6
      cmake/EthExecutableHelper.cmake
  3. 1
      cmake/FindMHD.cmake
  4. 18
      cmake/FindOpenCL.cmake
  5. 6
      cmake/scripts/copydlls.cmake
  6. 6
      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. 9
      libdevcore/Worker.cpp
  14. 5
      libethash-cl/CMakeLists.txt
  15. 74
      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. 13
      libethereum/BlockChainSync.h
  20. 8
      libethereum/TransactionQueue.cpp
  21. 41
      libp2p/Common.h
  22. 83
      libp2p/Host.cpp
  23. 2
      libp2p/Host.h
  24. 99
      libp2p/NodeTable.cpp
  25. 24
      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. 40
      test/libp2p/net.cpp
  47. 7
      test/libp2p/peer.cpp
  48. 65
      test/libsolidity/SolidityEndToEndTest.cpp
  49. 15
      test/libsolidity/SolidityNameAndTypeResolution.cpp
  50. 87
      test/libsolidity/SolidityNatspecJSON.cpp
  51. 41
      test/libsolidity/SolidityWallet.cpp
  52. 9
      test/libsolidity/solidityExecutionFramework.h
  53. 89
      test/libwhisper/whisperDB.cpp
  54. 24
      test/libwhisper/whisperTopic.cpp

29
CMakeLists.txt

@ -44,7 +44,7 @@ option(TESTS "Build the tests." ON)
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(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
function(configureProject)
@ -219,6 +219,9 @@ if (GUI)
set(JSONRPC ON)
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")
set(SERPENT OFF)
set(SOLIDITY OFF)
@ -510,26 +513,26 @@ endif ()
if (WIN32)
# packaging stuff
include(InstallRequiredSystemLibraries)
set(CPACK_PACKAGE_NAME "Ethereum")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Ethereum")
set(CPACK_PACKAGE_NAME "Ethereum (++)")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The Ethereum (++) Toolset")
set(CPACK_PACKAGE_VENDOR "ethereum.org")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
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")
# seems to be not working
# set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/alethzero/alethzero.bmp")
# our stuff
set(CPACK_COMPONENT_ALETHZERO_GROUP "Applications")
set(CPACK_COMPONENT_MIX_GROUP "Applications")
set(CPACK_COMPONENT_SOLC_GROUP "CLI")
set(CPACK_COMPONENT_ETH_GROUP "CLI")
set(CPACK_COMPONENT_ETHMINER_GROUP "CLI")
set(CPACK_COMPONENT_RLP_GROUP "CLI")
set(CPACK_COMPONENT_ABI_GROUP "CLI")
#set(CPACK_COMPONENT_ALETHZERO_GROUP "Applications")
#set(CPACK_COMPONENT_MIX_GROUP "Applications")
#set(CPACK_COMPONENT_SOLC_GROUP "CLI")
#set(CPACK_COMPONENT_ETH_GROUP "CLI")
#set(CPACK_COMPONENT_ETHMINER_GROUP "CLI")
#set(CPACK_COMPONENT_RLP_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
if (CMAKE_CL_64)
@ -540,7 +543,7 @@ if (WIN32)
set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION}")
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_URL_INFO_ABOUT "https://github.com/ethereum/cpp-ethereum")
set(CPACK_NSIS_CONTACT "ethereum.org")

6
cmake/EthExecutableHelper.cmake

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

1
cmake/FindMHD.cmake

@ -52,4 +52,3 @@ find_package_handle_standard_args(mhd DEFAULT_MSG
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_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)
find_package_handle_standard_args(
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 ;)
if ("${CONF}" STREQUAL "Release")
set(DLL ${DLL_RELEASE})
else () # Debug
if ("${CONF}" STREQUAL "Debug")
set(DLL ${DLL_DEBUG})
else ()
set(DLL ${DLL_RELEASE})
endif()
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${DLL}" "${DESTINATION}")

6
eth/CMakeLists.txt

@ -30,7 +30,7 @@ if (JSONRPC)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls(${EXECUTABLE} CURL_DLLS)
eth_copy_dlls("${EXECUTABLE}" CURL_DLLS)
endif()
endif()
@ -41,8 +41,8 @@ if (JSCONSOLE)
target_link_libraries(${EXECUTABLE} jsconsole)
endif()
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls("${EXECUTABLE}" MHD_DLLS EVMJIT_DLLS)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
eth_copy_dlls("${EXECUTABLE}" MHD_DLLS EVMJIT_DLLS OpenCL_DLLS)
endif()
if (APPLE)

2
eth/main.cpp

@ -1090,7 +1090,6 @@ int main(int argc, char** argv)
string jsonAdmin;
bool upnp = true;
WithExisting withExisting = WithExisting::Trust;
bool jit = false;
string sentinel;
/// Networking params.
@ -1530,7 +1529,6 @@ int main(int argc, char** argv)
};
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);
netPrefs.discovery = !disableDiscovery;
netPrefs.pin = pinning;

4
ethminer/CMakeLists.txt

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

8
ethminer/main.cpp

@ -20,6 +20,14 @@
* 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 <chrono>
#include <fstream>

20
evmjit/CMakeLists.txt

@ -16,11 +16,21 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "D
endif()
# LLVM
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)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT LLVM_DIR)
# Workaround for Ubuntu broken LLVM package
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")
execute_process(COMMAND llvm-config-3.7 --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS)
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)

3
exp/CMakeLists.txt

@ -6,9 +6,6 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${DB_INCLUDE_DIRS})
if (ETHASHCL)
include_directories(${OpenCL_INCLUDE_DIRS})
endif ()
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 llvm 3.7svn
call :download microhttpd 0.9.2
call :download OpenCL_ICD 1
call :download qt 5.4.1
call :download miniupnpc 1.9
call :download v8 3.15.9

9
libdevcore/Worker.cpp

@ -50,11 +50,16 @@ void Worker::startWorking()
// cnote << "Trying to set Started: Thread was" << (unsigned)ex << "; " << ok;
(void)ok;
try
{
startedWorking();
// cnote << "Entering work loop...";
workLoop();
// cnote << "Finishing up worker thread...";
doneWorking();
}
catch (std::exception const& _e)
{
clog(WarnChannel) << "Exception thrown in Worker thread: " << _e.what();
}
// ex = WorkerState::Stopping;
// 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(${Boost_INCLUDE_DIRS})
include_directories(${OpenCL_INCLUDE_DIRS})
include_directories(..)
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( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

74
libethash-cl/ethash_cl_miner.cpp

@ -78,23 +78,37 @@ ethash_cl_miner::~ethash_cl_miner()
finish();
}
string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _deviceId)
std::vector<cl::Platform> ethash_cl_miner::getPlatforms()
{
vector<cl::Platform> platforms;
try
{
cl::Platform::get(&platforms);
if (platforms.empty())
}
catch(cl::Error const& err)
{
ETHCL_LOG("No OpenCL platforms found.");
return string();
#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
unsigned platform_num = min<unsigned>(_platformId, platforms.size() - 1);
vector<cl::Device> devices = getDevices(platforms, _platformId);
if (devices.empty())
{
ETHCL_LOG("No OpenCL devices found.");
return string();
return {};
}
// 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;
unsigned platform_num = min<unsigned>(_platformId, _platforms.size() - 1);
try
{
_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;
}
unsigned ethash_cl_miner::getNumPlatforms()
{
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
vector<cl::Platform> platforms = getPlatforms();
if (platforms.empty())
return 0;
return platforms.size();
}
unsigned ethash_cl_miner::getNumDevices(unsigned _platformId)
{
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
vector<cl::Platform> platforms = getPlatforms();
if (platforms.empty())
{
ETHCL_LOG("No OpenCL platforms found.");
return 0;
}
vector<cl::Device> devices = getDevices(platforms, _platformId);
if (devices.empty())
@ -160,7 +180,7 @@ bool ethash_cl_miner::configureGPU(
// by default let's only consider the DAG of the first epoch
uint64_t dagSize = ethash_get_datasize(_currentBlock);
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;
_device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &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)
{
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
vector<cl::Platform> platforms = getPlatforms();
if (platforms.empty())
{
ETHCL_LOG("No OpenCL platforms found.");
return false;
}
for (unsigned i = 0; i < platforms.size(); ++i)
if (searchForAllDevices(i, _callback))
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)
{
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
vector<cl::Platform> platforms = getPlatforms();
if (platforms.empty())
return false;
if (_platformId >= platforms.size())
return false;
@ -222,21 +239,18 @@ bool ethash_cl_miner::searchForAllDevices(unsigned _platformId, function<bool(cl
void ethash_cl_miner::doForAllDevices(function<void(cl::Device const&)> _callback)
{
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
vector<cl::Platform> platforms = getPlatforms();
if (platforms.empty())
{
ETHCL_LOG("No OpenCL platforms found.");
return;
}
for (unsigned i = 0; i < platforms.size(); ++i)
doForAllDevices(i, _callback);
}
void ethash_cl_miner::doForAllDevices(unsigned _platformId, function<void(cl::Device const&)> _callback)
{
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
vector<cl::Platform> platforms = getPlatforms();
if (platforms.empty())
return;
if (_platformId >= platforms.size())
return;
@ -274,13 +288,9 @@ bool ethash_cl_miner::init(
// get all platforms
try
{
vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
vector<cl::Platform> platforms = getPlatforms();
if (platforms.empty())
{
ETHCL_LOG("No OpenCL platforms found.");
return false;
}
// use selected platform
_platformId = min<unsigned>(_platformId, platforms.size() - 1);

1
libethash-cl/ethash_cl_miner.h

@ -75,6 +75,7 @@ public:
private:
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::CommandQueue m_queue;

4
libethcore/CMakeLists.txt

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

40
libethereum/BlockChainSync.cpp

@ -916,26 +916,23 @@ void PV61Sync::requestSubchain(std::shared_ptr<EthereumPeer> _peer)
if (syncPeer != m_chainSyncPeers.end())
{
// Already downoading, request next batch
h256s& d = m_downloadingChainMap.at(syncPeer->second);
_peer->requestHashes(d.back());
SubChain const& s = m_downloadingChainMap.at(syncPeer->second);
_peer->requestHashes(s.lastHash);
}
else if (needsSyncing(_peer))
{
if (!m_readyChainMap.empty())
{
clog(NetAllDetail) << "Helping with hashchin download";
h256s& d = m_readyChainMap.begin()->second;
_peer->requestHashes(d.back());
m_downloadingChainMap[m_readyChainMap.begin()->first] = move(d);
SubChain& s = m_readyChainMap.begin()->second;
_peer->requestHashes(s.lastHash);
m_downloadingChainMap[m_readyChainMap.begin()->first] = move(s);
m_chainSyncPeers[_peer] = m_readyChainMap.begin()->first;
m_readyChainMap.erase(m_readyChainMap.begin());
}
else if (!m_downloadingChainMap.empty() && m_hashScanComplete)
{
// Lead syncer is done, just grab whatever we can
h256s& d = m_downloadingChainMap.begin()->second;
_peer->requestHashes(d.back());
}
_peer->requestHashes(m_downloadingChainMap.begin()->second.lastHash);
}
}
@ -974,7 +971,7 @@ void PV61Sync::completeSubchain(std::shared_ptr<EthereumPeer> _peer, unsigned _n
//Done chain-get
m_syncingNeededBlocks.clear();
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_knownHashes.clear();
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)
{
// 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;
_peer->m_syncHashNumber = 0;
requestSubchain(_peer);
@ -1038,7 +1035,7 @@ void PV61Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
// Got new subchain marker
assert(_hashes.size() == 1);
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)
{
_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
for (auto const& downloader: m_downloadingChainMap)
if (downloader.second.back() == _peer->m_syncHash)
if (downloader.second.lastHash == _peer->m_syncHash)
{
number = downloader.first;
break;
@ -1071,7 +1068,7 @@ void PV61Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
}
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
m_chainSyncPeers.erase(_peer);
@ -1079,7 +1076,7 @@ void PV61Sync::onPeerHashes(std::shared_ptr<EthereumPeer> _peer, h256s const& _h
return;
}
h256s& hashes = downloadingPeer->second;
SubChain& subChain = downloadingPeer->second;
unsigned knowns = 0;
unsigned unknowns = 0;
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)
{
unknowns++;
hashes.push_back(h);
subChain.hashes.push_back(h);
}
else
knowns++;
subChain.lastHash = h;
}
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << hashes.back();
if (hashes.size() > c_hashSubchainSize)
clog(NetMessageSummary) << knowns << "knowns," << unknowns << "unknowns; now at" << subChain.lastHash;
if (subChain.hashes.size() > c_hashSubchainSize)
{
_peer->disable("Too many subchain hashes");
restartSync();
@ -1175,11 +1173,11 @@ SyncStatus PV61Sync::status() const
{
res.hashesReceived = 0;
for (auto const& d : m_readyChainMap)
res.hashesReceived += d.second.size();
res.hashesReceived += d.second.hashes.size();
for (auto const& d : m_downloadingChainMap)
res.hashesReceived += d.second.size();
res.hashesReceived += d.second.hashes.size();
for (auto const& d : m_completeChainMap)
res.hashesReceived += d.second.size();
res.hashesReceived += d.second.hashes.size();
}
return res;
}

13
libethereum/BlockChainSync.h

@ -126,7 +126,6 @@ private:
void logNewBlock(h256 const& _h);
EthereumHost& m_host;
HashDownloadMan m_hashMan;
};
@ -308,9 +307,15 @@ private:
/// Check if downloading hashes in parallel
bool isPV61Syncing() const;
std::map<unsigned, h256s> m_completeChainMap; ///< Fully downloaded subchains
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
struct SubChain
{
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
h256Hash m_knownHashes; ///< Subchain start markers. Used to track suchain completion
unsigned m_syncingBlockNumber = 0; ///< Current subchain marker

8
libethereum/TransactionQueue.cpp

@ -161,6 +161,8 @@ ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transactio
{
fs->second.erase(t);
--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;
auto cs = m_currentByAddressAndNonce.find(_a);
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);
if (fs != m_future.end() && !fs->second.empty())
ret = std::max(ret, fs->second.rbegin()->first);
return ret + 1;
ret = std::max(ret, fs->second.rbegin()->first + 1);
return ret;
}
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/Exceptions.h>
#include <libdevcore/RLP.h>
#include <libdevcore/Guards.h>
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
@ -214,6 +215,46 @@ struct Node
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.

83
libp2p/Host.cpp

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

2
libp2p/Host.h

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

99
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):
m_node(Node(_alias.pub(), _endpoint)),
m_secret(_alias.sec()),
m_io(_io),
m_socket(new NodeSocket(m_io, *this, (bi::udp::endpoint)m_node.endpoint)),
m_socket(new NodeSocket(_io, *this, (bi::udp::endpoint)m_node.endpoint)),
m_socketPointer(m_socket.get()),
m_bucketRefreshTimer(m_io),
m_evictionCheckTimer(m_io),
m_disabled(!_enabled)
m_timers(_io)
{
for (unsigned i = 0; i < s_bins; 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();
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()
{
// Cancel scheduled tasks to ensure.
m_evictionCheckTimer.cancel();
m_bucketRefreshTimer.cancel();
// Disconnect socket so that deallocation is safe.
m_timers.stop();
m_socketPointer->disconnect();
}
@ -117,16 +115,6 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node, NodeRelation _relati
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> nodes;
@ -164,14 +152,17 @@ shared_ptr<NodeEntry> NodeTable::nodeEntry(NodeId _id)
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;
if (_round == s_maxSteps)
{
clog(NodeTableEvent) << "Terminating discover after " << _round << " rounds.";
doDiscovery();
return;
}
else if (!_round && !_tried)
@ -195,6 +186,7 @@ void NodeTable::discover(NodeId _node, unsigned _round, shared_ptr<set<shared_pt
if (tried.empty())
{
clog(NodeTableEvent) << "Terminating discover after " << _round << " rounds.";
doDiscovery();
return;
}
@ -204,13 +196,11 @@ void NodeTable::discover(NodeId _node, unsigned _round, shared_ptr<set<shared_pt
tried.pop_front();
}
auto self(shared_from_this());
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)
m_timers.schedule(c_reqTimeout.count() * 2, [this, _node, _round, _tried](boost::system::error_code const& _ec)
{
if (_ec)
return;
discover(_node, _round + 1, _tried);
clog(NodeTableWarn) << "Discovery timer canceled!";
doDiscover(_node, _round + 1, _tried);
});
}
@ -310,15 +300,15 @@ void NodeTable::evict(shared_ptr<NodeEntry> _leastSeen, shared_ptr<NodeEntry> _n
if (!m_socketPointer->isOpen())
return;
unsigned ec;
unsigned evicts;
DEV_GUARDED(x_evictions)
{
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)
doCheckEvictions(boost::system::error_code());
if (evicts == 1)
doCheckEvictions();
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 (removed)
clog(NodeTableWarn) << "DANGER: Bucket overflow when swapping node position.";
// It's only contested iff nodeentry exists
contested = s.nodes.front().lock();
if (!contested)
{
s.nodes.pop_front();
s.nodes.push_back(node);
s.touch();
if (!removed && m_nodeEventHandler)
m_nodeEventHandler->appendEvent(node->id, NodeEntryAdded);
}
@ -363,8 +354,6 @@ void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _en
else
{
s.nodes.push_back(node);
s.touch();
if (!removed && m_nodeEventHandler)
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())
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)
m_timers.schedule(c_evictionCheckInterval.count(), [this](boost::system::error_code const& _ec)
{
if (_ec)
return;
@ -605,28 +589,23 @@ void NodeTable::doCheckEvictions(boost::system::error_code const& _ec)
dropNode(n);
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)
{
if (ec)
return;
clog(NodeTableEvent) << "refreshing buckets";
bool connected = m_socketPointer->isOpen();
if (connected)
{
clog(NodeTableEvent) << "performing random discovery";
NodeId randNodeId;
crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(0, h256::size));
crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(h256::size, h256::size));
discover(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);
doDiscover(randNodeId);
});
}
void PingNode::streamRLP(RLPStream& _s) const

24
libp2p/NodeTable.h

@ -128,6 +128,7 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this<NodeTable>
public:
enum NodeRelation { Unknown = 0, Known };
enum DiscoverType { Random = 0 };
/// 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);
@ -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.
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.
std::list<NodeId> nodes() const;
@ -184,16 +182,14 @@ private:
/// Intervals
/* 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_bucketRefresh = std::chrono::milliseconds(7200); ///< Refresh interval prevents bucket from becoming stale. [Kademlia]
struct NodeBucket
{
unsigned distance;
TimePoint modified;
std::list<std::weak_ptr<NodeEntry>> nodes;
void touch() { modified = std::chrono::steady_clock::now(); }
};
/// Used to ping endpoint.
@ -210,7 +206,7 @@ private:
/// 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.
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.
std::vector<std::shared_ptr<NodeEntry>> nearestNodeEntries(NodeId _target);
@ -240,10 +236,10 @@ private:
/// Tasks
/// 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.
void doRefreshBuckets(boost::system::error_code const& _ec);
/// Looks up a random node at @c_bucketRefresh interval.
void doDiscovery();
std::unique_ptr<NodeTableEventHandler> m_nodeEventHandler; ///< Event handler for node events.
@ -251,7 +247,7 @@ private:
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.
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.
std::array<NodeBucket, s_bins> m_state; ///< State of p2p node network.
@ -265,14 +261,10 @@ private:
Mutex x_findNodeTimeout;
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.
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.
boost::asio::deadline_timer m_evictionCheckTimer; ///< Timer for handling node evictions.
bool m_disabled; ///< Disable discovery.
DeadlineOps m_timers;
};
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;
}
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
{
if (!m_inheritableMembers)
@ -482,7 +503,7 @@ void StructDefinition::checkRecursion() const
);
}
};
check(this, {});
check(this, StructPointersSet{});
}
TypePointer EnumDefinition::getType(ContractDefinition const*) const
@ -830,11 +851,14 @@ void FunctionCall::checkTypeRequirements(TypePointers const*)
return;
}
/// For error message: Struct members that were removed during conversion to memory.
set<string> membersRemovedForStructConstructor;
if (isStructConstructorCall())
{
TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
auto const& structType = dynamic_cast<StructType const&>(*type.getActualType());
functionType = structType.constructorType();
membersRemovedForStructConstructor = structType.membersMissingInMemory();
}
else
functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
@ -847,13 +871,22 @@ void FunctionCall::checkTypeRequirements(TypePointers const*)
// function parameters
TypePointers const& parameterTypes = functionType->getParameterTypes();
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError(
{
string msg =
"Wrong argument count for function call: " +
toString(m_arguments.size()) +
" arguments given but expected " +
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)
{
@ -972,10 +1005,22 @@ void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes)
++it;
}
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(
"Member \"" + *m_memberName + "\" not found or not visible "
"after argument-dependent lookup in " + type.toString()
));
}
else if (possibleMembers.size() > 1)
BOOST_THROW_EXCEPTION(createTypeError(
"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.
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:
/// Checks that two functions defined in this contract with the same name have different
/// arguments and that there is at most one constructor.
@ -302,6 +308,10 @@ private:
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
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;
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
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")
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
add_dependencies(${EXECUTABLE} BuildInfo.h)
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} evmasm)

10
libsolidity/CompilerStack.cpp

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

17
libsolidity/ExpressionCompiler.cpp

@ -48,12 +48,23 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
{
if (!_varDecl.getValue())
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);
_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)

26
libsolidity/InterfaceHandler.cpp

@ -16,7 +16,7 @@ InterfaceHandler::InterfaceHandler()
m_lastTag = DocTagType::None;
}
unique_ptr<string> InterfaceHandler::getDocumentation(
string InterfaceHandler::getDocumentation(
ContractDefinition const& _contractDef,
DocumentationType _type
)
@ -24,9 +24,9 @@ unique_ptr<string> InterfaceHandler::getDocumentation(
switch(_type)
{
case DocumentationType::NatspecUser:
return getUserDocumentation(_contractDef);
return userDocumentation(_contractDef);
case DocumentationType::NatspecDev:
return getDevDocumentation(_contractDef);
return devDocumentation(_contractDef);
case DocumentationType::ABIInterface:
return getABIInterface(_contractDef);
case DocumentationType::ABISolidityInterface:
@ -34,10 +34,10 @@ unique_ptr<string> InterfaceHandler::getDocumentation(
}
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);
@ -103,10 +103,10 @@ unique_ptr<string> InterfaceHandler::getABIInterface(ContractDefinition const& _
event["inputs"] = params;
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() + "{";
@ -140,10 +140,10 @@ unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition
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 methods(Json::objectValue);
@ -165,10 +165,10 @@ unique_ptr<string> InterfaceHandler::getUserDocumentation(ContractDefinition con
}
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
// 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
BOOST_THROW_EXCEPTION(
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;
}
@ -229,7 +229,7 @@ unique_ptr<string> InterfaceHandler::getDevDocumentation(ContractDefinition cons
}
doc["methods"] = methods;
return unique_ptr<string>(new string(Json::FastWriter().write(doc)));
return Json::StyledWriter().write(doc);
}
/* -- private -- */

25
libsolidity/InterfaceHandler.h

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

2
libsolidity/LValue.cpp

@ -41,7 +41,7 @@ StackVariable::StackVariable(CompilerContext& _compilerContext, Declaration cons
void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
{
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(
CompilerError() <<
errinfo_sourceLocation(_location) <<

1
libsolidity/Parser.cpp

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

26
libsolidity/Types.cpp

@ -1040,14 +1040,6 @@ u256 StructType::getStorageSize() const
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 ret = "struct " + m_struct.getName();
@ -1064,9 +1056,13 @@ MemberList const& StructType::getMembers() const
MemberList::MemberMap members;
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(
variable->getName(),
copyForLocationIfReference(variable->getType()),
copyForLocationIfReference(type),
variable.get())
);
}
@ -1077,8 +1073,7 @@ MemberList const& StructType::getMembers() const
TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const
{
auto copy = make_shared<StructType>(m_struct);
copy->m_location = _location;
auto copy = make_shared<StructType>(m_struct, _location);
copy->m_isPointer = _isPointer;
return copy;
}
@ -1122,6 +1117,15 @@ u256 StructType::memoryOffsetOfMember(string const& _name) const
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
{
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();

9
libsolidity/Types.h

@ -574,14 +574,14 @@ class StructType: public ReferenceType
{
public:
virtual Category getCategory() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct):
ReferenceType(DataLocation::Storage), m_struct(_struct) {}
explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage):
ReferenceType(_location), m_struct(_struct) {}
virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize(bool _padded) const override;
u256 memorySize() const;
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 MemberList const& getMembers() const override;
@ -597,6 +597,9 @@ public:
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:
StructDefinition const& m_struct;
/// 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()
{
m_readOptions.verify_checksums = true;
string path = dev::getDataDir("shh");
boost::filesystem::create_directories(path);
leveldb::Options op;
@ -60,6 +61,15 @@ void WhisperDB::insert(dev::h256 const& _key, string const& _value)
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)
{
leveldb::Slice slice((char const*)_key.data(), _key.size);
@ -67,3 +77,60 @@ void WhisperDB::kill(dev::h256 const& _key)
if (!status.ok())
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/FixedHash.h>
#include "Common.h"
#include "Message.h"
namespace dev
{
@ -43,7 +44,9 @@ class WhisperDB
std::string lookup(dev::h256 const& _key) const;
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 loadAll(std::map<h256, Envelope>& o_dst);
private:
leveldb::ReadOptions m_readOptions;

74
libwhisper/WhisperHost.cpp

@ -20,21 +20,24 @@
*/
#include "WhisperHost.h"
#include <libdevcore/CommonIO.h>
#include <libdevcore/Log.h>
#include <libp2p/All.h>
#include "WhisperDB.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
WhisperHost::WhisperHost(): Worker("shh")
WhisperHost::WhisperHost(bool _useDB): Worker("shh"), m_useDB(_useDB)
{
loadMessagesFromBD();
}
WhisperHost::~WhisperHost()
{
saveMessagesToBD();
}
void WhisperHost::streamMessage(h256 _m, RLPStream& _s) const
@ -200,3 +203,70 @@ void WhisperHost::noteAdvertiseTopicsOfInterest()
for (auto i: peerSessions())
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;
public:
WhisperHost();
WhisperHost(bool _useDB = false);
virtual ~WhisperHost();
unsigned protocolVersion() const { return WhisperProtocolVersion; }
/// remove old messages
void cleanup();
void cleanup(); ///< remove old 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; }
@ -62,8 +61,7 @@ public:
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 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;
virtual h256s watchMessages(unsigned _watchId) override; ///< returns IDs of messages, which match specific watch criteria
virtual Envelope envelope(h256 _m) const override { try { dev::ReadGuard l(x_messages); return m_messages.at(_m); } catch (...) { return Envelope(); } }
protected:
@ -74,6 +72,8 @@ private:
virtual void onStarting() override { startWorking(); }
virtual void onStopping() override { stopWorking(); }
void streamMessage(h256 _m, RLPStream& _s) const;
void saveMessagesToBD();
void loadMessagesFromBD();
mutable dev::SharedMutex x_messages;
std::map<h256, Envelope> m_messages;
@ -83,6 +83,8 @@ private:
std::map<h256, InstalledFilter> m_filters;
std::map<unsigned, ClientWatch> m_watches;
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());
dev::solidity::InterfaceHandler interfaceHandler;
m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition));
m_contractInterface = QString::fromStdString(interfaceHandler.getABIInterface(contractDefinition));
if (m_contractInterface.isEmpty())
m_contractInterface = "[]";
if (contractDefinition.getLocation().sourceName.get())

4
neth/main.cpp

@ -339,7 +339,6 @@ int main(int argc, char** argv)
bool upnp = true;
bool forceMining = false;
bool killChain = false;
bool jit = false;
bool structuredLogging = false;
string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S";
string clientName;
@ -544,10 +543,9 @@ int main(int argc, char** argv)
cout << credits();
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 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(
clientImplString,
dbPath,

5
solc/CommandLineInterface.cpp

@ -449,6 +449,11 @@ bool CommandLineInterface::processInput()
<< boost::diagnostic_information(_exception);
return false;
}
catch (DocstringParsingError const& _exception)
{
cerr << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_exception) << endl;
return false;
}
catch (Exception const& _exception)
{
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);
}
catch (DocstringParsingError const& exception)
{
return formatError(exception, "Documentation parsing error", compiler);
}
catch (Exception const& exception)
{
output["error"] = "Exception during compilation: " + boost::diagnostic_information(exception);

2
test/TestHelper.h

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

40
test/libp2p/net.cpp

@ -308,35 +308,17 @@ BOOST_AUTO_TEST_CASE(kademlia)
if (test::Options::get().nonetwork)
return;
// Not yet a 'real' test.
TestNodeTableHost node(8);
node.start();
node.nodeTable->discover(); // ideally, joining with empty node table logs warning we can check for
node.setup();
node.populate();
// clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
node.populateAll();
// clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
auto nodes = node.nodeTable->nodes();
nodes.sort();
node.nodeTable->reset();
// clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
node.populate(1);
// clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
node.nodeTable->discover();
this_thread::sleep_for(chrono::milliseconds(2000));
// clog << "NodeTable:\n" << *node.nodeTable.get() << endl;
BOOST_REQUIRE_EQUAL(node.nodeTable->count(), 8);
auto netNodes = node.nodeTable->nodes();
netNodes.sort();
}
BOOST_AUTO_TEST_CASE(udpOnce)
@ -355,6 +337,28 @@ BOOST_AUTO_TEST_SUITE_END()
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)
{
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));
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)
this_thread::sleep_for(chrono::milliseconds(step));
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)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE_EQUAL(host1.peerCount(), 1);
BOOST_REQUIRE_EQUAL(host2.peerCount(), 1);
//Temporary disabled
//BOOST_REQUIRE_EQUAL(host1.peerCount(), 1);
//BOOST_REQUIRE_EQUAL(host2.peerCount(), 1);
}
BOOST_AUTO_TEST_CASE(networkConfig)

65
test/libsolidity/SolidityEndToEndTest.cpp

@ -25,6 +25,7 @@
#include <tuple>
#include <boost/test/unit_test.hpp>
#include <libdevcore/Hash.h>
#include <libsolidity/Exceptions.h>
#include <test/libsolidity/solidityExecutionFramework.h>
using namespace std;
@ -4657,6 +4658,32 @@ BOOST_AUTO_TEST_CASE(bytes_memory_index_access)
) == 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)
{
char const* sourceCode = R"(
@ -5033,6 +5060,44 @@ BOOST_AUTO_TEST_CASE(literal_strings)
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()

15
test/libsolidity/SolidityNameAndTypeResolution.cpp

@ -2134,6 +2134,21 @@ BOOST_AUTO_TEST_CASE(invalid_integer_literal_exp)
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()
}

87
test/libsolidity/SolidityNatspecJSON.cpp

@ -21,6 +21,7 @@
*/
#include "../TestHelper.h"
#include <string>
#include <json/json.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/Exceptions.h>
@ -38,9 +39,11 @@ class DocumentationChecker
public:
DocumentationChecker(): m_compilerStack(false) {}
void checkNatspec(std::string const& _code,
void checkNatspec(
std::string const& _code,
std::string const& _expectedDocumentationString,
bool _userDocumentation)
bool _userDocumentation
)
{
std::string generatedDocumentationString;
ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing failed");
@ -53,9 +56,11 @@ public:
m_reader.parse(generatedDocumentationString, generatedDocumentation);
Json::Value expectedDocumentation;
m_reader.parse(_expectedDocumentationString, expectedDocumentation);
BOOST_CHECK_MESSAGE(expectedDocumentation == generatedDocumentation,
BOOST_CHECK_MESSAGE(
expectedDocumentation == generatedDocumentation,
"Expected " << _expectedDocumentationString <<
"\n but got:\n" << generatedDocumentationString);
"\n but got:\n" << generatedDocumentationString
);
}
private:
@ -229,18 +234,6 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params)
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)
{
char const* sourceCode = "contract test {\n"
@ -488,46 +481,48 @@ BOOST_AUTO_TEST_CASE(dev_author_at_function)
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)
{
char const* sourceCode = "contract test {\n"
" /// I do something awesome\n"
" function mul(uint a) returns(uint d) { return a * 7; }\n"
"}\n";
char const* sourceCode = R"(
contract test {
/// I do something awesome
function mul(uint a) returns(uint d) { return a * 7; }
}
)";
char const* natspec = "{"
"\"methods\":{"
" \"mul(uint256)\":{ \"notice\": \"I do something awesome\"}"
"}}";
char const* natspec = R"ABCDEF(
{
"methods" : {
"mul(uint256)" : {
"notice" : "I do something awesome"
}
}
}
)ABCDEF";
checkNatspec(sourceCode, natspec, true);
}
BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag)
{
char const* sourceCode = "contract test {\n"
" /// I do something awesome\n"
" /// which requires two lines to explain\n"
" function mul(uint a) returns(uint d) { return a * 7; }\n"
"}\n";
char const* sourceCode = R"(
contract test {
/// I do something awesome
/// which requires two lines to explain
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);
}

41
test/libsolidity/SolidityWallet.cpp

@ -89,11 +89,16 @@ contract multiowned {
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
// as well as the selection of addresses capable of confirming them.
function multiowned() {
m_required = 1;
m_numOwners = 1;
m_owners[m_numOwners] = uint(msg.sender);
m_ownerIndex[uint(msg.sender)] = m_numOwners;
function multiowned(address[] _owners, uint _required) {
m_numOwners = _owners.length + 1;
m_owners[1] = uint(msg.sender);
m_ownerIndex[uint(msg.sender)] = 1;
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
@ -122,6 +127,7 @@ contract multiowned {
m_ownerIndex[uint(_to)] = ownerIndex;
OwnerChanged(_from, _to);
}
function addOwner(address _owner) onlymanyowners(sha3(msg.data, block.number)) external {
if (isOwner(_owner)) return;
@ -346,15 +352,10 @@ contract Wallet is multisig, multiowned, daylimit {
bytes data;
}
// EVENTS
event Created(bytes32 indexed identifier);
// METHODS
// constructor - just pass on the owner arra to the multiowned.
function Wallet(bytes32 identifier) {
Created(identifier);
// constructor - just pass on the owner array to the multiowned.
function Wallet(address[] _owners, uint _required) multiowned(_owners, _required) {
}
// kills the contract sending everything to `_to`.
@ -423,7 +424,7 @@ static unique_ptr<bytes> s_compiledWallet;
class WalletTestFramework: public ExecutionFramework
{
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)
{
@ -433,7 +434,8 @@ protected:
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
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());
}
};
@ -506,6 +508,17 @@ BOOST_AUTO_TEST_CASE(remove_owner)
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)
{
deployWallet(200);

9
test/libsolidity/solidityExecutionFramework.h

@ -28,6 +28,7 @@
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/Exceptions.h>
namespace dev
{
@ -57,6 +58,14 @@ public:
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(
std::string const& _sourceCode,
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 <boost/test/unit_test.hpp>
#include <libp2p/Host.h>
#include <libwhisper/WhisperDB.h>
#include <libwhisper/WhisperHost.h>
using namespace std;
using namespace dev;
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)
{
@ -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()

24
test/libwhisper/whisperTopic.cpp

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

Loading…
Cancel
Save