Browse Source

Merge pull request #1614 from ethereum/minerefactor

Minerefactor
cl-refactor
Gav Wood 10 years ago
parent
commit
0631eb54b7
  1. 9
      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. 69
      eth/main.cpp
  11. 149
      exp/main.cpp
  12. 2
      libdevcore/Common.cpp
  13. 4
      libdevcore/CommonData.h
  14. 71
      libdevcore/Guards.h
  15. 42
      libdevcore/Worker.cpp
  16. 19
      libdevcore/Worker.h
  17. 47
      libethash-cl/CMakeLists.txt
  18. 86
      libethash-cl/bin2h.cmake
  19. 4014
      libethash-cl/cl.hpp
  20. 334
      libethash-cl/ethash_cl_miner.cpp
  21. 45
      libethash-cl/ethash_cl_miner.h
  22. 460
      libethash-cl/ethash_cl_miner_kernel.cl
  23. 4
      libethash/ethash.h
  24. 6
      libethash/io.c
  25. 9
      libethcore/BlockInfo.cpp
  26. 1
      libethcore/BlockInfo.h
  27. 5
      libethcore/Common.cpp
  28. 33
      libethcore/Common.h
  29. 264
      libethcore/Ethash.cpp
  30. 134
      libethcore/Ethash.h
  31. 214
      libethcore/EthashAux.cpp
  32. 67
      libethcore/EthashAux.h
  33. 220
      libethcore/Ethasher.cpp
  34. 109
      libethcore/Ethasher.h
  35. 0
      libethcore/Miner.cpp
  36. 169
      libethcore/Miner.h
  37. 1
      libethcore/Params.cpp
  38. 1
      libethcore/Params.h
  39. 204
      libethcore/ProofOfWork.cpp
  40. 155
      libethcore/ProofOfWork.h
  41. 32
      libethereum/BlockChain.cpp
  42. 5
      libethereum/BlockChain.h
  43. 13
      libethereum/BlockQueue.cpp
  44. 6
      libethereum/BlockQueue.h
  45. 384
      libethereum/Client.cpp
  46. 111
      libethereum/Client.h
  47. 4
      libethereum/ClientBase.cpp
  48. 45
      libethereum/ClientBase.h
  49. 0
      libethereum/Farm.cpp
  50. 194
      libethereum/Farm.h
  51. 13
      libethereum/Interface.h
  52. 96
      libethereum/Miner.cpp
  53. 178
      libethereum/Miner.h
  54. 14
      libethereum/State.cpp
  55. 31
      libethereum/State.h
  56. 6
      libethereum/TransactionQueue.cpp
  57. 18
      libethereum/TransactionQueue.h
  58. 2
      libethereumx/Ethereum.h
  59. 7
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  60. 4
      libwebthree/WebThree.cpp
  61. 3
      libwebthree/WebThree.h
  62. 2
      mix/ContractCallDataEncoder.cpp
  63. 33
      mix/MixClient.cpp
  64. 8
      mix/MixClient.h
  65. 10
      neth/main.cpp
  66. 31
      test/TestHelper.cpp
  67. 5
      test/TestHelper.h
  68. 111
      test/blockchain.cpp
  69. 12
      test/dagger.cpp
  70. 11
      test/stateOriginal.cpp
  71. 2
      third/MainWin.cpp

9
CMakeLists.txt

@ -243,6 +243,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)

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

69
eth/main.cpp

