Browse Source

Merge branch 'develop' into netFix

cl-refactor
subtly 10 years ago
parent
commit
7e645db4cf
  1. 28
      CMakeLists.txt
  2. 2
      alethzero/CMakeLists.txt
  3. 1
      alethzero/DownloadView.h
  4. 6
      alethzero/Main.ui
  5. 15
      alethzero/MainWin.cpp
  6. 1
      alethzero/MainWin.h
  7. 11
      alethzero/MiningView.cpp
  8. 4
      alethzero/MiningView.h
  9. 4
      alethzero/Transact.cpp
  10. 161
      cmake/CMakeParseArguments.cmake
  11. 16
      cmake/EthDependencies.cmake
  12. 33
      cmake/FindCpuid.cmake
  13. 136
      cmake/FindOpenCL.cmake
  14. 382
      cmake/FindPackageHandleStandardArgs.cmake
  15. 57
      cmake/FindPackageMessage.cmake
  16. 329
      eth/main.cpp
  17. 149
      exp/main.cpp
  18. 2
      libdevcore/Common.cpp
  19. 4
      libdevcore/CommonData.h
  20. 71
      libdevcore/Guards.h
  21. 42
      libdevcore/Worker.cpp
  22. 19
      libdevcore/Worker.h
  23. 19
      libethash-cl/CMakeLists.txt
  24. 86
      libethash-cl/bin2h.cmake
  25. 4014
      libethash-cl/cl.hpp
  26. 366
      libethash-cl/ethash_cl_miner.cpp
  27. 55
      libethash-cl/ethash_cl_miner.h
  28. 460
      libethash-cl/ethash_cl_miner_kernel.cl
  29. 4
      libethash/ethash.h
  30. 6
      libethash/io.c
  31. 9
      libethcore/BlockInfo.cpp
  32. 1
      libethcore/BlockInfo.h
  33. 11
      libethcore/CMakeLists.txt
  34. 5
      libethcore/Common.cpp
  35. 33
      libethcore/Common.h
  36. 328
      libethcore/Ethash.cpp
  37. 140
      libethcore/Ethash.h
  38. 214
      libethcore/EthashAux.cpp
  39. 67
      libethcore/EthashAux.h
  40. 220
      libethcore/Ethasher.cpp
  41. 109
      libethcore/Ethasher.h
  42. 0
      libethcore/Miner.cpp
  43. 172
      libethcore/Miner.h
  44. 1
      libethcore/Params.cpp
  45. 1
      libethcore/Params.h
  46. 204
      libethcore/ProofOfWork.cpp
  47. 155
      libethcore/ProofOfWork.h
  48. 32
      libethereum/BlockChain.cpp
  49. 5
      libethereum/BlockChain.h
  50. 13
      libethereum/BlockQueue.cpp
  51. 6
      libethereum/BlockQueue.h
  52. 4
      libethereum/CMakeLists.txt
  53. 384
      libethereum/Client.cpp
  54. 111
      libethereum/Client.h
  55. 4
      libethereum/ClientBase.cpp
  56. 45
      libethereum/ClientBase.h
  57. 5
      libethereum/EthereumHost.cpp
  58. 0
      libethereum/Farm.cpp
  59. 205
      libethereum/Farm.h
  60. 13
      libethereum/Interface.h
  61. 96
      libethereum/Miner.cpp
  62. 178
      libethereum/Miner.h
  63. 14
      libethereum/State.cpp
  64. 31
      libethereum/State.h
  65. 6
      libethereum/TransactionQueue.cpp
  66. 18
      libethereum/TransactionQueue.h
  67. 2
      libethereumx/Ethereum.h
  68. 7
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  69. 4
      libwebthree/WebThree.cpp
  70. 3
      libwebthree/WebThree.h
  71. 19
      mix/ClientModel.cpp
  72. 8
      mix/ClientModel.h
  73. 5
      mix/CodeModel.cpp
  74. 2
      mix/ContractCallDataEncoder.cpp
  75. 37
      mix/MixClient.cpp
  76. 10
      mix/MixClient.h
  77. 22
      mix/Web3Server.h
  78. 5
      mix/qml.qrc
  79. 6
      mix/qml/DeploymentDialog.qml
  80. 8
      mix/qml/LogsPane.qml
  81. 1
      mix/qml/MainContent.qml
  82. 1
      mix/qml/ProjectList.qml
  83. 9
      mix/qml/ProjectModel.qml
  84. 57
      mix/qml/StateDialog.qml
  85. 19
      mix/qml/StateListModel.qml
  86. 71
      mix/qml/StatusPane.qml
  87. 30
      mix/qml/StepActionImage.qml
  88. 351
      mix/qml/js/NetworkDeployment.js
  89. 345
      mix/qml/js/ProjectModel.js
  90. 7
      mix/test/qml/TestMain.qml
  91. 20
      mix/test/qml/js/TestMiner.js
  92. 18
      mix/test/qml/js/TestProject.js
  93. 10
      neth/main.cpp
  94. 31
      test/TestHelper.cpp
  95. 5
      test/TestHelper.h
  96. 111
      test/blockchain.cpp
  97. 12
      test/dagger.cpp
  98. 11
      test/stateOriginal.cpp
  99. 2
      third/MainWin.cpp

28
CMakeLists.txt

@ -63,6 +63,14 @@ function(configureProject)
add_definitions(-DETH_GUI)
endif()
if (CPUID_FOUND)
add_definitions(-DETH_CPUID)
endif()
if (CURL_FOUND)
add_definitions(-DETH_CURL)
endif()
add_definitions(-DETH_TRUE)
endfunction()
@ -149,6 +157,7 @@ else ()
endif ()
# Backwards compatibility
if (HEADLESS)
message("*** WARNING: -DHEADLESS=1 option is DEPRECATED! Use -DBUNDLE=minimal or -DGUI=0")
set(BUNDLE "minimal")
endif ()
@ -243,6 +252,15 @@ elseif (BUNDLE STREQUAL "full")
set(TOOLS ON)
set(TESTS ON)
set(FATDB ON)
elseif (BUNDLE STREQUAL "core")
set(SERPENT OFF)
set(SOLIDITY ON)
set(USENPM OFF)
set(GUI ON)
set(NCURSES OFF)
set(TOOLS ON)
set(TESTS OFF)
set(FATDB ON)
elseif (BUNDLE STREQUAL "tests")
set(SERPENT ${DECENT_PLATFORM})
set(SOLIDITY ON)
@ -262,8 +280,6 @@ elseif (BUNDLE STREQUAL "user")
set(TESTS OFF)
endif ()
configureProject()
# Default CMAKE_BUILD_TYPE to "Release".
set(CMAKE_BUILD_TYPE CACHE STRING "Release")
if ("x${CMAKE_BUILD_TYPE}" STREQUAL "x")
@ -280,6 +296,10 @@ if ("x${TARGET_PLATFORM}" STREQUAL "x")
endif ()
endif ()
include(EthDependencies)
configureProject()
message("------------------------------------------------------------------------")
message("-- CMake Version ${CMAKE_VERSION}")
message("-- CMAKE_BUILD_TYPE Build type ${CMAKE_BUILD_TYPE}")
@ -287,6 +307,8 @@ message("-- TARGET_PLATFORM Target platform ${TARGET_P
message("-- BUNDLE Build bundle ${BUNDLE}")
message("--------------------------------------------------------------- features")
message("-- Chromium support ${ETH_HAVE_WEBENGINE}")
message("-- Hardware identification support ${CPUID_FOUND}")
message("-- HTTP Request support ${CURL_FOUND}")
message("-- VMTRACE VM execution tracing ${VMTRACE}")
message("-- PROFILING Profiling support ${PROFILING}")
message("-- FATDB Full database exploring ${FATDB}")
@ -312,9 +334,7 @@ endif ()
include(EthCompilerSettings)
message("-- CXXFLAGS: ${CMAKE_CXX_FLAGS}")
# this must be an include, as a function it would mess up with variable scope!
include(EthDependencies)
include(EthExecutableHelper)
createBuildInfo()

2
alethzero/CMakeLists.txt

@ -59,7 +59,7 @@ target_link_libraries(${EXECUTABLE} jsqrc)
target_link_libraries(${EXECUTABLE} natspec)
target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES})
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
if (SERPENT)
target_link_libraries(${EXECUTABLE} serpent)
endif()

1
alethzero/DownloadView.h

@ -32,7 +32,6 @@
#endif
namespace dev { namespace eth {
struct MineInfo;
class DownloadMan;
}}

6
alethzero/Main.ui

@ -180,6 +180,7 @@
<addaction name="separator"/>
<addaction name="usePrivate"/>
<addaction name="jitvm"/>
<addaction name="retryUnknown"/>
</widget>
<widget class="QMenu" name="menu_View">
<property name="title">
@ -1679,6 +1680,11 @@ font-size: 14pt</string>
<string>&amp;GPU Mining</string>
</property>
</action>
<action name="retryUnknown">
<property name="text">
<string>Retry Unknown Parent Blocks</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

15
alethzero/MainWin.cpp

@ -45,7 +45,7 @@
#endif
#include <libdevcrypto/FileSystem.h>
#include <libethcore/CommonJS.h>
#include <libethcore/Ethasher.h>
#include <libethcore/EthashAux.h>
#include <liblll/Compiler.h>
#include <liblll/CodeFragment.h>
#include <libsolidity/Scanner.h>
@ -164,7 +164,7 @@ Main::Main(QWidget *parent) :
statusBar()->addPermanentWidget(ui->chainStatus);
statusBar()->addPermanentWidget(ui->blockCount);
ui->blockCount->setText(QString("PV%2 D%3 H%4 v%5").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(c_ethashVersion).arg(dev::Version));
ui->blockCount->setText(QString("PV%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(QString::fromStdString(ProofOfWork::name())).arg(ProofOfWork::revision()).arg(dev::Version));
connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved()));
@ -952,7 +952,7 @@ void Main::on_preview_triggered()
void Main::refreshMining()
{
MineProgress p = ethereum()->miningProgress();
MiningProgress p = ethereum()->miningProgress();
ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining");
if (!ui->miningView->isVisible())
return;
@ -1481,7 +1481,7 @@ void Main::on_blocks_currentItemChanged()
s << "<div>Difficulty: <b>" << info.difficulty << "</b>" << "</div>";
if (info.number)
{
auto e = Ethasher::eval(info);
auto e = EthashAux::eval(info);
s << "<div>Proof-of-Work: <b>" << e.value << " &lt;= " << (h256)u256((bigint(1) << 256) / info.difficulty) << "</b> (mixhash: " << e.mixHash.abridged() << ")" << "</div>";
s << "<div>Parent: <b>" << info.parentHash << "</b>" << "</div>";
}
@ -1510,7 +1510,7 @@ void Main::on_blocks_currentItemChanged()
s << line << "Nonce: <b>" << uncle.nonce << "</b>" << "</div>";
s << line << "Hash w/o nonce: <b>" << uncle.headerHash(WithoutNonce) << "</b>" << "</div>";
s << line << "Difficulty: <b>" << uncle.difficulty << "</b>" << "</div>";
auto e = Ethasher::eval(uncle);
auto e = EthashAux::eval(uncle);
s << line << "Proof-of-Work: <b>" << e.value << " &lt;= " << (h256)u256((bigint(1) << 256) / uncle.difficulty) << "</b> (mixhash: " << e.mixHash.abridged() << ")" << "</div>";
}
if (info.parentHash)
@ -1752,6 +1752,11 @@ void Main::on_clearPending_triggered()
refreshAll();
}
void Main::on_retryUnknown_triggered()
{
ethereum()->retryUnkonwn();
}
void Main::on_killBlockchain_triggered()
{
writeSettings();

1
alethzero/MainWin.h

@ -163,6 +163,7 @@ private slots:
void on_usePrivate_triggered();
void on_turboMining_triggered();
void on_jitvm_triggered();
void on_retryUnknown_triggered();
// Debugger
void on_debugCurrent_triggered();

11
alethzero/MiningView.cpp

@ -36,7 +36,7 @@ using namespace dev::eth;
// types
using dev::eth::MineInfo;
using dev::eth::MineProgress;
using dev::eth::MiningProgress;
// functions
using dev::toString;
@ -50,12 +50,13 @@ MiningView::MiningView(QWidget* _p): QWidget(_p)
{
}
void MiningView::appendStats(list<MineInfo> const& _i, MineProgress const& _p)
void MiningView::appendStats(list<MineInfo> const& _i, MiningProgress const& _p)
{
(void)_p;
if (_i.empty())
return;
unsigned o = m_values.size();
/* unsigned o = m_values.size();
for (MineInfo const& i: _i)
{
m_values.push_back(i.best);
@ -91,7 +92,7 @@ void MiningView::appendStats(list<MineInfo> const& _i, MineProgress const& _p)
m_completes.erase(remove_if(m_completes.begin(), m_completes.end(), [](int i){return i < 0;}), m_completes.end());
m_progress = _p;
update();
update();*/
}
void MiningView::resetStats()
@ -101,6 +102,7 @@ void MiningView::resetStats()
void MiningView::paintEvent(QPaintEvent*)
{
/*
Grapher g;
QPainter p(this);
@ -115,4 +117,5 @@ void MiningView::paintEvent(QPaintEvent*)
g.ruleY(r - 1, QColor(128, 128, 128));
for (auto r: m_completes)
g.ruleY(r, QColor(192, 64, 64));
*/
}

4
alethzero/MiningView.h

@ -42,14 +42,14 @@ class MiningView: public QWidget
public:
MiningView(QWidget* _p = nullptr);
void appendStats(std::list<dev::eth::MineInfo> const& _l, dev::eth::MineProgress const& _p);
void appendStats(std::list<dev::eth::MineInfo> const& _l, dev::eth::MiningProgress const& _p);
void resetStats();
protected:
virtual void paintEvent(QPaintEvent*);
private:
dev::eth::MineProgress m_progress;
dev::eth::MiningProgress m_progress;
unsigned m_duration = 300;
std::vector<float> m_values;
std::vector<float> m_bests;

4
alethzero/Transact.cpp

@ -37,7 +37,7 @@
#include <libnatspec/NatspecExpressionEvaluator.h>
#include <libethereum/Client.h>
#include <libethereum/Utility.h>
#ifndef _MSC_VER
#if ETH_SERPENT
#include <libserpent/funcs.h>
#include <libserpent/util.h>
#endif
@ -220,7 +220,7 @@ static tuple<vector<string>, bytes, string> userInputToCode(string const& _user,
errors.push_back("Solidity: Uncaught exception");
}
}
#ifndef _MSC_VER
#if ETH_SERPENT
else if (sourceIsSerpent(_user))
{
try

161
cmake/CMakeParseArguments.cmake

@ -0,0 +1,161 @@
#.rst:
# CMakeParseArguments
# -------------------
#
#
#
# CMAKE_PARSE_ARGUMENTS(<prefix> <options> <one_value_keywords>
# <multi_value_keywords> args...)
#
# CMAKE_PARSE_ARGUMENTS() is intended to be used in macros or functions
# for parsing the arguments given to that macro or function. It
# processes the arguments and defines a set of variables which hold the
# values of the respective options.
#
# The <options> argument contains all options for the respective macro,
# i.e. keywords which can be used when calling the macro without any
# value following, like e.g. the OPTIONAL keyword of the install()
# command.
#
# The <one_value_keywords> argument contains all keywords for this macro
# which are followed by one value, like e.g. DESTINATION keyword of the
# install() command.
#
# The <multi_value_keywords> argument contains all keywords for this
# macro which can be followed by more than one value, like e.g. the
# TARGETS or FILES keywords of the install() command.
#
# When done, CMAKE_PARSE_ARGUMENTS() will have defined for each of the
# keywords listed in <options>, <one_value_keywords> and
# <multi_value_keywords> a variable composed of the given <prefix>
# followed by "_" and the name of the respective keyword. These
# variables will then hold the respective value from the argument list.
# For the <options> keywords this will be TRUE or FALSE.
#
# All remaining arguments are collected in a variable
# <prefix>_UNPARSED_ARGUMENTS, this can be checked afterwards to see
# whether your macro was called with unrecognized parameters.
#
# As an example here a my_install() macro, which takes similar arguments
# as the real install() command:
#
# ::
#
# function(MY_INSTALL)
# set(options OPTIONAL FAST)
# set(oneValueArgs DESTINATION RENAME)
# set(multiValueArgs TARGETS CONFIGURATIONS)
# cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}"
# "${multiValueArgs}" ${ARGN} )
# ...
#
#
#
# Assume my_install() has been called like this:
#
# ::
#
# my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub)
#
#
#
# After the cmake_parse_arguments() call the macro will have set the
# following variables:
#
# ::
#
# MY_INSTALL_OPTIONAL = TRUE
# MY_INSTALL_FAST = FALSE (this option was not used when calling my_install()
# MY_INSTALL_DESTINATION = "bin"
# MY_INSTALL_RENAME = "" (was not used)
# MY_INSTALL_TARGETS = "foo;bar"
# MY_INSTALL_CONFIGURATIONS = "" (was not used)
# MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (no value expected after "OPTIONAL"
#
#
#
# You can then continue and process these variables.
#
# Keywords terminate lists of values, e.g. if directly after a
# one_value_keyword another recognized keyword follows, this is
# interpreted as the beginning of the new option. E.g.
# my_install(TARGETS foo DESTINATION OPTIONAL) would result in
# MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION
# would be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor.
#=============================================================================
# Copyright 2010 Alexander Neundorf <neundorf@kde.org>
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
if(__CMAKE_PARSE_ARGUMENTS_INCLUDED)
return()
endif()
set(__CMAKE_PARSE_ARGUMENTS_INCLUDED TRUE)
function(CMAKE_PARSE_ARGUMENTS prefix _optionNames _singleArgNames _multiArgNames)
# first set all result variables to empty/FALSE
foreach(arg_name ${_singleArgNames} ${_multiArgNames})
set(${prefix}_${arg_name})
endforeach()
foreach(option ${_optionNames})
set(${prefix}_${option} FALSE)
endforeach()
set(${prefix}_UNPARSED_ARGUMENTS)
set(insideValues FALSE)
set(currentArgName)
# now iterate over all arguments and fill the result variables
foreach(currentArg ${ARGN})
list(FIND _optionNames "${currentArg}" optionIndex) # ... then this marks the end of the arguments belonging to this keyword
list(FIND _singleArgNames "${currentArg}" singleArgIndex) # ... then this marks the end of the arguments belonging to this keyword
list(FIND _multiArgNames "${currentArg}" multiArgIndex) # ... then this marks the end of the arguments belonging to this keyword
if(${optionIndex} EQUAL -1 AND ${singleArgIndex} EQUAL -1 AND ${multiArgIndex} EQUAL -1)
if(insideValues)
if("${insideValues}" STREQUAL "SINGLE")
set(${prefix}_${currentArgName} ${currentArg})
set(insideValues FALSE)
elseif("${insideValues}" STREQUAL "MULTI")
list(APPEND ${prefix}_${currentArgName} ${currentArg})
endif()
else()
list(APPEND ${prefix}_UNPARSED_ARGUMENTS ${currentArg})
endif()
else()
if(NOT ${optionIndex} EQUAL -1)
set(${prefix}_${currentArg} TRUE)
set(insideValues FALSE)
elseif(NOT ${singleArgIndex} EQUAL -1)
set(currentArgName ${currentArg})
set(${prefix}_${currentArgName})
set(insideValues "SINGLE")
elseif(NOT ${multiArgIndex} EQUAL -1)
set(currentArgName ${currentArg})
set(${prefix}_${currentArgName})
set(insideValues "MULTI")
endif()
endif()
endforeach()
# propagate the result variables to the caller:
foreach(arg_name ${_singleArgNames} ${_multiArgNames} ${_optionNames})
set(${prefix}_${arg_name} ${${prefix}_${arg_name}} PARENT_SCOPE)
endforeach()
set(${prefix}_UNPARSED_ARGUMENTS ${${prefix}_UNPARSED_ARGUMENTS} PARENT_SCOPE)
endfunction()

16
cmake/EthDependencies.cmake

@ -59,7 +59,6 @@ if (JSONRPC)
find_package(MHD)
message(" - microhttpd header: ${MHD_INCLUDE_DIRS}")
message(" - microhttpd lib : ${MHD_LIBRARIES}")
endif() #JSONRPC
# TODO readline package does not yet check for correct version number
@ -86,7 +85,7 @@ endif()
# TODO it is also not required in msvc build
find_package (Gmp 6.0.0)
if (GMP_FOUND)
message(" - gmp Header: ${GMP_INCLUDE_DIRS}")
message(" - gmp header: ${GMP_INCLUDE_DIRS}")
message(" - gmp lib : ${GMP_LIBRARIES}")
endif()
@ -96,6 +95,19 @@ find_package (CURL)
message(" - curl header: ${CURL_INCLUDE_DIRS}")
message(" - curl lib : ${CURL_LIBRARIES}")
# cpuid required for eth
find_package (Cpuid)
if (CPUID_FOUND)
message(" - cpuid header: ${CPUID_INCLUDE_DIRS}")
message(" - cpuid lib : ${CPUID_LIBRARIES}")
endif()
find_package (OpenCL)
if (OpenCL_FOUND)
message(" - opencl header: ${OpenCL_INCLUDE_DIRES}")
message(" - opencl lib : ${OpenCL_LIBRARIES}")
endif()
# find location of jsonrpcstub
find_program(ETH_JSON_RPC_STUB jsonrpcstub)
message(" - jsonrpcstub location : ${ETH_JSON_RPC_STUB}")

33
cmake/FindCpuid.cmake

@ -0,0 +1,33 @@
# Find libcpuid
#
# Find the libcpuid includes and library
#
# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH
#
# This module defines
# CPUID_INCLUDE_DIRS, where to find header, etc.
# CPUID_LIBRARIES, the libraries needed to use cpuid.
# CPUID_FOUND, If false, do not try to use cpuid.
# only look in default directories
find_path(
CPUID_INCLUDE_DIR
NAMES libcpuid/libcpuid.h
DOC "libcpuid include dir"
)
find_library(
CPUID_LIBRARY
NAMES cpuid
DOC "libcpuid library"
)
set(CPUID_INCLUDE_DIRS ${CPUID_INCLUDE_DIR})
set(CPUID_LIBRARIES ${CPUID_LIBRARY})
# handle the QUIETLY and REQUIRED arguments and set CPUID_FOUND to TRUE
# if all listed variables are TRUE, hide their existence from configuration view
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(cpuid DEFAULT_MSG CPUID_INCLUDE_DIR CPUID_LIBRARY)
mark_as_advanced (CPUID_INCLUDE_DIR CPUID_LIBRARY)

136
cmake/FindOpenCL.cmake

@ -0,0 +1,136 @@
#.rst:
# FindOpenCL
# ----------
#
# Try to find OpenCL
#
# Once done this will define::
#
# OpenCL_FOUND - True if OpenCL was found
# OpenCL_INCLUDE_DIRS - include directories for OpenCL
# OpenCL_LIBRARIES - link against this library to use OpenCL
# OpenCL_VERSION_STRING - Highest supported OpenCL version (eg. 1.2)
# OpenCL_VERSION_MAJOR - The major version of the OpenCL implementation
# OpenCL_VERSION_MINOR - The minor version of the OpenCL implementation
#
# The module will also define two cache variables::
#
# OpenCL_INCLUDE_DIR - the OpenCL include directory
# OpenCL_LIBRARY - the path to the OpenCL library
#
#=============================================================================
# Copyright 2014 Matthaeus G. Chajdas
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
function(_FIND_OPENCL_VERSION)
include(CheckSymbolExists)
include(CMakePushCheckState)
set(CMAKE_REQUIRED_QUIET ${OpenCL_FIND_QUIETLY})
CMAKE_PUSH_CHECK_STATE()
foreach(VERSION "2_0" "1_2" "1_1" "1_0")
set(CMAKE_REQUIRED_INCLUDES "${OpenCL_INCLUDE_DIR}")
if(APPLE)
CHECK_SYMBOL_EXISTS(
CL_VERSION_${VERSION}
"${OpenCL_INCLUDE_DIR}/OpenCL/cl.h"
OPENCL_VERSION_${VERSION})
else()
CHECK_SYMBOL_EXISTS(
CL_VERSION_${VERSION}
"${OpenCL_INCLUDE_DIR}/CL/cl.h"
OPENCL_VERSION_${VERSION})
endif()
if(OPENCL_VERSION_${VERSION})
string(REPLACE "_" "." VERSION "${VERSION}")
set(OpenCL_VERSION_STRING ${VERSION} PARENT_SCOPE)
string(REGEX MATCHALL "[0-9]+" version_components "${VERSION}")
list(GET version_components 0 major_version)
list(GET version_components 1 minor_version)
set(OpenCL_VERSION_MAJOR ${major_version} PARENT_SCOPE)
set(OpenCL_VERSION_MINOR ${minor_version} PARENT_SCOPE)
break()
endif()
endforeach()
CMAKE_POP_CHECK_STATE()
endfunction()
find_path(OpenCL_INCLUDE_DIR
NAMES
CL/cl.h OpenCL/cl.h
PATHS
ENV "PROGRAMFILES(X86)"
ENV AMDAPPSDKROOT
ENV INTELOCLSDKROOT
ENV NVSDKCOMPUTE_ROOT
ENV CUDA_PATH
ENV ATISTREAMSDKROOT
PATH_SUFFIXES
include
OpenCL/common/inc
"AMD APP/include")
_FIND_OPENCL_VERSION()
if(WIN32)
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
find_library(OpenCL_LIBRARY
NAMES OpenCL
PATHS
ENV "PROGRAMFILES(X86)"
ENV AMDAPPSDKROOT
ENV INTELOCLSDKROOT
ENV CUDA_PATH
ENV NVSDKCOMPUTE_ROOT
ENV ATISTREAMSDKROOT
PATH_SUFFIXES
"AMD APP/lib/x86"
lib/x86
lib/Win32
OpenCL/common/lib/Win32)
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
find_library(OpenCL_LIBRARY
NAMES OpenCL
PATHS
ENV "PROGRAMFILES(X86)"
ENV AMDAPPSDKROOT
ENV INTELOCLSDKROOT
ENV CUDA_PATH
ENV NVSDKCOMPUTE_ROOT
ENV ATISTREAMSDKROOT
PATH_SUFFIXES
"AMD APP/lib/x86_64"
lib/x86_64
lib/x64
OpenCL/common/lib/x64)
endif()
else()
find_library(OpenCL_LIBRARY
NAMES OpenCL)
endif()
set(OpenCL_LIBRARIES ${OpenCL_LIBRARY})
set(OpenCL_INCLUDE_DIRS ${OpenCL_INCLUDE_DIR})
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
find_package_handle_standard_args(
OpenCL
FOUND_VAR OpenCL_FOUND
REQUIRED_VARS OpenCL_LIBRARY OpenCL_INCLUDE_DIR
VERSION_VAR OpenCL_VERSION_STRING)
mark_as_advanced(
OpenCL_INCLUDE_DIR
OpenCL_LIBRARY)

382
cmake/FindPackageHandleStandardArgs.cmake

@ -0,0 +1,382 @@
#.rst:
# FindPackageHandleStandardArgs
# -----------------------------
#
#
#
# FIND_PACKAGE_HANDLE_STANDARD_ARGS(<name> ... )
#
# This function is intended to be used in FindXXX.cmake modules files.
# It handles the REQUIRED, QUIET and version-related arguments to
# find_package(). It also sets the <packagename>_FOUND variable. The
# package is considered found if all variables <var1>... listed contain
# valid results, e.g. valid filepaths.
#
# There are two modes of this function. The first argument in both
# modes is the name of the Find-module where it is called (in original
# casing).
#
# The first simple mode looks like this:
#
# ::
#
# FIND_PACKAGE_HANDLE_STANDARD_ARGS(<name>
# (DEFAULT_MSG|"Custom failure message") <var1>...<varN> )
#
# If the variables <var1> to <varN> are all valid, then
# <UPPERCASED_NAME>_FOUND will be set to TRUE. If DEFAULT_MSG is given
# as second argument, then the function will generate itself useful
# success and error messages. You can also supply a custom error
# message for the failure case. This is not recommended.
#
# The second mode is more powerful and also supports version checking:
#
# ::
#
# FIND_PACKAGE_HANDLE_STANDARD_ARGS(NAME
# [FOUND_VAR <resultVar>]
# [REQUIRED_VARS <var1>...<varN>]
# [VERSION_VAR <versionvar>]
# [HANDLE_COMPONENTS]
# [CONFIG_MODE]
# [FAIL_MESSAGE "Custom failure message"] )
#
# In this mode, the name of the result-variable can be set either to
# either <UPPERCASED_NAME>_FOUND or <OriginalCase_Name>_FOUND using the
# FOUND_VAR option. Other names for the result-variable are not
# allowed. So for a Find-module named FindFooBar.cmake, the two
# possible names are FooBar_FOUND and FOOBAR_FOUND. It is recommended
# to use the original case version. If the FOUND_VAR option is not
# used, the default is <UPPERCASED_NAME>_FOUND.
#
# As in the simple mode, if <var1> through <varN> are all valid,
# <packagename>_FOUND will be set to TRUE. After REQUIRED_VARS the
# variables which are required for this package are listed. Following
# VERSION_VAR the name of the variable can be specified which holds the
# version of the package which has been found. If this is done, this
# version will be checked against the (potentially) specified required
# version used in the find_package() call. The EXACT keyword is also
# handled. The default messages include information about the required
# version and the version which has been actually found, both if the
# version is ok or not. If the package supports components, use the
# HANDLE_COMPONENTS option to enable handling them. In this case,
# find_package_handle_standard_args() will report which components have
# been found and which are missing, and the <packagename>_FOUND variable
# will be set to FALSE if any of the required components (i.e. not the
# ones listed after OPTIONAL_COMPONENTS) are missing. Use the option
# CONFIG_MODE if your FindXXX.cmake module is a wrapper for a
# find_package(... NO_MODULE) call. In this case VERSION_VAR will be
# set to <NAME>_VERSION and the macro will automatically check whether
# the Config module was found. Via FAIL_MESSAGE a custom failure
# message can be specified, if this is not used, the default message
# will be displayed.
#
# Example for mode 1:
#
# ::
#
# find_package_handle_standard_args(LibXml2 DEFAULT_MSG
# LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR)
#
#
#
# LibXml2 is considered to be found, if both LIBXML2_LIBRARY and
# LIBXML2_INCLUDE_DIR are valid. Then also LIBXML2_FOUND is set to
# TRUE. If it is not found and REQUIRED was used, it fails with
# FATAL_ERROR, independent whether QUIET was used or not. If it is
# found, success will be reported, including the content of <var1>. On
# repeated Cmake runs, the same message won't be printed again.
#
# Example for mode 2:
#
# ::
#
# find_package_handle_standard_args(LibXslt
# FOUND_VAR LibXslt_FOUND
# REQUIRED_VARS LibXslt_LIBRARIES LibXslt_INCLUDE_DIRS
# VERSION_VAR LibXslt_VERSION_STRING)
#
# In this case, LibXslt is considered to be found if the variable(s)
# listed after REQUIRED_VAR are all valid, i.e. LibXslt_LIBRARIES and
# LibXslt_INCLUDE_DIRS in this case. The result will then be stored in
# LibXslt_FOUND . Also the version of LibXslt will be checked by using
# the version contained in LibXslt_VERSION_STRING. Since no
# FAIL_MESSAGE is given, the default messages will be printed.
#
# Another example for mode 2:
#
# ::
#
# find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4)
# find_package_handle_standard_args(Automoc4 CONFIG_MODE)
#
# In this case, FindAutmoc4.cmake wraps a call to find_package(Automoc4
# NO_MODULE) and adds an additional search directory for automoc4. Here
# the result will be stored in AUTOMOC4_FOUND. The following
# FIND_PACKAGE_HANDLE_STANDARD_ARGS() call produces a proper
# success/error message.
#=============================================================================
# Copyright 2007-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/CMakeParseArguments.cmake)
# internal helper macro
macro(_FPHSA_FAILURE_MESSAGE _msg)
if (${_NAME}_FIND_REQUIRED)
message(FATAL_ERROR "${_msg}")
else ()
if (NOT ${_NAME}_FIND_QUIETLY)
message(STATUS "${_msg}")
endif ()
endif ()
endmacro()
# internal helper macro to generate the failure message when used in CONFIG_MODE:
macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE)
# <name>_CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found:
if(${_NAME}_CONFIG)
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing: ${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})")
else()
# If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version.
# List them all in the error message:
if(${_NAME}_CONSIDERED_CONFIGS)
set(configsText "")
list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount)
math(EXPR configsCount "${configsCount} - 1")
foreach(currentConfigIndex RANGE ${configsCount})
list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename)
list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version)
set(configsText "${configsText} ${filename} (version ${version})\n")
endforeach()
if (${_NAME}_NOT_FOUND_MESSAGE)
set(configsText "${configsText} Reason given by package: ${${_NAME}_NOT_FOUND_MESSAGE}\n")
endif()
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}")
else()
# Simple case: No Config-file was found at all:
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}")
endif()
endif()
endmacro()
function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG)
# set up the arguments for CMAKE_PARSE_ARGUMENTS and check whether we are in
# new extended or in the "old" mode:
set(options CONFIG_MODE HANDLE_COMPONENTS)
set(oneValueArgs FAIL_MESSAGE VERSION_VAR FOUND_VAR)
set(multiValueArgs REQUIRED_VARS)
set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} )
list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX)
if(${INDEX} EQUAL -1)
set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG})
set(FPHSA_REQUIRED_VARS ${ARGN})
set(FPHSA_VERSION_VAR)
else()
CMAKE_PARSE_ARGUMENTS(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN})
if(FPHSA_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"")
endif()
if(NOT FPHSA_FAIL_MESSAGE)
set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG")
endif()
endif()
# now that we collected all arguments, process them
if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG")
set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}")
endif()
# In config-mode, we rely on the variable <package>_CONFIG, which is set by find_package()
# when it successfully found the config-file, including version checking:
if(FPHSA_CONFIG_MODE)
list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG)
list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS)
set(FPHSA_VERSION_VAR ${_NAME}_VERSION)
endif()
if(NOT FPHSA_REQUIRED_VARS)
message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()")
endif()
list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR)
string(TOUPPER ${_NAME} _NAME_UPPER)
string(TOLOWER ${_NAME} _NAME_LOWER)
if(FPHSA_FOUND_VAR)
if(FPHSA_FOUND_VAR MATCHES "^${_NAME}_FOUND$" OR FPHSA_FOUND_VAR MATCHES "^${_NAME_UPPER}_FOUND$")
set(_FOUND_VAR ${FPHSA_FOUND_VAR})
else()
message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_NAME}_FOUND\" and \"${_NAME_UPPER}_FOUND\" are valid names.")
endif()
else()
set(_FOUND_VAR ${_NAME_UPPER}_FOUND)
endif()
# collect all variables which were not found, so they can be printed, so the
# user knows better what went wrong (#6375)
set(MISSING_VARS "")
set(DETAILS "")
# check if all passed variables are valid
unset(${_FOUND_VAR})
foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS})
if(NOT ${_CURRENT_VAR})
set(${_FOUND_VAR} FALSE)
set(MISSING_VARS "${MISSING_VARS} ${_CURRENT_VAR}")
else()
set(DETAILS "${DETAILS}[${${_CURRENT_VAR}}]")
endif()
endforeach()
if(NOT "${${_FOUND_VAR}}" STREQUAL "FALSE")
set(${_FOUND_VAR} TRUE)
endif()
# component handling
unset(FOUND_COMPONENTS_MSG)
unset(MISSING_COMPONENTS_MSG)
if(FPHSA_HANDLE_COMPONENTS)
foreach(comp ${${_NAME}_FIND_COMPONENTS})
if(${_NAME}_${comp}_FOUND)
if(NOT DEFINED FOUND_COMPONENTS_MSG)
set(FOUND_COMPONENTS_MSG "found components: ")
endif()
set(FOUND_COMPONENTS_MSG "${FOUND_COMPONENTS_MSG} ${comp}")
else()
if(NOT DEFINED MISSING_COMPONENTS_MSG)
set(MISSING_COMPONENTS_MSG "missing components: ")
endif()
set(MISSING_COMPONENTS_MSG "${MISSING_COMPONENTS_MSG} ${comp}")
if(${_NAME}_FIND_REQUIRED_${comp})
set(${_FOUND_VAR} FALSE)
set(MISSING_VARS "${MISSING_VARS} ${comp}")
endif()
endif()
endforeach()
set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}")
set(DETAILS "${DETAILS}[c${COMPONENT_MSG}]")
endif()
# version handling:
set(VERSION_MSG "")
set(VERSION_OK TRUE)
set(VERSION ${${FPHSA_VERSION_VAR}})
# check with DEFINED here as the requested or found version may be "0"
if (DEFINED ${_NAME}_FIND_VERSION)
if(DEFINED ${FPHSA_VERSION_VAR})
if(${_NAME}_FIND_VERSION_EXACT) # exact version required
# count the dots in the version string
string(REGEX REPLACE "[^.]" "" _VERSION_DOTS "${VERSION}")
# add one dot because there is one dot more than there are components
string(LENGTH "${_VERSION_DOTS}." _VERSION_DOTS)
if (_VERSION_DOTS GREATER ${_NAME}_FIND_VERSION_COUNT)
# Because of the C++ implementation of find_package() ${_NAME}_FIND_VERSION_COUNT
# is at most 4 here. Therefore a simple lookup table is used.
if (${_NAME}_FIND_VERSION_COUNT EQUAL 1)
set(_VERSION_REGEX "[^.]*")
elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 2)
set(_VERSION_REGEX "[^.]*\\.[^.]*")
elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 3)
set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*")
else ()
set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*")
endif ()
string(REGEX REPLACE "^(${_VERSION_REGEX})\\..*" "\\1" _VERSION_HEAD "${VERSION}")
unset(_VERSION_REGEX)
if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _VERSION_HEAD)
set(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"")
set(VERSION_OK FALSE)
else ()
set(VERSION_MSG "(found suitable exact version \"${VERSION}\")")
endif ()
unset(_VERSION_HEAD)
else ()
if (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}")
set(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"")
set(VERSION_OK FALSE)
else ()
set(VERSION_MSG "(found suitable exact version \"${VERSION}\")")
endif ()
endif ()
unset(_VERSION_DOTS)
else() # minimum version specified:
if ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}")
set(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"")
set(VERSION_OK FALSE)
else ()
set(VERSION_MSG "(found suitable version \"${VERSION}\", minimum required is \"${${_NAME}_FIND_VERSION}\")")
endif ()
endif()
else()
# if the package was not found, but a version was given, add that to the output:
if(${_NAME}_FIND_VERSION_EXACT)
set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")")
else()
set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")")
endif()
endif()
else ()
if(VERSION)
set(VERSION_MSG "(found version \"${VERSION}\")")
endif()
endif ()
if(VERSION_OK)
set(DETAILS "${DETAILS}[v${VERSION}(${${_NAME}_FIND_VERSION})]")
else()
set(${_FOUND_VAR} FALSE)
endif()
# print the result:
if (${_FOUND_VAR})
FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}")
else ()
if(FPHSA_CONFIG_MODE)
_FPHSA_HANDLE_FAILURE_CONFIG_MODE()
else()
if(NOT VERSION_OK)
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})")
else()
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing: ${MISSING_VARS}) ${VERSION_MSG}")
endif()
endif()
endif ()
set(${_FOUND_VAR} ${${_FOUND_VAR}} PARENT_SCOPE)
endfunction()

57
cmake/FindPackageMessage.cmake

@ -0,0 +1,57 @@
#.rst:
# FindPackageMessage
# ------------------
#
#
#
# FIND_PACKAGE_MESSAGE(<name> "message for user" "find result details")
#
# This macro is intended to be used in FindXXX.cmake modules files. It
# will print a message once for each unique find result. This is useful
# for telling the user where a package was found. The first argument
# specifies the name (XXX) of the package. The second argument
# specifies the message to display. The third argument lists details
# about the find result so that if they change the message will be
# displayed again. The macro also obeys the QUIET argument to the
# find_package command.
#
# Example:
#
# ::
#
# if(X11_FOUND)
# FIND_PACKAGE_MESSAGE(X11 "Found X11: ${X11_X11_LIB}"
# "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]")
# else()
# ...
# endif()
#=============================================================================
# Copyright 2008-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
function(FIND_PACKAGE_MESSAGE pkg msg details)
# Avoid printing a message repeatedly for the same find result.
if(NOT ${pkg}_FIND_QUIETLY)
string(REPLACE "\n" "" details "${details}")
set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg})
if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}")
# The message has not yet been printed.
message(STATUS "${msg}")
# Save the find details in the cache to avoid printing the same
# message again.
set("${DETAILS_VAR}" "${details}"
CACHE INTERNAL "Details about finding ${pkg}")
endif()
endif()
endfunction()

329
eth/main.cpp

@ -32,19 +32,23 @@
#include <libdevcrypto/FileSystem.h>
#include <libevmcore/Instruction.h>
#include <libdevcore/StructuredLogger.h>
#include <libethcore/ProofOfWork.h>
#include <libethcore/EthashAux.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <libethereum/All.h>
#include <libwebthree/WebThree.h>
#if ETH_READLINE
#if ETH_READLINE || !ETH_TRUE
#include <readline/readline.h>
#include <readline/history.h>
#endif
#if ETH_JSONRPC
#if ETH_JSONRPC || !ETH_TRUE
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#endif
#include <libethcore/Ethasher.h>
#if ETH_CURL || !ETH_TRUE
#include <curl/curl.h>
#endif
#include "BuildInfo.h"
using namespace std;
using namespace dev;
@ -88,7 +92,7 @@ void interactiveHelp()
<< " send Execute a given transaction with current secret." << endl
<< " contract Create a new contract with current secret." << endl
<< " peers List the peers that are connected" << endl
#if ETH_FATDB
#if ETH_FATDB || !ETH_TRUE
<< " listaccounts List the accounts on the network." << endl
<< " listcontracts List the contracts on the network." << endl
#endif
@ -110,42 +114,54 @@ void help()
<< " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << endl
<< " -b,--bootstrap Connect to the default Ethereum peerserver." << endl
<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (Default: 15)." << endl
<< " -c,--client-name <name> Add a name to your client's version string (default: blank)." << endl
<< " --client-name <name> Add a name to your client's version string (default: blank)." << endl
<< " -C,--cpu When mining, use the CPU." << endl
<< " -d,--db-path <path> Load database from path (default: ~/.ethereum " << endl
<< " <APPDATA>/Etherum or Library/Application Support/Ethereum)." << endl
<< " --benchmark-warmup <seconds> Set the duration of warmup for the benchmark tests (default: 15)." << endl
<< " --benchmark-trial <seconds> Set the duration for each trial for the benchmark tests (default: 3)." << endl
<< " --benchmark-trials <n> Set the duration of warmup for the benchmark tests (default: 5)." << endl
<< " -D,--create-dag <this/next/number> Create the DAG in preparation for mining on given block and exit." << endl
<< " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (Default: 30.679)." << endl
<< " -E,--export <file> Export file as a concatenated series of blocks and exit." << endl
<< " --from <n> Export only from block n; n may be a decimal, a '0x' prefixed hash, or 'latest'." << endl
<< " --to <n> Export only to block n (inclusive); n may be a decimal, a '0x' prefixed hash, or 'latest'." << endl
<< " --only <n> Equivalent to --export-from n --export-to n." << endl
<< " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl
<< " -f,--force-mining Mine even when there are no transactions to mine (Default: off)" << endl
#if ETH_JSONRPC || !ETH_TRUE
<< " -F,--farm Put into mining farm mode (default GPU with CPU as fallback)." << endl
#endif
<< " -G,--gpu When mining use the GPU." << endl
<< " -h,--help Show this help message and exit." << endl
<< " -i,--interactive Enter interactive mode (default: non-interactive)." << endl
<< " -I,--import <file> Import file as a concatenated series of blocks and exit." << endl
#if ETH_JSONRPC
#if ETH_JSONRPC || !ETH_TRUE
<< " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl
<< " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl
#endif
#if ETH_EVMJIT || !ETH_TRUE
<< " -J,--jit Enable EVM JIT (default: off)." << endl
#endif
<< " -K,--kill First kill the blockchain." << endl
<< " --listen-ip <port> Listen on the given port for incoming connections (default: 30303)." << endl
<< " -l,--listen <ip> Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl
<< " -u,--public-ip <ip> Force public ip to given (default: auto)." << endl
<< " --listen <port> Listen on the given port for incoming connections (default: 30303)." << endl
<< " --listen-ip <ip>(:<port>) Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl
<< " --public-ip <ip> Force public ip to given (default: auto)." << endl
<< " -m,--mining <on/off/number> Enable mining, optionally for a specified number of blocks (Default: off)" << endl
<< " -n,--upnp <on/off> Use upnp for NAT (default: on)." << endl
<< " -M,--benchmark Benchmark for mining and exit; use with --cpu and --gpu." << endl
<< " -o,--mode <full/peer> Start a full node or a peer node (Default: full)." << endl
<< " -p,--port <port> Connect to remote port (default: 30303)." << endl
<< " --opencl-device <n> When mining use OpenCL device n (default: 0)." << endl
<< " --port <port> Connect to remote port (default: 30303)." << endl
<< " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl
<< " --phone-home <on/off> When benchmarking, publish results (Default: on)" << endl
<< " -R,--rebuild First rebuild the blockchain from the existing database." << endl
<< " -r,--remote <host> Connect to remote host (default: none)." << endl
<< " -r,--remote <host>(:<port>) Connect to remote host (default: none)." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl
<< " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl
<< " -S,--session-secret <secretkeyhex> Set the secret key for use with send command, for this session only." << endl
<< " --upnp <on/off> Use upnp for NAT (default: on)." << endl
<< " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl
<< " -x,--peers <number> Attempt to connect to given number of peers (Default: 5)." << endl
<< " -V,--version Show the version and exit." << endl
#if ETH_EVMJIT
<< " --jit Use EVM JIT (default: off)." << endl
#endif
<< " -w,--check-pow <headerHash> <seedHash> <difficulty> <nonce> Check PoW credentials for validity." << endl
<< " -x,--peers <number> Attempt to connect to given number of peers (Default: 5)." << endl
;
exit(0);
}
@ -210,7 +226,7 @@ void doInitDAG(unsigned _n)
BlockInfo bi;
bi.number = _n;
cout << "Initializing DAG for epoch beginning #" << (bi.number / 30000 * 30000) << " (seedhash " << bi.seedHash().abridged() << "). This will take a while." << endl;
Ethasher::get()->full(bi);
Ethash::prep(bi);
exit(0);
}
@ -219,7 +235,9 @@ enum class OperationMode
Node,
Import,
Export,
DAGInit
DAGInit,
Benchmark,
Farm
};
enum class Format
@ -229,6 +247,83 @@ enum class Format
Human
};
enum class MinerType
{
CPU,
GPU
};
void doBenchmark(MinerType _m, bool _phoneHome, unsigned _warmupDuration = 15, unsigned _trialDuration = 3, unsigned _trials = 5)
{
BlockInfo genesis = CanonBlockChain::genesis();
genesis.difficulty = 1 << 18;
cdebug << genesis.boundary();
GenericFarm<Ethash> f;
f.onSolutionFound([&](ProofOfWork::Solution) { return false; });
string platformInfo = _m == MinerType::CPU ? ProofOfWork::CPUMiner::platformInfo() : _m == MinerType::GPU ? ProofOfWork::GPUMiner::platformInfo() : "";
cout << "Benchmarking on platform: " << platformInfo << endl;
cout << "Preparing DAG..." << endl;
Ethash::prep(genesis);
genesis.difficulty = u256(1) << 63;
genesis.noteDirty();
f.setWork(genesis);
if (_m == MinerType::CPU)
f.startCPU();
else if (_m == MinerType::GPU)
f.startGPU();
map<uint64_t, MiningProgress> results;
uint64_t mean = 0;
uint64_t innerMean = 0;
for (unsigned i = 0; i <= _trials; ++i)
{
if (!i)
cout << "Warming up..." << endl;
else
cout << "Trial " << i << "... " << flush;
this_thread::sleep_for(chrono::seconds(i ? _trialDuration : _warmupDuration));
auto mp = f.miningProgress();
f.resetMiningProgress();
if (!i)
continue;
auto rate = mp.rate();
cout << rate << endl;
results[rate] = mp;
mean += rate;
if (i > 1 && i < 5)
innerMean += rate;
}
f.stop();
cout << "min/mean/max: " << results.begin()->second.rate() << "/" << (mean / _trials) << "/" << results.rbegin()->second.rate() << " H/s" << endl;
cout << "inner mean: " << (innerMean / (_trials - 2)) << " H/s" << endl;
(void)_phoneHome;
if (_phoneHome)
{
cout << "Phoning home to find world ranking..." << endl;
// TODO: send f.miningInfo() along with f.platformInfo() to Marian.
}
exit(0);
}
void doFarm(MinerType _m)
{
(void)_m;
// TODO: Set up JSONRPC client: to implement:
// { "name": "eth_getWork", "params": [], "order": [], "returns": [<powHash>, <seedHash>, <boundary>]},
// { "name": "eth_submitWork", "params": [<nonce>, <mixHash>], "order": [], "returns": true},
exit(0);
}
int main(int argc, char** argv)
{
// Init defaults
@ -238,6 +333,10 @@ int main(int argc, char** argv)
OperationMode mode = OperationMode::Node;
string dbPath;
/// Mining options
MinerType minerType = MinerType::CPU;
unsigned openclDevice = 0;
/// File name for import/export.
string filename;
@ -271,10 +370,10 @@ int main(int argc, char** argv)
/// Mining params
unsigned mining = ~(unsigned)0;
int miners = -1;
bool forceMining = false;
KeyPair us = KeyPair::create();
Address coinbase = us.address();
KeyPair sigKey = KeyPair::create();
Secret sessionSecret;
Address coinbase = sigKey.address();
/// Structured logging params
bool structuredLogging = false;
@ -285,12 +384,18 @@ int main(int argc, char** argv)
double etherPrice = 30.679;
double blockFees = 15.0;
/// Benchmarking params
bool phoneHome = true;
unsigned benchmarkWarmup = 15;
unsigned benchmarkTrial = 3;
unsigned benchmarkTrials = 5;
string configFile = getDataDir() + "/config.rlp";
bytes b = contents(configFile);
if (b.size())
{
RLP config(b);
us = KeyPair(config[0].toHash<Secret>());
sigKey = KeyPair(config[0].toHash<Secret>());
coinbase = config[1].toHash<Address>();
}
@ -300,13 +405,25 @@ int main(int argc, char** argv)
if (arg == "--listen-ip" && i + 1 < argc)
listenIP = argv[++i];
else if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc)
{
if (arg == "-l")
cerr << "-l is DEPRECATED. It will be removed for the Frontier. Use --listen-port instead." << endl;
listenPort = (short)atoi(argv[++i]);
}
else if ((arg == "-u" || arg == "--public-ip" || arg == "--public") && i + 1 < argc)
{
if (arg == "-u")
cerr << "-u is DEPRECATED. It will be removed for the Frontier. Use --public-ip instead." << endl;
publicIP = argv[++i];
}
else if ((arg == "-r" || arg == "--remote") && i + 1 < argc)
remoteHost = argv[++i];
else if ((arg == "-p" || arg == "--port") && i + 1 < argc)
{
if (arg == "-p")
cerr << "-p is DEPRECATED. It will be removed for the Frontier. Use --port instead (or place directly as host:port)." << endl;
remotePort = (short)atoi(argv[++i]);
}
else if ((arg == "-I" || arg == "--import") && i + 1 < argc)
{
mode = OperationMode::Import;
@ -317,6 +434,30 @@ int main(int argc, char** argv)
mode = OperationMode::Export;
filename = argv[++i];
}
else if (arg == "-F" || arg == "--farm")
mode = OperationMode::Farm;
else if (arg == "--opencl-device" && i + 1 < argc)
try {
openclDevice = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--phone-home" && i + 1 < argc)
{
string m = argv[++i];
if (isTrue(m))
phoneHome = true;
else if (isFalse(m))
phoneHome = false;
else
{
cerr << "Bad " << arg << " option: " << m << endl;
return -1;
}
}
else if (arg == "--format" && i + 1 < argc)
{
string m = argv[++i];
@ -338,8 +479,10 @@ int main(int argc, char** argv)
exportFrom = argv[++i];
else if (arg == "--only" && i + 1 < argc)
exportTo = exportFrom = argv[++i];
else if ((arg == "-n" || arg == "--upnp") && i + 1 < argc)
else if ((arg == "-n" || arg == "-u" || arg == "--upnp") && i + 1 < argc)
{
if (arg == "-n")
cerr << "-n is DEPRECATED. It will be removed for the Frontier. Use --upnp instead." << endl;
string m = argv[++i];
if (isTrue(m))
upnp = true;
@ -351,18 +494,48 @@ int main(int argc, char** argv)
return -1;
}
}
else if (arg == "--benchmark-warmup" && i + 1 < argc)
try {
benchmarkWarmup = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--benchmark-trial" && i + 1 < argc)
try {
benchmarkTrial = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "--benchmark-trials" && i + 1 < argc)
try {
benchmarkTrials = stol(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "-K" || arg == "--kill-blockchain" || arg == "--kill")
killChain = WithExisting::Kill;
else if (arg == "-B" || arg == "--rebuild")
killChain = WithExisting::Verify;
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
{
if (arg == "-c")
cerr << "-c is DEPRECATED. It will be removed for the Frontier. Use --client-name instead." << endl;
clientName = argv[++i];
}
else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc)
try
{
try {
coinbase = h160(fromHex(argv[++i], WhenError::Throw));
}
catch (BadHexCharacter& _e)
catch (BadHexCharacter&)
{
cerr << "Bad hex in " << arg << " option: " << argv[i] << endl;
return -1;
@ -372,8 +545,14 @@ int main(int argc, char** argv)
cerr << "Bad " << arg << " option: " << argv[i] << endl;
return -1;
}
else if (arg == "-C" || arg == "--cpu")
minerType = MinerType::CPU;
else if (arg == "-G" || arg == "--gpu")
minerType = MinerType::GPU;
else if ((arg == "-s" || arg == "--secret") && i + 1 < argc)
us = KeyPair(h256(fromHex(argv[++i])));
sigKey = KeyPair(h256(fromHex(argv[++i])));
else if ((arg == "-S" || arg == "--session-secret") && i + 1 < argc)
sessionSecret = h256(fromHex(argv[++i]));
else if (arg == "--structured-logging-format" && i + 1 < argc)
structuredLoggingFormat = string(argv[++i]);
else if (arg == "--structured-logging")
@ -399,6 +578,45 @@ int main(int argc, char** argv)
return -1;
}
}
else if ((arg == "-w" || arg == "--check-pow") && i + 4 < argc)
{
string m;
try
{
BlockInfo bi;
m = boost::to_lower_copy(string(argv[++i]));
h256 powHash(m);
m = boost::to_lower_copy(string(argv[++i]));
h256 seedHash;
if (m.size() == 64 || m.size() == 66)
seedHash = h256(m);
else
seedHash = EthashAux::seedHash(stol(m));
m = boost::to_lower_copy(string(argv[++i]));
bi.difficulty = u256(m);
auto boundary = bi.boundary();
m = boost::to_lower_copy(string(argv[++i]));
bi.nonce = h64(m);
auto r = EthashAux::eval(seedHash, powHash, bi.nonce);
bool valid = r.value < boundary;
cout << (valid ? "VALID :-)" : "INVALID :-(") << endl;
cout << r.value << (valid ? " < " : " >= ") << boundary << endl;
cout << " where " << boundary << " = 2^256 / " << bi.difficulty << endl;
cout << " and " << r.value << " = ethash(" << powHash << ", " << bi.nonce << ")" << endl;
cout << " with seed as " << seedHash << endl;
if (valid)
cout << "(mixHash = " << r.mixHash << ")" << endl;
cout << "SHA3( light(seed) ) = " << sha3(bytesConstRef((byte const*)EthashAux::light(seedHash), EthashAux::params(seedHash).cache_size)) << endl;
exit(0);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << m << endl;
return -1;
}
}
else if (arg == "-M" || arg == "--benchmark")
mode = OperationMode::Benchmark;
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{
try
@ -477,8 +695,6 @@ int main(int argc, char** argv)
g_logVerbosity = atoi(argv[++i]);
else if ((arg == "-x" || arg == "--peers") && i + 1 < argc)
peers = atoi(argv[++i]);
else if ((arg == "-t" || arg == "--miners") && i + 1 < argc)
miners = atoi(argv[++i]);
else if ((arg == "-o" || arg == "--mode") && i + 1 < argc)
{
string m = argv[++i];
@ -492,15 +708,12 @@ int main(int argc, char** argv)
return -1;
}
}
else if (arg == "--jit")
{
#if ETH_EVMJIT
else if (arg == "-J" || arg == "--jit")
{
jit = true;
#else
cerr << "EVM JIT not enabled" << endl;
return -1;
#endif
}
#endif
else if (arg == "-h" || arg == "--help")
help();
else if (arg == "-V" || arg == "--version")
@ -514,15 +727,26 @@ int main(int argc, char** argv)
{
RLPStream config(2);
config << us.secret() << coinbase;
config << sigKey.secret() << coinbase;
writeFile(configFile, config.out());
}
if (sessionSecret)
sigKey = KeyPair(sessionSecret);
ProofOfWork::GPUMiner::setDefaultDevice(openclDevice);
// Two codepaths is necessary since named block require database, but numbered
// blocks are superuseful to have when database is already open in another process.
if (mode == OperationMode::DAGInit && !(initDAG == LatestBlock || initDAG == PendingBlock))
doInitDAG(initDAG);
if (mode == OperationMode::Benchmark)
doBenchmark(minerType, phoneHome, benchmarkWarmup, benchmarkTrial, benchmarkTrials);
if (mode == OperationMode::Farm)
doFarm(minerType);
if (!clientName.empty())
clientName += "/";
@ -537,9 +761,7 @@ int main(int argc, char** argv)
killChain,
nodeMode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
netPrefs,
&nodesState,
miners
);
&nodesState);
if (mode == OperationMode::DAGInit)
doInitDAG(web3.ethereum()->blockChain().number() + (initDAG == PendingBlock ? 30000 : 0));
@ -632,7 +854,7 @@ int main(int argc, char** argv)
c->setAddress(coinbase);
}
cout << "Transaction Signer: " << us.address() << endl;
cout << "Transaction Signer: " << sigKey.address() << endl;
cout << "Mining Benefactor: " << coinbase << endl;
web3.startNetwork();
@ -647,8 +869,7 @@ int main(int argc, char** argv)
if (jsonrpc > -1)
{
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({us})));
jsonrpcServer->setIdentities({us});
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({sigKey})));
jsonrpcServer->StartListening();
}
#endif
@ -772,8 +993,7 @@ int main(int argc, char** argv)
if (jsonrpc < 0)
jsonrpc = SensibleHttpPort;
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({us})));
jsonrpcServer->setIdentities({us});
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({sigKey})));
jsonrpcServer->StartListening();
}
else if (cmd == "jsonstop")
@ -785,12 +1005,11 @@ int main(int argc, char** argv)
#endif
else if (cmd == "address")
{
cout << "Current address:" << endl
<< toHex(us.address().asArray()) << endl;
cout << "Current address:" << endl << sigKey.address() << endl;
}
else if (cmd == "secret")
{
cout << "Secret Key: " << toHex(us.secret().asArray()) << endl;
cout << "Secret Key: " << sigKey.secret() << endl;
}
else if (c && cmd == "block")
{
@ -805,7 +1024,7 @@ int main(int argc, char** argv)
}
else if (c && cmd == "balance")
{
cout << "Current balance: " << formatBalance( c->balanceAt(us.address())) << " = " <<c->balanceAt(us.address()) << " wei" << endl;
cout << "Current balance: " << formatBalance( c->balanceAt(sigKey.address())) << " = " <<c->balanceAt(sigKey.address()) << " wei" << endl;
}
else if (c && cmd == "transact")
{
@ -921,7 +1140,7 @@ int main(int argc, char** argv)
try
{
Address dest = h160(fromHex(hexAddr, WhenError::Throw));
c->submitTransaction(us.secret(), amount, dest, bytes(), minGas);
c->submitTransaction(sigKey.secret(), amount, dest, bytes(), minGas);
}
catch (BadHexCharacter& _e)
{
@ -990,7 +1209,7 @@ int main(int argc, char** argv)
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else
c->submitTransaction(us.secret(), endowment, init, gas, gasPrice);
c->submitTransaction(sigKey.secret(), endowment, init, gas, gasPrice);
}
else
cwarn << "Require parameters: contract ENDOWMENT GASPRICE GAS CODEHEX";
@ -1107,7 +1326,7 @@ int main(int argc, char** argv)
{
string hexSec;
iss >> hexSec;
us = KeyPair(h256(fromHex(hexSec)));
sigKey = KeyPair(h256(fromHex(hexSec)));
}
else
cwarn << "Require parameter: setSecret HEXSECRETKEY";
@ -1147,7 +1366,7 @@ int main(int argc, char** argv)
string path;
iss >> path;
RLPStream config(2);
config << us.secret() << coinbase;
config << sigKey.secret() << coinbase;
writeFile(path, config.out());
}
else
@ -1163,7 +1382,7 @@ int main(int argc, char** argv)
if (b.size())
{
RLP config(b);
us = KeyPair(config[0].toHash<Secret>());
sigKey = KeyPair(config[0].toHash<Secret>());
coinbase = config[1].toHash<Address>();
}
else

149
exp/main.cpp

@ -25,6 +25,7 @@
#include "libethash-cl/cl.hpp"
#endif
#include <functional>
#include <boost/filesystem.hpp>
#include <libdevcore/RangeMask.h>
#include <libdevcore/Log.h>
#include <libdevcore/Common.h>
@ -34,11 +35,12 @@
#include <libdevcore/CommonIO.h>
#include <libdevcrypto/TrieDB.h>
#include <libp2p/All.h>
#include <libethcore/Ethasher.h>
#include <libethcore/ProofOfWork.h>
#include <libethereum/All.h>
#include <libethereum/Farm.h>
#include <libethereum/AccountDiff.h>
#include <libethereum/DownloadMan.h>
#include <libethereum/Client.h>
#include <liblll/All.h>
#include <libwhisper/WhisperPeer.h>
#include <libwhisper/WhisperHost.h>
@ -106,21 +108,144 @@ int main()
cnote << "State after transaction: " << s;
cnote << before.diff(s);
}
#else
#elif 0
int main()
{
#if ETH_ETHASHCL
EthashCL ecl;
GenericFarm<Ethash> f;
BlockInfo genesis = CanonBlockChain::genesis();
genesis.difficulty = 1 << 18;
cdebug << (h256)u256((bigint(1) << 256) / genesis.difficulty);
std::pair<MineInfo, Ethash::Proof> r;
while (!r.first.completed)
r = ecl.mine(genesis, 1000);
cdebug << r.second.mixHash << r.second.nonce;
EthashCL::assignResult(r.second, genesis);
assert(EthashCPU::verify(genesis));
#endif
cdebug << genesis.boundary();
auto mine = [](GenericFarm<Ethash>& f, BlockInfo const& g, unsigned timeout) {
BlockInfo bi = g;
bool completed = false;
f.onSolutionFound([&](ProofOfWork::Solution sol)
{
ProofOfWork::assignResult(sol, bi);
return completed = true;
});
f.setWork(bi);
for (unsigned i = 0; !completed && i < timeout * 10; ++i, cout << f.miningProgress() << "\r" << flush)
this_thread::sleep_for(chrono::milliseconds(100));
cout << endl << flush;
cdebug << bi.mixHash << bi.nonce << (Ethash::verify(bi) ? "GOOD" : "bad");
};
Ethash::prep(genesis);
genesis.difficulty = u256(1) << 40;
genesis.noteDirty();
f.startCPU();
mine(f, genesis, 10);
f.startGPU();
cdebug << "Good:";
genesis.difficulty = 1 << 18;
genesis.noteDirty();
mine(f, genesis, 30);
cdebug << "Bad:";
genesis.difficulty = (u256(1) << 40);
genesis.noteDirty();
mine(f, genesis, 30);
f.stop();
return 0;
}
#elif 0
void mine(State& s, BlockChain const& _bc)
{
s.commitToMine(_bc);
GenericFarm<ProofOfWork> f;
bool completed = false;
f.onSolutionFound([&](ProofOfWork::Solution sol)
{
return completed = s.completeMine<ProofOfWork>(sol);
});
f.setWork(s.info());
f.startCPU();
while (!completed)
this_thread::sleep_for(chrono::milliseconds(20));
}
#elif 0
int main()
{
cnote << "Testing State...";
KeyPair me = sha3("Gav Wood");
KeyPair myMiner = sha3("Gav's Miner");
// KeyPair you = sha3("123");
Defaults::setDBPath(boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count()));
OverlayDB stateDB = State::openDB();
CanonBlockChain bc;
cout << bc;
State s(stateDB, BaseState::CanonGenesis, myMiner.address());
cout << s;
// Sync up - this won't do much until we use the last state.
s.sync(bc);
cout << s;
// Mine to get some ether!
mine(s, bc);
bc.attemptImport(s.blockData(), stateDB);
cout << bc;
s.sync(bc);
cout << s;
// Inject a transaction to transfer funds from miner to me.
Transaction t(1000, 10000, 30000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret());
assert(t.sender() == myMiner.address());
s.execute(bc.lastHashes(), t);
cout << s;
// Mine to get some ether and set in stone.
s.commitToMine(bc);
s.commitToMine(bc);
mine(s, bc);
bc.attemptImport(s.blockData(), stateDB);
cout << bc;
s.sync(bc);
cout << s;
return 0;
}
#else
int main()
{
string tempDir = boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count());
KeyPair myMiner = sha3("Gav's Miner");
p2p::Host net("Test");
cdebug << "Path:" << tempDir;
Client c(&net, tempDir);
c.setAddress(myMiner.address());
this_thread::sleep_for(chrono::milliseconds(1000));
c.startMining();
this_thread::sleep_for(chrono::milliseconds(6000));
c.stopMining();
return 0;
}
#endif