@ -32,6 +32,8 @@
#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>
@ -44,7 +46,6 @@
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#endif
#include <libethcore/Ethasher.h>
#include "BuildInfo.h"
using namespace std;
using namespace dev;
@ -111,6 +112,7 @@ void help()
<< " -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
<< " -C,--check-pow <headerHash> <seedHash> <difficulty> <nonce> Check PoW credentials for validity." << endl
<< " -d,--db-path <path> Load database from path (default: ~/.ethereum " << endl
<< " <APPDATA>/Etherum or Library/Application Support/Ethereum)." << endl
<< " -D,--create-dag <this/next/number> Create the DAG in preparation for mining on given block and exit." << endl
@ -126,13 +128,16 @@ void help()
#if ETH_JSONRPC
<< " -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
<< " -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
<< " -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
<< " -n,-u,--upnp <on/off> Use upnp for NAT (default: on)." << 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
<< " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl
@ -140,7 +145,6 @@ void help()
<< " -r,--remote <host> Connect to remote host (default: none)." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl
<< " -S,--temporary-secret <secretkeyhex> Set the secret key for use with send command, for this session only." << endl
<< " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << 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
@ -208,7 +212,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);
}
@ -269,7 +273,6 @@ int main(int argc, char** argv)
/// Mining params
unsigned mining = ~(unsigned)0;
int miners = -1;
bool forceMining = false;
KeyPair sigKey = KeyPair::create();
Secret sessionSecret;
@ -337,7 +340,7 @@ 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)
{
string m = argv[++i];
if (isTrue(m))
@ -361,7 +364,7 @@ int main(int argc, char** argv)
{
coinbase = h160(fromHex(argv[++i], WhenError::Throw));
}
catch (BadHexCharacter& _e)
catch (BadHexCharacter&)
{
cerr << "Bad hex in " << arg << " option: " << argv[i] << endl;
return -1;
@ -400,6 +403,43 @@ int main(int argc, char** argv)
return -1;
}
}
else if ((arg == "-C" || 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 == "-B" || arg == "--block-fees") && i + 1 < argc)
{
try
@ -478,8 +518,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];
@ -493,15 +531,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")
@ -541,9 +576,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));

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;

47
libethash-cl/CMakeLists.txt

@ -0,0 +1,47 @@
cmake_minimum_required(VERSION 2.8)
set(LIBRARY ethash-cl)
#set(CMAKE_BUILD_TYPE Release)
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)
if (NOT MSVC)
# Initialize CXXFLAGS for c++11
set(CMAKE_CXX_FLAGS "-Wall -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O4 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
# Compiler-specific C++11 activation.
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
execute_process(
COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
if (NOT (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7))
message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.7 or greater.")
endif ()
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
else ()
message(FATAL_ERROR "Your C++ compiler does not support C++11.")
endif ()
endif()
set(OpenCL_FOUND TRUE)
set(OpenCL_INCLUDE_DIRS /usr/include/CL)
set(OpenCL_LIBRARIES -lOpenCL)
if (NOT OpenCL_FOUND)
find_package(OpenCL)
endif()
if (OpenCL_FOUND)
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -Werror -pedantic -fPIC ${CMAKE_CXX_FLAGS}")
include_directories(${OpenCL_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
include_directories(..)
add_library(${LIBRARY} ethash_cl_miner.cpp ethash_cl_miner.h cl.hpp)
TARGET_LINK_LIBRARIES(${LIBRARY} ${OpenCL_LIBRARIES} ethash)
endif()

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

334
libethash-cl/ethash_cl_miner.cpp

@ -0,0 +1,334 @@
/*
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()
{
}
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)
{
// 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
unsigned device_num = 0;
cl::Device& device = devices[device_num];
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
}

45
libethash-cl/ethash_cl_miner.h

@ -0,0 +1,45 @@
#pragma once
#define __CL_ENABLE_EXCEPTIONS
#define CL_USE_DEPRECATED_OPENCL_2_0_APIS
#include "cl.hpp"
#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);
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
};

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>;
}
}

264
libethcore/Ethash.cpp

@ -0,0 +1,264 @@
/*
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 <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
#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);
}
}
#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;
};
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);
}
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();
}
#endif
}
}

134
libethcore/Ethash.h

@ -0,0 +1,134 @@
/*
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(); }
protected:
void kickOff() override
{
stopWorking();
startWorking();
}
void pause() override { stopWorking(); }
private:
void workLoop() override;
};
#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; }
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
};
#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

169
libethcore/Miner.h

@ -0,0 +1,169 @@
/*
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.
};
struct MineInfo: public MiningProgress {};
inline std::ostream& operator<<(std::ostream& _out, MiningProgress _p)
{
_out << (_p.hashes * 1000 / _p.ms) << "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() { return m_hashCount; }
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.
};
}

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;

0
libethereum/Farm.cpp

194
libethereum/Farm.h

@ -0,0 +1,194 @@
/*
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;
}
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.

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();

33
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;
@ -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();
}
}

8
mix/MixClient.h

@ -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

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