2
libdevcore/Common.cpp

@ -27,7 +27,7 @@ using namespace dev;
namespace dev
{
char const* Version = "0.9.7";
char const* Version = "0.9.8";
}

4
libdevcore/CommonData.h

@ -116,9 +116,9 @@ inline void toBigEndian(_T _val, _Out& o_out)
template <class _T, class _In>
inline _T fromBigEndian(_In const& _bytes)
{
_T ret = 0;
_T ret = (_T)0;
for (auto i: _bytes)
ret = (ret << 8) | (byte)(typename std::make_unsigned<typename _In::value_type>::type)i;
ret = (_T)((ret << 8) | (byte)(typename std::make_unsigned<typename _In::value_type>::type)i);
return ret;
}

71
libdevcore/Guards.h

@ -38,4 +38,75 @@ using UpgradableGuard = boost::upgrade_lock<boost::shared_mutex>;
using UpgradeGuard = boost::upgrade_to_unique_lock<boost::shared_mutex>;
using WriteGuard = boost::unique_lock<boost::shared_mutex>;
template <class GuardType, class MutexType>
struct GenericGuardBool: GuardType
{
GenericGuardBool(MutexType& _m): GuardType(_m) {}
bool b = true;
};
template <class MutexType>
struct GenericUnguardBool
{
GenericUnguardBool(MutexType& _m): m(_m) { m.unlock(); }
~GenericUnguardBool() { m.lock(); }
bool b = true;
MutexType& m;
};
template <class MutexType>
struct GenericUnguardSharedBool
{
GenericUnguardSharedBool(MutexType& _m): m(_m) { m.unlock_shared(); }
~GenericUnguardSharedBool() { m.lock_shared(); }
bool b = true;
MutexType& m;
};
/** @brief Simple block guard.
* The expression/block following is guarded though the given mutex.
* Usage:
* @code
* Mutex m;
* unsigned d;
* ...
* ETH_GUARDED(m) d = 1;
* ...
* ETH_GUARDED(m) { for (auto d = 10; d > 0; --d) foo(d); d = 0; }
* @endcode
*
* There are several variants of this basic mechanism for different Mutex types and Guards.
*
* There is also the UNGUARD variant which allows an unguarded expression/block to exist within a
* guarded expression. eg:
*
* @code
* Mutex m;
* int d;
* ...
* ETH_GUARDED(m)
* {
* for (auto d = 50; d > 25; --d)
* foo(d);
* ETH_UNGUARDED(m)
* bar();
* for (; d > 0; --d)
* foo(d);
* }
* @endcode
*/
#define ETH_GUARDED(MUTEX) \
for (GenericGuardBool<Guard, Mutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
#define ETH_READ_GUARDED(MUTEX) \
for (GenericGuardBool<ReadGuard, SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
#define ETH_WRITE_GUARDED(MUTEX) \
for (GenericGuardBool<WriteGuard, SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
#define ETH_RECURSIVE_GUARDED(MUTEX) \
for (GenericGuardBool<RecursiveGuard, RecursiveMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
#define ETH_UNGUARDED(MUTEX) \
for (GenericUnguardBool<Mutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
#define ETH_READ_UNGUARDED(MUTEX) \
for (GenericUnguardSharedBool<SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
#define ETH_WRITE_UNGUARDED(MUTEX) \
for (GenericUnguardBool<SharedMutex> __eth_l(MUTEX); __eth_l.b; __eth_l.b = false)
}

42
libdevcore/Worker.cpp

@ -27,24 +27,28 @@
using namespace std;
using namespace dev;
void Worker::startWorking()
void Worker::startWorking(IfRunning _ir)
{
cnote << "startWorking for thread" << m_name;
// cnote << "startWorking for thread" << m_name;
Guard l(x_work);
if (m_work)
return;
if (m_work && m_work->joinable())
try {
if (_ir == IfRunning::Detach)
m_work->detach();
else if (_ir == IfRunning::Join)
m_work->join();
else
return;
} catch (...) {}
cnote << "Spawning" << m_name;
m_stop = false;
m_work.reset(new thread([&]()
{
setThreadName(m_name.c_str());
startedWorking();
while (!m_stop)
{
if (m_idleWaitMs)
this_thread::sleep_for(chrono::milliseconds(m_idleWaitMs));
doWork();
}
workLoop();
m_work->detach();
cnote << "Finishing up worker thread";
doneWorking();
}));
@ -52,14 +56,26 @@ void Worker::startWorking()
void Worker::stopWorking()
{
cnote << "stopWorking for thread" << m_name;
// cnote << "stopWorking for thread" << m_name;
Guard l(x_work);
if (!m_work)
if (!m_work || !m_work->joinable())
return;
cnote << "Stopping" << m_name;
m_stop = true;
m_work->join();
try {
m_work->join();
}
catch (...) {}
m_work.reset();
cnote << "Stopped" << m_name;
}
void Worker::workLoop()
{
while (!m_stop)
{
if (m_idleWaitMs)
this_thread::sleep_for(chrono::milliseconds(m_idleWaitMs));
doWork();
}
}

19
libdevcore/Worker.h

@ -23,11 +23,19 @@
#include <string>
#include <thread>
#include <atomic>
#include "Guards.h"
namespace dev
{
enum class IfRunning
{
Fail,
Join,
Detach
};
class Worker
{
protected:
@ -45,7 +53,7 @@ protected:
void setName(std::string _n) { if (!isWorking()) m_name = _n; }
/// Starts worker thread; causes startedWorking() to be called.
void startWorking();
void startWorking(IfRunning _ir = IfRunning::Fail);
/// Stop worker thread; causes call to stopWorking().
void stopWorking();
@ -57,11 +65,18 @@ protected:
virtual void startedWorking() {}
/// Called continuously following sleep for m_idleWaitMs.
virtual void doWork() = 0;
virtual void doWork() {}
/// Overrides doWork(); should call shouldStop() often and exit when true.
virtual void workLoop();
bool shouldStop() const { return m_stop; }
/// Called when is to be stopped, just prior to thread being joined.
virtual void doneWorking() {}
/// Blocks caller into worker thread has finished.
void join() const { Guard l(x_work); try { if (m_work) m_work->join(); } catch (...) {} }
private:
std::string m_name;
unsigned m_idleWaitMs = 0;

19
libethash-cl/CMakeLists.txt

@ -0,0 +1,19 @@
set(EXECUTABLE ethash-cl)
include(bin2h.cmake)
bin2h(SOURCE_FILE ethash_cl_miner_kernel.cl
VARIABLE_NAME ethash_cl_miner_kernel
HEADER_FILE ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h)
aux_source_directory(. SRC_LIST)
file(GLOB HEADERS "*.h")
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${OpenCL_INCLUDE_DIRS}})
include_directories(..)
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
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} )

86
libethash-cl/bin2h.cmake

@ -0,0 +1,86 @@
# https://gist.github.com/sivachandran/3a0de157dccef822a230
include(CMakeParseArguments)
# Function to wrap a given string into multiple lines at the given column position.
# Parameters:
# VARIABLE - The name of the CMake variable holding the string.
# AT_COLUMN - The column position at which string will be wrapped.
function(WRAP_STRING)
set(oneValueArgs VARIABLE AT_COLUMN)
cmake_parse_arguments(WRAP_STRING "${options}" "${oneValueArgs}" "" ${ARGN})
string(LENGTH ${${WRAP_STRING_VARIABLE}} stringLength)
math(EXPR offset "0")
while(stringLength GREATER 0)
if(stringLength GREATER ${WRAP_STRING_AT_COLUMN})
math(EXPR length "${WRAP_STRING_AT_COLUMN}")
else()
math(EXPR length "${stringLength}")
endif()
string(SUBSTRING ${${WRAP_STRING_VARIABLE}} ${offset} ${length} line)
set(lines "${lines}\n${line}")
math(EXPR stringLength "${stringLength} - ${length}")
math(EXPR offset "${offset} + ${length}")
endwhile()
set(${WRAP_STRING_VARIABLE} "${lines}" PARENT_SCOPE)
endfunction()
# Function to embed contents of a file as byte array in C/C++ header file(.h). The header file
# will contain a byte array and integer variable holding the size of the array.
# Parameters
# SOURCE_FILE - The path of source file whose contents will be embedded in the header file.
# VARIABLE_NAME - The name of the variable for the byte array. The string "_SIZE" will be append
# to this name and will be used a variable name for size variable.
# HEADER_FILE - The path of header file.
# APPEND - If specified appends to the header file instead of overwriting it
# NULL_TERMINATE - If specified a null byte(zero) will be append to the byte array. This will be
# useful if the source file is a text file and we want to use the file contents
# as string. But the size variable holds size of the byte array without this
# null byte.
# Usage:
# bin2h(SOURCE_FILE "Logo.png" HEADER_FILE "Logo.h" VARIABLE_NAME "LOGO_PNG")
function(BIN2H)
set(options APPEND NULL_TERMINATE)
set(oneValueArgs SOURCE_FILE VARIABLE_NAME HEADER_FILE)
cmake_parse_arguments(BIN2H "${options}" "${oneValueArgs}" "" ${ARGN})
# reads source file contents as hex string
file(READ ${BIN2H_SOURCE_FILE} hexString HEX)
string(LENGTH ${hexString} hexStringLength)
# appends null byte if asked
if(BIN2H_NULL_TERMINATE)
set(hexString "${hexString}00")
endif()
# wraps the hex string into multiple lines at column 32(i.e. 16 bytes per line)
wrap_string(VARIABLE hexString AT_COLUMN 32)
math(EXPR arraySize "${hexStringLength} / 2")
# adds '0x' prefix and comma suffix before and after every byte respectively
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " arrayValues ${hexString})
# removes trailing comma
string(REGEX REPLACE ", $" "" arrayValues ${arrayValues})
# converts the variable name into proper C identifier
IF (${CMAKE_VERSION} GREATER 2.8.10) # fix for legacy cmake
string(MAKE_C_IDENTIFIER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME)
ENDIF()
string(TOUPPER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME)
# declares byte array and the length variables
set(arrayDefinition "const unsigned char ${BIN2H_VARIABLE_NAME}[] = { ${arrayValues} };")
set(arraySizeDefinition "const size_t ${BIN2H_VARIABLE_NAME}_SIZE = ${arraySize};")
set(declarations "${arrayDefinition}\n\n${arraySizeDefinition}\n\n")
if(BIN2H_APPEND)
file(APPEND ${BIN2H_HEADER_FILE} "${declarations}")
else()
file(WRITE ${BIN2H_HEADER_FILE} "${declarations}")
endif()
endfunction()

4014
libethash-cl/cl.hpp

File diff suppressed because it is too large

366
libethash-cl/ethash_cl_miner.cpp

@ -0,0 +1,366 @@
/*
This file is part of c-ethash.
c-ethash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
c-ethash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ethash_cl_miner.cpp
* @author Tim Hughes <tim@twistedfury.com>
* @date 2015
*/
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstdlib>
#include <assert.h>
#include <queue>
#include <vector>
#include <libethash/util.h>
#include <libethash/ethash.h>
#include "ethash_cl_miner.h"
#include "ethash_cl_miner_kernel.h"
#define ETHASH_BYTES 32
// workaround lame platforms
#if !CL_VERSION_1_2
#define CL_MAP_WRITE_INVALIDATE_REGION CL_MAP_WRITE
#define CL_MEM_HOST_READ_ONLY 0
#endif
#undef min
#undef max
static void add_definition(std::string& source, char const* id, unsigned value)
{
char buf[256];
sprintf(buf, "#define %s %uu\n", id, value);
source.insert(source.begin(), buf, buf + strlen(buf));
}
ethash_cl_miner::search_hook::~search_hook() {}
ethash_cl_miner::ethash_cl_miner()
: m_opencl_1_1()
{
}
std::string ethash_cl_miner::platform_info()
{
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (platforms.empty())
{
debugf("No OpenCL platforms found.\n");
return std::string();
}
// get GPU device of the default platform
std::vector<cl::Device> devices;
platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
debugf("No OpenCL devices found.\n");
return std::string();
}
// use default device
unsigned device_num = 0;
cl::Device& device = devices[device_num];
std::string device_version = device.getInfo<CL_DEVICE_VERSION>();
return "{ platform: '" + platforms[0].getInfo<CL_PLATFORM_NAME>() + "', device: '" + device.getInfo<CL_DEVICE_NAME>() + "', version: '" + device_version + "' }";
}
void ethash_cl_miner::finish()
{
if (m_queue())
{
m_queue.finish();
}
}
bool ethash_cl_miner::init(ethash_params const& params, std::function<void(void*)> _fillDAG, unsigned workgroup_size, unsigned _deviceId)
{
// store params
m_params = params;
// get all platforms
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (platforms.empty())
{
debugf("No OpenCL platforms found.\n");
return false;
}
// use default platform
fprintf(stderr, "Using platform: %s\n", platforms[0].getInfo<CL_PLATFORM_NAME>().c_str());
// get GPU device of the default platform
std::vector<cl::Device> devices;
platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices);
if (devices.empty())
{
debugf("No OpenCL devices found.\n");
return false;
}
// use default device
cl::Device& device = devices[std::min<unsigned>(_deviceId, devices.size() - 1)];
for (unsigned n = 0; n < devices.size(); ++n)
{
auto version = devices[n].getInfo<CL_DEVICE_VERSION>();
auto name = devices[n].getInfo<CL_DEVICE_NAME>();
fprintf(stderr, "%s %d: %s (%s)\n", n == _deviceId ? "USING " : " ", n, name.c_str(), version.c_str());
}
std::string device_version = device.getInfo<CL_DEVICE_VERSION>();
fprintf(stderr, "Using device: %s (%s)\n", device.getInfo<CL_DEVICE_NAME>().c_str(),device_version.c_str());
if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0)
{
debugf("OpenCL 1.0 is not supported.\n");
return false;
}
if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0)
{
m_opencl_1_1 = true;
}
// create context
m_context = cl::Context(std::vector<cl::Device>(&device, &device + 1));
m_queue = cl::CommandQueue(m_context, device);
// use requested workgroup size, but we require multiple of 8
m_workgroup_size = ((workgroup_size + 7) / 8) * 8;
// patch source code
std::string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE);
add_definition(code, "GROUP_SIZE", m_workgroup_size);
add_definition(code, "DAG_SIZE", (unsigned)(params.full_size / ETHASH_MIX_BYTES));
add_definition(code, "ACCESSES", ETHASH_ACCESSES);
add_definition(code, "MAX_OUTPUTS", c_max_search_results);
//debugf("%s", code.c_str());
// create miner OpenCL program
cl::Program::Sources sources;
sources.push_back({code.c_str(), code.size()});
cl::Program program(m_context, sources);
try
{
program.build({device});
}
catch (cl::Error err)
{
debugf("%s\n", program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
return false;
}
m_hash_kernel = cl::Kernel(program, "ethash_hash");
m_search_kernel = cl::Kernel(program, "ethash_search");
// create buffer for dag
m_dag = cl::Buffer(m_context, CL_MEM_READ_ONLY, params.full_size);
// create buffer for header
m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32);
// compute dag on CPU
{
// if this throws then it's because we probably need to subdivide the dag uploads for compatibility
void* dag_ptr = m_queue.enqueueMapBuffer(m_dag, true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, params.full_size);
// memcpying 1GB: horrible... really. horrible. but necessary since we can't mmap *and* gpumap.
_fillDAG(dag_ptr);
m_queue.enqueueUnmapMemObject(m_dag, dag_ptr);
}
// create mining buffers
for (unsigned i = 0; i != c_num_buffers; ++i)
{
m_hash_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY | (!m_opencl_1_1 ? CL_MEM_HOST_READ_ONLY : 0), 32*c_hash_batch_size);
m_search_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY, (c_max_search_results + 1) * sizeof(uint32_t));
}
return true;
}
void ethash_cl_miner::hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count)
{
struct pending_batch
{
unsigned base;
unsigned count;
unsigned buf;
};
std::queue<pending_batch> pending;
// update header constant buffer
m_queue.enqueueWriteBuffer(m_header, true, 0, 32, header);
/*
__kernel void ethash_combined_hash(
__global hash32_t* g_hashes,
__constant hash32_t const* g_header,
__global hash128_t const* g_dag,
ulong start_nonce,
uint isolate
)
*/
m_hash_kernel.setArg(1, m_header);
m_hash_kernel.setArg(2, m_dag);
m_hash_kernel.setArg(3, nonce);
m_hash_kernel.setArg(4, ~0u); // have to pass this to stop the compile unrolling the loop
unsigned buf = 0;
for (unsigned i = 0; i < count || !pending.empty(); )
{
// how many this batch
if (i < count)
{
unsigned const this_count = std::min<unsigned>(count - i, c_hash_batch_size);
unsigned const batch_count = std::max<unsigned>(this_count, m_workgroup_size);
// supply output hash buffer to kernel
m_hash_kernel.setArg(0, m_hash_buf[buf]);
// execute it!
m_queue.enqueueNDRangeKernel(
m_hash_kernel,
cl::NullRange,
cl::NDRange(batch_count),
cl::NDRange(m_workgroup_size)
);
m_queue.flush();
pending.push({i, this_count, buf});
i += this_count;
buf = (buf + 1) % c_num_buffers;
}
// read results
if (i == count || pending.size() == c_num_buffers)
{
pending_batch const& batch = pending.front();
// could use pinned host pointer instead, but this path isn't that important.
uint8_t* hashes = (uint8_t*)m_queue.enqueueMapBuffer(m_hash_buf[batch.buf], true, CL_MAP_READ, 0, batch.count * ETHASH_BYTES);
memcpy(ret + batch.base*ETHASH_BYTES, hashes, batch.count*ETHASH_BYTES);
m_queue.enqueueUnmapMemObject(m_hash_buf[batch.buf], hashes);
pending.pop();
}
}
}
void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook)
{
struct pending_batch
{
uint64_t start_nonce;
unsigned buf;
};
std::queue<pending_batch> pending;
static uint32_t const c_zero = 0;
// update header constant buffer
m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header);
for (unsigned i = 0; i != c_num_buffers; ++i)
{
m_queue.enqueueWriteBuffer(m_search_buf[i], false, 0, 4, &c_zero);
}
#if CL_VERSION_1_2 && 0
cl::Event pre_return_event;
if (!m_opencl_1_1)
{
m_queue.enqueueBarrierWithWaitList(NULL, &pre_return_event);
}
else
#endif
{
m_queue.finish();
}
/*
__kernel void ethash_combined_search(
__global hash32_t* g_hashes, // 0
__constant hash32_t const* g_header, // 1
__global hash128_t const* g_dag, // 2
ulong start_nonce, // 3
ulong target, // 4
uint isolate // 5
)
*/
m_search_kernel.setArg(1, m_header);
m_search_kernel.setArg(2, m_dag);
// pass these to stop the compiler unrolling the loops
m_search_kernel.setArg(4, target);
m_search_kernel.setArg(5, ~0u);
unsigned buf = 0;
for (uint64_t start_nonce = 0; ; start_nonce += c_search_batch_size)
{
// supply output buffer to kernel
m_search_kernel.setArg(0, m_search_buf[buf]);
m_search_kernel.setArg(3, start_nonce);
// execute it!
m_queue.enqueueNDRangeKernel(m_search_kernel, cl::NullRange, c_search_batch_size, m_workgroup_size);
pending.push({start_nonce, buf});
buf = (buf + 1) % c_num_buffers;
// read results
if (pending.size() == c_num_buffers)
{
pending_batch const& batch = pending.front();
// could use pinned host pointer instead
uint32_t* results = (uint32_t*)m_queue.enqueueMapBuffer(m_search_buf[batch.buf], true, CL_MAP_READ, 0, (1+c_max_search_results) * sizeof(uint32_t));
unsigned num_found = std::min<unsigned>(results[0], c_max_search_results);
uint64_t nonces[c_max_search_results];
for (unsigned i = 0; i != num_found; ++i)
{
nonces[i] = batch.start_nonce + results[i+1];
}
m_queue.enqueueUnmapMemObject(m_search_buf[batch.buf], results);
bool exit = num_found && hook.found(nonces, num_found);
exit |= hook.searched(batch.start_nonce, c_search_batch_size); // always report searched before exit
if (exit)
break;
// reset search buffer if we're still going
if (num_found)
m_queue.enqueueWriteBuffer(m_search_buf[batch.buf], true, 0, 4, &c_zero);
pending.pop();
}
}
// not safe to return until this is ready
#if CL_VERSION_1_2 && 0
if (!m_opencl_1_1)
{
pre_return_event.wait();
}
#endif
}

55
libethash-cl/ethash_cl_miner.h

@ -0,0 +1,55 @@
#pragma once
#define __CL_ENABLE_EXCEPTIONS
#define CL_USE_DEPRECATED_OPENCL_2_0_APIS
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#include "cl.hpp"
#pragma clang diagnostic pop
#else
#include "cl.hpp"
#endif
#include <time.h>
#include <functional>
#include <libethash/ethash.h>
class ethash_cl_miner
{
public:
struct search_hook
{
virtual ~search_hook(); // always a virtual destructor for a class with virtuals.
// reports progress, return true to abort
virtual bool found(uint64_t const* nonces, uint32_t count) = 0;
virtual bool searched(uint64_t start_nonce, uint32_t count) = 0;
};
public:
ethash_cl_miner();
bool init(ethash_params const& params, std::function<void(void*)> _fillDAG, unsigned workgroup_size = 64, unsigned _deviceId = 0);
static std::string platform_info();
void finish();
void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count);
void search(uint8_t const* header, uint64_t target, search_hook& hook);
private:
enum { c_max_search_results = 63, c_num_buffers = 2, c_hash_batch_size = 1024, c_search_batch_size = 1024*256 };
ethash_params m_params;
cl::Context m_context;
cl::CommandQueue m_queue;
cl::Kernel m_hash_kernel;
cl::Kernel m_search_kernel;
cl::Buffer m_dag;
cl::Buffer m_header;
cl::Buffer m_hash_buf[c_num_buffers];
cl::Buffer m_search_buf[c_num_buffers];
unsigned m_workgroup_size;
bool m_opencl_1_1;
};

460
libethash-cl/ethash_cl_miner_kernel.cl

@ -0,0 +1,460 @@
// author Tim Hughes <tim@twistedfury.com>
// Tested on Radeon HD 7850
// Hashrate: 15940347 hashes/s
// Bandwidth: 124533 MB/s
// search kernel should fit in <= 84 VGPRS (3 wavefronts)
#define THREADS_PER_HASH (128 / 16)
#define HASHES_PER_LOOP (GROUP_SIZE / THREADS_PER_HASH)
#define FNV_PRIME 0x01000193
__constant uint2 const Keccak_f1600_RC[24] = {
(uint2)(0x00000001, 0x00000000),
(uint2)(0x00008082, 0x00000000),
(uint2)(0x0000808a, 0x80000000),
(uint2)(0x80008000, 0x80000000),
(uint2)(0x0000808b, 0x00000000),
(uint2)(0x80000001, 0x00000000),
(uint2)(0x80008081, 0x80000000),
(uint2)(0x00008009, 0x80000000),
(uint2)(0x0000008a, 0x00000000),
(uint2)(0x00000088, 0x00000000),
(uint2)(0x80008009, 0x00000000),
(uint2)(0x8000000a, 0x00000000),
(uint2)(0x8000808b, 0x00000000),
(uint2)(0x0000008b, 0x80000000),
(uint2)(0x00008089, 0x80000000),
(uint2)(0x00008003, 0x80000000),
(uint2)(0x00008002, 0x80000000),
(uint2)(0x00000080, 0x80000000),
(uint2)(0x0000800a, 0x00000000),
(uint2)(0x8000000a, 0x80000000),
(uint2)(0x80008081, 0x80000000),
(uint2)(0x00008080, 0x80000000),
(uint2)(0x80000001, 0x00000000),
(uint2)(0x80008008, 0x80000000),
};
void keccak_f1600_round(uint2* a, uint r, uint out_size)
{
#if !__ENDIAN_LITTLE__
for (uint i = 0; i != 25; ++i)
a[i] = a[i].yx;
#endif
uint2 b[25];
uint2 t;
// Theta
b[0] = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20];
b[1] = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21];
b[2] = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22];
b[3] = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23];
b[4] = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24];
t = b[4] ^ (uint2)(b[1].x << 1 | b[1].y >> 31, b[1].y << 1 | b[1].x >> 31);
a[0] ^= t;
a[5] ^= t;
a[10] ^= t;
a[15] ^= t;
a[20] ^= t;
t = b[0] ^ (uint2)(b[2].x << 1 | b[2].y >> 31, b[2].y << 1 | b[2].x >> 31);
a[1] ^= t;
a[6] ^= t;
a[11] ^= t;
a[16] ^= t;
a[21] ^= t;
t = b[1] ^ (uint2)(b[3].x << 1 | b[3].y >> 31, b[3].y << 1 | b[3].x >> 31);
a[2] ^= t;
a[7] ^= t;
a[12] ^= t;
a[17] ^= t;
a[22] ^= t;
t = b[2] ^ (uint2)(b[4].x << 1 | b[4].y >> 31, b[4].y << 1 | b[4].x >> 31);
a[3] ^= t;
a[8] ^= t;
a[13] ^= t;
a[18] ^= t;
a[23] ^= t;
t = b[3] ^ (uint2)(b[0].x << 1 | b[0].y >> 31, b[0].y << 1 | b[0].x >> 31);
a[4] ^= t;
a[9] ^= t;
a[14] ^= t;
a[19] ^= t;
a[24] ^= t;
// Rho Pi
b[0] = a[0];
b[10] = (uint2)(a[1].x << 1 | a[1].y >> 31, a[1].y << 1 | a[1].x >> 31);
b[7] = (uint2)(a[10].x << 3 | a[10].y >> 29, a[10].y << 3 | a[10].x >> 29);
b[11] = (uint2)(a[7].x << 6 | a[7].y >> 26, a[7].y << 6 | a[7].x >> 26);
b[17] = (uint2)(a[11].x << 10 | a[11].y >> 22, a[11].y << 10 | a[11].x >> 22);
b[18] = (uint2)(a[17].x << 15 | a[17].y >> 17, a[17].y << 15 | a[17].x >> 17);
b[3] = (uint2)(a[18].x << 21 | a[18].y >> 11, a[18].y << 21 | a[18].x >> 11);
b[5] = (uint2)(a[3].x << 28 | a[3].y >> 4, a[3].y << 28 | a[3].x >> 4);
b[16] = (uint2)(a[5].y << 4 | a[5].x >> 28, a[5].x << 4 | a[5].y >> 28);
b[8] = (uint2)(a[16].y << 13 | a[16].x >> 19, a[16].x << 13 | a[16].y >> 19);
b[21] = (uint2)(a[8].y << 23 | a[8].x >> 9, a[8].x << 23 | a[8].y >> 9);
b[24] = (uint2)(a[21].x << 2 | a[21].y >> 30, a[21].y << 2 | a[21].x >> 30);
b[4] = (uint2)(a[24].x << 14 | a[24].y >> 18, a[24].y << 14 | a[24].x >> 18);
b[15] = (uint2)(a[4].x << 27 | a[4].y >> 5, a[4].y << 27 | a[4].x >> 5);
b[23] = (uint2)(a[15].y << 9 | a[15].x >> 23, a[15].x << 9 | a[15].y >> 23);
b[19] = (uint2)(a[23].y << 24 | a[23].x >> 8, a[23].x << 24 | a[23].y >> 8);
b[13] = (uint2)(a[19].x << 8 | a[19].y >> 24, a[19].y << 8 | a[19].x >> 24);
b[12] = (uint2)(a[13].x << 25 | a[13].y >> 7, a[13].y << 25 | a[13].x >> 7);
b[2] = (uint2)(a[12].y << 11 | a[12].x >> 21, a[12].x << 11 | a[12].y >> 21);
b[20] = (uint2)(a[2].y << 30 | a[2].x >> 2, a[2].x << 30 | a[2].y >> 2);
b[14] = (uint2)(a[20].x << 18 | a[20].y >> 14, a[20].y << 18 | a[20].x >> 14);
b[22] = (uint2)(a[14].y << 7 | a[14].x >> 25, a[14].x << 7 | a[14].y >> 25);
b[9] = (uint2)(a[22].y << 29 | a[22].x >> 3, a[22].x << 29 | a[22].y >> 3);
b[6] = (uint2)(a[9].x << 20 | a[9].y >> 12, a[9].y << 20 | a[9].x >> 12);
b[1] = (uint2)(a[6].y << 12 | a[6].x >> 20, a[6].x << 12 | a[6].y >> 20);
// Chi
a[0] = bitselect(b[0] ^ b[2], b[0], b[1]);
a[1] = bitselect(b[1] ^ b[3], b[1], b[2]);
a[2] = bitselect(b[2] ^ b[4], b[2], b[3]);
a[3] = bitselect(b[3] ^ b[0], b[3], b[4]);
if (out_size >= 4)
{
a[4] = bitselect(b[4] ^ b[1], b[4], b[0]);
a[5] = bitselect(b[5] ^ b[7], b[5], b[6]);
a[6] = bitselect(b[6] ^ b[8], b[6], b[7]);
a[7] = bitselect(b[7] ^ b[9], b[7], b[8]);
a[8] = bitselect(b[8] ^ b[5], b[8], b[9]);
if (out_size >= 8)
{
a[9] = bitselect(b[9] ^ b[6], b[9], b[5]);
a[10] = bitselect(b[10] ^ b[12], b[10], b[11]);
a[11] = bitselect(b[11] ^ b[13], b[11], b[12]);
a[12] = bitselect(b[12] ^ b[14], b[12], b[13]);
a[13] = bitselect(b[13] ^ b[10], b[13], b[14]);
a[14] = bitselect(b[14] ^ b[11], b[14], b[10]);
a[15] = bitselect(b[15] ^ b[17], b[15], b[16]);
a[16] = bitselect(b[16] ^ b[18], b[16], b[17]);
a[17] = bitselect(b[17] ^ b[19], b[17], b[18]);
a[18] = bitselect(b[18] ^ b[15], b[18], b[19]);
a[19] = bitselect(b[19] ^ b[16], b[19], b[15]);
a[20] = bitselect(b[20] ^ b[22], b[20], b[21]);
a[21] = bitselect(b[21] ^ b[23], b[21], b[22]);
a[22] = bitselect(b[22] ^ b[24], b[22], b[23]);
a[23] = bitselect(b[23] ^ b[20], b[23], b[24]);
a[24] = bitselect(b[24] ^ b[21], b[24], b[20]);
}
}
// Iota
a[0] ^= Keccak_f1600_RC[r];
#if !__ENDIAN_LITTLE__
for (uint i = 0; i != 25; ++i)
a[i] = a[i].yx;
#endif
}
void keccak_f1600_no_absorb(ulong* a, uint in_size, uint out_size, uint isolate)
{
for (uint i = in_size; i != 25; ++i)
{
a[i] = 0;
}
#if __ENDIAN_LITTLE__
a[in_size] ^= 0x0000000000000001;
a[24-out_size*2] ^= 0x8000000000000000;
#else
a[in_size] ^= 0x0100000000000000;
a[24-out_size*2] ^= 0x0000000000000080;
#endif
// Originally I unrolled the first and last rounds to interface
// better with surrounding code, however I haven't done this
// without causing the AMD compiler to blow up the VGPR usage.
uint r = 0;
do
{
// This dynamic branch stops the AMD compiler unrolling the loop
// and additionally saves about 33% of the VGPRs, enough to gain another
// wavefront. Ideally we'd get 4 in flight, but 3 is the best I can
// massage out of the compiler. It doesn't really seem to matter how
// much we try and help the compiler save VGPRs because it seems to throw
// that information away, hence the implementation of keccak here
// doesn't bother.
if (isolate)
{
keccak_f1600_round((uint2*)a, r++, 25);
}
}
while (r < 23);
// final round optimised for digest size
keccak_f1600_round((uint2*)a, r++, out_size);
}
#define copy(dst, src, count) for (uint i = 0; i != count; ++i) { (dst)[i] = (src)[i]; }
#define countof(x) (sizeof(x) / sizeof(x[0]))
uint fnv(uint x, uint y)
{
return x * FNV_PRIME ^ y;
}
uint4 fnv4(uint4 x, uint4 y)
{
return x * FNV_PRIME ^ y;
}
uint fnv_reduce(uint4 v)
{
return fnv(fnv(fnv(v.x, v.y), v.z), v.w);
}
typedef union
{
ulong ulongs[32 / sizeof(ulong)];
uint uints[32 / sizeof(uint)];
} hash32_t;
typedef union
{
ulong ulongs[64 / sizeof(ulong)];
uint4 uint4s[64 / sizeof(uint4)];
} hash64_t;
typedef union
{
uint uints[128 / sizeof(uint)];
uint4 uint4s[128 / sizeof(uint4)];
} hash128_t;
hash64_t init_hash(__constant hash32_t const* header, ulong nonce, uint isolate)
{
hash64_t init;
uint const init_size = countof(init.ulongs);
uint const hash_size = countof(header->ulongs);
// sha3_512(header .. nonce)
ulong state[25];
copy(state, header->ulongs, hash_size);
state[hash_size] = nonce;
keccak_f1600_no_absorb(state, hash_size + 1, init_size, isolate);
copy(init.ulongs, state, init_size);
return init;
}
uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, uint isolate)
{
uint4 mix = init;
// share init0
if (thread_id == 0)
*share = mix.x;
barrier(CLK_LOCAL_MEM_FENCE);
uint init0 = *share;
uint a = 0;
do
{
bool update_share = thread_id == (a/4) % THREADS_PER_HASH;
#pragma unroll
for (uint i = 0; i != 4; ++i)
{
if (update_share)
{
uint m[4] = { mix.x, mix.y, mix.z, mix.w };
*share = fnv(init0 ^ (a+i), m[i]) % DAG_SIZE;
}
barrier(CLK_LOCAL_MEM_FENCE);
mix = fnv4(mix, g_dag[*share].uint4s[thread_id]);
}
}
while ((a += 4) != (ACCESSES & isolate));
return fnv_reduce(mix);
}
hash32_t final_hash(hash64_t const* init, hash32_t const* mix, uint isolate)
{
ulong state[25];
hash32_t hash;
uint const hash_size = countof(hash.ulongs);
uint const init_size = countof(init->ulongs);
uint const mix_size = countof(mix->ulongs);
// keccak_256(keccak_512(header..nonce) .. mix);
copy(state, init->ulongs, init_size);
copy(state + init_size, mix->ulongs, mix_size);
keccak_f1600_no_absorb(state, init_size+mix_size, hash_size, isolate);
// copy out
copy(hash.ulongs, state, hash_size);
return hash;
}
hash32_t compute_hash_simple(
__constant hash32_t const* g_header,
__global hash128_t const* g_dag,
ulong nonce,
uint isolate
)
{
hash64_t init = init_hash(g_header, nonce, isolate);
hash128_t mix;
for (uint i = 0; i != countof(mix.uint4s); ++i)
{
mix.uint4s[i] = init.uint4s[i % countof(init.uint4s)];
}
uint mix_val = mix.uints[0];
uint init0 = mix.uints[0];
uint a = 0;
do
{
uint pi = fnv(init0 ^ a, mix_val) % DAG_SIZE;
uint n = (a+1) % countof(mix.uints);
#pragma unroll
for (uint i = 0; i != countof(mix.uints); ++i)
{
mix.uints[i] = fnv(mix.uints[i], g_dag[pi].uints[i]);
mix_val = i == n ? mix.uints[i] : mix_val;
}
}
while (++a != (ACCESSES & isolate));
// reduce to output
hash32_t fnv_mix;
for (uint i = 0; i != countof(fnv_mix.uints); ++i)
{
fnv_mix.uints[i] = fnv_reduce(mix.uint4s[i]);
}
return final_hash(&init, &fnv_mix, isolate);
}
typedef union
{
struct
{
hash64_t init;
uint pad; // avoid lds bank conflicts
};
hash32_t mix;
} compute_hash_share;
hash32_t compute_hash(
__local compute_hash_share* share,
__constant hash32_t const* g_header,
__global hash128_t const* g_dag,
ulong nonce,
uint isolate
)
{
uint const gid = get_global_id(0);
// Compute one init hash per work item.
hash64_t init = init_hash(g_header, nonce, isolate);
// Threads work together in this phase in groups of 8.
uint const thread_id = gid % THREADS_PER_HASH;
uint const hash_id = (gid % GROUP_SIZE) / THREADS_PER_HASH;
hash32_t mix;
uint i = 0;
do
{
// share init with other threads
if (i == thread_id)
share[hash_id].init = init;
barrier(CLK_LOCAL_MEM_FENCE);
uint4 thread_init = share[hash_id].init.uint4s[thread_id % (64 / sizeof(uint4))];
barrier(CLK_LOCAL_MEM_FENCE);
uint thread_mix = inner_loop(thread_init, thread_id, share[hash_id].mix.uints, g_dag, isolate);
share[hash_id].mix.uints[thread_id] = thread_mix;
barrier(CLK_LOCAL_MEM_FENCE);
if (i == thread_id)
mix = share[hash_id].mix;
barrier(CLK_LOCAL_MEM_FENCE);
}
while (++i != (THREADS_PER_HASH & isolate));
return final_hash(&init, &mix, isolate);
}
__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1)))
__kernel void ethash_hash_simple(
__global hash32_t* g_hashes,
__constant hash32_t const* g_header,
__global hash128_t const* g_dag,
ulong start_nonce,
uint isolate
)
{
uint const gid = get_global_id(0);
g_hashes[gid] = compute_hash_simple(g_header, g_dag, start_nonce + gid, isolate);
}
__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1)))
__kernel void ethash_search_simple(
__global volatile uint* restrict g_output,
__constant hash32_t const* g_header,
__global hash128_t const* g_dag,
ulong start_nonce,
ulong target,
uint isolate
)
{
uint const gid = get_global_id(0);
hash32_t hash = compute_hash_simple(g_header, g_dag, start_nonce + gid, isolate);
if (as_ulong(as_uchar8(hash.ulongs[0]).s76543210) < target)
{
uint slot = min(MAX_OUTPUTS, atomic_inc(&g_output[0]) + 1);
g_output[slot] = gid;
}
}
__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1)))
__kernel void ethash_hash(
__global hash32_t* g_hashes,
__constant hash32_t const* g_header,
__global hash128_t const* g_dag,
ulong start_nonce,
uint isolate
)
{
__local compute_hash_share share[HASHES_PER_LOOP];
uint const gid = get_global_id(0);
g_hashes[gid] = compute_hash(share, g_header, g_dag, start_nonce + gid, isolate);
}
__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1)))
__kernel void ethash_search(
__global volatile uint* restrict g_output,
__constant hash32_t const* g_header,
__global hash128_t const* g_dag,
ulong start_nonce,
ulong target,
uint isolate
)
{
__local compute_hash_share share[HASHES_PER_LOOP];
uint const gid = get_global_id(0);
hash32_t hash = compute_hash(share, g_header, g_dag, start_nonce + gid, isolate);
if (as_ulong(as_uchar8(hash.ulongs[0]).s76543210) < target)
{
uint slot = min(MAX_OUTPUTS, atomic_inc(&g_output[0]) + 1);
g_output[slot] = gid;
}
}

4
libethash/ethash.h

@ -85,7 +85,7 @@ typedef uint8_t const ethash_seedhash_t[32];
typedef void const* ethash_light_t;
static inline ethash_light_t ethash_new_light(ethash_params const* params, ethash_seedhash_t seed) {
void* ret = malloc(params->cache_size);
void* ret = malloc((size_t)params->cache_size);
ethash_mkcache(ret, params, seed);
return ret;
}
@ -98,7 +98,7 @@ static inline void ethash_delete_light(ethash_light_t light) {
typedef void const* ethash_full_t;
static inline ethash_full_t ethash_new_full(ethash_params const* params, ethash_light_t light) {
void* ret = malloc(params->full_size);
void* ret = malloc((size_t)params->full_size);
ethash_compute_full_data(ret, params, light);
return ret;
}

6
libethash/io.c

@ -61,13 +61,13 @@ bool ethash_io_write(char const *dirname,
{
char info_buffer[DAG_MEMO_BYTESIZE];
// allocate the bytes
uint8_t *temp_data_ptr = malloc(params->full_size);
uint8_t *temp_data_ptr = malloc((size_t)params->full_size);
if (!temp_data_ptr) {
goto end;
}
ethash_compute_full_data(temp_data_ptr, params, cache);
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_FILE_NAME), temp_data_ptr, params->full_size)) {
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_FILE_NAME), temp_data_ptr, (size_t)params->full_size)) {
goto fail_free;
}
@ -77,7 +77,7 @@ bool ethash_io_write(char const *dirname,
}
*data = temp_data_ptr;
*data_size = params->full_size;
*data_size = (size_t)params->full_size;
return true;
fail_free:

9
libethcore/BlockInfo.cpp

@ -23,6 +23,7 @@
#include <libdevcore/RLP.h>
#include <libdevcrypto/TrieDB.h>
#include <libethcore/Common.h>
#include "EthashAux.h"
#include "ProofOfWork.h"
#include "Exceptions.h"
#include "Params.h"
@ -63,8 +64,7 @@ void BlockInfo::clear()
h256 const& BlockInfo::seedHash() const
{
if (!m_seedHash)
for (u256 n = number; n >= c_epochDuration; n -= c_epochDuration)
m_seedHash = sha3(m_seedHash);
m_seedHash = EthashAux::seedHash((unsigned)number);
return m_seedHash;
}
@ -145,9 +145,14 @@ void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const
throw;
}
if (number > ~(unsigned)0)
throw InvalidNumber();
// check it hashes according to proof of work or that it's the genesis block.
if (_s == CheckEverything && parentHash && !ProofOfWork::verify(*this))
BOOST_THROW_EXCEPTION(InvalidBlockNonce() << errinfo_hash256(headerHash(WithoutNonce)) << errinfo_nonce(nonce) << errinfo_difficulty(difficulty));
else if (_s == QuickNonce && parentHash && !ProofOfWork::preVerify(*this))
BOOST_THROW_EXCEPTION(InvalidBlockNonce() << errinfo_hash256(headerHash(WithoutNonce)) << errinfo_nonce(nonce) << errinfo_difficulty(difficulty));
if (_s != CheckNothing)
{

1
libethcore/BlockInfo.h

@ -39,6 +39,7 @@ enum IncludeNonce
enum Strictness
{
CheckEverything,
QuickNonce,
IgnoreNonce,
CheckNothing
};

11
libethcore/CMakeLists.txt

@ -11,6 +11,9 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})
if (CPUID_FOUND)
include_directories(${Cpuid_INCLUDE_DIRS})
endif ()
set(EXECUTABLE ethcore)
@ -26,6 +29,14 @@ target_link_libraries(${EXECUTABLE} ethash)
target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} devcore)
if (ETHASHCL)
target_link_libraries(${EXECUTABLE} ethash-cl)
endif ()
if (CPUID_FOUND)
target_link_libraries(${EXECUTABLE} ${CPUID_LIBRARIES})
endif ()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

5
libethcore/Common.cpp

@ -22,8 +22,8 @@
#include "Common.h"
#include <random>
#include <libdevcrypto/SHA3.h>
#include "Ethasher.h"
#include "Exceptions.h"
#include "ProofOfWork.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -33,7 +33,6 @@ namespace dev
namespace eth
{
const unsigned c_ethashVersion = c_ethashRevision;
const unsigned c_protocolVersion = 60;
const unsigned c_minorProtocolVersion = 0;
const unsigned c_databaseBaseVersion = 9;
@ -43,7 +42,7 @@ const unsigned c_databaseVersionModifier = 1;
const unsigned c_databaseVersionModifier = 0;
#endif
const unsigned c_databaseVersion = c_databaseBaseVersion + (c_databaseVersionModifier << 8) + (c_ethashVersion << 9);
const unsigned c_databaseVersion = c_databaseBaseVersion + (c_databaseVersionModifier << 8) + (ProofOfWork::revision() << 9);
vector<pair<u256, string>> const& units()
{

33
libethcore/Common.h

@ -41,9 +41,6 @@ extern const unsigned c_minorProtocolVersion;
/// Current database version.
extern const unsigned c_databaseVersion;
/// Current database version.
extern const unsigned c_ethashVersion;
/// User-friendly string representation of the amount _b in wei.
std::string formatBalance(bigint const& _b);
@ -96,5 +93,35 @@ enum class ImportResult
BadChain
};
/// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight.
class Signal
{
public:
class HandlerAux
{
friend class Signal;
public:
~HandlerAux() { if (m_s) m_s->m_fire.erase(m_i); m_s = nullptr; }
private:
HandlerAux(unsigned _i, Signal* _s): m_i(_i), m_s(_s) {}
unsigned m_i = 0;
Signal* m_s = nullptr;
};
using Callback = std::function<void()>;
std::shared_ptr<HandlerAux> add(Callback const& _h) { auto n = m_fire.empty() ? 0 : (m_fire.rbegin()->first + 1); m_fire[n] = _h; return std::shared_ptr<HandlerAux>(new HandlerAux(n, this)); }
void operator()() { for (auto const& f: m_fire) f.second(); }
private:
std::map<unsigned, Callback> m_fire;
};
using Handler = std::shared_ptr<Signal::HandlerAux>;
}
}

328
libethcore/Ethash.cpp

@ -0,0 +1,328 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Ethash.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Ethash.h"
#include <boost/detail/endian.hpp>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <chrono>
#include <array>
#include <thread>
#include <random>
#include <thread>
#include <libdevcore/Guards.h>
#include <libdevcore/Log.h>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/FileSystem.h>
#include <libethash/ethash.h>
#if ETH_ETHASHCL || !ETH_TRUE
#include <libethash-cl/ethash_cl_miner.h>
#endif
#if ETH_CPUID || !ETH_TRUE
#define HAVE_STDINT_H
#include <libcpuid/libcpuid.h>
#endif
#include "BlockInfo.h"
#include "EthashAux.h"
using namespace std;
using namespace std::chrono;
namespace dev
{
namespace eth
{
const Ethash::WorkPackage Ethash::NullWorkPackage = Ethash::WorkPackage();
std::string Ethash::name()
{
return "Ethash";
}
unsigned Ethash::revision()
{
return ETHASH_REVISION;
}
Ethash::WorkPackage Ethash::package(BlockInfo const& _bi)
{
WorkPackage ret;
ret.boundary = _bi.boundary();
ret.headerHash = _bi.headerHash(WithoutNonce);
ret.seedHash = _bi.seedHash();
return ret;
}
void Ethash::prep(BlockInfo const& _header)
{
EthashAux::full(_header);
}
bool Ethash::preVerify(BlockInfo const& _header)
{
if (_header.number >= ETHASH_EPOCH_LENGTH * 2048)
return false;
h256 boundary = u256((bigint(1) << 256) / _header.difficulty);
return !!ethash_quick_check_difficulty(
_header.headerHash(WithoutNonce).data(),
(uint64_t)(u64)_header.nonce,
_header.mixHash.data(),
boundary.data());
}
bool Ethash::verify(BlockInfo const& _header)
{
bool pre = preVerify(_header);
#if !ETH_DEBUG
if (!pre)
return false;
#endif
auto result = EthashAux::eval(_header);
bool slow = result.value <= _header.boundary() && result.mixHash == _header.mixHash;
#if ETH_DEBUG || !ETH_TRUE
if (!pre && slow)
{
cwarn << "WARNING: evaluated result gives true whereas ethash_quick_check_difficulty gives false.";
cwarn << "headerHash:" << _header.headerHash(WithoutNonce);
cwarn << "nonce:" << _header.nonce;
cwarn << "mixHash:" << _header.mixHash;
cwarn << "difficulty:" << _header.difficulty;
cwarn << "boundary:" << _header.boundary();
cwarn << "result.value:" << result.value;
cwarn << "result.mixHash:" << result.mixHash;
}
#endif
return slow;
}
void Ethash::CPUMiner::workLoop()
{
auto tid = std::this_thread::get_id();
static std::mt19937_64 s_eng((time(0) + std::hash<decltype(tid)>()(tid)));
uint64_t tryNonce = (uint64_t)(u64)Nonce::random(s_eng);
ethash_return_value ethashReturn;
WorkPackage w = work();
auto p = EthashAux::params(w.seedHash);
void const* dagPointer = EthashAux::full(w.seedHash).data();
uint8_t const* headerHashPointer = w.headerHash.data();
h256 boundary = w.boundary;
unsigned hashCount = 1;
for (; !shouldStop(); tryNonce++, hashCount++)
{
ethash_compute_full(&ethashReturn, dagPointer, &p, headerHashPointer, tryNonce);
h256 value = h256(ethashReturn.result, h256::ConstructFromPointer);
if (value <= boundary && submitProof(Solution{(Nonce)(u64)tryNonce, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)}))
break;
if (!(hashCount % 1000))
accumulateHashes(1000);
}
}
static string jsonEncode(map<string, string> const& _m)
{
string ret = "{";
for (auto const& i: _m)
{
string k = boost::replace_all_copy(boost::replace_all_copy(i.first, "\\", "\\\\"), "'", "\\'");
string v = boost::replace_all_copy(boost::replace_all_copy(i.second, "\\", "\\\\"), "'", "\\'");
if (ret.size() > 1)
ret += ", ";
ret += "\"" + k + "\":\"" + v + "\"";
}
return ret + "}";
}
std::string Ethash::CPUMiner::platformInfo()
{
string baseline = toString(std::thread::hardware_concurrency()) + "-thread CPU";
#if ETH_CPUID || !ETH_TRUE
if (!cpuid_present())
return baseline;
struct cpu_raw_data_t raw;
struct cpu_id_t data;
if (cpuid_get_raw_data(&raw) < 0)
return baseline;
if (cpu_identify(&raw, &data) < 0)
return baseline;
map<string, string> m;
m["vendor"] = data.vendor_str;
m["codename"] = data.cpu_codename;
m["brand"] = data.brand_str;
m["L1 cache"] = toString(data.l1_data_cache);
m["L2 cache"] = toString(data.l2_cache);
m["L3 cache"] = toString(data.l3_cache);
m["cores"] = toString(data.num_cores);
m["threads"] = toString(data.num_logical_cpus);
m["clocknominal"] = toString(cpu_clock_by_os());
m["clocktested"] = toString(cpu_clock_measure(200, 0));
/*
printf(" MMX : %s\n", data.flags[CPU_FEATURE_MMX] ? "present" : "absent");
printf(" MMX-extended: %s\n", data.flags[CPU_FEATURE_MMXEXT] ? "present" : "absent");
printf(" SSE : %s\n", data.flags[CPU_FEATURE_SSE] ? "present" : "absent");
printf(" SSE2 : %s\n", data.flags[CPU_FEATURE_SSE2] ? "present" : "absent");
printf(" 3DNow! : %s\n", data.flags[CPU_FEATURE_3DNOW] ? "present" : "absent");
*/
return jsonEncode(m);
#else
return baseline;
#endif
}
#if ETH_ETHASHCL || !ETH_TRUE
class EthashCLHook: public ethash_cl_miner::search_hook
{
public:
EthashCLHook(Ethash::GPUMiner* _owner): m_owner(_owner) {}
void abort()
{
Guard l(x_all);
if (m_aborted)
return;
// cdebug << "Attempting to abort";
m_abort = true;
for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout)
std::this_thread::sleep_for(chrono::milliseconds(30));
// if (!m_aborted)
// cwarn << "Couldn't abort. Abandoning OpenCL process.";
}
void reset()
{
m_aborted = m_abort = false;
}
protected:
virtual bool found(uint64_t const* _nonces, uint32_t _count) override
{
// dev::operator <<(std::cerr << "Found nonces: ", vector<uint64_t>(_nonces, _nonces + _count)) << std::endl;
for (uint32_t i = 0; i < _count; ++i)
{
if (m_owner->report(_nonces[i]))
{
m_aborted = true;
return true;
}
}
return false;
}
virtual bool searched(uint64_t _startNonce, uint32_t _count) override
{
Guard l(x_all);
// std::cerr << "Searched " << _count << " from " << _startNonce << std::endl;
m_owner->accumulateHashes(_count);
m_last = _startNonce + _count;
if (m_abort)
{
m_aborted = true;
return true;
}
return false;
}
private:
Mutex x_all;
uint64_t m_last;
bool m_abort = false;
bool m_aborted = true;
Ethash::GPUMiner* m_owner = nullptr;
};
unsigned Ethash::GPUMiner::s_deviceId = 0;
Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci):
Miner(_ci),
m_hook(new EthashCLHook(this))
{
}
Ethash::GPUMiner::~GPUMiner()
{
pause();
delete m_miner;
delete m_hook;
}
bool Ethash::GPUMiner::report(uint64_t _nonce)
{
Nonce n = (Nonce)(u64)_nonce;
Result r = EthashAux::eval(work().seedHash, work().headerHash, n);
if (r.value < work().boundary)
return submitProof(Solution{n, r.mixHash});
return false;
}
void Ethash::GPUMiner::kickOff()
{
m_hook->reset();
startWorking();
}
void Ethash::GPUMiner::workLoop()
{
// take local copy of work since it may end up being overwritten by kickOff/pause.
WorkPackage w = work();
if (!m_miner || m_minerSeed != w.seedHash)
{
m_minerSeed = w.seedHash;
delete m_miner;
m_miner = new ethash_cl_miner;
auto p = EthashAux::params(m_minerSeed);
auto cb = [&](void* d) { EthashAux::full(m_minerSeed, bytesRef((byte*)d, p.full_size)); };
m_miner->init(p, cb, 32, s_deviceId);
}
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);
m_miner->search(w.headerHash.data(), upper64OfBoundary, *m_hook);
}
void Ethash::GPUMiner::pause()
{
m_hook->abort();
stopWorking();
}
std::string Ethash::GPUMiner::platformInfo()
{
return ethash_cl_miner::platform_info();
}
#endif
}
}

140
libethcore/Ethash.h

@ -0,0 +1,140 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Ethash.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*
* A proof of work algorithm.
*/
#pragma once
#include <chrono>
#include <thread>
#include <cstdint>
#include <libdevcore/CommonIO.h>
#include "Common.h"
#include "BlockInfo.h"
#include "Miner.h"
class ethash_cl_miner;
namespace dev
{
namespace eth
{
class EthashCLHook;
class Ethash
{
public:
using Miner = GenericMiner<Ethash>;
struct Solution
{
Nonce nonce;
h256 mixHash;
};
struct Result
{
h256 value;
h256 mixHash;
};
struct WorkPackage
{
WorkPackage() = default;
void reset() { headerHash = h256(); }
operator bool() const { return headerHash != h256(); }
h256 boundary;
h256 headerHash; ///< When h256() means "pause until notified a new work package is available".
h256 seedHash;
};
static const WorkPackage NullWorkPackage;
static std::string name();
static unsigned revision();
static void prep(BlockInfo const& _header);
static bool verify(BlockInfo const& _header);
static bool preVerify(BlockInfo const& _header);
static WorkPackage package(BlockInfo const& _header);
static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; }
class CPUMiner: public Miner, Worker
{
public:
CPUMiner(ConstructionInfo const& _ci): Miner(_ci), Worker("miner" + toString(index())) {}
static unsigned instances() { return std::thread::hardware_concurrency(); }
static std::string platformInfo();
static void setDefaultDevice(unsigned) {}
protected:
void kickOff() override
{
stopWorking();
startWorking();
}
void pause() override { stopWorking(); }
private:
void workLoop() override;
static unsigned s_deviceId;
};
#if ETH_ETHASHCL || !ETH_TRUE
class GPUMiner: public Miner, Worker
{
friend class dev::eth::EthashCLHook;
public:
GPUMiner(ConstructionInfo const& _ci);
~GPUMiner();
static unsigned instances() { return 1; }
static std::string platformInfo();
static void setDefaultDevice(unsigned _id) { s_deviceId = _id; }
protected:
void kickOff() override;
void pause() override;
private:
void workLoop() override;
bool report(uint64_t _nonce);
using Miner::accumulateHashes;
EthashCLHook* m_hook = nullptr;
ethash_cl_miner* m_miner = nullptr;
h256 m_minerSeed; ///< Last seed in m_miner
static unsigned s_deviceId;
};
#else
using GPUMiner = CPUMiner;
#endif
};
}
}

214
libethcore/EthashAux.cpp

@ -0,0 +1,214 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file EthashAux.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "EthashAux.h"
#include <boost/detail/endian.hpp>
#include <boost/filesystem.hpp>
#include <chrono>
#include <array>
#include <random>
#include <thread>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libdevcore/Log.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/SHA3.h>
#include <libdevcrypto/FileSystem.h>
#include <libethcore/Params.h>
#include "BlockInfo.h"
using namespace std;
using namespace chrono;
using namespace dev;
using namespace eth;
#define ETH_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
EthashAux* dev::eth::EthashAux::s_this = nullptr;
EthashAux::~EthashAux()
{
while (!m_lights.empty())
killCache(m_lights.begin()->first);
}
ethash_params EthashAux::params(BlockInfo const& _header)
{
return params((unsigned)_header.number);
}
ethash_params EthashAux::params(unsigned _n)
{
ethash_params p;
p.cache_size = ethash_get_cachesize(_n);
p.full_size = ethash_get_datasize(_n);
return p;
}
h256 EthashAux::seedHash(unsigned _number)
{
unsigned epoch = _number / ETHASH_EPOCH_LENGTH;
RecursiveGuard l(get()->x_this);
if (epoch >= get()->m_seedHashes.size())
{
h256 ret;
unsigned n = 0;
if (!get()->m_seedHashes.empty())
{
ret = get()->m_seedHashes.back();
n = get()->m_seedHashes.size() - 1;
}
get()->m_seedHashes.resize(epoch + 1);
cdebug << "Searching for seedHash of epoch " << epoch;
for (; n <= epoch; ++n, ret = sha3(ret))
{
get()->m_seedHashes[n] = ret;
cdebug << "Epoch" << n << "is" << ret.abridged();
}
}
return get()->m_seedHashes[epoch];
}
ethash_params EthashAux::params(h256 const& _seedHash)
{
RecursiveGuard l(get()->x_this);
unsigned epoch = 0;
try
{
epoch = get()->m_epochs.at(_seedHash);
}
catch (...)
{
cdebug << "Searching for seedHash " << _seedHash.abridged();
for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) {}
if (epoch == 2048)
{
std::ostringstream error;
error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048);
throw std::invalid_argument(error.str());
}
}
return params(epoch * ETHASH_EPOCH_LENGTH);
}
void EthashAux::killCache(h256 const& _s)
{
RecursiveGuard l(x_this);
if (m_lights.count(_s))
{
ethash_delete_light(m_lights.at(_s));
m_lights.erase(_s);
}
}
void const* EthashAux::light(BlockInfo const& _header)
{
return light(_header.seedHash());
}
void const* EthashAux::light(h256 const& _seedHash)
{
RecursiveGuard l(get()->x_this);
if (!get()->m_lights.count(_seedHash))
{
ethash_params p = params(_seedHash);
get()->m_lights[_seedHash] = ethash_new_light(&p, _seedHash.data());
}
return get()->m_lights[_seedHash];
}
bytesConstRef EthashAux::full(BlockInfo const& _header, bytesRef _dest)
{
return full(_header.seedHash(), _dest);
}
bytesConstRef EthashAux::full(h256 const& _seedHash, bytesRef _dest)
{
RecursiveGuard l(get()->x_this);
if (get()->m_fulls.count(_seedHash) && _dest)
{
assert(get()->m_fulls.size() <= _dest.size());
get()->m_fulls.at(_seedHash).copyTo(_dest);
return _dest;
}
if (!get()->m_fulls.count(_seedHash))
{
// @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr.
/* if (!m_fulls.empty())
{
delete [] m_fulls.begin()->second.data();
m_fulls.erase(m_fulls.begin());
}*/
try {
boost::filesystem::create_directories(getDataDir("ethash"));
} catch (...) {}
auto info = rlpList(Ethash::revision(), _seedHash);
std::string oldMemoFile = getDataDir("ethash") + "/full";
std::string memoFile = getDataDir("ethash") + "/full-R" + toString(ETHASH_REVISION) + "-" + toHex(_seedHash.ref().cropped(0, 8));
if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info)
{
// memofile valid - rename.
boost::filesystem::rename(oldMemoFile, memoFile);
}
ETH_IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile));
ETH_IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info"));
ethash_params p = params(_seedHash);
assert(!_dest || _dest.size() >= p.full_size); // must be big enough.
bytesRef r = contentsNew(memoFile, _dest);
if (!r)
{
// file didn't exist.
if (_dest)
// buffer was passed in - no insertion into cache nor need to allocate
r = _dest;
else
r = bytesRef(new byte[p.full_size], p.full_size);
ethash_prep_full(r.data(), &p, light(_seedHash));
writeFile(memoFile, r);
}
if (_dest)
return _dest;
get()->m_fulls[_seedHash] = r;
}
return get()->m_fulls[_seedHash];
}
Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce)
{
return eval(_header.seedHash(), _header.headerHash(WithoutNonce), _nonce);
}
Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce)
{
auto p = EthashAux::params(_seedHash);
ethash_return_value r;
if (EthashAux::get()->m_fulls.count(_seedHash))
ethash_compute_full(&r, EthashAux::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce);
else
ethash_compute_light(&r, EthashAux::get()->light(_seedHash), &p, _headerHash.data(), (uint64_t)(u64)_nonce);
// cdebug << "EthashAux::eval sha3(cache):" << sha3(EthashAux::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer);
return Ethash::Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)};
}

67
libethcore/EthashAux.h

@ -0,0 +1,67 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file EthashAux.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <libethash/ethash.h>
#include "Ethash.h"
namespace dev
{
namespace eth{
class EthashAux
{
public:
~EthashAux();
static EthashAux* get() { if (!s_this) s_this = new EthashAux(); return s_this; }
using LightType = void const*;
using FullType = void const*;
static h256 seedHash(unsigned _number);
static ethash_params params(BlockInfo const& _header);
static ethash_params params(h256 const& _seedHash);
static ethash_params params(unsigned _n);
static LightType light(BlockInfo const& _header);
static LightType light(h256 const& _seedHash);
static bytesConstRef full(BlockInfo const& _header, bytesRef _dest = bytesRef());
static bytesConstRef full(h256 const& _header, bytesRef _dest = bytesRef());
static Ethash::Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); }
static Ethash::Result eval(BlockInfo const& _header, Nonce const& _nonce);
static Ethash::Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce);
private:
EthashAux() {}
void killCache(h256 const& _s);
static EthashAux* s_this;
RecursiveMutex x_this;
std::map<h256, LightType> m_lights;
std::map<h256, bytesRef> m_fulls;
std::map<h256, unsigned> m_epochs;
h256s m_seedHashes;
};
}
}

220
libethcore/Ethasher.cpp

@ -1,220 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Ethasher.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <boost/detail/endian.hpp>
#include <boost/filesystem.hpp>
#include <chrono>
#include <array>
#include <random>
#include <thread>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libdevcore/Log.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/SHA3.h>
#include <libdevcrypto/FileSystem.h>
#include <libethcore/Params.h>
#include "BlockInfo.h"
#include "Ethasher.h"
using namespace std;
using namespace chrono;
using namespace dev;
using namespace eth;
Ethasher* dev::eth::Ethasher::s_this = nullptr;
Ethasher::~Ethasher()
{
while (!m_lights.empty())
killCache(m_lights.begin()->first);
}
void Ethasher::killCache(h256 const& _s)
{
RecursiveGuard l(x_this);
if (m_lights.count(_s))
{
ethash_delete_light(m_lights.at(_s));
m_lights.erase(_s);
}
}
void const* Ethasher::light(BlockInfo const& _header)
{
RecursiveGuard l(x_this);
if (_header.number > c_ethashEpochLength * 2048)
{
std::ostringstream error;
error << "block number is too high; max is " << c_ethashEpochLength * 2048 << "(was " << _header.number << ")";
throw std::invalid_argument( error.str() );
}
if (!m_lights.count(_header.seedHash()))
{
ethash_params p = params((unsigned)_header.number);
m_lights[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data());
}
return m_lights[_header.seedHash()];
}
#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
bytesConstRef Ethasher::full(BlockInfo const& _header)
{
RecursiveGuard l(x_this);
if (!m_fulls.count(_header.seedHash()))
{
// @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr.
/* if (!m_fulls.empty())
{
delete [] m_fulls.begin()->second.data();
m_fulls.erase(m_fulls.begin());
}*/
try {
boost::filesystem::create_directories(getDataDir("ethash"));
} catch (...) {}
auto info = rlpList(c_ethashRevision, _header.seedHash());
std::string oldMemoFile = getDataDir("ethash") + "/full";
std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_header.seedHash().ref().cropped(0, 8));
if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info)
{
// memofile valid - rename.
boost::filesystem::rename(oldMemoFile, memoFile);
}
IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile));
IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info"));
m_fulls[_header.seedHash()] = contentsNew(memoFile);
if (!m_fulls[_header.seedHash()])
{
ethash_params p = params((unsigned)_header.number);
m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size);
auto c = light(_header);
ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c);
writeFile(memoFile, m_fulls[_header.seedHash()]);
}
}
return m_fulls[_header.seedHash()];
}
ethash_params Ethasher::params(BlockInfo const& _header)
{
return params((unsigned)_header.number);
}
void Ethasher::readFull(BlockInfo const& _header, void* _dest)
{
if (!m_fulls.count(_header.seedHash()))
{
// @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr.
/* if (!m_fulls.empty())
{
delete [] m_fulls.begin()->second.data();
m_fulls.erase(m_fulls.begin());
}*/
try {
boost::filesystem::create_directories(getDataDir("ethash"));
} catch (...) {}
auto info = rlpList(c_ethashRevision, _header.seedHash());
std::string oldMemoFile = getDataDir("ethash") + "/full";
std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_header.seedHash().ref().cropped(0, 8));
if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info)
{
// memofile valid - rename.
boost::filesystem::rename(oldMemoFile, memoFile);
}
IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile));
IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info"));
ethash_params p = params((unsigned)_header.number);
bytesRef r = contentsNew(memoFile, bytesRef((byte*)_dest, p.full_size));
if (!r)
{
auto c = light(_header);
ethash_prep_full(_dest, &p, c);
writeFile(memoFile, bytesConstRef((byte*)_dest, p.full_size));
}
}
}
ethash_params Ethasher::params(unsigned _n)
{
ethash_params p;
p.cache_size = ethash_get_cachesize(_n);
p.full_size = ethash_get_datasize(_n);
return p;
}
bool Ethasher::verify(BlockInfo const& _header)
{
if (_header.number >= c_ethashEpochLength * 2048)
return false;
h256 boundary = u256((bigint(1) << 256) / _header.difficulty);
bool quick = ethash_quick_check_difficulty(
_header.headerHash(WithoutNonce).data(),
(uint64_t)(u64)_header.nonce,
_header.mixHash.data(),
boundary.data());
#if !ETH_DEBUG
if (!quick)
return false;
#endif
auto result = eval(_header);
bool slow = result.value <= boundary && result.mixHash == _header.mixHash;
#if ETH_DEBUG
if (!quick && slow)
{
cwarn << "WARNING: evaluated result gives true whereas ethash_quick_check_difficulty gives false.";
cwarn << "headerHash:" << _header.headerHash(WithoutNonce);
cwarn << "nonce:" << _header.nonce;
cwarn << "mixHash:" << _header.mixHash;
cwarn << "difficulty:" << _header.difficulty;
cwarn << "boundary:" << boundary;
cwarn << "result.value:" << result.value;
cwarn << "result.mixHash:" << result.mixHash;
}
#endif
return slow;
}
Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce)
{
auto p = Ethasher::params(_header);
ethash_return_value r;
if (Ethasher::get()->m_fulls.count(_header.seedHash()))
ethash_compute_full(&r, Ethasher::get()->full(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
else
ethash_compute_light(&r, Ethasher::get()->light(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce);
// cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer);
return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)};
}

109
libethcore/Ethasher.h

@ -1,109 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Ethasher.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*
* ProofOfWork algorithm.
*/
#pragma once
#include <chrono>
#include <thread>
#include <cstdint>
#include <libdevcore/Guards.h>
#include <libdevcore/Log.h>
#include <libdevcrypto/SHA3.h>
#include <libethash/ethash.h> // TODO: REMOVE once everything merged into this class and an opaque API can be provided.
static const unsigned c_ethashRevision = ETHASH_REVISION;
static const unsigned c_ethashEpochLength = ETHASH_EPOCH_LENGTH;
#include "Common.h"
#include "BlockInfo.h"
namespace dev
{
namespace eth
{
class Ethasher
{
public:
Ethasher() {}
~Ethasher();
static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; }
using LightType = void const*;
using FullType = void const*;
LightType light(BlockInfo const& _header);
bytesConstRef full(BlockInfo const& _header);
static ethash_params params(BlockInfo const& _header);
static ethash_params params(unsigned _n);
void readFull(BlockInfo const& _header, void* _dest);
struct Result
{
h256 value;
h256 mixHash;
};
static Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); }
static Result eval(BlockInfo const& _header, Nonce const& _nonce);
static bool verify(BlockInfo const& _header);
class Miner
{
public:
Miner(BlockInfo const& _header):
m_headerHash(_header.headerHash(WithoutNonce)),
m_params(Ethasher::params(_header)),
m_datasetPointer(Ethasher::get()->full(_header).data())
{}
inline h256 mine(uint64_t _nonce)
{
ethash_compute_full(&m_ethashReturn, m_datasetPointer, &m_params, m_headerHash.data(), _nonce);
// cdebug << "Ethasher::mine hh:" << m_headerHash << "nonce:" << (Nonce)(u64)_nonce << " => " << h256(m_ethashReturn.result, h256::ConstructFromPointer);
return h256(m_ethashReturn.result, h256::ConstructFromPointer);
}
inline h256 lastMixHash() const
{
return h256(m_ethashReturn.mix_hash, h256::ConstructFromPointer);
}
private:
ethash_return_value m_ethashReturn;
h256 m_headerHash;
ethash_params m_params;
void const* m_datasetPointer;
};
private:
void killCache(h256 const& _s);
static Ethasher* s_this;
RecursiveMutex x_this;
std::map<h256, LightType> m_lights;
std::map<h256, bytesRef> m_fulls;
};
}
}

0
libethcore/Miner.cpp

172
libethcore/Miner.h

@ -0,0 +1,172 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Miner.h
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#pragma once
#include <thread>
#include <list>
#include <atomic>
#include <libdevcore/Common.h>
#include <libdevcore/Worker.h>
#include <libethcore/Common.h>
namespace dev
{
namespace eth
{
/**
* @brief Describes the progress of a mining operation.
*/
struct MiningProgress
{
// MiningProgress& operator+=(MiningProgress const& _mp) { hashes += _mp.hashes; ms = std::max(ms, _mp.ms); return *this; }
uint64_t hashes = 0; ///< Total number of hashes computed.
uint64_t ms = 0; ///< Total number of milliseconds of mining thus far.
uint64_t rate() const { return hashes * 1000 / ms; }
};
struct MineInfo: public MiningProgress {};
inline std::ostream& operator<<(std::ostream& _out, MiningProgress _p)
{
_out << _p.rate() << " H/s = " << _p.hashes << " hashes / " << (double(_p.ms) / 1000) << " s";
return _out;
}
template <class PoW> class GenericMiner;
/**
* @brief Class for hosting one or more Miners.
* @warning Must be implemented in a threadsafe manner since it will be called from multiple
* miner threads.
*/
template <class PoW> class GenericFarmFace
{
public:
using WorkPackage = typename PoW::WorkPackage;
using Solution = typename PoW::Solution;
using Miner = GenericMiner<PoW>;
/**
* @brief Called from a Miner to note a WorkPackage has a solution.
* @param _p The solution.
* @param _wp The WorkPackage that the Solution is for; this will be reset if the work is accepted.
* @param _finder The miner that found it.
* @return true iff the solution was good (implying that mining should be .
*/
virtual bool submitProof(Solution const& _p, Miner* _finder) = 0;
};
/**
* @brief A miner - a member and adoptee of the Farm.
* @warning Not threadsafe. It is assumed Farm will synchronise calls to/from this class.
*/
template <class PoW> class GenericMiner
{
public:
using WorkPackage = typename PoW::WorkPackage;
using Solution = typename PoW::Solution;
using FarmFace = GenericFarmFace<PoW>;
using ConstructionInfo = std::pair<FarmFace*, unsigned>;
GenericMiner(ConstructionInfo const& _ci):
m_farm(_ci.first),
m_index(_ci.second)
{}
// API FOR THE FARM TO CALL IN WITH
void setWork(WorkPackage const& _work = WorkPackage())
{
auto old = m_work;
{
Guard l(x_work);
m_work = _work;
}
if (!!_work)
{
pause();
kickOff();
}
else if (!_work && !!old)
pause();
m_hashCount = 0;
}
uint64_t hashCount() const { return m_hashCount; }
void resetHashCount() { m_hashCount = 0; }
unsigned index() const { return m_index; }
protected:
// REQUIRED TO BE REIMPLEMENTED BY A SUBCLASS:
/**
* @brief Begin working on a given work package, discarding any previous work.
* @param _work The package for which to find a solution.
*/
virtual void kickOff() = 0;
/**
* @brief No work left to be done. Pause until told to kickOff().
*/
virtual void pause() = 0;
// AVAILABLE FOR A SUBCLASS TO CALL:
/**
* @brief Notes that the Miner found a solution.
* @param _s The solution.
* @return true if the solution was correct and that the miner should pause.
*/
bool submitProof(Solution const& _s)
{
if (!m_farm)
return true;
if (m_farm->submitProof(_s, this))
{
Guard l(x_work);
m_work.reset();
return true;
}
return false;
}
WorkPackage const& work() const { Guard l(x_work); return m_work; }
void accumulateHashes(unsigned _n) { m_hashCount += _n; }
private:
FarmFace* m_farm = nullptr;
unsigned m_index;
uint64_t m_hashCount = 0;
WorkPackage m_work;
mutable Mutex x_work;
};
}
}

1
libethcore/Params.cpp

@ -30,7 +30,6 @@ namespace eth
//--- BEGIN: AUTOGENERATED FROM github.com/ethereum/common/params.json
u256 const c_genesisDifficulty = 131072;
u256 const c_maximumExtraDataSize = 1024;
u256 const c_epochDuration = 30000;
u256 const c_genesisGasLimit = 3141592;
u256 const c_minGasLimit = 125000;
u256 const c_gasLimitBoundDivisor = 1024;

1
libethcore/Params.h

@ -37,7 +37,6 @@ extern u256 const c_minimumDifficulty;
extern u256 const c_difficultyBoundDivisor;
extern u256 const c_durationLimit;
extern u256 const c_maximumExtraDataSize;
extern u256 const c_epochDuration;
extern u256 const c_stackLimit;
extern u256 const c_tierStepGas[8]; ///< Once per operation, for a selection of them.

204
libethcore/ProofOfWork.cpp

@ -19,208 +19,6 @@
* @date 2014
*/
#include <boost/detail/endian.hpp>
#include <boost/filesystem.hpp>
#include <chrono>
#include <array>
#include <thread>
#include <random>
#include <thread>
#include <libdevcore/Guards.h>
#include <libdevcore/Log.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/FileSystem.h>
#include <libdevcore/Common.h>
#if ETH_ETHASHCL
#include <libethash-cl/ethash_cl_miner.h>
#endif
#include "BlockInfo.h"
#include "Ethasher.h"
#include "ProofOfWork.h"
using namespace std;
using namespace std::chrono;
namespace dev
{
namespace eth
{
bool EthashPoW::verify(BlockInfo const& _header)
{
return Ethasher::verify(_header);
}
std::pair<MineInfo, EthashCPU::Proof> EthashCPU::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue)
{
Ethasher::Miner m(_header);
std::pair<MineInfo, Proof> ret;
auto tid = std::this_thread::get_id();
static std::mt19937_64 s_eng((time(0) + *reinterpret_cast<unsigned*>(m_last.data()) + std::hash<decltype(tid)>()(tid)));
uint64_t tryNonce = (uint64_t)(u64)(m_last = Nonce::random(s_eng));
h256 boundary = u256((bigint(1) << 256) / _header.difficulty);
ret.first.requirement = log2((double)(u256)boundary);
// 2^ 0 32 64 128 256
// [--------*-------------------------]
//
// evaluate until we run out of time
auto startTime = std::chrono::steady_clock::now();
double best = 1e99; // high enough to be effectively infinity :)
Proof result;
unsigned hashCount = 0;
for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; tryNonce++, hashCount++)
{
h256 val(m.mine(tryNonce));
best = std::min<double>(best, log2((double)(u256)val));
if (val <= boundary)
{
ret.first.completed = true;
assert(Ethasher::eval(_header, (Nonce)(u64)tryNonce).value == val);
result.mixHash = m.lastMixHash();
result.nonce = u64(tryNonce);
BlockInfo test = _header;
assignResult(result, test);
assert(verify(test));
break;
}
}
ret.first.hashes = hashCount;
ret.first.best = best;
ret.second = result;
if (ret.first.completed)
{
BlockInfo test = _header;
assignResult(result, test);
assert(verify(test));
}
return ret;
}
#if ETH_ETHASHCL || !ETH_TRUE
/*
class ethash_cl_miner
{
public:
struct search_hook
{
// reports progress, return true to abort
virtual bool found(uint64_t const* nonces, uint32_t count) = 0;
virtual bool searched(uint64_t start_nonce, uint32_t count) = 0;
};
ethash_cl_miner();
bool init(ethash_params const& params, const uint8_t seed[32], unsigned workgroup_size = 64);
void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count);
void search(uint8_t const* header, uint64_t target, search_hook& hook);
};
*/
struct EthashCLHook: public ethash_cl_miner::search_hook
{
void abort()
{
if (m_aborted)
return;
cdebug << "Attempting to abort";
m_abort = true;
for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout)
std::this_thread::sleep_for(chrono::milliseconds(30));
if (!m_aborted)
cwarn << "Couldn't abort. Abandoning OpenCL process.";
m_aborted = m_abort = false;
m_found.clear();
}
vector<Nonce> fetchFound() { vector<Nonce> ret; Guard l(x_all); std::swap(ret, m_found); return ret; }
uint64_t fetchTotal() { Guard l(x_all); auto ret = m_total; m_total = 0; return ret; }
protected:
virtual bool found(uint64_t const* _nonces, uint32_t _count) override
{
Guard l(x_all);
for (unsigned i = 0; i < _count; ++i)
m_found.push_back((Nonce)(u64)_nonces[i]);
m_aborted = true;
cdebug << "Found nonces: " << vector<uint64_t>(_nonces, _nonces + _count);
return true;
}
virtual bool searched(uint64_t _startNonce, uint32_t _count) override
{
Guard l(x_all);
cdebug << "Searched" << _count << "from" << _startNonce;
m_total += _count;
m_last = _startNonce + _count;
if (m_abort)
{
m_aborted = true;
return true;
}
return false;
}
private:
Mutex x_all;
vector<Nonce> m_found;
uint64_t m_total;
uint64_t m_last;
bool m_abort = false;
bool m_aborted = true;
};
EthashCL::EthashCL():
m_hook(new EthashCLHook)
{
}
EthashCL::~EthashCL()
{
}
std::pair<MineInfo, Ethash::Proof> EthashCL::mine(BlockInfo const& _header, unsigned _msTimeout, bool)
{
if (!m_lastHeader || m_lastHeader.seedHash() != _header.seedHash())
{
if (m_miner)
m_hook->abort();
m_miner.reset(new ethash_cl_miner);
auto cb = [&](void* d) {
Ethasher::get()->readFull(_header, d);
};
m_miner->init(Ethasher::params(_header), cb, 32);
}
if (m_lastHeader != _header)
{
m_hook->abort();
static std::random_device s_eng;
auto hh = _header.headerHash(WithoutNonce);
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_header.boundary() >> 192);
m_miner->search(hh.data(), upper64OfBoundary, *m_hook);
}
m_lastHeader = _header;
std::this_thread::sleep_for(chrono::milliseconds(_msTimeout));
auto found = m_hook->fetchFound();
if (!found.empty())
{
for (auto const& n: found)
{
auto result = Ethasher::eval(_header, n);
if (result.value < _header.boundary())
return std::make_pair(MineInfo(true), EthashCL::Proof{n, result.mixHash});
}
}
return std::make_pair(MineInfo(false), EthashCL::Proof());
}
#endif
}
}
using namespace dev;

155
libethcore/ProofOfWork.h

@ -18,159 +18,30 @@
* @author Gav Wood <i@gavwood.com>
* @date 2014
*
* ProofOfWork algorithm. Or not.
* Determines the PoW algorithm.
*/
#pragma once
#include <chrono>
#include <thread>
#include <cstdint>
#include <libdevcrypto/SHA3.h>
#include "Common.h"
#include "BlockInfo.h"
#define FAKE_DAGGER 1
class ethash_cl_miner;
struct ethash_cl_search_hook;
#include "Ethash.h"
namespace dev
{
namespace eth
{
struct MineInfo
{
MineInfo() = default;
MineInfo(bool _completed): completed(_completed) {}
void combine(MineInfo const& _m) { requirement = std::max(requirement, _m.requirement); best = std::min(best, _m.best); hashes += _m.hashes; completed = completed || _m.completed; }
double requirement = 0;
double best = 1e99;
unsigned hashes = 0;
bool completed = false;
};
class EthashPoW
{
public:
struct Proof
{
Nonce nonce;
h256 mixHash;
};
static bool verify(BlockInfo const& _header);
static void assignResult(Proof const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; }
virtual unsigned defaultTimeout() const { return 100; }
virtual std::pair<MineInfo, Proof> mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) = 0;
};
class EthashCPU: public EthashPoW
{
public:
std::pair<MineInfo, Proof> mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override;
protected:
Nonce m_last;
};
#if ETH_ETHASHCL || !ETH_TRUE
class EthashCLHook;
class EthashCL: public EthashPoW
{
public:
EthashCL();
~EthashCL();
std::pair<MineInfo, Proof> mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override;
unsigned defaultTimeout() const override { return 500; }
protected:
Nonce m_last;
BlockInfo m_lastHeader;
Nonce m_mined;
std::unique_ptr<ethash_cl_miner> m_miner;
std::unique_ptr<EthashCLHook> m_hook;
};
using Ethash = EthashCL;
#else
using Ethash = EthashCPU;
#endif
template <class Evaluator>
class ProofOfWorkEngine: public Evaluator
{
public:
using Proof = Nonce;
static bool verify(BlockInfo const& _header) { return (bigint)(u256)Evaluator::eval(_header.headerHash(WithoutNonce), _header.nonce) <= (bigint(1) << 256) / _header.difficulty; }
inline std::pair<MineInfo, Proof> mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true);
static void assignResult(Proof const& _r, BlockInfo& _header) { _header.nonce = _r; }
unsigned defaultTimeout() const { return 100; }
protected:
Nonce m_last;
};
class SHA3Evaluator
{
public:
static h256 eval(h256 const& _root, Nonce const& _nonce) { h256 b[2] = { _root, h256(_nonce) }; return sha3(bytesConstRef((byte const*)&b[0], 64)); }
};
using SHA3ProofOfWork = ProofOfWorkEngine<SHA3Evaluator>;
/**
* The proof of work algorithm base type.
*
* Must implement a basic templated interface, including:
* typename Result
* typename Solution
* typename CPUMiner
* typename GPUMiner
* void assignResult(BlockInfo&, Result)
* and a few others. TODO
*/
using ProofOfWork = Ethash;
template <class Evaluator>
std::pair<MineInfo, typename ProofOfWorkEngine<Evaluator>::Proof> ProofOfWorkEngine<Evaluator>::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue)
{
auto headerHashWithoutNonce = _header.headerHash(WithoutNonce);
auto difficulty = _header.difficulty;
std::pair<MineInfo, Nonce> ret;
static std::mt19937_64 s_eng((time(0) + *reinterpret_cast<unsigned*>(m_last.data())));
Nonce::Arith s = (m_last = Nonce::random(s_eng));
bigint d = (bigint(1) << 256) / difficulty;
ret.first.requirement = log2((double)d);
// 2^ 0 32 64 128 256
// [--------*-------------------------]
//
// evaluate until we run out of time
auto startTime = std::chrono::steady_clock::now();
double best = 1e99; // high enough to be effectively infinity :)
ProofOfWorkEngine<Evaluator>::Proof solution;
unsigned h = 0;
for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; s++, h++)
{
solution = (ProofOfWorkEngine<Evaluator>::Proof)s;
auto e = (bigint)(u256)Evaluator::eval(headerHashWithoutNonce, solution);
best = std::min<double>(best, log2((double)e));
if (e <= d)
{
ret.first.completed = true;
break;
}
}
ret.first.hashes = h;
ret.first.best = best;
ret.second = solution;
if (ret.first.completed)
{
BlockInfo test = _header;
assignResult(solution, test);
assert(verify(test));
}
return ret;
}
}
}

32
libethereum/BlockChain.cpp

@ -36,7 +36,6 @@
#include <libethcore/Exceptions.h>
#include <libethcore/ProofOfWork.h>
#include <libethcore/BlockInfo.h>
#include <libethcore/Ethasher.h>
#include <liblll/Compiler.h>
#include "GenesisInfo.h"
#include "State.h"
@ -231,8 +230,7 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
bytes b = block(queryExtras<BlockHash, ExtraBlockHash>(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value);
BlockInfo bi(b);
if (bi.number % c_ethashEpochLength == 1)
Ethasher::get()->full(bi);
ProofOfWork::prep(bi);
if (bi.parentHash != lastHash)
{
@ -307,14 +305,8 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
try
{
auto r = import(block, _stateDB);
bool isOld = true;
for (auto const& h: r.first)
if (h == r.second)
isOld = false;
else if (isOld)
dead.push_back(h);
else
fresh.push_back(h);
fresh += r.first;
dead += r.second;
}
catch (UnknownParent)
{
@ -334,7 +326,7 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
return make_tuple(fresh, dead, _bq.doneDrain(badBlocks));
}
pair<h256s, h256> BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, Aversion _force) noexcept
ImportRoute BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, Aversion _force) noexcept
{
try
{
@ -343,11 +335,11 @@ pair<h256s, h256> BlockChain::attemptImport(bytes const& _block, OverlayDB const
catch (...)
{
cwarn << "Unexpected exception! Could not import block!" << boost::current_exception_diagnostic_information();
return make_pair(h256s(), h256());
return make_pair(h256s(), h256s());
}
}
pair<h256s, h256> BlockChain::import(bytes const& _block, OverlayDB const& _db, Aversion _force)
ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Aversion _force)
{
//@tidy This is a behemoth of a method - could do to be split into a few smaller ones.
@ -626,7 +618,17 @@ pair<h256s, h256> BlockChain::import(bytes const& _block, OverlayDB const& _db,
cnote << "checkBest:" << checkBest;
#endif
return make_pair(route, common);
h256s fresh;
h256s dead;
bool isOld = true;
for (auto const& h: route)
if (h == common)
isOld = false;
else if (isOld)
dead.push_back(h);
else
fresh.push_back(h);
return make_pair(fresh, dead);
}
void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end)

5
libethereum/BlockChain.h

@ -68,6 +68,7 @@ ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0);
using BlocksHash = std::map<h256, bytes>;
using TransactionHashes = h256s;
using UncleHashes = h256s;
using ImportRoute = std::pair<h256s, h256s>;
enum {
ExtraDetails = 0,
@ -108,11 +109,11 @@ public:
/// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB.
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
std::pair<h256s, h256> attemptImport(bytes const& _block, OverlayDB const& _stateDB, Aversion _force = Aversion::AvoidOldBlocks) noexcept;
ImportRoute attemptImport(bytes const& _block, OverlayDB const& _stateDB, Aversion _force = Aversion::AvoidOldBlocks) noexcept;
/// Import block into disk-backed DB
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
std::pair<h256s, h256> import(bytes const& _block, OverlayDB const& _stateDB, Aversion _force = Aversion::AvoidOldBlocks);
ImportRoute import(bytes const& _block, OverlayDB const& _stateDB, Aversion _force = Aversion::AvoidOldBlocks);
/// Returns true if the given block is known (though not necessarily a part of the canon chain).
bool isKnown(h256 const& _hash) const;

13
libethereum/BlockQueue.cpp

@ -102,6 +102,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
m_readySet.insert(h);
noteReadyWithoutWriteGuard(h);
m_onReady();
return ImportResult::Success;
}
}
@ -182,3 +183,15 @@ void BlockQueue::noteReadyWithoutWriteGuard(h256 _good)
m_unknown.erase(r.first, r.second);
}
}
void BlockQueue::retryAllUnknown()
{
for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it)
{
m_ready.push_back(it->second.second);
auto newReady = it->second.first;
m_unknownSet.erase(newReady);
m_readySet.insert(newReady);
}
m_unknown.clear();
}

6
libethereum/BlockQueue.h

@ -71,6 +71,9 @@ public:
/// Notify the queue that the chain has changed and a new block has attained 'ready' status (i.e. is in the chain).
void noteReady(h256 _b) { WriteGuard l(m_lock); noteReadyWithoutWriteGuard(_b); }
/// Force a retry of all the blocks with unknown parents.
void retryAllUnknown();
/// Get information on the items queued.
std::pair<unsigned, unsigned> items() const { ReadGuard l(m_lock); return std::make_pair(m_ready.size(), m_unknown.size()); }
@ -83,6 +86,8 @@ public:
/// Get some infomration on the current status.
BlockQueueStatus status() const { ReadGuard l(m_lock); return BlockQueueStatus{m_ready.size(), m_future.size(), m_unknown.size(), m_knownBad.size()}; }
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); }
private:
void noteReadyWithoutWriteGuard(h256 _b);
void notePresentWithoutWriteGuard(bytesConstRef _block);
@ -95,6 +100,7 @@ private:
std::multimap<h256, std::pair<h256, bytes>> m_unknown; ///< For transactions that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears.
std::multimap<unsigned, bytes> m_future; ///< Set of blocks that are not yet valid.
std::set<h256> m_knownBad; ///< Set of blocks that we know will never be valid.
Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast.
};
}

4
libethereum/CMakeLists.txt

@ -34,10 +34,6 @@ target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
target_link_libraries(${EXECUTABLE} secp256k1)
if (ETHASHCL)
target_link_libraries(${EXECUTABLE} ethash-cl)
target_link_libraries(${EXECUTABLE} OpenCL)
endif ()
if (CMAKE_COMPILER_IS_MINGW)
target_link_libraries(${EXECUTABLE} ssp shlwapi)

384
libethereum/Client.cpp

@ -117,7 +117,7 @@ void BasicGasPricer::update(BlockChain const& _bc)
}
}
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners):
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId):
Worker("eth"),
m_vc(_dbPath),
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
@ -126,14 +126,14 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for
m_preMine(m_stateDB, BaseState::CanonGenesis),
m_postMine(m_stateDB)
{
m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue);
m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue);
m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); });
m_gp->update(m_bc);
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
if (_miners > -1)
setMiningThreads(_miners);
else
setMiningThreads();
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
m_vc.setOk();
@ -142,7 +142,7 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for
startWorking();
}
Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners):
Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId):
Worker("eth"),
m_vc(_dbPath),
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
@ -151,14 +151,14 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string c
m_preMine(m_stateDB),
m_postMine(m_stateDB)
{
m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue);
m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue);
m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); });
m_gp->update(m_bc);
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
if (_miners > -1)
setMiningThreads(_miners);
else
setMiningThreads();
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
m_vc.setOk();
@ -192,6 +192,27 @@ bool Client::isSyncing() const
return false;
}
void Client::startedWorking()
{
// Synchronise the state according to the head of the block chain.
// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
cdebug << "startedWorking()";
WriteGuard l(x_stateDB);
cdebug << m_bc.number() << m_bc.currentHash();
cdebug << "Pre:" << m_preMine.info();
cdebug << "Post:" << m_postMine.info();
cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce);
m_preMine.sync(m_bc);
m_postMine = m_preMine;
cdebug << "Pre:" << m_preMine.info();
cdebug << "Post:" << m_postMine.info();
cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce);
}
void Client::doneWorking()
{
// Synchronise the state according to the head of the block chain.
@ -210,7 +231,7 @@ void Client::killChain()
m_tq.clear();
m_bq.clear();
m_localMiners.clear();
m_farm.stop();
m_preMine = State();
m_postMine = State();
@ -229,8 +250,6 @@ void Client::killChain()
doWork();
setMiningThreads(0);
startWorking();
if (wasMining)
startMining();
@ -250,11 +269,7 @@ void Client::clearPending()
m_postMine = m_preMine;
}
{
ReadGuard l(x_localMiners);
for (auto& m: m_localMiners)
m.noteStateChange();
}
startMining();
noteChanged(changeds);
}
@ -271,26 +286,6 @@ static string filtersToString(T const& _fs)
return ret.str();
}
void Client::noteChanged(h256Set const& _filters)
{
Guard l(x_filtersWatches);
if (_filters.size())
cnote << "noteChanged(" << filtersToString(_filters) << ")";
// accrue all changes left in each filter into the watches.
for (auto& w: m_watches)
if (_filters.count(w.second.id))
{
cwatch << "!!!" << w.first << (m_filters.count(w.second.id) ? w.second.id.abridged() : w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???");
if (m_filters.count(w.second.id)) // Normal filtering watch
w.second.changes += m_filters.at(w.second.id).changes;
else // Special ('pending'/'latest') watch
w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0));
}
// clear the filters now.
for (auto& i: m_filters)
i.second.changes.clear();
}
void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _transactionHash)
{
Guard l(x_filtersWatches);
@ -337,50 +332,24 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed)
void Client::setForceMining(bool _enable)
{
m_forceMining = _enable;
ReadGuard l(x_localMiners);
for (auto& m: m_localMiners)
m.noteStateChange();
}
void Client::setMiningThreads(unsigned _threads)
{
stopMining();
auto t = _threads ? _threads : thread::hardware_concurrency();
#if ETH_ETHASHCL || !ETH_TRUE
if (m_turboMining)
t = 1;
#endif
WriteGuard l(x_localMiners);
m_localMiners.clear();
m_localMiners.resize(t);
unsigned i = 0;
for (auto& m: m_localMiners)
m.setup(this, i++);
if (isMining())
startMining();
}
MineProgress Client::miningProgress() const
MiningProgress Client::miningProgress() const
{
MineProgress ret;
ReadGuard l(x_localMiners);
for (auto& m: m_localMiners)
ret.combine(m.miningProgress());
return ret;
return MiningProgress();
}
uint64_t Client::hashrate() const
{
uint64_t ret = 0;
ReadGuard l(x_localMiners);
for (LocalMiner const& m: m_localMiners)
ret += m.miningProgress().hashes / m.miningProgress().ms;
return ret / 1000;
return 0;
}
std::list<MineInfo> Client::miningHistory()
{
std::list<MineInfo> ret;
ReadGuard l(x_localMiners);
/* ReadGuard l(x_localMiners);
if (m_localMiners.empty())
return ret;
ret = m_localMiners[0].miningHistory();
@ -391,11 +360,11 @@ std::list<MineInfo> Client::miningHistory()
auto li = l.begin();
for (; ri != ret.end() && li != l.end(); ++ri, ++li)
ri->combine(*li);
}
}*/
return ret;
}
void Client::setupState(State& _s)
/*void Client::setupState(State& _s)
{
{
ReadGuard l(x_stateDB);
@ -416,7 +385,7 @@ void Client::setupState(State& _s)
}
else
_s.commitToMine(m_bc);
}
}*/
ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u256 _gasPrice, Address const& _from)
{
@ -442,170 +411,187 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
return ret;
}
pair<h256, u256> Client::getWork()
ProofOfWork::WorkPackage Client::getWork()
{
Guard l(x_remoteMiner);
return ProofOfWork::package(m_miningInfo);
}
bool Client::submitWork(ProofOfWork::Solution const& _solution)
{
bytes newBlock;
{
ReadGuard l(x_stateDB);
m_remoteMiner.update(m_postMine, m_bc);
WriteGuard l(x_stateDB);
if (!m_postMine.completeMine<ProofOfWork>(_solution))
return false;
newBlock = m_postMine.blockData();
}
return make_pair(m_remoteMiner.workHash(), m_remoteMiner.difficulty());
m_bq.import(&newBlock, m_bc);
/*
ImportRoute ir = m_bc.attemptImport(newBlock, m_stateDB);
if (!ir.first.empty())
onChainChanged(ir);*/
return true;
}
bool Client::submitWork(ProofOfWork::Proof const& _proof)
void Client::syncBlockQueue()
{
Guard l(x_remoteMiner);
return m_remoteMiner.submitWork(_proof);
ImportRoute ir;
cwork << "BQ ==> CHAIN ==> STATE";
{
WriteGuard l(x_stateDB);
OverlayDB db = m_stateDB;
ETH_WRITE_UNGUARDED(x_stateDB)
tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100);
if (ir.first.empty())
return;
m_stateDB = db;
}
onChainChanged(ir);
}
void Client::doWork()
void Client::syncTransactionQueue()
{
// TODO: Use condition variable rather than polling.
bool stillGotWork = false;
// returns TransactionReceipts, once for each transaction.
cwork << "postSTATE <== TQ";
cworkin << "WORK";
h256Set changeds;
TransactionReceipts newPendingReceipts;
auto maintainMiner = [&](Miner& m)
{
if (m.isComplete())
{
// TODO: enable a short-circuit option since we mined it. will need to get the end state from the miner.
auto lm = dynamic_cast<LocalMiner*>(&m);
h256s hs;
h256 c;
if (false && lm && !m_verifyOwnBlocks)
{
// TODO: implement
//m_bc.attemptImport(m_blockData(), m_stateDB, lm->state());
// TODO: derive hs from lm->state()
}
else
{
cwork << "CHAIN <== postSTATE";
WriteGuard l(x_stateDB);
tie(hs, c) = m_bc.attemptImport(m.blockData(), m_stateDB);
}
if (hs.size())
{
for (auto const& h: hs)
if (h != c)
appendFromNewBlock(h, changeds);
changeds.insert(ChainChangedFilter);
}
for (auto& m: m_localMiners)
m.noteStateChange();
}
};
{
ReadGuard l(x_localMiners);
for (auto& m: m_localMiners)
maintainMiner(m);
}
ETH_WRITE_GUARDED(x_stateDB)
newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp);
if (newPendingReceipts.size())
{
Guard l(x_remoteMiner);
maintainMiner(m_remoteMiner);
for (size_t i = 0; i < newPendingReceipts.size(); i++)
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
changeds.insert(PendingChangedFilter);
// TODO: Tell farm about new transaction (i.e. restartProofOfWork mining).
onPostStateChanged();
// Tell watches about the new transactions.
noteChanged(changeds);
// Tell network about the new transactions.
if (auto h = m_host.lock())
h->noteNewTransactions();
}
}
// Synchronise state to block chain.
// This should remove any transactions on our queue that are included within our state.
// It also guarantees that the state reflects the longest (valid!) chain on the block chain.
// This might mean reverting to an earlier state and replaying some blocks, or, (worst-case:
// if there are no checkpoints before our fork) reverting to the genesis block and replaying
// all blocks.
// Resynchronise state with block chain & trans
bool resyncStateNeeded = false;
void Client::onChainChanged(ImportRoute const& _ir)
{
// insert transactions that we are declaring the dead part of the chain
for (auto const& h: _ir.second)
{
WriteGuard l(x_stateDB);
cwork << "BQ ==> CHAIN ==> STATE";
OverlayDB db = m_stateDB;
x_stateDB.unlock();
h256s fresh;
h256s dead;
bool sgw;
tie(fresh, dead, sgw) = m_bc.sync(m_bq, db, 100);
// insert transactions that we are declaring the dead part of the chain
for (auto const& h: dead)
clog(ClientNote) << "Dead block:" << h.abridged();
for (auto const& t: m_bc.transactions(h))
{
clog(ClientNote) << "Dead block:" << h.abridged();
for (auto const& t: m_bc.transactions(h))
{
clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None);
m_tq.import(t);
}
clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None);
m_tq.import(t);
}
}
// remove transactions from m_tq nicely rather than relying on out of date nonce later on.
for (auto const& h: fresh)
// remove transactions from m_tq nicely rather than relying on out of date nonce later on.
for (auto const& h: _ir.first)
{
clog(ClientChat) << "Live block:" << h.abridged();
for (auto const& th: m_bc.transactionHashes(h))
{
clog(ClientChat) << "Live block:" << h.abridged();
for (auto const& th: m_bc.transactionHashes(h))
{
clog(ClientNote) << "Safely dropping transaction " << th.abridged();
m_tq.drop(th);
}
clog(ClientNote) << "Safely dropping transaction " << th.abridged();
m_tq.drop(th);
}
}
stillGotWork = stillGotWork | sgw;
if (!fresh.empty())
{
for (auto i: fresh)
appendFromNewBlock(i, changeds);
changeds.insert(ChainChangedFilter);
}
x_stateDB.lock();
if (fresh.size())
m_stateDB = db;
if (auto h = m_host.lock())
h->noteNewBlocks();
cwork << "preSTATE <== CHAIN";
h256Set changeds;
for (auto const& h: _ir.first)
appendFromNewBlock(h, changeds);
changeds.insert(ChainChangedFilter);
// RESTART MINING
// LOCKS REALLY NEEDED?
ETH_WRITE_GUARDED(x_stateDB)
if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address())
{
if (isMining())
cnote << "New block on chain: Restarting mining operation.";
cnote << "New block on chain.";
m_postMine = m_preMine;
resyncStateNeeded = true;
changeds.insert(PendingChangedFilter);
// TODO: Move transactions pending from m_postMine back to transaction queue.
ETH_WRITE_UNGUARDED(x_stateDB)
onPostStateChanged();
}
// returns TransactionReceipts, once for each transaction.
cwork << "postSTATE <== TQ";
TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp);
if (newPendingReceipts.size())
{
for (size_t i = 0; i < newPendingReceipts.size(); i++)
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
changeds.insert(PendingChangedFilter);
noteChanged(changeds);
}
if (isMining())
cnote << "Additional transaction ready: Restarting mining operation.";
resyncStateNeeded = true;
if (auto h = m_host.lock())
h->noteNewTransactions();
void Client::onPostStateChanged()
{
cnote << "Post state changed: Restarting mining...";
if (isMining())
{
{
WriteGuard l(x_stateDB);
m_postMine.commitToMine(m_bc);
m_miningInfo = m_postMine.info();
}
m_farm.setWork(m_miningInfo);
}
}
if (!changeds.empty())
if (auto h = m_host.lock())
h->noteNewBlocks();
void Client::startMining()
{
if (m_turboMining)
m_farm.startGPU();
else
m_farm.startCPU();
onPostStateChanged();
}
if (resyncStateNeeded)
{
ReadGuard l(x_localMiners);
for (auto& m: m_localMiners)
m.noteStateChange();
}
void Client::noteChanged(h256Set const& _filters)
{
Guard l(x_filtersWatches);
if (_filters.size())
cnote << "noteChanged(" << filtersToString(_filters) << ")";
// accrue all changes left in each filter into the watches.
for (auto& w: m_watches)
if (_filters.count(w.second.id))
{
cwatch << "!!!" << w.first << (m_filters.count(w.second.id) ? w.second.id.abridged() : w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???");
if (m_filters.count(w.second.id)) // Normal filtering watch
w.second.changes += m_filters.at(w.second.id).changes;
else // Special ('pending'/'latest') watch
w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0));
}
// clear the filters now.
for (auto& i: m_filters)
i.second.changes.clear();
}
cwork << "noteChanged" << changeds.size() << "items";
noteChanged(changeds);
cworkout << "WORK";
void Client::doWork()
{
// TODO: Use condition variable rather than this rubbish.
bool t = true;
if (m_syncTransactionQueue.compare_exchange_strong(t, false))
syncTransactionQueue();
if (!stillGotWork)
this_thread::sleep_for(chrono::milliseconds(100));
t = true;
if (m_syncBlockQueue.compare_exchange_strong(t, false))
syncBlockQueue();
checkWatchGarbage();
this_thread::sleep_for(chrono::milliseconds(20));
}
void Client::checkWatchGarbage()
{
if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5))
{
// watches garbage collection

111
libethereum/Client.h

@ -40,8 +40,8 @@
#include "TransactionQueue.h"
#include "State.h"
#include "CommonNet.h"
#include "Miner.h"
#include "ABI.h"
#include "Farm.h"
#include "ClientBase.h"
namespace dev
@ -72,28 +72,6 @@ private:
std::string m_path;
};
class RemoteMiner: public Miner
{
public:
RemoteMiner() {}
void update(State const& _provisional, BlockChain const& _bc) { m_state = _provisional; m_state.commitToMine(_bc); }
h256 workHash() const { return m_state.info().headerHash(IncludeNonce::WithoutNonce); }
u256 const& difficulty() const { return m_state.info().difficulty; }
bool submitWork(ProofOfWork::Proof const& _result) { return (m_isComplete = m_state.completeMine(_result)); }
virtual bool isComplete() const override { return m_isComplete; }
virtual bytes const& blockData() const { return m_state.blockData(); }
virtual void noteStateChange() override {}
private:
bool m_isComplete = false;
State m_state;
};
class BasicGasPricer: public GasPricer
{
public:
@ -122,18 +100,15 @@ struct ClientDetail: public LogChannel { static const char* name() { return " C
/**
* @brief Main API hub for interfacing with Ethereum.
*/
class Client: public MinerHost, public ClientBase, Worker
class Client: public ClientBase, Worker
{
friend class Miner;
public:
/// New-style Constructor.
explicit Client(
p2p::Host* _host,
std::string const& _dbPath = std::string(),
WithExisting _forceAction = WithExisting::Trust,
u256 _networkId = 0,
int _miners = -1
u256 _networkId = 0
);
explicit Client(
@ -141,8 +116,7 @@ public:
std::shared_ptr<GasPricer> _gpForAdoption, // pass it in with new.
std::string const& _dbPath = std::string(),
WithExisting _forceAction = WithExisting::Trust,
u256 _networkId = 0,
int _miners = -1
u256 _networkId = 0
);
/// Destructor.
@ -191,32 +165,32 @@ public:
/// Are we allowed to GPU mine?
bool turboMining() const { return m_turboMining; }
/// Enable/disable GPU mining.
void setTurboMining(bool _enable = true) { bool was = isMining(); stopMining(); m_turboMining = _enable; setMiningThreads(0); if (was) startMining(); }
void setTurboMining(bool _enable = true) { m_turboMining = _enable; if (isMining()) startMining(); }
/// Stops mining and sets the number of mining threads (0 for automatic).
void setMiningThreads(unsigned _threads = 0) override;
/// Get the effective number of mining threads.
unsigned miningThreads() const override { ReadGuard l(x_localMiners); return m_localMiners.size(); }
/// Start mining.
/// NOT thread-safe - call it & stopMining only from a single thread
void startMining() override { startWorking(); { ReadGuard l(x_localMiners); for (auto& m: m_localMiners) m.start(); } }
void startMining() override;
/// Stop mining.
/// NOT thread-safe
void stopMining() override { { ReadGuard l(x_localMiners); for (auto& m: m_localMiners) m.stop(); } }
/// Are we mining now?
bool isMining() const override { { ReadGuard l(x_localMiners); if (!m_localMiners.empty() && m_localMiners[0].isRunning()) return true; } return false; }
void stopMining() override { m_farm.stop(); }
/// Are we mining now?
bool isMining() const override { return m_farm.isMining(); }
/// The hashrate...
uint64_t hashrate() const override;
/// Check the progress of the mining.
MineProgress miningProgress() const override;
MiningProgress miningProgress() const override;
/// Get and clear the mining history.
std::list<MineInfo> miningHistory();
/// Update to the latest transactions and get hash of the current block to be mined minus the
/// nonce (the 'work hash') and the difficulty to be met.
virtual std::pair<h256, u256> getWork() override;
/// Submit the proof for the proof-of-work.
virtual bool submitWork(ProofOfWork::Proof const& _proof) override;
virtual ProofOfWork::WorkPackage getWork() override;
/** @brief Submit the proof for the proof-of-work.
* @param _s A valid solution.
* @return true if the solution was indeed valid and accepted.
*/
virtual bool submitWork(ProofOfWork::Solution const& _proof) override;
// Debug stuff:
@ -228,6 +202,8 @@ public:
void clearPending();
/// Kills the blockchain. Just for debug use.
void killChain();
/// Retries all blocks with unknown parents.
void retryUnkonwn() { m_bq.retryAllUnknown(); }
protected:
/// InterfaceStub methods
@ -255,16 +231,36 @@ protected:
void noteChanged(h256Set const& _filters);
private:
/// Called when Worker is starting.
void startedWorking() override;
/// Do some work. Handles blockchain maintenance and mining.
virtual void doWork();
void doWork() override;
/// Called when Worker is exiting.
virtual void doneWorking();
void doneWorking() override;
/// Magically called when the chain has changed. An import route is provided.
/// Called by either submitWork() or in our main thread through syncBlockQueue().
void onChainChanged(ImportRoute const& _ir);
/// Signal handler for when the block queue needs processing.
void syncBlockQueue();
/// Signal handler for when the block queue needs processing.
void syncTransactionQueue();
/// Magically called when m_tq needs syncing. Be nice and don't block.
void onTransactionQueueReady() { m_syncTransactionQueue = true; }
/// Overrides for being a mining host.
virtual void setupState(State& _s);
virtual bool turbo() const { return m_turboMining; }
virtual bool force() const { return m_forceMining; }
/// Magically called when m_tq needs syncing. Be nice and don't block.
void onBlockQueueReady() { m_syncBlockQueue = true; }
/// Called when the post state has changed (i.e. when more transactions are in it or we're mining on a new block).
/// This updates m_miningInfo.
void onPostStateChanged();
void checkWatchGarbage();
VersionChecker m_vc; ///< Dummy object to check & update the protocol version.
CanonBlockChain m_bc; ///< Maintains block database.
@ -275,20 +271,25 @@ private:
OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it.
State m_preMine; ///< The present state of the client.
State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added).
BlockInfo m_miningInfo; ///< The header we're attempting to mine on (derived from m_postMine).
std::weak_ptr<EthereumHost> m_host; ///< Our Ethereum Host. Don't do anything if we can't lock.
mutable Mutex x_remoteMiner; ///< The remote miner lock.
RemoteMiner m_remoteMiner; ///< The remote miner.
GenericFarm<ProofOfWork> m_farm; ///< Our mining farm.
Handler m_tqReady;
Handler m_bqReady;
std::vector<LocalMiner> m_localMiners; ///< The in-process miners.
mutable SharedMutex x_localMiners; ///< The in-process miners lock.
bool m_paranoia = false; ///< Should we be paranoid about our state?
bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping.
bool m_forceMining = false; ///< Mine even when there are no transactions pending?
bool m_verifyOwnBlocks = true; ///< Should be verify blocks that we mined?
bool m_paranoia = false; ///< Should we be paranoid about our state?
mutable std::chrono::system_clock::time_point m_lastGarbageCollection;
///< When did we last both doing GC on the watches?
// TODO!!!!!! REPLACE WITH A PROPER X-THREAD ASIO SIGNAL SYSTEM (could just be condition variables)
std::atomic<bool> m_syncTransactionQueue = {false};
std::atomic<bool> m_syncBlockQueue = {false};
};
}

4
libethereum/ClientBase.cpp

@ -20,10 +20,12 @@
* @date 2015
*/
#include <libdevcore/StructuredLogger.h>
#include "ClientBase.h"
#include <libdevcore/StructuredLogger.h>
#include "BlockChain.h"
#include "Executive.h"
#include "State.h"
using namespace std;
using namespace dev;

45
libethereum/ClientBase.h

@ -25,6 +25,7 @@
#include <chrono>
#include "Interface.h"
#include "LogFilter.h"
#include "TransactionQueue.h"
namespace dev {
@ -60,15 +61,15 @@ struct ClientWatch
};
struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; };
#define cwatch dev::LogOutputStream<dev::eth::WatchChannel, true>()
#define cwatch LogOutputStream<WatchChannel, true>()
struct WorkInChannel: public LogChannel { static const char* name() { return ">W>"; } static const int verbosity = 16; };
struct WorkOutChannel: public LogChannel { static const char* name() { return "<W<"; } static const int verbosity = 16; };
struct WorkChannel: public LogChannel { static const char* name() { return "-W-"; } static const int verbosity = 21; };
#define cwork dev::LogOutputStream<dev::eth::WorkChannel, true>()
#define cworkin dev::LogOutputStream<dev::eth::WorkInChannel, true>()
#define cworkout dev::LogOutputStream<dev::eth::WorkOutChannel, true>()
#define cwork LogOutputStream<WorkChannel, true>()
#define cworkin LogOutputStream<WorkInChannel, true>()
#define cworkout LogOutputStream<WorkOutChannel, true>()
class ClientBase: public dev::eth::Interface
class ClientBase: public Interface
{
public:
ClientBase() {}
@ -110,18 +111,18 @@ public:
virtual LocalisedLogEntries checkWatch(unsigned _watchId) override;
virtual h256 hashFromNumber(BlockNumber _number) const override;
virtual eth::BlockInfo blockInfo(h256 _hash) const override;
virtual eth::BlockDetails blockDetails(h256 _hash) const override;
virtual eth::Transaction transaction(h256 _transactionHash) const override;
virtual eth::Transaction transaction(h256 _blockHash, unsigned _i) const override;
virtual eth::Transactions transactions(h256 _blockHash) const override;
virtual eth::TransactionHashes transactionHashes(h256 _blockHash) const override;
virtual eth::BlockInfo uncle(h256 _blockHash, unsigned _i) const override;
virtual eth::UncleHashes uncleHashes(h256 _blockHash) const override;
virtual BlockInfo blockInfo(h256 _hash) const override;
virtual BlockDetails blockDetails(h256 _hash) const override;
virtual Transaction transaction(h256 _transactionHash) const override;
virtual Transaction transaction(h256 _blockHash, unsigned _i) const override;
virtual Transactions transactions(h256 _blockHash) const override;
virtual TransactionHashes transactionHashes(h256 _blockHash) const override;
virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const override;
virtual UncleHashes uncleHashes(h256 _blockHash) const override;
virtual unsigned transactionCount(h256 _blockHash) const override;
virtual unsigned uncleCount(h256 _blockHash) const override;
virtual unsigned number() const override;
virtual eth::Transactions pending() const override;
virtual Transactions pending() const override;
virtual h256s pendingHashes() const override;
void injectBlock(bytes const& _block);
@ -142,15 +143,13 @@ public:
/// TODO: consider moving it to a separate interface
virtual void setMiningThreads(unsigned _threads) override { (void)_threads; BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::setMiningThreads")); }
virtual unsigned miningThreads() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::miningThreads")); }
virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::startMining")); }
virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::stopMining")); }
virtual bool isMining() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::isMining")); }
virtual uint64_t hashrate() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::hashrate")); }
virtual eth::MineProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::miningProgress")); }
virtual std::pair<h256, u256> getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::getWork")); }
virtual bool submitWork(eth::ProofOfWork::Proof const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::submitWork")); }
virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::startMining")); }
virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::stopMining")); }
virtual bool isMining() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::isMining")); }
virtual uint64_t hashrate() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::hashrate")); }
virtual MiningProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::miningProgress")); }
virtual ProofOfWork::WorkPackage getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::getWork")); }
virtual bool submitWork(ProofOfWork::Solution const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::submitWork")); }
State asOf(BlockNumber _h) const;

5
libethereum/EthereumHost.cpp

@ -133,9 +133,10 @@ void EthereumHost::noteDoneBlocks(EthereumPeer* _who, bool _clemency)
{
// Done our chain-get.
clog(NetNote) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished.";
clog(NetNote) << "TODO: PUNISH.";
m_banned.insert(_who->session()->id()); // We know who you are!
_who->disable("Peer sent hashes but was unable to provide the blocks.");
// m_banned.insert(_who->session()->id()); // We know who you are!
// _who->disable("Peer sent hashes but was unable to provide the blocks.");
}
m_man.reset();
}

0
libethereum/Farm.cpp

205
libethereum/Farm.h

@ -0,0 +1,205 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Farm.h
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#pragma once
#include <thread>
#include <list>
#include <atomic>
#include <libdevcore/Common.h>
#include <libdevcore/Worker.h>
#include <libethcore/Common.h>
#include <libethcore/Miner.h>
#include <libethcore/BlockInfo.h>
#include <libethcore/ProofOfWork.h>
namespace dev
{
namespace eth
{
/**
* @brief A collective of Miners.
* Miners ask for work, then submit proofs
* @threadsafe
*/
template <class PoW>
class GenericFarm: public GenericFarmFace<PoW>
{
public:
using WorkPackage = typename PoW::WorkPackage;
using Solution = typename PoW::Solution;
using Miner = GenericMiner<PoW>;
~GenericFarm()
{
stop();
}
/**
* @brief Sets the current mining mission.
* @param _bi The block (header) we wish to be mining.
*/
void setWork(BlockInfo const& _bi)
{
WriteGuard l(x_minerWork);
m_header = _bi;
auto p = PoW::package(m_header);
if (p.headerHash == m_work.headerHash)
return;
m_work = p;
for (auto const& m: m_miners)
m->setWork(m_work);
resetTimer();
}
/**
* @brief (Re)start miners for CPU only.
* @returns true if started properly.
*/
bool startCPU() { return start<typename PoW::CPUMiner>(); }
/**
* @brief (Re)start miners for GPU only.
* @returns true if started properly.
*/
bool startGPU() { return start<typename PoW::GPUMiner>(); }
/**
* @brief Stop all mining activities.
*/
void stop()
{
WriteGuard l(x_minerWork);
m_miners.clear();
m_work.reset();
m_isMining = false;
}
bool isMining() const
{
return m_isMining;
}
/**
* @brief Get information on the progress of mining this work package.
* @return The progress with mining so far.
*/
MiningProgress const& miningProgress() const
{
MiningProgress p;
p.ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_lastStart).count();
{
ReadGuard l2(x_minerWork);
for (auto const& i: m_miners)
p.hashes += i->hashCount();
}
ReadGuard l(x_progress);
m_progress = p;
return m_progress;
}
/**
* @brief Reset the mining progess counter.
*/
void resetMiningProgress()
{
ETH_READ_GUARDED(x_minerWork)
for (auto const& i: m_miners)
i->resetHashCount();
resetTimer();
}
using SolutionFound = std::function<bool(Solution const&)>;
/**
* @brief Provides a valid header based upon that received previously with setWork().
* @param _bi The now-valid header.
* @return true if the header was good and that the Farm should pause until more work is submitted.
*/
void onSolutionFound(SolutionFound const& _handler) { m_onSolutionFound = _handler; }
WorkPackage work() const { ReadGuard l(x_minerWork); return m_work; }
private:
/**
* @brief Called from a Miner to note a WorkPackage has a solution.
* @param _p The solution.
* @param _wp The WorkPackage that the Solution is for.
* @return true iff the solution was good (implying that mining should be .
*/
bool submitProof(Solution const& _s, Miner* _m) override
{
if (m_onSolutionFound && m_onSolutionFound(_s))
{
WriteGuard ul(x_minerWork);
for (std::shared_ptr<Miner> const& m: m_miners)
if (m.get() != _m)
m->setWork();
m_work.reset();
return true;
}
return false;
}
/**
* @brief Start a number of miners.
*/
template <class MinerType>
bool start()
{
WriteGuard l(x_minerWork);
if (!m_miners.empty() && !!std::dynamic_pointer_cast<MinerType>(m_miners[0]))
return true;
m_miners.clear();
m_miners.reserve(MinerType::instances());
for (unsigned i = 0; i < MinerType::instances(); ++i)
{
m_miners.push_back(std::shared_ptr<Miner>(new MinerType(std::make_pair(this, i))));
m_miners.back()->setWork(m_work);
}
m_isMining = true;
resetTimer();
return true;
}
void resetTimer()
{
m_lastStart = std::chrono::steady_clock::now();
}
mutable SharedMutex x_minerWork;
std::vector<std::shared_ptr<Miner>> m_miners;
WorkPackage m_work;
BlockInfo m_header;
std::atomic<bool> m_isMining = {false};
mutable SharedMutex x_progress;
mutable MiningProgress m_progress;
std::chrono::steady_clock::time_point m_lastStart;
SolutionFound m_onSolutionFound;
};
}
}

13
libethereum/Interface.h

@ -26,11 +26,11 @@
#include <libdevcore/Guards.h>
#include <libdevcrypto/Common.h>
#include <libethcore/Params.h>
#include <libethcore/ProofOfWork.h>
#include "LogFilter.h"
#include "Transaction.h"
#include "AccountDiff.h"
#include "BlockDetails.h"
#include "Miner.h"
namespace dev
{
@ -171,11 +171,6 @@ public:
/// Get the coinbase address.
virtual Address address() const = 0;
/// Stops mining and sets the number of mining threads (0 for automatic).
virtual void setMiningThreads(unsigned _threads = 0) = 0;
/// Get the effective number of mining threads.
virtual unsigned miningThreads() const = 0;
/// Start mining.
/// NOT thread-safe - call it & stopMining only from a single thread
virtual void startMining() = 0;
@ -188,12 +183,12 @@ public:
virtual uint64_t hashrate() const = 0;
/// Get hash of the current block to be mined minus the nonce (the 'work hash').
virtual std::pair<h256, u256> getWork() = 0;
virtual ProofOfWork::WorkPackage getWork() = 0;
/// Submit the nonce for the proof-of-work.
virtual bool submitWork(ProofOfWork::Proof const& _proof) = 0;
virtual bool submitWork(ProofOfWork::Solution const& _proof) = 0;
/// Check the progress of the mining.
virtual MineProgress miningProgress() const = 0;
virtual MiningProgress miningProgress() const = 0;
protected:
int m_default = PendingBlock;

96
libethereum/Miner.cpp

@ -1,96 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Miner.cpp
* @author Gav Wood <i@gavwood.com>
* @author Giacomo Tazzari
* @date 2014
*/
#include "Miner.h"
#include <libdevcore/CommonIO.h>
#include "State.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
Miner::~Miner() {}
LocalMiner::LocalMiner(MinerHost* _host, unsigned _id):
AsyncMiner(_host, _id),
Worker("miner-" + toString(_id))
{
m_pow.reset(_host->turbo() ? new Ethash : (Ethash*)new EthashCPU);
}
void LocalMiner::setup(MinerHost* _host, unsigned _id)
{
AsyncMiner::setup(_host, _id);
setName("miner-" + toString(m_id));
m_pow.reset(_host->turbo() ? new Ethash : (Ethash*)new EthashCPU);
}
void LocalMiner::doWork()
{
// Do some mining.
if (m_miningStatus != Waiting && m_miningStatus != Mined)
{
if (m_miningStatus == Preparing)
{
m_host->setupState(m_mineState);
if (m_host->force() || m_mineState.pending().size())
m_miningStatus = Mining;
else
m_miningStatus = Waiting;
{
Guard l(x_mineInfo);
m_mineProgress.best = (double)-1;
m_mineProgress.hashes = 0;
m_mineProgress.ms = 0;
}
}
if (m_miningStatus == Mining)
{
// Mine for a while.
MineInfo mineInfo = m_mineState.mine(m_pow.get());
{
Guard l(x_mineInfo);
m_mineProgress.best = min(m_mineProgress.best, mineInfo.best);
m_mineProgress.current = mineInfo.best;
m_mineProgress.requirement = mineInfo.requirement;
m_mineProgress.ms += 100;
m_mineProgress.hashes += mineInfo.hashes;
m_mineHistory.push_back(mineInfo);
}
if (mineInfo.completed)
{
m_mineState.completeMine();
m_host->onComplete();
m_miningStatus = Mined;
}
else
m_host->onProgressed();
}
}
else
{
this_thread::sleep_for(chrono::milliseconds(100));
}
}

178
libethereum/Miner.h

@ -1,178 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Miner.h
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <thread>
#include <list>
#include <atomic>
#include <libdevcore/Common.h>
#include <libdevcore/Worker.h>
#include <libethcore/Common.h>
#include "State.h"
namespace dev
{
namespace eth
{
/**
* @brief Describes the progress of a mining operation.
*/
struct MineProgress
{
void combine(MineProgress const& _m) { requirement = std::max(requirement, _m.requirement); best = std::min(best, _m.best); current = std::max(current, _m.current); hashes += _m.hashes; ms = std::max(ms, _m.ms); }
double requirement = 0; ///< The PoW requirement - as the second logarithm of the minimum acceptable hash.
double best = 1e99; ///< The PoW achievement - as the second logarithm of the minimum found hash.
double current = 0; ///< The most recent PoW achievement - as the second logarithm of the presently found hash.
unsigned hashes = 0; ///< Total number of hashes computed.
unsigned ms = 0; ///< Total number of milliseconds of mining thus far.
};
/**
* @brief Class for hosting one or more Miners.
* @warning Must be implemented in a threadsafe manner since it will be called from multiple
* miner threads.
*/
class MinerHost
{
public:
virtual void setupState(State& _s) = 0; ///< Reset the given State object to the one that should be being mined.
virtual void onProgressed() {} ///< Called once some progress has been made.
virtual void onComplete() {} ///< Called once a block is found.
virtual bool force() const = 0; ///< @returns true iff the Miner should mine regardless of the number of transactions.
virtual bool turbo() const = 0; ///< @returns true iff the Miner should use GPU if possible.
};
class Miner
{
public:
virtual ~Miner();
virtual void noteStateChange() = 0;
virtual bool isComplete() const = 0;
virtual bytes const& blockData() const = 0;
};
class AsyncMiner: public Miner
{
public:
/// Null constructor.
AsyncMiner(): m_host(nullptr) {}
/// Constructor.
AsyncMiner(MinerHost* _host, unsigned _id = 0): m_host(_host), m_id(_id) {}
/// Setup its basics.
void setup(MinerHost* _host, unsigned _id = 0) { m_host = _host; m_id = _id; }
/// Start mining.
virtual void start() {}
/// Stop mining.
virtual void stop() {}
/// @returns true iff the mining has been start()ed. It may still not be actually mining, depending on the host's turbo() & force().
virtual bool isRunning() const { return false; }
protected:
MinerHost* m_host = nullptr; ///< Our host.
unsigned m_id = 0; ///< Our unique id.
};
/**
* @brief Implements Miner.
* To begin mining, use start() & stop(). noteStateChange() can be used to reset the mining and set up the
* State object according to the host. Use isRunning() to determine if the miner has been start()ed.
* Use isComplete() to determine if the miner has finished mining.
*
* blockData() can be used to retrieve the complete block, ready for insertion into the BlockChain.
*
* Information on the mining can be queried through miningProgress() and miningHistory().
* @threadsafe
* @todo Signal Miner to restart once with condition variables.
*/
class LocalMiner: public AsyncMiner, Worker
{
public:
/// Null constructor.
LocalMiner() {}
/// Constructor.
LocalMiner(MinerHost* _host, unsigned _id = 0);
/// Move-constructor.
LocalMiner(LocalMiner&& _m): Worker((Worker&&)_m) { std::swap(m_host, _m.m_host); std::swap(m_pow, _m.m_pow); }
/// Move-assignment.
LocalMiner& operator=(LocalMiner&& _m) { Worker::operator=((Worker&&)_m); std::swap(m_host, _m.m_host); std::swap(m_pow, _m.m_pow); return *this; }
/// Destructor. Stops miner.
~LocalMiner() { stop(); }
/// Setup its basics.
void setup(MinerHost* _host, unsigned _id = 0);
/// Start mining.
void start() { startWorking(); }
/// Stop mining.
void stop() { stopWorking(); }
/// Call to notify Miner of a state change.
virtual void noteStateChange() override { m_miningStatus = Preparing; }
/// @returns true iff the mining has been start()ed. It may still not be actually mining, depending on the host's turbo() & force().
bool isRunning() const override { return isWorking(); }
/// @returns true if mining is complete.
virtual bool isComplete() const override { return m_miningStatus == Mined; }
/// @returns the internal State object.
virtual bytes const& blockData() const override { return m_mineState.blockData(); }
/// Check the progress of the mining.
MineProgress miningProgress() const { Guard l(x_mineInfo); return m_mineProgress; }
/// Get and clear the mining history.
std::list<MineInfo> miningHistory() { Guard l(x_mineInfo); auto ret = m_mineHistory; m_mineHistory.clear(); return ret; }
/// @returns the state on which we mined.
State const& state() const { return m_mineState; }
private:
/// Do some work on the mining.
virtual void doWork();
enum MiningStatus { Waiting, Preparing, Mining, Mined, Stopping, Stopped };
MiningStatus m_miningStatus = Waiting; ///< TODO: consider mutex/atomic variable.
State m_mineState; ///< The state on which we are mining, generally equivalent to m_postMine.
std::unique_ptr<EthashPoW> m_pow; ///< Our miner.
mutable Mutex x_mineInfo; ///< Lock for the mining progress & history.
MineProgress m_mineProgress; ///< What's our progress?
std::list<MineInfo> m_mineHistory; ///< What the history of our mining?
};
}
}

14
libethereum/State.cpp

@ -856,20 +856,6 @@ void State::commitToMine(BlockChain const& _bc)
m_committedToMine = true;
}
bool State::completeMine(ProofOfWork::Proof const& _nonce)
{
ProofOfWork::assignResult(_nonce, m_currentBlock);
// if (!m_pow.verify(m_currentBlock))
// return false;
cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock);
completeMine();
return true;
}
void State::completeMine()
{
cdebug << "Completing mine!";

31
libethereum/State.h

@ -30,6 +30,7 @@
#include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h>
#include <libethcore/ProofOfWork.h>
#include <libethcore/Miner.h>
#include <libethcore/Params.h>
#include <libevm/ExtVMFace.h>
#include "TransactionQueue.h"
@ -162,30 +163,19 @@ public:
/// Pass in a solution to the proof-of-work.
/// @returns true iff the given nonce is a proof-of-work for this State's block.
bool completeMine(ProofOfWork::Proof const& _result);
/// Attempt to find valid nonce for block that this state represents.
/// This function is thread-safe. You can safely have other interactions with this object while it is happening.
/// @param _msTimeout Timeout before return in milliseconds.
/// @returns Information on the mining.
template <class ProofOfWork> MineInfo mine(ProofOfWork* _pow)
template <class PoW>
bool completeMine(typename PoW::Solution const& _result)
{
// Update difficulty according to timestamp.
m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock);
PoW::assignResult(_result, m_currentBlock);
MineInfo ret;
typename ProofOfWork::Proof r;
std::tie(ret, r) = _pow->mine(m_currentBlock, _pow->defaultTimeout(), true);
// if (!m_pow.verify(m_currentBlock))
// return false;
if (!ret.completed)
m_currentBytes.clear();
else
{
ProofOfWork::assignResult(r, m_currentBlock);
cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock);
}
cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << PoW::verify(m_currentBlock);
return ret;
completeMine();
return true;
}
/** Commit to DB and build the final block if the previous call to mine()'s result is completion.
@ -369,7 +359,6 @@ private:
/// Debugging only. Good for checking the Trie is in shape.
void paranoia(std::string const& _when, bool _enforceRefs = false) const;
OverlayDB m_db; ///< Our overlay for the state tree.
SecureTrieDB<Address, OverlayDB> m_state; ///< Our state tree, as an OverlayDB DB.
Transactions m_transactions; ///< The current list of transactions that we've included in the state.

6
libethereum/TransactionQueue.cpp

@ -28,7 +28,7 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
ImportResult TransactionQueue::import(bytesConstRef _transactionRLP)
ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallback const& _cb)
{
// Check if we already know this transaction.
h256 h = sha3(_transactionRLP);
@ -50,8 +50,10 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP)
// If valid, append to blocks.
m_current[h] = t;
m_known.insert(h);
if (_cb)
m_callbacks[h] = _cb;
ctxq << "Queued vaguely legit-looking transaction" << h.abridged();
m_onReady();
}
catch (Exception const& _e)
{

18
libethereum/TransactionQueue.h

@ -21,11 +21,12 @@
#pragma once
#include <functional>
#include <boost/thread.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
#include <libdevcore/Log.h>
#include "libethcore/Common.h"
#include <libethcore/Common.h>
#include "Transaction.h"
namespace dev
@ -45,8 +46,10 @@ struct TransactionQueueChannel: public LogChannel { static const char* name() {
class TransactionQueue
{
public:
ImportResult import(bytes const& _tx) { return import(&_tx); }
ImportResult import(bytesConstRef _tx);
using ImportCallback = std::function<void(ImportResult)>;
ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback()) { return import(&_tx, _cb); }
ImportResult import(bytesConstRef _tx, ImportCallback const& _cb = ImportCallback());
void drop(h256 _txHash);
@ -57,12 +60,15 @@ public:
void noteGood(std::pair<h256, Transaction> const& _t);
void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); }
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); }
private:
mutable boost::shared_mutex m_lock; ///< General lock.
std::set<h256> m_known; ///< Hashes of transactions in both sets.
std::map<h256, Transaction> m_current; ///< Map of SHA3(tx) to tx.
mutable boost::shared_mutex m_lock; ///< General lock.
std::set<h256> m_known; ///< Hashes of transactions in both sets.
std::map<h256, Transaction> m_current; ///< Map of SHA3(tx) to tx.
std::multimap<Address, std::pair<h256, Transaction>> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX.
std::map<h256, std::function<void(ImportResult)>> m_callbacks; ///< Called once.
Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast.
};
}

2
libethereumx/Ethereum.h

@ -52,7 +52,7 @@ class Client;
*/
class Ethereum
{
friend class Miner;
friend class OldMiner;
public:
/// Constructor. After this, everything should be set up to go.

7
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -758,8 +758,9 @@ Json::Value WebThreeStubServerBase::eth_getWork()
{
Json::Value ret(Json::arrayValue);
auto r = client()->getWork();
ret.append(toJS(r.first));
ret.append(toJS(r.second));
ret.append(toJS(r.headerHash));
ret.append(toJS(r.seedHash));
ret.append(toJS(r.boundary));
return ret;
}
@ -767,7 +768,7 @@ bool WebThreeStubServerBase::eth_submitWork(string const& _nonce, string const&
{
try
{
return client()->submitWork(ProofOfWork::Proof{jsToFixed<Nonce::size>(_nonce), jsToFixed<32>(_mixHash)});
return client()->submitWork(ProofOfWork::Solution{jsToFixed<Nonce::size>(_nonce), jsToFixed<32>(_mixHash)});
}
catch (...)
{

4
libwebthree/WebThree.cpp

@ -42,7 +42,7 @@ WebThreeDirect::WebThreeDirect(
WithExisting _we,
std::set<std::string> const& _interfaces,
NetworkPreferences const& _n,
bytesConstRef _network, int _miners
bytesConstRef _network
):
m_clientVersion(_clientVersion),
m_net(_clientVersion, _n, _network)
@ -50,7 +50,7 @@ WebThreeDirect::WebThreeDirect(
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
if (_interfaces.count("eth"))
m_ethereum.reset(new eth::Client(&m_net, _dbPath, _we, 0, _miners));
m_ethereum.reset(new eth::Client(&m_net, _dbPath, _we, 0));
if (_interfaces.count("shh"))
m_whisper = m_net.registerCapability<WhisperHost>(new WhisperHost);

3
libwebthree/WebThree.h

@ -112,8 +112,7 @@ public:
WithExisting _we = WithExisting::Trust,
std::set<std::string> const& _interfaces = {"eth", "shh"},
p2p::NetworkPreferences const& _n = p2p::NetworkPreferences(),
bytesConstRef _network = bytesConstRef(),
int _miners = -1
bytesConstRef _network = bytesConstRef()
);
/// Destructor.

19
mix/ClientModel.cpp

@ -134,12 +134,17 @@ void ClientModel::mine()
});
}
QString ClientModel::newAddress()
QString ClientModel::newSecret()
{
KeyPair a = KeyPair::create();
return QString::fromStdString(toHex(a.secret().ref()));
}
QString ClientModel::address(QString const& _secret)
{
return QString::fromStdString(toHex(KeyPair(Secret(_secret.toStdString())).address().ref()));
}
QString ClientModel::encodeAbiString(QString _string)
{
ContractCallDataEncoder encoder;
@ -210,24 +215,28 @@ void ClientModel::setupState(QVariantMap _state)
transactionSequence.push_back(transactionSettings);
}
}
executeSequence(transactionSequence, accounts);
executeSequence(transactionSequence, accounts, Secret(_state.value("miner").toMap().value("secret").toString().toStdString()));
}
void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence, map<Secret, u256> const& _balances)
void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence, map<Secret, u256> const& _balances, Secret const& _miner)
{
if (m_running)
BOOST_THROW_EXCEPTION(ExecutionStateException());
{
qWarning() << "Waiting for current execution to complete";
m_runFuture.waitForFinished();
}
m_running = true;
emit runStarted();
emit runStateChanged();
m_client->resetState(_balances, _miner);
m_web3Server->setAccounts(m_client->userAccounts());
//run sequence
m_runFuture = QtConcurrent::run([=]()
{
try
{
m_client->resetState(_balances);
onStateReset();
for (TransactionSettings const& transaction: _sequence)
{

8
mix/ClientModel.h

@ -162,8 +162,10 @@ public slots:
Q_INVOKABLE void debugRecord(unsigned _index);
/// Show the debugger for an empty record
Q_INVOKABLE void emptyRecord();
/// Generate new adress
Q_INVOKABLE QString newAddress();
/// Generate new secret
Q_INVOKABLE QString newSecret();
/// retrieve the address of @arg _secret
Q_INVOKABLE QString address(QString const& _secret);
/// Encode a string to ABI parameter. Returns a hex string
Q_INVOKABLE QString encodeAbiString(QString _string);
@ -207,7 +209,7 @@ private:
RecordLogEntry* lastBlock() const;
QVariantMap contractAddresses() const;
QVariantMap gasCosts() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence, std::map<Secret, u256> const& _balances);
void executeSequence(std::vector<TransactionSettings> const& _sequence, std::map<Secret, u256> const& _balances, Secret const& _miner);
dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings());
void callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
void onNewTransaction();

5
mix/CodeModel.cpp

@ -269,7 +269,10 @@ void CodeModel::runCompilationJob(int _jobId)
if (c_predefinedContracts.count(n) != 0)
continue;
QString name = QString::fromStdString(n);
QString sourceName = QString::fromStdString(*cs.getContractDefinition(n).getLocation().sourceName);
ContractDefinition const& contractDefinition = cs.getContractDefinition(n);
if (!contractDefinition.isFullyImplemented())
continue;
QString sourceName = QString::fromStdString(*contractDefinition.getLocation().sourceName);
auto sourceIter = m_pendingContracts.find(sourceName);
QString source = sourceIter != m_pendingContracts.end() ? sourceIter->second : QString();
CompiledContract* contract = new CompiledContract(cs, name, source);

2
mix/ContractCallDataEncoder.cpp

@ -118,7 +118,7 @@ unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, Solidit
result = bytes(alignSize);
toBigEndian((u256)i, result);
}
catch (std::exception const& ex)
catch (std::exception const&)
{
// manage input as a string.
QByteArray bytesAr = src.toLocal8Bit();

37
mix/MixClient.cpp

@ -20,6 +20,7 @@
* Ethereum IDE client.
*/
#include "MixClient.h"
#include <vector>
#include <libdevcore/Exceptions.h>
#include <libethereum/CanonBlockChain.h>
@ -28,10 +29,8 @@
#include <libethereum/ExtVM.h>
#include <libethereum/BlockChain.h>
#include <libevm/VM.h>
#include "Exceptions.h"
#include "MixClient.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -67,7 +66,7 @@ MixClient::~MixClient()
{
}
void MixClient::resetState(std::map<Secret, u256> _accounts)
void MixClient::resetState(std::map<Secret, u256> _accounts, Secret _miner)
{
WriteGuard l(x_state);
Guard fl(x_filtersWatches);
@ -91,7 +90,7 @@ void MixClient::resetState(std::map<Secret, u256> _accounts)
h256 stateRoot = accountState.root();
m_bc.reset();
m_bc.reset(new MixBlockChain(m_dbPath, stateRoot));
m_state = eth::State(m_stateDB, BaseState::PreExisting, genesisState.begin()->first);
m_state = eth::State(m_stateDB, BaseState::PreExisting, KeyPair(_miner).address());
m_state.sync(bc());
m_startState = m_state;
WriteGuard lx(x_executions);
@ -250,9 +249,17 @@ void MixClient::mine()
{
WriteGuard l(x_state);
m_state.commitToMine(bc());
ProofOfWork pow;
while (!m_state.mine(&pow).completed) {}
m_state.completeMine();
GenericFarm<ProofOfWork> f;
bool completed = false;
f.onSolutionFound([&](ProofOfWork::Solution sol)
{
return completed = m_state.completeMine<ProofOfWork>(sol);
});
f.setWork(m_state.info());
f.startCPU();
while (!completed)
this_thread::sleep_for(chrono::milliseconds(20));
bc().import(m_state.blockData(), m_stateDB);
m_state.sync(bc());
m_startState = m_state;
@ -372,16 +379,6 @@ void MixClient::setAddress(Address _us)
m_state.setAddress(_us);
}
void MixClient::setMiningThreads(unsigned _threads)
{
m_miningThreads = _threads;
}
unsigned MixClient::miningThreads() const
{
return m_miningThreads;
}
void MixClient::startMining()
{
//no-op
@ -402,9 +399,9 @@ uint64_t MixClient::hashrate() const
return 0;
}
eth::MineProgress MixClient::miningProgress() const
eth::MiningProgress MixClient::miningProgress() const
{
return eth::MineProgress();
return eth::MiningProgress();
}
}

10
mix/MixClient.h

@ -48,7 +48,7 @@ public:
MixClient(std::string const& _dbPath);
virtual ~MixClient();
/// Reset state to the empty state with given balance.
void resetState(std::map<Secret, u256> _accounts);
void resetState(std::map<Secret, u256> _accounts, Secret _miner = Secret());
void mine();
ExecutionResult lastExecution() const;
ExecutionResult execution(unsigned _index) const;
@ -63,15 +63,13 @@ public:
dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict);
void setAddress(Address _us) override;
void setMiningThreads(unsigned _threads) override;
unsigned miningThreads() const override;
void startMining() override;
void stopMining() override;
bool isMining() const override;
uint64_t hashrate() const override;
eth::MineProgress miningProgress() const override;
std::pair<h256, u256> getWork() override { return std::pair<h256, u256>(); }
bool submitWork(eth::ProofOfWork::Proof const&) override { return false; }
eth::MiningProgress miningProgress() const override;
eth::ProofOfWork::WorkPackage getWork() override { return eth::ProofOfWork::WorkPackage(); }
bool submitWork(eth::ProofOfWork::Solution const&) override { return false; }
virtual void flushTransactions() override {}
/// @returns the last mined block information

22
mix/Web3Server.h

@ -1,18 +1,18 @@
/*
This file is part of cpp-ethereum.
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Web3Server.h
* @author Arkadiy Paronyan arkadiy@ethdev.com

5
mix/qml.qrc

@ -48,8 +48,8 @@
<file>qml/StatusPaneStyle.qml</file>
<file>qml/StepActionImage.qml</file>
<file>qml/StorageView.qml</file>
<file>qml/StatesComboBox.qml</file>
<file>qml/StructView.qml</file>
<file>qml/StatesComboBox.qml</file>
<file>qml/StructView.qml</file>
<file>qml/Style.qml</file>
<file>qml/TabStyle.qml</file>
<file>qml/TransactionDialog.qml</file>
@ -63,5 +63,6 @@
<file>qml/js/TransactionHelper.js</file>
<file>qml/js/Printer.js</file>
<file>qml/js/ansi2html.js</file>
<file>qml/js/NetworkDeployment.js</file>
</qresource>
</RCC>

6
mix/qml/DeploymentDialog.qml

@ -6,7 +6,7 @@ import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/ProjectModel.js" as ProjectModelCode
import "js/NetworkDeployment.js" as NetworkDeploymentCode
import "js/QEtherHelper.js" as QEtherHelper
import "."
@ -356,7 +356,7 @@ Dialog {
tooltip: qsTr("Deploy contract(s) and Package resources files.")
onTriggered: {
var inError = [];
var ethUrl = ProjectModelCode.formatAppUrl(applicationUrlEth.text);
var ethUrl = NetworkDeploymentCode.formatAppUrl(applicationUrlEth.text);
for (var k in ethUrl)
{
if (ethUrl[k].length > 32)
@ -367,7 +367,7 @@ Dialog {
if (contractRedeploy.checked)
deployWarningDialog.open();
else
ProjectModelCode.startDeployProject(false);
NetworkDeploymentCode.startDeployProject(false);
}
}
}

8
mix/qml/LogsPane.qml

@ -6,7 +6,9 @@ import org.ethereum.qml.SortFilterProxyModel 1.0
Rectangle
{
property variant currentStatus;
property variant currentStatus
property int contentXPos: logStyle.generic.layout.dateWidth + logStyle.generic.layout.typeWidth - 70
function clear()
{
logsModel.clear();
@ -117,6 +119,10 @@ Rectangle
return cl;
}
Component.onCompleted:
{
logsPane.contentXPos = logContent.x
}
MouseArea
{

1
mix/qml/MainContent.qml

@ -24,6 +24,7 @@ Rectangle {
property alias webViewVisible: webPreview.visible
property alias webView: webPreview
property alias projectViewVisible: projectList.visible
property alias projectNavigator: projectList
property alias runOnProjectLoad: mainSettings.runOnProjectLoad
property alias rightPane: rightView
property alias codeEditor: codeEditor

1
mix/qml/ProjectList.qml

@ -8,6 +8,7 @@ import "."
Item {
property bool renameMode: false;
property alias sections: sectionRepeater
ProjectFilesStyle {
id: projectFilesStyle

9
mix/qml/ProjectModel.qml

@ -5,6 +5,7 @@ import QtQuick.Controls 1.0
import QtQuick.Dialogs 1.1
import Qt.labs.settings 1.0
import "js/ProjectModel.js" as ProjectModelCode
import "js/NetworkDeployment.js" as NetworkDeploymentCode
Item {
id: projectModel
@ -69,9 +70,9 @@ Item {
function getDocumentIdByName(documentName) { return ProjectModelCode.getDocumentIdByName(documentName); }
function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); }
function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); }
function deployProject() { ProjectModelCode.deployProject(false); }
function registerToUrlHint() { ProjectModelCode.registerToUrlHint(); }
function formatAppUrl() { ProjectModelCode.formatAppUrl(url); }
function deployProject() { NetworkDeploymentCode.deployProject(false); }
function registerToUrlHint() { NetworkDeploymentCode.registerToUrlHint(); }
function formatAppUrl() { NetworkDeploymentCode.formatAppUrl(url); }
Connections {
target: mainApplication
@ -155,7 +156,7 @@ Item {
icon: StandardIcon.Question
standardButtons: StandardButton.Ok | StandardButton.Abort
onAccepted: {
ProjectModelCode.startDeployProject(true);
NetworkDeploymentCode.startDeployProject(true);
}
}

57
mix/qml/StateDialog.qml

@ -14,7 +14,7 @@ Dialog {
modality: Qt.ApplicationModal
width: 630
height: 480
height: 500
title: qsTr("Edit State")
visible: false
@ -22,6 +22,8 @@ Dialog {
property alias isDefault: defaultCheckBox.checked
property alias model: transactionsModel
property alias transactionDialog: transactionDialog
property alias minerComboBox: comboMiner
property alias newAccAction: newAccountAction
property int stateIndex
property var stateTransactions: []
property var stateAccounts: []
@ -45,16 +47,21 @@ Dialog {
accountsModel.clear();
stateAccounts = [];
var miner = 0;
for (var k = 0; k < item.accounts.length; k++)
{
accountsModel.append(item.accounts[k]);
stateAccounts.push(item.accounts[k]);
if (item.miner && item.accounts[k].name === item.miner.name)
miner = k;
}
visible = true;
isDefault = setDefault;
titleField.focus = true;
defaultCheckBox.enabled = !isDefault;
comboMiner.model = stateAccounts;
comboMiner.currentIndex = miner;
forceActiveFocus();
}
@ -75,8 +82,17 @@ Dialog {
}
item.transactions = stateTransactions;
item.accounts = stateAccounts;
for (var k = 0; k < stateAccounts.length; k++)
{
if (stateAccounts[k].name === comboMiner.currentText)
{
item.miner = stateAccounts[k];
break;
}
}
return item;
}
contentItem: Rectangle {
color: stateDialogStyle.generic.backgroundColor
Rectangle {
@ -124,6 +140,7 @@ Dialog {
Button
{
id: newAccountButton
anchors.top: accountsLabel.bottom
anchors.topMargin: 10
iconSource: "qrc:/qml/img/plus.png"
@ -134,10 +151,16 @@ Dialog {
id: newAccountAction
tooltip: qsTr("Add new Account")
onTriggered:
{
add();
}
function add()
{
var account = stateListModel.newAccount("1000000", QEther.Ether);
stateAccounts.push(account);
accountsModel.append(account);
return account;
}
}
}
@ -159,7 +182,7 @@ Dialog {
TableViewColumn {
role: "name"
title: qsTr("Name")
width: 150
width: 230
delegate: Item {
RowLayout
{
@ -180,8 +203,12 @@ Dialog {
alertAlreadyUsed.open();
else
{
if (stateAccounts[styleData.row].name === comboMiner.currentText)
comboMiner.currentIndex = 0;
stateAccounts.splice(styleData.row, 1);
accountsModel.remove(styleData.row);
comboMiner.model = stateAccounts;
comboMiner.update();
}
}
}
@ -190,7 +217,12 @@ Dialog {
anchors.verticalCenter: parent.verticalCenter
onTextChanged: {
if (styleData.row > -1)
stateAccounts[styleData.row].name = text;
{
stateAccounts[styleData.row].name = text
var index = comboMiner.currentIndex;
comboMiner.model = stateAccounts;
comboMiner.currentIndex = index;
}
}
text: {
return styleData.value
@ -226,6 +258,25 @@ Dialog {
Layout.fillWidth: true
}
RowLayout
{
Layout.fillWidth: true
DefaultLabel {
Layout.preferredWidth: 85
text: qsTr("Miner")
}
ComboBox {
id: comboMiner
textRole: "name"
Layout.fillWidth: true
}
}
CommonSeparator
{
Layout.fillWidth: true
}
RowLayout
{
Layout.fillWidth: true

19
mix/qml/StateListModel.qml

@ -21,7 +21,8 @@ Item {
return {
title: s.title,
transactions: s.transactions.map(fromPlainTransactionItem),
accounts: s.accounts.map(fromPlainAccountItem)
accounts: s.accounts.map(fromPlainAccountItem),
miner: s.miner
};
}
@ -37,6 +38,7 @@ Item {
function fromPlainTransactionItem(t) {
if (!t.sender)
t.sender = defaultAccount; //support for old project
var r = {
contractId: t.contractId,
functionId: t.functionId,
@ -59,7 +61,8 @@ Item {
return {
title: s.title,
transactions: s.transactions.map(toPlainTransactionItem),
accounts: s.accounts.map(toPlainAccountItem)
accounts: s.accounts.map(toPlainAccountItem),
miner: s.miner
};
}
@ -81,7 +84,7 @@ Item {
balance: {
value: t.balance.value,
unit: t.balance.unit
}
},
};
}
@ -95,6 +98,7 @@ Item {
gasAuto: t.gasAuto,
gasPrice: { value: t.gasPrice.value, unit: t.gasPrice.unit },
stdContract: t.stdContract,
sender: t.sender,
parameters: {}
};
for (var key in t.parameters)
@ -176,8 +180,9 @@ Item {
function newAccount(_balance, _unit, _secret)
{
if (!_secret)
_secret = clientModel.newAddress();
var name = qsTr("Account") + "-" + _secret.substring(0, 4);
_secret = clientModel.newSecret();
var address = clientModel.address(_secret);
var name = qsTr("Account") + "-" + address.substring(0, 4);
return { name: name, secret: _secret, balance: QEtherHelper.createEther(_balance, _unit) };
}
@ -188,7 +193,9 @@ Item {
accounts: []
};
item.accounts.push(newAccount("1000000", QEther.Ether, defaultAccount));
var account = newAccount("1000000", QEther.Ether, defaultAccount)
item.accounts.push(account);
item.miner = account;
//add all stdc contracts
for (var i = 0; i < contractLibrary.model.count; i++) {

71
mix/qml/StatusPane.qml

@ -187,25 +187,32 @@ Rectangle {
else
width = undefined
}
}
Button
{
anchors.fill: parent
id: toolTip
action: toolTipInfo
text: ""
z: 3;
style:
ButtonStyle {
background:Rectangle {
color: "transparent"
}
Button
{
anchors.fill: parent
id: toolTip
action: toolTipInfo
text: ""
z: 3;
style:
ButtonStyle {
background:Rectangle {
color: "transparent"
}
MouseArea {
anchors.fill: parent
onClicked: {
}
MouseArea {
anchors.fill: parent
onClicked: {
var globalCoord = goToLineBtn.mapToItem(statusContainer, 0, 0);
if (mouseX > globalCoord.x
&& mouseX < globalCoord.x + goToLineBtn.width
&& mouseY > globalCoord.y
&& mouseY < globalCoord.y + goToLineBtn.height)
goToCompilationError.trigger(goToLineBtn);
else
logsContainer.toggle();
}
}
}
}
@ -240,13 +247,13 @@ Rectangle {
background: Rectangle {
color: "transparent"
Image {
Image {
source: "qrc:/qml/img/warningicon.png"
height: 30
width: 30
sourceSize.width: 30
sourceSize.height: 30
anchors.centerIn: parent
sourceSize.height: 30
anchors.centerIn: parent
}
}
}
@ -297,14 +304,17 @@ Rectangle {
{
if (logsContainer.state === "opened")
{
statusContainer.visible = true
logsContainer.state = "closed"
}
else
{
statusContainer.visible = false
logsContainer.state = "opened";
logsContainer.focus = true;
forceActiveFocus();
calCoord();
calCoord()
move()
}
}
@ -317,20 +327,29 @@ Rectangle {
function calCoord()
{
if (!logsContainer.parent.parent)
return
var top = logsContainer;
while (top.parent)
top = top.parent
var coordinates = logsContainer.mapToItem(top, 0, 0);
logsContainer.parent = top;
logsShadow.parent = top;
logsContainer.x = status.x + statusContainer.x - logStyle.generic.layout.dateWidth - logStyle.generic.layout.typeWidth + 70
logsShadow.x = status.x + statusContainer.x - logStyle.generic.layout.dateWidth - logStyle.generic.layout.typeWidth + 70;
top.onWidthChanged.connect(move)
top.onHeightChanged.connect(move)
}
function move()
{
var statusGlobalCoord = status.mapToItem(null, 0, 0);
logsContainer.x = statusGlobalCoord.x - logPane.contentXPos
logsShadow.x = statusGlobalCoord.x - logPane.contentXPos
logsShadow.z = 1
logsContainer.z = 2
if (Qt.platform.os === "osx")
{
logsContainer.y = statusContainer.y;
logsShadow.y = statusContainer.y;
logsContainer.y = statusGlobalCoord.y;
logsShadow.y = statusGlobalCoord.y;
}
}
@ -341,6 +360,10 @@ Rectangle {
LogsPane
{
id: logPane;
onContentXPosChanged:
{
parent.move();
}
}
states: [

30
mix/qml/StepActionImage.qml

@ -3,8 +3,6 @@ import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
Rectangle {
id: buttonActionContainer
property string disableStateImg
@ -15,7 +13,6 @@ Rectangle {
property bool buttonRight
signal clicked
color: "transparent"
width: 35
height: 24
@ -43,8 +40,8 @@ Rectangle {
right: parent.right
top: parent.top
bottom: parent.bottom
bottomMargin:debugImg.pressed? 0 : 1;
topMargin:debugImg.pressed? 1 : 0;
bottomMargin: debugImg.pressed ? 0 : 1;
topMargin: debugImg.pressed ? 1 : 0;
}
color: "#FCFBFC"
radius: 3
@ -65,15 +62,14 @@ Rectangle {
right: parent.right
top: parent.top
bottom: parent.bottom
bottomMargin:debugImg.pressed? 0 : 1;
topMargin:debugImg.pressed? 1 : 0;
bottomMargin: debugImg.pressed ? 0 : 1;
topMargin: debugImg.pressed? 1 : 0;
}
color: "#FCFBFC"
radius: 3
}
}
Rectangle {
id: contentRectangle
width: 25
@ -87,8 +83,8 @@ Rectangle {
right: parent.right
top: parent.top
bottom: parent.bottom
bottomMargin:debugImg.pressed? 0 : 1;
topMargin:debugImg.pressed? 1 : 0;
bottomMargin: debugImg.pressed ? 0 : 1;
topMargin: debugImg.pressed ? 1 : 0;
}
color: "#FCFBFC"
@ -96,7 +92,7 @@ Rectangle {
id: debugImage
source: enabledStateImg
anchors.centerIn: parent
anchors.topMargin: debugImg.pressed? 1 : 0;
anchors.topMargin: debugImg.pressed ? 1 : 0;
fillMode: Image.PreserveAspectFit
width: 15
@ -105,28 +101,24 @@ Rectangle {
}
Button {
anchors.fill: parent
id: debugImg
action: buttonAction
style: Rectangle {
color: "transparent"
style: ButtonStyle {
background: Rectangle {
color: "transparent"
}
}
}
Action {
tooltip: buttonTooltip
id: buttonAction
shortcut: buttonShortcut
onTriggered: {
// contentRectangle.anchors.bottomMargin = 0
// contentRectangle.anchors.topMargin = 1
buttonActionContainer.clicked();
}
}
}
}

351
mix/qml/js/NetworkDeployment.js

@ -0,0 +1,351 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file NetworkDeployment.js
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @author Yann yann@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
Qt.include("TransactionHelper.js")
var jsonRpcRequestId = 1;
function deployProject(force) {
saveAll(); //TODO: ask user
deploymentDialog.open();
}
function startDeployProject(erasePrevious)
{
var date = new Date();
var deploymentId = date.toLocaleString(Qt.locale(), "ddMMyyHHmmsszzz");
if (!erasePrevious)
{
finalizeDeployment(deploymentId, projectModel.deploymentAddresses);
return;
}
var jsonRpcUrl = "http://127.0.0.1:8080";
console.log("Deploying " + deploymentId + " to " + jsonRpcUrl);
deploymentStarted();
var ctrNames = Object.keys(codeModel.contracts);
var ctrAddresses = {};
deployContracts(0, ctrAddresses, ctrNames, function (){
finalizeDeployment(deploymentId, ctrAddresses);
});
}
function deployContracts(ctrIndex, ctrAddresses, ctrNames, callBack)
{
var code = codeModel.contracts[ctrNames[ctrIndex]].codeHex;
var requests = [{
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": deploymentDialog.gasToUse, "code": code } ],
id: 0
}];
rpcCall(requests, function (httpCall, response){
var txt = qsTr("Please wait while " + ctrNames[ctrIndex] + " is published ...")
deploymentStepChanged(txt);
console.log(txt);
ctrAddresses[ctrNames[ctrIndex]] = JSON.parse(response)[0].result
deploymentDialog.waitForTrCountToIncrement(function(status) {
if (status === -1)
{
trCountIncrementTimeOut();
return;
}
ctrIndex++;
if (ctrIndex < ctrNames.length)
deployContracts(ctrIndex, ctrAddresses, ctrNames, callBack);
else
callBack();
});
});
}
function finalizeDeployment(deploymentId, addresses) {
deploymentStepChanged(qsTr("Packaging application ..."));
var deploymentDir = projectPath + deploymentId + "/";
projectModel.deploymentDir = deploymentDir;
fileIo.makeDir(deploymentDir);
for (var i = 0; i < projectListModel.count; i++) {
var doc = projectListModel.get(i);
if (doc.isContract)
continue;
if (doc.isHtml) {
//inject the script to access contract API
//TODO: use a template
var html = fileIo.readFile(doc.path);
var insertAt = html.indexOf("<head>")
if (insertAt < 0)
insertAt = 0;
else
insertAt += 6;
html = html.substr(0, insertAt) +
"<script src=\"deployment.js\"></script>" +
html.substr(insertAt);
fileIo.writeFile(deploymentDir + doc.fileName, html);
}
else
fileIo.copyFile(doc.path, deploymentDir + doc.fileName);
}
//write deployment js
var deploymentJs =
"// Autogenerated by Mix\n" +
"contracts = {};\n";
for (var c in codeModel.contracts) {
var contractAccessor = "contracts[\"" + codeModel.contracts[c].contract.name + "\"]";
deploymentJs += contractAccessor + " = {\n" +
"\tinterface: " + codeModel.contracts[c].contractInterface + ",\n" +
"\taddress: \"" + addresses[c] + "\"\n" +
"};\n" +
contractAccessor + ".contractClass = web3.eth.contract(" + contractAccessor + ".interface);\n" +
contractAccessor + ".contract = new " + contractAccessor + ".contractClass(" + contractAccessor + ".address);\n";
}
fileIo.writeFile(deploymentDir + "deployment.js", deploymentJs);
deploymentAddresses = addresses;
saveProject();
var packageRet = fileIo.makePackage(deploymentDir);
deploymentDialog.packageHash = packageRet[0];
deploymentDialog.packageBase64 = packageRet[1];
deploymentDialog.localPackageUrl = packageRet[2] + "?hash=" + packageRet[0];
var applicationUrlEth = deploymentDialog.applicationUrlEth;
applicationUrlEth = formatAppUrl(applicationUrlEth);
deploymentStepChanged(qsTr("Registering application on the Ethereum network ..."));
checkEthPath(applicationUrlEth, function () {
deploymentComplete();
deployResourcesDialog.text = qsTr("Register Web Application to finalize deployment.");
deployResourcesDialog.open();
});
}
function checkEthPath(dappUrl, callBack)
{
if (dappUrl.length === 1)
registerContentHash(deploymentDialog.eth, callBack); // we directly create a dapp under the root registrar.
else
{
// the first owned reigstrar must have been created to follow the path.
var str = createString(dappUrl[0]);
var requests = [];
requests.push({
//register()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "gas": 150000, "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0x6be16bed" + str.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
var res = JSON.parse(response);
var addr = normalizeAddress(res[0].result);
if (addr.replace(/0+/g, "") === "")
{
var errorTxt = qsTr("Path does not exists " + JSON.stringify(dappUrl) + ". Please register using Registration Dapp. Aborting.");
deploymentError(errorTxt);
console.log(errorTxt);
}
else
{
dappUrl.splice(0, 1);
checkRegistration(dappUrl, addr, callBack);
}
});
}
}
function checkRegistration(dappUrl, addr, callBack)
{
if (dappUrl.length === 1)
registerContentHash(addr, callBack); // We do not create the register for the last part, just registering the content hash.
else
{
var txt = qsTr("Checking " + JSON.stringify(dappUrl) + " ... in registrar " + addr);
deploymentStepChanged(txt);
console.log(txt);
var requests = [];
var registrar = {}
var str = createString(dappUrl[0]);
requests.push({
//getOwner()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "gas" : 2000, "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x893d20e8" } ],
id: jsonRpcRequestId++
});
requests.push({
//register()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x6be16bed" + str.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
var res = JSON.parse(response);
var nextAddr = normalizeAddress(res[1].result);
var errorTxt;
if (res[1].result === "0x")
{
errorTxt = qsTr("Error when creating new owned regsitrar. Please use the regsitration Dapp. Aborting");
deploymentError(errorTxt);
console.log(errorTxt);
}
else if (normalizeAddress(deploymentDialog.currentAccount) !== normalizeAddress(res[0].result))
{
errorTxt = qsTr("You are not the owner of " + dappUrl[0] + ". Aborting");
deploymentError(errorTxt);
console.log(errorTxt);
}
else if (nextAddr.replace(/0+/g, "") !== "")
{
dappUrl.splice(0, 1);
checkRegistration(dappUrl, nextAddr, callBack);
}
else
{
var txt = qsTr("Registering sub domain " + dappUrl[0] + " ...");
console.log(txt);
deploymentStepChanged(txt);
//current registrar is owned => ownedregistrar creation and continue.
requests = [];
requests.push({
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 20000, "code": "0x60056013565b61059e8061001d6000396000f35b33600081905550560060003560e060020a90048063019848921461009a578063449c2090146100af5780635d574e32146100cd5780635fd4b08a146100e1578063618242da146100f65780636be16bed1461010b5780636c4489b414610129578063893d20e8146101585780639607730714610173578063c284bc2a14610187578063e50f599a14610198578063e5811b35146101af578063ec7b9200146101cd57005b6100a560043561031b565b8060005260206000f35b6100ba6004356103a0565b80600160a060020a031660005260206000f35b6100db600435602435610537565b60006000f35b6100ec600435610529565b8060005260206000f35b6101016004356103dd565b8060005260206000f35b6101166004356103bd565b80600160a060020a031660005260206000f35b61013460043561034b565b82600160a060020a031660005281600160a060020a03166020528060405260606000f35b610160610341565b80600160a060020a031660005260206000f35b6101816004356024356102b4565b60006000f35b6101926004356103fd565b60006000f35b6101a96004356024356044356101f2565b60006000f35b6101ba6004356101eb565b80600160a060020a031660005260206000f35b6101d8600435610530565b80600160a060020a031660005260206000f35b6000919050565b600054600160a060020a031633600160a060020a031614610212576102af565b8160026000858152602001908152602001600020819055508061023457610287565b81600160a060020a0316837f680ad70765443c2967675ab0fb91a46350c01c6df59bf9a41ff8a8dd097464ec60006000a3826001600084600160a060020a03168152602001908152602001600020819055505b827f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b505050565b600054600160a060020a031633600160a060020a0316146102d457610317565b806002600084815260200190815260200160002060010181905550817f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b5050565b60006001600083600160a060020a03168152602001908152602001600020549050919050565b6000600054905090565b6000600060006002600085815260200190815260200160002054925060026000858152602001908152602001600020600101549150600260008581526020019081526020016000206002015490509193909250565b600060026000838152602001908152602001600020549050919050565b600060026000838152602001908152602001600020600101549050919050565b600060026000838152602001908152602001600020600201549050919050565b600054600160a060020a031633600160a060020a03161461041d57610526565b80600160006002600085815260200190815260200160002054600160a060020a031681526020019081526020016000205414610458576104d2565b6002600082815260200190815260200160002054600160a060020a0316817f680ad70765443c2967675ab0fb91a46350c01c6df59bf9a41ff8a8dd097464ec60006000a36000600160006002600085815260200190815260200160002054600160a060020a03168152602001908152602001600020819055505b6002600082815260200190815260200160002060008101600090556001810160009055600281016000905550807f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b50565b6000919050565b6000919050565b600054600160a060020a031633600160a060020a0316146105575761059a565b806002600084815260200190815260200160002060020181905550817f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b505056" } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function(httpRequest, response) {
var newCtrAddress = normalizeAddress(JSON.parse(response)[0].result);
requests = [];
var txt = qsTr("Please wait " + dappUrl[0] + " is registering ...");
deploymentStepChanged(txt);
console.log(txt);
deploymentDialog.waitForTrCountToIncrement(function(status) {
if (status === -1)
{
trCountIncrementTimeOut();
return;
}
var crLevel = createString(dappUrl[0]).encodeValueAsString();
requests.push({
//setRegister()
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function(request, response){
dappUrl.splice(0, 1);
checkRegistration(dappUrl, newCtrAddress, callBack);
});
});
});
}
});
}
}
function trCountIncrementTimeOut()
{
var error = qsTr("Something went wrong during the deployment. Please verify the amount of gas for this transaction and check your balance.")
console.log(error);
deploymentError(error);
}
function registerContentHash(registrar, callBack)
{
var txt = qsTr("Finalizing Dapp registration ...");
deploymentStepChanged(txt);
console.log(txt);
var requests = [];
var paramTitle = clientModel.encodeAbiString(projectModel.projectTitle);
requests.push({
//setContent()
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle + deploymentDialog.packageHash } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
callBack();
});
}
function registerToUrlHint()
{
deploymentStepChanged(qsTr("Registering application Resources (" + deploymentDialog.applicationUrlHttp) + ") ...");
var requests = [];
var paramUrlHttp = createString(deploymentDialog.applicationUrlHttp);
requests.push({
//urlHint => suggestUrl
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 30000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
deploymentComplete();
});
}
function normalizeAddress(addr)
{
addr = addr.replace('0x', '');
if (addr.length <= 40)
return addr;
var left = addr.length - 40;
return addr.substring(left);
}
function formatAppUrl(url)
{
if (url.toLowerCase().indexOf("eth://") === 0)
url = url.substring(6);
if (url.toLowerCase().indexOf(projectModel.projectTitle + ".") === 0)
url = url.substring(projectModel.projectTitle.length + 1);
if (url === "")
return [projectModel.projectTitle];
var ret;
if (url.indexOf("/") === -1)
ret = url.split('.').reverse();
else
{
var slash = url.indexOf("/");
var left = url.substring(0, slash);
var leftA = left.split(".");
leftA.reverse();
var right = url.substring(slash + 1);
var rightA = right.split('/');
ret = leftA.concat(rightA);
}
if (ret[0].toLowerCase() === "eth")
ret.splice(0, 1);
ret.push(projectModel.projectTitle);
return ret;
}

345
mix/qml/js/ProjectModel.js

@ -19,8 +19,6 @@
* @date 2015
* Ethereum IDE client.
*/
Qt.include("QEtherHelper.js")
Qt.include("TransactionHelper.js")
var htmlTemplate = "<html>\n<head>\n<script>\n</script>\n</head>\n<body>\n<script>\n</script>\n</body>\n</html>";
var contractTemplate = "contract Contract {\n}\n";
@ -83,7 +81,10 @@ function saveProjectFile()
deploymentDir: projectModel.deploymentDir
};
for (var i = 0; i < projectListModel.count; i++)
projectData.files.push(projectListModel.get(i).fileName);
projectData.files.push({
title: projectListModel.get(i).name,
fileName: projectListModel.get(i).fileName,
});
projectFileSaving(projectData);
var json = JSON.stringify(projectData, null, "\t");
@ -122,7 +123,11 @@ function loadProject(path) {
projectData.files = [];
for(var i = 0; i < projectData.files.length; i++) {
addFile(projectData.files[i]);
var entry = projectData.files[i];
if (typeof(entry) === "string")
addFile(entry); //TODO: remove old project file support
else
addFile(entry.fileName, entry.title);
}
if (mainApplication.trackLastProject)
projectSettings.lastProjectPath = path;
@ -140,7 +145,7 @@ function loadProject(path) {
});
}
function addFile(fileName) {
function addFile(fileName, title) {
var p = projectPath + fileName;
var extension = fileName.substring(fileName.lastIndexOf("."), fileName.length);
var isContract = extension === ".sol";
@ -154,7 +159,7 @@ function addFile(fileName) {
contract: false,
path: p,
fileName: fileName,
name: fileName,
name: title !== undefined ? title : fileName,
documentId: fileName,
syntaxMode: syntaxMode,
isText: isContract || isHtml || isCss || isJs,
@ -344,331 +349,3 @@ function generateFileName(name, extension) {
} while (fileIo.fileExists(filePath));
return fileName
}
var jsonRpcRequestId = 1;
function deployProject(force) {
saveAll(); //TODO: ask user
deploymentDialog.open();
}
function startDeployProject(erasePrevious)
{
var date = new Date();
var deploymentId = date.toLocaleString(Qt.locale(), "ddMMyyHHmmsszzz");
if (!erasePrevious)
{
finalizeDeployment(deploymentId, projectModel.deploymentAddresses);
return;
}
var jsonRpcUrl = "http://127.0.0.1:8080";
console.log("Deploying " + deploymentId + " to " + jsonRpcUrl);
deploymentStarted();
var ctrNames = Object.keys(codeModel.contracts);
var ctrAddresses = {};
deployContracts(0, ctrAddresses, ctrNames, function (){
finalizeDeployment(deploymentId, ctrAddresses);
});
}
function deployContracts(ctrIndex, ctrAddresses, ctrNames, callBack)
{
var code = codeModel.contracts[ctrNames[ctrIndex]].codeHex;
var requests = [{
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": deploymentDialog.gasToUse, "code": code } ],
id: 0
}];
rpcCall(requests, function (httpCall, response){
var txt = qsTr("Please wait while " + ctrNames[ctrIndex] + " is published ...")
deploymentStepChanged(txt);
console.log(txt);
ctrAddresses[ctrNames[ctrIndex]] = JSON.parse(response)[0].result
deploymentDialog.waitForTrCountToIncrement(function(status) {
if (status === -1)
{
trCountIncrementTimeOut();
return;
}
ctrIndex++;
if (ctrIndex < ctrNames.length)
deployContracts(ctrIndex, ctrAddresses, ctrNames, callBack);
else
callBack();
});
});
}
function finalizeDeployment(deploymentId, addresses) {
deploymentStepChanged(qsTr("Packaging application ..."));
var deploymentDir = projectPath + deploymentId + "/";
projectModel.deploymentDir = deploymentDir;
fileIo.makeDir(deploymentDir);
for (var i = 0; i < projectListModel.count; i++) {
var doc = projectListModel.get(i);
if (doc.isContract)
continue;
if (doc.isHtml) {
//inject the script to access contract API
//TODO: use a template
var html = fileIo.readFile(doc.path);
var insertAt = html.indexOf("<head>")
if (insertAt < 0)
insertAt = 0;
else
insertAt += 6;
html = html.substr(0, insertAt) +
"<script src=\"deployment.js\"></script>" +
html.substr(insertAt);
fileIo.writeFile(deploymentDir + doc.fileName, html);
}
else
fileIo.copyFile(doc.path, deploymentDir + doc.fileName);
}
//write deployment js
var deploymentJs =
"// Autogenerated by Mix\n" +
"contracts = {};\n";
for (var c in codeModel.contracts) {
var contractAccessor = "contracts[\"" + codeModel.contracts[c].contract.name + "\"]";
deploymentJs += contractAccessor + " = {\n" +
"\tinterface: " + codeModel.contracts[c].contractInterface + ",\n" +
"\taddress: \"" + addresses[c] + "\"\n" +
"};\n" +
contractAccessor + ".contractClass = web3.eth.contract(" + contractAccessor + ".interface);\n" +
contractAccessor + ".contract = new " + contractAccessor + ".contractClass(" + contractAccessor + ".address);\n";
}
fileIo.writeFile(deploymentDir + "deployment.js", deploymentJs);
deploymentAddresses = addresses;
saveProject();
var packageRet = fileIo.makePackage(deploymentDir);
deploymentDialog.packageHash = packageRet[0];
deploymentDialog.packageBase64 = packageRet[1];
deploymentDialog.localPackageUrl = packageRet[2] + "?hash=" + packageRet[0];
var applicationUrlEth = deploymentDialog.applicationUrlEth;
applicationUrlEth = formatAppUrl(applicationUrlEth);
deploymentStepChanged(qsTr("Registering application on the Ethereum network ..."));
checkEthPath(applicationUrlEth, function () {
deploymentComplete();
deployResourcesDialog.text = qsTr("Register Web Application to finalize deployment.");
deployResourcesDialog.open();
});
}
function checkEthPath(dappUrl, callBack)
{
if (dappUrl.length === 1)
registerContentHash(deploymentDialog.eth, callBack); // we directly create a dapp under the root registrar.
else
{
// the first owned reigstrar must have been created to follow the path.
var str = createString(dappUrl[0]);
var requests = [];
requests.push({
//register()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "gas": 150000, "from": deploymentDialog.currentAccount, "to": '0x' + deploymentDialog.eth, "data": "0x6be16bed" + str.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
var res = JSON.parse(response);
var addr = normalizeAddress(res[0].result);
if (addr.replace(/0+/g, "") === "")
{
var errorTxt = qsTr("Path does not exists " + JSON.stringify(dappUrl) + ". Please register using Registration Dapp. Aborting.");
deploymentError(errorTxt);
console.log(errorTxt);
}
else
{
dappUrl.splice(0, 1);
checkRegistration(dappUrl, addr, callBack);
}
});
}
}
function checkRegistration(dappUrl, addr, callBack)
{
if (dappUrl.length === 1)
registerContentHash(addr, callBack); // We do not create the register for the last part, just registering the content hash.
else
{
var txt = qsTr("Checking " + JSON.stringify(dappUrl) + " ... in registrar " + addr);
deploymentStepChanged(txt);
console.log(txt);
var requests = [];
var registrar = {}
var str = createString(dappUrl[0]);
requests.push({
//getOwner()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "gas" : 2000, "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x893d20e8" } ],
id: jsonRpcRequestId++
});
requests.push({
//register()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "from": deploymentDialog.currentAccount, "to": '0x' + addr, "data": "0x6be16bed" + str.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
var res = JSON.parse(response);
var nextAddr = normalizeAddress(res[1].result);
var errorTxt;
if (res[1].result === "0x")
{
errorTxt = qsTr("Error when creating new owned regsitrar. Please use the regsitration Dapp. Aborting");
deploymentError(errorTxt);
console.log(errorTxt);
}
else if (normalizeAddress(deploymentDialog.currentAccount) !== normalizeAddress(res[0].result))
{
errorTxt = qsTr("You are not the owner of " + dappUrl[0] + ". Aborting");
deploymentError(errorTxt);
console.log(errorTxt);
}
else if (nextAddr.replace(/0+/g, "") !== "")
{
dappUrl.splice(0, 1);
checkRegistration(dappUrl, nextAddr, callBack);
}
else
{
var txt = qsTr("Registering sub domain " + dappUrl[0] + " ...");
console.log(txt);
deploymentStepChanged(txt);
//current registrar is owned => ownedregistrar creation and continue.
requests = [];
requests.push({
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 20000, "code": "0x60056013565b61059e8061001d6000396000f35b33600081905550560060003560e060020a90048063019848921461009a578063449c2090146100af5780635d574e32146100cd5780635fd4b08a146100e1578063618242da146100f65780636be16bed1461010b5780636c4489b414610129578063893d20e8146101585780639607730714610173578063c284bc2a14610187578063e50f599a14610198578063e5811b35146101af578063ec7b9200146101cd57005b6100a560043561031b565b8060005260206000f35b6100ba6004356103a0565b80600160a060020a031660005260206000f35b6100db600435602435610537565b60006000f35b6100ec600435610529565b8060005260206000f35b6101016004356103dd565b8060005260206000f35b6101166004356103bd565b80600160a060020a031660005260206000f35b61013460043561034b565b82600160a060020a031660005281600160a060020a03166020528060405260606000f35b610160610341565b80600160a060020a031660005260206000f35b6101816004356024356102b4565b60006000f35b6101926004356103fd565b60006000f35b6101a96004356024356044356101f2565b60006000f35b6101ba6004356101eb565b80600160a060020a031660005260206000f35b6101d8600435610530565b80600160a060020a031660005260206000f35b6000919050565b600054600160a060020a031633600160a060020a031614610212576102af565b8160026000858152602001908152602001600020819055508061023457610287565b81600160a060020a0316837f680ad70765443c2967675ab0fb91a46350c01c6df59bf9a41ff8a8dd097464ec60006000a3826001600084600160a060020a03168152602001908152602001600020819055505b827f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b505050565b600054600160a060020a031633600160a060020a0316146102d457610317565b806002600084815260200190815260200160002060010181905550817f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b5050565b60006001600083600160a060020a03168152602001908152602001600020549050919050565b6000600054905090565b6000600060006002600085815260200190815260200160002054925060026000858152602001908152602001600020600101549150600260008581526020019081526020016000206002015490509193909250565b600060026000838152602001908152602001600020549050919050565b600060026000838152602001908152602001600020600101549050919050565b600060026000838152602001908152602001600020600201549050919050565b600054600160a060020a031633600160a060020a03161461041d57610526565b80600160006002600085815260200190815260200160002054600160a060020a031681526020019081526020016000205414610458576104d2565b6002600082815260200190815260200160002054600160a060020a0316817f680ad70765443c2967675ab0fb91a46350c01c6df59bf9a41ff8a8dd097464ec60006000a36000600160006002600085815260200190815260200160002054600160a060020a03168152602001908152602001600020819055505b6002600082815260200190815260200160002060008101600090556001810160009055600281016000905550807f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b50565b6000919050565b6000919050565b600054600160a060020a031633600160a060020a0316146105575761059a565b806002600084815260200190815260200160002060020181905550817f18d67da0cd86808336a3aa8912f6ea70c5250f1a98b586d1017ef56fe199d4fc60006000a25b505056" } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function(httpRequest, response) {
var newCtrAddress = normalizeAddress(JSON.parse(response)[0].result);
requests = [];
var txt = qsTr("Please wait " + dappUrl[0] + " is registering ...");
deploymentStepChanged(txt);
console.log(txt);
deploymentDialog.waitForTrCountToIncrement(function(status) {
if (status === -1)
{
trCountIncrementTimeOut();
return;
}
var crLevel = createString(dappUrl[0]).encodeValueAsString();
requests.push({
//setRegister()
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "to": '0x' + addr, "data": "0x96077307" + crLevel + deploymentDialog.pad(newCtrAddress) } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function(request, response){
dappUrl.splice(0, 1);
checkRegistration(dappUrl, newCtrAddress, callBack);
});
});
});
}
});
}
}
function trCountIncrementTimeOut()
{
var error = qsTr("Something went wrong during the deployment. Please verify the amount of gas for this transaction and check your balance.")
console.log(error);
deploymentError(error);
}
function registerContentHash(registrar, callBack)
{
var txt = qsTr("Finalizing Dapp registration ...");
deploymentStepChanged(txt);
console.log(txt);
var requests = [];
var paramTitle = clientModel.encodeAbiString(projectModel.projectTitle);
requests.push({
//setContent()
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "from": deploymentDialog.currentAccount, "gas": 30000, "gasPrice": "10", "to": '0x' + registrar, "data": "0x5d574e32" + paramTitle + deploymentDialog.packageHash } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
callBack();
});
}
function registerToUrlHint()
{
deploymentStepChanged(qsTr("Registering application Resources (" + deploymentDialog.applicationUrlHttp) + ") ...");
var requests = [];
var paramUrlHttp = createString(deploymentDialog.applicationUrlHttp);
requests.push({
//urlHint => suggestUrl
jsonrpc: "2.0",
method: "eth_sendTransaction",
params: [ { "to": '0x' + deploymentDialog.urlHintContract, "gas": 30000, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
deploymentComplete();
});
}
function normalizeAddress(addr)
{
addr = addr.replace('0x', '');
if (addr.length <= 40)
return addr;
var left = addr.length - 40;
return addr.substring(left);
}
function formatAppUrl(url)
{
if (url.toLowerCase().indexOf("eth://") === 0)
url = url.substring(6);
if (url.toLowerCase().indexOf(projectModel.projectTitle + ".") === 0)
url = url.substring(projectModel.projectTitle.length + 1);
if (url === "")
return [projectModel.projectTitle];
var ret;
if (url.indexOf("/") === -1)
ret = url.split('.').reverse();
else
{
var slash = url.indexOf("/");
var left = url.substring(0, slash);
var leftA = left.split(".");
leftA.reverse();
var right = url.substring(slash + 1);
var rightA = right.split('/');
ret = leftA.concat(rightA);
}
if (ret[0].toLowerCase() === "eth")
ret.splice(0, 1);
ret.push(projectModel.projectTitle);
return ret;
}

7
mix/test/qml/TestMain.qml

@ -4,6 +4,8 @@ import org.ethereum.qml.TestService 1.0
import "../../qml"
import "js/TestDebugger.js" as TestDebugger
import "js/TestTutorial.js" as TestTutorial
import "js/TestMiner.js" as TestMiner
import "js/TestProject.js" as TestProject
TestCase
{
@ -49,9 +51,9 @@ TestCase
function editContract(c)
{
mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText(c);
ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S
if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000))
fail("not compiled");
ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S
}
function editHtml(c)
@ -74,5 +76,8 @@ TestCase
function test_dbg_transactionWithParameter() { TestDebugger.test_transactionWithParameter(); }
function test_dbg_constructorParameters() { TestDebugger.test_constructorParameters(); }
function test_dbg_arrayParametersAndStorage() { TestDebugger.test_arrayParametersAndStorage(); }
function test_miner_getDefaultiner() { TestMiner.test_getDefaultMiner(); }
function test_miner_selectMiner() { TestMiner.test_selectMiner(); }
function test_project_contractRename() { TestProject.test_contractRename(); }
}

20
mix/test/qml/js/TestMiner.js

@ -0,0 +1,20 @@
function test_getDefaultMiner()
{
newProject();
var state = mainApplication.projectModel.stateListModel.get(0);
compare(state.miner.secret, "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074");
}
function test_selectMiner()
{
newProject();
mainApplication.projectModel.stateListModel.editState(0);
var account = mainApplication.projectModel.stateDialog.newAccAction.add();
account = mainApplication.projectModel.stateDialog.newAccAction.add();
mainApplication.projectModel.stateDialog.minerComboBox.currentIndex = 2;
ts.waitForRendering(mainApplication.projectModel.stateDialog.minerComboBox, 3000);
mainApplication.projectModel.stateDialog.acceptAndClose();
var state = mainApplication.projectModel.stateListModel.get(0);
compare(state.miner.secret, account.secret);
}

18
mix/test/qml/js/TestProject.js

@ -0,0 +1,18 @@
function test_contractRename()
{
newProject();
tryCompare(mainApplication.mainContent.projectNavigator.sections.itemAt(0).model.get(0), "name", "Contract");
editContract("contract Renamed {}");
if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000))
fail("Error running transaction");
wait(1000);
tryCompare(mainApplication.mainContent.projectNavigator.sections.itemAt(0).model.get(0), "name", "Renamed");
mainApplication.projectModel.stateListModel.editState(0);
mainApplication.projectModel.stateDialog.model.editTransaction(2);
var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog;
tryCompare(transactionDialog, "contractId", "Renamed");
tryCompare(transactionDialog, "functionId", "Renamed");
transactionDialog.close();
mainApplication.projectModel.stateDialog.close();
tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(2), "contract", "Renamed");
}

10
neth/main.cpp

@ -40,7 +40,6 @@
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#endif
#include <libethcore/Ethasher.h>
#include "BuildInfo.h"
#undef KEY_EVENT // from windows.h
@ -332,7 +331,6 @@ int main(int argc, char** argv)
unsigned mining = ~(unsigned)0;
NodeMode mode = NodeMode::Full;
unsigned peers = 5;
int miners = -1;
#if ETH_JSONRPC
int jsonrpc = 8080;
#endif
@ -502,8 +500,6 @@ int main(int argc, char** argv)
g_logVerbosity = atoi(argv[++i]);
else if ((arg == "-x" || arg == "--peers") && i + 1 < argc)
peers = atoi(argv[++i]);
else if ((arg == "-t" || arg == "--miners") && i + 1 < argc)
miners = atoi(argv[++i]);
else if ((arg == "-o" || arg == "--mode") && i + 1 < argc)
{
string m = argv[++i];
@ -553,9 +549,7 @@ int main(int argc, char** argv)
killChain ? WithExisting::Kill : WithExisting::Trust,
mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
netPrefs,
&nodesState,
miners
);
&nodesState);
web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
@ -1253,7 +1247,7 @@ int main(int argc, char** argv)
if (c && c->isMining())
{
mvwprintw(consolewin, qheight - 1, width / 4 - 11, "Mining ON");
dev::eth::MineProgress p = c->miningProgress();
dev::eth::MiningProgress p = c->miningProgress();
auto speed = boost::format("%2% kH/s @ %1%s") % (p.ms / 1000) % (p.ms ? p.hashes / p.ms : 0);
mvwprintw(consolewin, qheight - 2, width / 4 - speed.str().length() - 2, speed.str().c_str());
}

31
test/TestHelper.cpp

@ -62,6 +62,37 @@ void connectClients(Client& c1, Client& c2)
c2.connect("127.0.0.1", c1Port);
#endif
}
void mine(State& s, BlockChain const& _bc)
{
s.commitToMine(_bc);
GenericFarm<ProofOfWork> f;
bool completed = false;
f.onSolutionFound([&](ProofOfWork::Solution sol)
{
return completed = s.completeMine<ProofOfWork>(sol);
});
f.setWork(s.info());
f.startCPU();
while (!completed)
this_thread::sleep_for(chrono::milliseconds(20));
}
void mine(BlockInfo& _bi)
{
GenericFarm<ProofOfWork> f;
bool completed = false;
f.onSolutionFound([&](ProofOfWork::Solution sol)
{
ProofOfWork::assignResult(sol, _bi);
return completed = true;
});
f.setWork(_bi);
f.startCPU();
while (!completed)
this_thread::sleep_for(chrono::milliseconds(20));
}
}
namespace test

5
test/TestHelper.h

@ -36,9 +36,12 @@ namespace eth
{
class Client;
class State;
void mine(Client& c, int numBlocks);
void connectClients(Client& c1, Client& c2);
void mine(State& _s, BlockChain const& _bc);
void mine(BlockInfo& _bi);
}
@ -225,7 +228,5 @@ public:
};
};
}
}

111
test/blockchain.cpp

@ -191,11 +191,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
{
state.sync(bc);
state.sync(bc, txs, gp);
state.commitToMine(bc);
MineInfo info;
ProofOfWork pow;
for (info.completed = false; !info.completed; info = state.mine(&pow)) {}
state.completeMine();
mine(state, bc);
}
catch (Exception const& _e)
{
@ -531,76 +527,55 @@ bytes createBlockRLPFromFields(mObject& _tObj)
return rlpStream.out();
}
void overwriteBlockHeader(BlockInfo& _currentBlockHeader, mObject& _blObj)
void overwriteBlockHeader(BlockInfo& _header, mObject& _blObj)
{
if (_blObj["blockHeader"].get_obj().size() != 14)
auto ho = _blObj["blockHeader"].get_obj();
if (ho.size() != 14)
{
BlockInfo tmp = _currentBlockHeader;
if (_blObj["blockHeader"].get_obj().count("parentHash"))
tmp.parentHash = h256(_blObj["blockHeader"].get_obj()["parentHash"].get_str());
if (_blObj["blockHeader"].get_obj().count("uncleHash"))
tmp.sha3Uncles = h256(_blObj["blockHeader"].get_obj()["uncleHash"].get_str());
if (_blObj["blockHeader"].get_obj().count("coinbase"))
tmp.coinbaseAddress = Address(_blObj["blockHeader"].get_obj()["coinbase"].get_str());
if (_blObj["blockHeader"].get_obj().count("stateRoot"))
tmp.stateRoot = h256(_blObj["blockHeader"].get_obj()["stateRoot"].get_str());
if (_blObj["blockHeader"].get_obj().count("transactionsTrie"))
tmp.transactionsRoot = h256(_blObj["blockHeader"].get_obj()["transactionsTrie"].get_str());
if (_blObj["blockHeader"].get_obj().count("receiptTrie"))
tmp.receiptsRoot = h256(_blObj["blockHeader"].get_obj()["receiptTrie"].get_str());
if (_blObj["blockHeader"].get_obj().count("bloom"))
tmp.logBloom = LogBloom(_blObj["blockHeader"].get_obj()["bloom"].get_str());
if (_blObj["blockHeader"].get_obj().count("difficulty"))
tmp.difficulty = toInt(_blObj["blockHeader"].get_obj()["difficulty"]);
if (_blObj["blockHeader"].get_obj().count("number"))
tmp.number = toInt(_blObj["blockHeader"].get_obj()["number"]);
if (_blObj["blockHeader"].get_obj().count("gasLimit"))
tmp.gasLimit = toInt(_blObj["blockHeader"].get_obj()["gasLimit"]);
if (_blObj["blockHeader"].get_obj().count("gasUsed"))
tmp.gasUsed = toInt(_blObj["blockHeader"].get_obj()["gasUsed"]);
if (_blObj["blockHeader"].get_obj().count("timestamp"))
tmp.timestamp = toInt(_blObj["blockHeader"].get_obj()["timestamp"]);
if (_blObj["blockHeader"].get_obj().count("extraData"))
tmp.extraData = importByteArray(_blObj["blockHeader"].get_obj()["extraData"].get_str());
if (_blObj["blockHeader"].get_obj().count("mixHash"))
tmp.mixHash = h256(_blObj["blockHeader"].get_obj()["mixHash"].get_str());
BlockInfo tmp = _header;
if (ho.count("parentHash"))
tmp.parentHash = h256(ho["parentHash"].get_str());
if (ho.count("uncleHash"))
tmp.sha3Uncles = h256(ho["uncleHash"].get_str());
if (ho.count("coinbase"))
tmp.coinbaseAddress = Address(ho["coinbase"].get_str());
if (ho.count("stateRoot"))
tmp.stateRoot = h256(ho["stateRoot"].get_str());
if (ho.count("transactionsTrie"))
tmp.transactionsRoot = h256(ho["transactionsTrie"].get_str());
if (ho.count("receiptTrie"))
tmp.receiptsRoot = h256(ho["receiptTrie"].get_str());
if (ho.count("bloom"))
tmp.logBloom = LogBloom(ho["bloom"].get_str());
if (ho.count("difficulty"))
tmp.difficulty = toInt(ho["difficulty"]);
if (ho.count("number"))
tmp.number = toInt(ho["number"]);
if (ho.count("gasLimit"))
tmp.gasLimit = toInt(ho["gasLimit"]);
if (ho.count("gasUsed"))
tmp.gasUsed = toInt(ho["gasUsed"]);
if (ho.count("timestamp"))
tmp.timestamp = toInt(ho["timestamp"]);
if (ho.count("extraData"))
tmp.extraData = importByteArray(ho["extraData"].get_str());
if (ho.count("mixHash"))
tmp.mixHash = h256(ho["mixHash"].get_str());
tmp.noteDirty();
// find new valid nonce
if (tmp != _currentBlockHeader)
if (tmp != _header)
{
_currentBlockHeader = tmp;
ProofOfWork pow;
std::pair<MineInfo, Ethash::Proof> ret;
while (!ProofOfWork::verify(_currentBlockHeader))
{
ret = pow.mine(_currentBlockHeader, 1000, true);
Ethash::assignResult(ret.second, _currentBlockHeader);
}
mine(tmp);
_header = tmp;
}
}
else
{
// take the blockheader as is
const bytes c_blockRLP = createBlockRLPFromFields(_blObj["blockHeader"].get_obj());
const bytes c_blockRLP = createBlockRLPFromFields(ho);
const RLP c_bRLP(c_blockRLP);
_currentBlockHeader.populateFromHeader(c_bRLP, IgnoreNonce);
_header.populateFromHeader(c_bRLP, IgnoreNonce);
}
}
@ -631,13 +606,7 @@ BlockInfo constructBlock(mObject& _o)
void updatePoW(BlockInfo& _bi)
{
ProofOfWork pow;
std::pair<MineInfo, Ethash::Proof> ret;
while (!ProofOfWork::verify(_bi))
{
ret = pow.mine(_bi, 10000, true);
Ethash::assignResult(ret.second, _bi);
}
mine(_bi);
_bi.noteDirty();
}

12
test/dagger.cpp

@ -25,7 +25,7 @@
#include "JsonSpiritHeaders.h"
#include <libdevcore/CommonIO.h>
#include <libethcore/ProofOfWork.h>
#include <libethcore/Ethasher.h>
#include <libethcore/EthashAux.h>
#include <boost/test/unit_test.hpp>
#include "TestHelper.h"
@ -63,18 +63,18 @@ BOOST_AUTO_TEST_CASE(basic_test)
unsigned cacheSize(o["cache_size"].get_int());
h256 cacheHash(o["cache_hash"].get_str());
BOOST_REQUIRE_EQUAL(Ethasher::get()->params(header).cache_size, cacheSize);
BOOST_REQUIRE_EQUAL(sha3(bytesConstRef((byte const*)Ethasher::get()->light(header), cacheSize)), cacheHash);
BOOST_REQUIRE_EQUAL(EthashAux::get()->params(header).cache_size, cacheSize);
BOOST_REQUIRE_EQUAL(sha3(bytesConstRef((byte const*)EthashAux::get()->light(header), cacheSize)), cacheHash);
#if TEST_FULL
unsigned fullSize(o["full_size"].get_int());
h256 fullHash(o["full_hash"].get_str());
BOOST_REQUIRE_EQUAL(Ethasher::get()->full(header).size(), fullSize);
BOOST_REQUIRE_EQUAL(sha3(Ethasher::get()->full(header)), fullHash);
BOOST_REQUIRE_EQUAL(EthashAux::get()->full(header).size(), fullSize);
BOOST_REQUIRE_EQUAL(sha3(EthashAux::get()->full(header)), fullHash);
#endif
h256 result(o["result"].get_str());
Ethasher::Result r = Ethasher::eval(header);
Ethash::Result r = EthashAux::eval(header);
BOOST_REQUIRE_EQUAL(r.value, result);
BOOST_REQUIRE_EQUAL(r.mixHash, header.mixHash);
}

11
test/stateOriginal.cpp

@ -25,7 +25,9 @@
#include <secp256k1/secp256k1.h>
#include <libethereum/CanonBlockChain.h>
#include <libethereum/State.h>
#include <libethereum/Farm.h>
#include <libethereum/Defaults.h>
#include "TestHelper.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -67,10 +69,8 @@ BOOST_AUTO_TEST_CASE(Complex)
cout << s;
// Mine to get some ether!
s.commitToMine(bc);
ProofOfWork pow;
while (!s.mine(&pow).completed) {}
s.completeMine();
mine(s, bc);
bc.attemptImport(s.blockData(), stateDB);
cout << bc;
@ -89,8 +89,7 @@ BOOST_AUTO_TEST_CASE(Complex)
// Mine to get some ether and set in stone.
s.commitToMine(bc);
s.commitToMine(bc);
while (!s.mine(&pow).completed) {}
s.completeMine();
mine(s, bc);
bc.attemptImport(s.blockData(), stateDB);
cout << bc;

2
third/MainWin.cpp

@ -465,7 +465,7 @@ void Main::on_urlEdit_returnPressed()
void Main::refreshMining()
{
dev::eth::MineProgress p = ethereum()->miningProgress();
dev::eth::MiningProgress p = ethereum()->miningProgress();
ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining");
}

Loading…
Cancel
Save