Browse Source

Refactor just about everything important in the core.

TODO: make compile :-)
cl-refactor
Gav Wood 10 years ago
parent
commit
c1045d4711
  1. 2
      libethcore/Common.cpp
  2. 37
      libethcore/Common.h
  3. 241
      libethcore/Ethash.cpp
  4. 128
      libethcore/Ethash.h
  5. 144
      libethcore/EthashAux.cpp
  6. 63
      libethcore/EthashAux.h
  7. 113
      libethcore/Ethasher.h
  8. 12
      libethcore/Miner.cpp
  9. 38
      libethcore/Miner.h
  10. 220
      libethcore/ProofOfWork.cpp
  11. 109
      libethcore/ProofOfWork.h
  12. 29
      libethereum/BlockChain.cpp
  13. 5
      libethereum/BlockChain.h
  14. 1
      libethereum/BlockQueue.cpp
  15. 3
      libethereum/BlockQueue.h
  16. 253
      libethereum/Client.cpp
  17. 67
      libethereum/Client.h
  18. 2
      libethereum/ClientBase.h
  19. 12
      libethereum/Farm.cpp
  20. 41
      libethereum/Farm.h
  21. 14
      libethereum/State.cpp
  22. 31
      libethereum/State.h
  23. 1
      libethereum/TransactionQueue.cpp
  24. 6
      libethereum/TransactionQueue.h

2
libethcore/Common.cpp

@ -22,7 +22,6 @@
#include "Common.h" #include "Common.h"
#include <random> #include <random>
#include <libdevcrypto/SHA3.h> #include <libdevcrypto/SHA3.h>
#include "Ethasher.h"
#include "Exceptions.h" #include "Exceptions.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -33,7 +32,6 @@ namespace dev
namespace eth namespace eth
{ {
const unsigned c_ethashVersion = c_ethashRevision;
const unsigned c_protocolVersion = 60; const unsigned c_protocolVersion = 60;
const unsigned c_minorProtocolVersion = 0; const unsigned c_minorProtocolVersion = 0;
const unsigned c_databaseBaseVersion = 9; const unsigned c_databaseBaseVersion = 9;

37
libethcore/Common.h

@ -96,5 +96,42 @@ enum class ImportResult
BadChain BadChain
}; };
class Signal;
class Handler
{
public:
Handler() = default;
Handler(Handler const&) = delete;
~Handler() { reset(); }
Handler& operator=(Handler const& _h) = delete;
void reset();
private:
Handler(unsigned _i, Signal* _s): m_i(_i), m_s(_s) {}
unsigned m_i = 0;
Signal* m_s = nullptr;
};
/// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight.
class Signal
{
public:
using Callback = std::function<void()>;
using Callbacks = std::vector<ReadyCallback>;
Handler add(Callback const& _h) { auto n = m_onReady.size() ? m_onReady.rbegin()->first + 1 : 0; m_onReady[n] = _h; return Handler(n, this); }
void operator()() { for (auto const& f: m_fire) f.second(); }
private:
std::map<unsigned, Callbacks> m_fire;
};
inline void Handler::reset() { if (m_s) m_s->m_fire->erase(m_i); m_s = nullptr; }
} }
} }

241
libethcore/Ethash.cpp

@ -0,0 +1,241 @@
/*
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 <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/FileSystem.h>
#include <libdevcore/Common.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;
std::string Ethash::name()
{
return "Ethash";
}
unsigned Ethash::revision()
{
return ETHASH_REVISION;
}
void Ethash::prep(BlockInfo const& _header)
{
if (_header.number % ETHASH_EPOCH_LENGTH == 1)
EthashAux::full(bi);
}
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
h256 boundary = u256((bigint(1) << 256) / _header.difficulty);
auto result = eval(_header);
bool slow = result.value <= 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:" << 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) + *reinterpret_cast<unsigned*>(m_last.data()) + std::hash<decltype(tid)>()(tid)));
uint64_t tryNonce = Nonce::random(s_eng);
ethash_return_value ethashReturn;
auto p = Ethash::params(m_work.seedHash);
void const* dagPointer = Ethash::full(m_work.headerHash).data();
uint8_t const* headerHashPointer = m_work.headerHash.data();
h256 boundary = m_work.boundary();
unsigned hashCount = 0;
for (; !shouldStop(); tryNonce++, hashCount++)
{
ethash_compute_full(&ethashReturn, dagPointer, &p, headerHashPointer, tryNonce);
h256 value = h256(ethashReturn.result, h256::ConstructFromPointer);
if (value <= boundary && submitProof(Solution{value, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)}))
break;
}
}
#if ETH_ETHASHCL || !ETH_TRUE
namespace dev { namespace eth {
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.";
m_aborted = m_abort = false;
}
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
{
// cdebug << "Found nonces: " << vector<uint64_t>(_nonces, _nonces + _count);
for (uint32_t i = 0; i < _count; ++i)
{
if (m_owner->found(_nonces[i]))
{
m_aborted = true;
return true;
}
}
return false;
}
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;
uint64_t m_total;
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))
{
}
void Ethash::GPUMiner::report(uint64_t _nonce)
{
Nonce n = (Nonce)(u64)_nonce;
Result r = Ethash::eval(m_work.seedHash, m_work.headerHash, n);
if (r.value < m_work.boundary)
return submitProof(Solution{n, r.mixHash});
return false;
}
void Ethash::GPUMiner::kickOff(WorkPackage const& _work)
{
if (!m_miner || m_minerSeed != _work.seedHash)
{
if (m_miner)
m_hook->abort();
m_miner.reset(new ethash_cl_miner);
auto p = Ethash::params(_work.seedHash);
auto cb = [&](void* d) { EthashAux::readFull(_work.seedHash, bytesRef((byte*)d, p.full_size)); };
m_miner->init(p, cb, 32);
}
if (m_lastWork.headerHash != _work.headerHash)
{
m_hook->abort();
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192);
m_miner->search(_work.headerHash, upper64OfBoundary, *m_hook);
}
m_work = _work;
}
void Ethash::GPUMiner::pause()
{
m_hook->abort();
}
#endif
}
}

128
libethcore/Ethash.h

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

144
libethcore/Ethasher.cpp → libethcore/EthashAux.cpp

@ -14,11 +14,13 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file Ethasher.cpp /** @file EthashAux.cpp
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>
* @date 2014 * @date 2014
*/ */
#include "EthashAux.h"
#include <boost/detail/endian.hpp> #include <boost/detail/endian.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <chrono> #include <chrono>
@ -33,7 +35,6 @@
#include <libdevcrypto/FileSystem.h> #include <libdevcrypto/FileSystem.h>
#include <libethcore/Params.h> #include <libethcore/Params.h>
#include "BlockInfo.h" #include "BlockInfo.h"
#include "Ethasher.h"
using namespace std; using namespace std;
using namespace chrono; using namespace chrono;
using namespace dev; using namespace dev;
@ -41,15 +42,50 @@ using namespace eth;
#define ETH_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} #define ETH_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {}
Ethasher* dev::eth::Ethasher::s_this = nullptr; EthashAux* dev::eth::EthashAux::s_this = nullptr;
Ethasher::~Ethasher() EthashAux::~EthashAux()
{ {
while (!m_lights.empty()) while (!m_lights.empty())
killCache(m_lights.begin()->first); killCache(m_lights.begin()->first);
} }
void Ethasher::killCache(h256 const& _s) 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;
}
ethash_params EthashAux::params(h256 const& _seedHash)
{
RecursiveGuard l(get()->x_this);
unsigned epoch = 0;
try
{
epoch = get()->m_seedHashes.at(_seedHash);
}
catch (...)
{
for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = h256(h)) {}
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());
}
get()->m_seedHashes[_seedHash] = epoch;
}
return params(epoch * ETHASH_EPOCH_LENGTH);
}
void EthashAux::killCache(h256 const& _s)
{ {
RecursiveGuard l(x_this); RecursiveGuard l(x_this);
if (m_lights.count(_s)) if (m_lights.count(_s))
@ -59,12 +95,12 @@ void Ethasher::killCache(h256 const& _s)
} }
} }
void const* Ethasher::light(BlockInfo const& _header) void const* EthashAux::light(BlockInfo const& _header)
{ {
return light(_header.seedHash()); return light(_header.seedHash());
} }
void const* Ethasher::light(h256 const& _seedHash) void const* EthashAux::light(h256 const& _seedHash)
{ {
RecursiveGuard l(x_this); RecursiveGuard l(x_this);
if (!m_lights.count(_header.seedHash())) if (!m_lights.count(_header.seedHash()))
@ -75,12 +111,12 @@ void const* Ethasher::light(h256 const& _seedHash)
return m_lights[_seedHash]; return m_lights[_seedHash];
} }
bytesConstRef Ethasher::full(BlockInfo const& _header, bytesRef _dest) bytesConstRef EthashAux::full(BlockInfo const& _header, bytesRef _dest)
{ {
return full(_header.seedHash(), _dest); return full(_header.seedHash(), _dest);
} }
bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest) bytesConstRef EthashAux::full(h256 const& _seedHash, bytesRef _dest)
{ {
RecursiveGuard l(x_this); RecursiveGuard l(x_this);
if (m_fulls.count(_seedHash) && _dest) if (m_fulls.count(_seedHash) && _dest)
@ -102,9 +138,9 @@ bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest)
boost::filesystem::create_directories(getDataDir("ethash")); boost::filesystem::create_directories(getDataDir("ethash"));
} catch (...) {} } catch (...) {}
auto info = rlpList(c_ethashRevision, _seedHash); auto info = rlpList(revision(), _seedHash);
std::string oldMemoFile = getDataDir("ethash") + "/full"; std::string oldMemoFile = getDataDir("ethash") + "/full";
std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_seedHash.ref().cropped(0, 8)); 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) if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info)
{ {
// memofile valid - rename. // memofile valid - rename.
@ -136,91 +172,19 @@ bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest)
return m_fulls[_seedHash]; return m_fulls[_seedHash];
} }
ethash_params Ethasher::params(BlockInfo const& _header) Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce)
{
return params((unsigned)_header.number);
}
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;
}
ethash_params Ethasher::params(h256 const& _seedHash)
{
unsigned epoch = 0;
try
{
epoch = m_seedHashes.at(_seedHash);
}
catch (...)
{
for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = h256(h)) {}
if (epoch == 2048)
{
std::ostringstream error;
error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (c_ethashEpochLength * 2048);
throw std::invalid_argument(error.str());
}
m_seedHashes[_seedHash] = epoch;
}
return params(epoch * c_ethashEpochLength);
}
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)
{ {
return eval(_header.seedHash(), _header.headerHash(WithoutNonce), _nonce); return eval(_header.seedHash(), _header.headerHash(WithoutNonce), _nonce);
} }
Ethasher::Result Ethasher::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce)
{ {
auto p = Ethasher::params(_header); auto p = EthashAux::params(_header);
ethash_return_value r; ethash_return_value r;
if (Ethasher::get()->m_fulls.count(_seedHash)) if (EthashAux::get()->m_fulls.count(_seedHash))
ethash_compute_full(&r, Ethasher::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce); ethash_compute_full(&r, EthashAux::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce);
else else
ethash_compute_light(&r, Ethasher::get()->light(_seedHash), &p, _headerHash.data(), (uint64_t)(u64)_nonce); ethash_compute_light(&r, EthashAux::get()->light(_seedHash), &p, _headerHash.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); // cdebug << "EthashAux::eval sha3(cache):" << sha3(EthashAux::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)}; return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)};
} }

63
libethcore/EthashAux.h

@ -0,0 +1,63 @@
/*
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
{
EthashAux() {}
~EthashAux();
static EthashAux* get() { if (!s_this) s_this = new EthashAux(); return s_this; }
using LightType = void const*;
using FullType = void const*;
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& _header);
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:
void killCache(h256 const& _s);
static Ethash* s_this;
RecursiveMutex x_this;
std::map<h256, LightType> m_lights;
std::map<h256, bytesRef> m_fulls;
std::map<h256, unsigned> m_seedHashes;
};
}
}

113
libethcore/Ethasher.h

@ -1,113 +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);
LightType light(h256 const& _header);
bytesConstRef full(BlockInfo const& _header, bytesRef _dest = bytesRef());
bytesConstRef full(h256 const& _header, bytesRef _dest = bytesRef());
static ethash_params params(BlockInfo const& _header);
static ethash_params params(h256 const& _seedHash);
static ethash_params params(unsigned _n);
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 Result eval(h256 const& _seedHash, h256 const& _headerHash, 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;
std::map<h256, unsigned> m_seedHashes;
};
}
}

12
libethcore/Miner.cpp

@ -1,12 +0,0 @@
#include "Miner.h"
Miner::Miner()
{
}
Miner::~Miner()
{
}

38
libethcore/Miner.h

@ -15,9 +15,8 @@
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file Miner.h /** @file Miner.h
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>
* @date 2014 * @date 2015
*/ */
#pragma once #pragma once
@ -28,7 +27,6 @@
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Worker.h> #include <libdevcore/Worker.h>
#include <libethcore/Common.h> #include <libethcore/Common.h>
#include "State.h"
namespace dev namespace dev
{ {
@ -36,15 +34,17 @@ namespace dev
namespace eth namespace eth
{ {
struct WorkPackage struct MineInfo
{ {
h256 boundary; MineInfo() = default;
h256 headerHash; ///< When h256() means "pause until notified a new work package is available". MineInfo(bool _completed): completed(_completed) {}
h256 seedHash; 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;
}; };
static const WorkPackage NullWorkPackage;
/** /**
* @brief Describes the progress of a mining operation. * @brief Describes the progress of a mining operation.
*/ */
@ -58,30 +58,40 @@ struct MiningProgress
unsigned ms = 0; ///< Total number of milliseconds of mining thus far. unsigned ms = 0; ///< Total number of milliseconds of mining thus far.
}; };
template <class PoW> class Miner;
/** /**
* @brief Class for hosting one or more Miners. * @brief Class for hosting one or more Miners.
* @warning Must be implemented in a threadsafe manner since it will be called from multiple * @warning Must be implemented in a threadsafe manner since it will be called from multiple
* miner threads. * miner threads.
*/ */
class FarmFace template <class PoW> class FarmFace
{ {
public: public:
using WorkPackage = typename PoW::WorkPackage;
using Solution = typename PoW::Solution;
using Miner = Miner<PoW>;
/** /**
* @brief Called from a Miner to note a WorkPackage has a solution. * @brief Called from a Miner to note a WorkPackage has a solution.
* @param _p The solution. * @param _p The solution.
* @param _wp The WorkPackage that the Solution is for. * @param _wp The WorkPackage that the Solution is for.
* @param _finder The miner that found it.
* @return true iff the solution was good (implying that mining should be . * @return true iff the solution was good (implying that mining should be .
*/ */
virtual bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp) = 0; virtual bool submitProof(Solution const& _p, WorkPackage const& _wp, Miner* _finder) = 0;
}; };
/** /**
* @brief A miner - a member and adoptee of the Farm. * @brief A miner - a member and adoptee of the Farm.
*/ */
class Miner template <class PoW> class Miner
{ {
public: public:
using ConstructionInfo = std::pair<FarmFace*, unsigned>; using ConstructionInfo = std::pair<FarmFace<PoW>*, unsigned>;
using WorkPackage = typename PoW::WorkPackage;
using Solution = typename PoW::Solution;
using FarmFace = FarmFace<PoW>;
Miner(ConstructionInfo const& _ci): Miner(ConstructionInfo const& _ci):
m_farm(_ci.first), m_farm(_ci.first),
@ -124,7 +134,7 @@ protected:
* @param _s The solution. * @param _s The solution.
* @return true if the solution was correct and that the miner should pause. * @return true if the solution was correct and that the miner should pause.
*/ */
bool submitProof(ProofOfWork::Solution const& _s) bool submitProof(Solution const& _s)
{ {
if (m_farm) if (m_farm)
{ {

220
libethcore/ProofOfWork.cpp

@ -19,224 +19,6 @@
* @date 2014 * @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 || !ETH_TRUE
#include <libethash-cl/ethash_cl_miner.h>
#endif
#include "BlockInfo.h"
#include "Ethasher.h"
#include "ProofOfWork.h" #include "ProofOfWork.h"
using namespace std; using namespace std;
using namespace std::chrono; using namespace dev;
namespace dev
{
namespace eth
{
void Ethash::CPUMiner::workLoop()
{
Solution solution;
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;
};
Ethasher::Miner m(_header);
std::pair<MineInfo, Solution> 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 = _header.boundary();
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 :)
Solution result;
unsigned hashCount = 0;
for (; !shouldStop(); tryNonce++, hashCount++)
{
h256 val(m.mine(tryNonce));
best = std::min<double>(best, log2((double)(u256)val));
if (val <= boundary)
{
if (submitProof(solution))
return;
}
}
ret.first.hashes = hashCount;
ret.first.best = best;
ret.second = result;
return;
}
#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);
};
*/
namespace dev { namespace eth {
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.";
m_aborted = m_abort = false;
}
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
{
// cdebug << "Found nonces: " << vector<uint64_t>(_nonces, _nonces + _count);
for (uint32_t i = 0; i < _count; ++i)
{
if (m_owner->found(_nonces[i]))
{
m_aborted = true;
return true;
}
}
return false;
}
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;
uint64_t m_total;
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))
{
}
void Ethash::GPUMiner::report(uint64_t _nonce)
{
Nonce n = (Nonce)(u64)_nonce;
Ethasher::Result r = Ethasher::eval(m_work.seedHash, m_work.headerHash, n);
if (r.value < m_work.boundary)
return submitProof(Solution{n, r.mixHash});
return false;
}
void Ethash::GPUMiner::kickOff(WorkPackage const& _work)
{
if (!m_miner || m_minerSeed != _work.seedHash)
{
if (m_miner)
m_hook->abort();
m_miner.reset(new ethash_cl_miner);
auto p = Ethasher::params(_work.seedHash);
auto cb = [&](void* d) { Ethasher::get()->readFull(_work.seedHash, bytesRef((byte*)d, p.full_size)); };
m_miner->init(p, cb, 32);
}
if (m_lastWork.headerHash != _work.headerHash)
{
m_hook->abort();
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192);
m_miner->search(_work.headerHash, upper64OfBoundary, *m_hook);
}
m_work = _work;
}
void Ethash::GPUMiner::pause()
{
m_hook->abort();
}
#endif
}
}

109
libethcore/ProofOfWork.h

@ -18,112 +18,29 @@
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>
* @date 2014 * @date 2014
* *
* ProofOfWork algorithm. Or not. * Determines the PoW algorithm.
*/ */
#pragma once #pragma once
#include <chrono> #include "Ethash.h"
#include <thread>
#include <cstdint>
#include <libdevcrypto/SHA3.h>
#include "Common.h"
#include "BlockInfo.h"
#include "Miner.h"
#define FAKE_DAGGER 1
class ethash_cl_miner;
namespace dev namespace dev
{ {
namespace eth namespace eth
{ {
struct MineInfo /**
{ * The proof of work algorithm base type.
MineInfo() = default; *
MineInfo(bool _completed): completed(_completed) {} * Must implement a basic templated interface, including:
void combine(MineInfo const& _m) { requirement = std::max(requirement, _m.requirement); best = std::min(best, _m.best); hashes += _m.hashes; completed = completed || _m.completed; } * typename Result
double requirement = 0; * typename Solution
double best = 1e99; * typename CPUMiner
unsigned hashes = 0; * typename GPUMiner
bool completed = false; * void assignResult(BlockInfo&, Result)
}; * and a few others. TODO
*/
class Ethash
{
public:
struct Solution
{
Nonce nonce;
h256 mixHash;
};
static bool verify(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 thread::hardware_concurrency(); }
protected:
void kickOff(WorkPackage const& _work) override
{
stopWorking();
m_work = _work;
startWorking();
}
void pause() override { stopWorking(); }
private:
void workLoop() override;
WorkPackage m_work;
MineInfo m_info;
};
#if ETH_ETHASHCL || !ETH_TRUE
class EthashCLHook;
class GPUMiner: public Miner
{
friend class EthashCLHook;
public:
GPUMiner(ConstructionInfo const& _ci);
static unsigned instances() { return 1; }
protected:
void kickOff(WorkPackage const& _work) override;
void pause() override;
private:
void report(uint64_t _nonce);
std::unique_ptr<EthashCLHook> m_hook;
std::unique_ptr<ethash_cl_miner> m_miner;
h256 m_minerSeed;
WorkPackage m_lastWork; ///< Work loaded into m_miner.
MineInfo m_info;
};
#else
using GPUMiner = CPUMiner;
#endif
};
using ProofOfWork = Ethash; using ProofOfWork = Ethash;
} }

29
libethereum/BlockChain.cpp

@ -231,8 +231,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); bytes b = block(queryExtras<BlockHash, ExtraBlockHash>(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value);
BlockInfo bi(b); BlockInfo bi(b);
if (bi.number % c_ethashEpochLength == 1) ProofOfWork::prep(bi);
Ethasher::get()->full(bi);
if (bi.parentHash != lastHash) if (bi.parentHash != lastHash)
{ {
@ -307,14 +306,8 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
try try
{ {
auto r = import(block, _stateDB); auto r = import(block, _stateDB);
bool isOld = true; fresh += r.first;
for (auto const& h: r.first) dead += r.second;
if (h == r.second)
isOld = false;
else if (isOld)
dead.push_back(h);
else
fresh.push_back(h);
} }
catch (UnknownParent) catch (UnknownParent)
{ {
@ -334,7 +327,7 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
return make_tuple(fresh, dead, _bq.doneDrain(badBlocks)); 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 try
{ {
@ -347,7 +340,7 @@ pair<h256s, h256> BlockChain::attemptImport(bytes const& _block, OverlayDB const
} }
} }
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. //@tidy This is a behemoth of a method - could do to be split into a few smaller ones.
@ -626,7 +619,17 @@ pair<h256s, h256> BlockChain::import(bytes const& _block, OverlayDB const& _db,
cnote << "checkBest:" << checkBest; cnote << "checkBest:" << checkBest;
#endif #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) 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 BlocksHash = std::map<h256, bytes>;
using TransactionHashes = h256s; using TransactionHashes = h256s;
using UncleHashes = h256s; using UncleHashes = h256s;
using ImportRoute = std::pair<h256s, h256s,>;
enum { enum {
ExtraDetails = 0, ExtraDetails = 0,
@ -108,11 +109,11 @@ public:
/// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB. /// 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. /// @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 /// Import block into disk-backed DB
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain. /// @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). /// Returns true if the given block is known (though not necessarily a part of the canon chain).
bool isKnown(h256 const& _hash) const; bool isKnown(h256 const& _hash) const;

1
libethereum/BlockQueue.cpp

@ -102,6 +102,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc)
m_readySet.insert(h); m_readySet.insert(h);
noteReadyWithoutWriteGuard(h); noteReadyWithoutWriteGuard(h);
m_onReady();
return ImportResult::Success; return ImportResult::Success;
} }
} }

3
libethereum/BlockQueue.h

@ -83,6 +83,8 @@ public:
/// Get some infomration on the current status. /// 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()}; } 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: private:
void noteReadyWithoutWriteGuard(h256 _b); void noteReadyWithoutWriteGuard(h256 _b);
void notePresentWithoutWriteGuard(bytesConstRef _block); void notePresentWithoutWriteGuard(bytesConstRef _block);
@ -95,6 +97,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<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::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. 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.
}; };
} }

253
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"), Worker("eth"),
m_vc(_dbPath), m_vc(_dbPath),
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), 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_preMine(m_stateDB, BaseState::CanonGenesis),
m_postMine(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_gp->update(m_bc);
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId)); m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
if (_miners > -1)
setMiningThreads(_miners);
else
setMiningThreads();
if (_dbPath.size()) if (_dbPath.size())
Defaults::setDBPath(_dbPath); Defaults::setDBPath(_dbPath);
m_vc.setOk(); m_vc.setOk();
@ -142,7 +142,7 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for
startWorking(); 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"), Worker("eth"),
m_vc(_dbPath), m_vc(_dbPath),
m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), 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_preMine(m_stateDB),
m_postMine(m_stateDB) m_postMine(m_stateDB)
{ {
m_tq->onReady([=](){ this->onTransactionQueueReady(); });
m_bq->onReady([=](){ this->onBlockQueueReady(); });
m_farm->onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); });
m_gp->update(m_bc); m_gp->update(m_bc);
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId)); m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
if (_miners > -1)
setMiningThreads(_miners);
else
setMiningThreads();
if (_dbPath.size()) if (_dbPath.size())
Defaults::setDBPath(_dbPath); Defaults::setDBPath(_dbPath);
m_vc.setOk(); m_vc.setOk();
@ -229,8 +229,6 @@ void Client::killChain()
doWork(); doWork();
setMiningThreads(0);
startWorking(); startWorking();
if (wasMining) if (wasMining)
startMining(); startMining();
@ -271,26 +269,6 @@ static string filtersToString(T const& _fs)
return ret.str(); 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) void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _transactionHash)
{ {
Guard l(x_filtersWatches); Guard l(x_filtersWatches);
@ -342,22 +320,6 @@ void Client::setForceMining(bool _enable)
m.noteStateChange(); 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++);
}
MineProgress Client::miningProgress() const MineProgress Client::miningProgress() const
{ {
MineProgress ret; MineProgress ret;
@ -452,82 +414,68 @@ pair<h256, u256> Client::getWork()
return make_pair(m_remoteMiner.workHash(), m_remoteMiner.difficulty()); return make_pair(m_remoteMiner.workHash(), m_remoteMiner.difficulty());
} }
bool Client::submitWork(ProofOfWork::Solution const& _proof) bool Client::submitWork(ProofOfWork::Solution const& _solution)
{ {
Guard l(x_remoteMiner); bytes newBlock;
return m_remoteMiner.submitWork(_proof); {
WriteGuard l(x_stateDB);
if (!m_postMine.completeMine(_solution))
return false;
newBlock = m_postMine.blockData();
} }
void Client::doWork() ImportRoute ir = m_bc.attemptImport(newBlock, m_stateDB);
if (!ir.first.empty())
onChainChanged(ir);
return true;
}
void Client::syncBlockQueue()
{ {
// TODO: Use condition variable rather than polling. ImportResult ir;
bool stillGotWork = false; {
WriteGuard l(x_stateDB);
cwork << "BQ ==> CHAIN ==> STATE";
OverlayDB db = m_stateDB;
x_stateDB.unlock();
cworkin << "WORK"; tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100);
h256Set changeds;
auto maintainMiner = [&](OldMiner& m) x_stateDB.lock();
{ if (fresh.size())
if (m.isComplete()) m_stateDB = db;
{
// 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
{ if (!ir.first.empty())
cwork << "CHAIN <== postSTATE"; onChainChanged(ir);
WriteGuard l(x_stateDB); return true;
tie(hs, c) = m_bc.attemptImport(m.blockData(), m_stateDB);
} }
if (hs.size())
void Client::syncTransactionQueue()
{ {
for (auto const& h: hs) // returns TransactionReceipts, once for each transaction.
if (h != c) cwork << "postSTATE <== TQ";
appendFromNewBlock(h, changeds); TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp);
changeds.insert(ChainChangedFilter); if (newPendingReceipts.size())
}
for (auto& m: m_localMiners)
m.noteStateChange();
}
};
{ {
ReadGuard l(x_localMiners); for (size_t i = 0; i < newPendingReceipts.size(); i++)
for (auto& m: m_localMiners) appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3());
maintainMiner(m);
changeds.insert(PendingChangedFilter);
if (isMining())
cnote << "Additional transaction ready: Restarting mining operation.";
resyncStateNeeded = true;
if (auto h = m_host.lock())
h->noteNewTransactions();
} }
{
Guard l(x_remoteMiner);
maintainMiner(m_remoteMiner);
} }
// Synchronise state to block chain. void Client::onChainChanged(ImportRoute const& _ir)
// 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;
{ {
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 // insert transactions that we are declaring the dead part of the chain
for (auto const& h: dead) for (auto const& h: _ir.second)
{ {
clog(ClientNote) << "Dead block:" << h.abridged(); clog(ClientNote) << "Dead block:" << h.abridged();
for (auto const& t: m_bc.transactions(h)) for (auto const& t: m_bc.transactions(h))
@ -538,7 +486,7 @@ void Client::doWork()
} }
// remove transactions from m_tq nicely rather than relying on out of date nonce later on. // remove transactions from m_tq nicely rather than relying on out of date nonce later on.
for (auto const& h: fresh) for (auto const& h: _ir.first)
{ {
clog(ClientChat) << "Live block:" << h.abridged(); clog(ClientChat) << "Live block:" << h.abridged();
for (auto const& th: m_bc.transactionHashes(h)) for (auto const& th: m_bc.transactionHashes(h))
@ -548,17 +496,20 @@ void Client::doWork()
} }
} }
stillGotWork = stillGotWork | sgw; if (auto h = m_host.lock())
if (!fresh.empty()) h->noteNewBlocks();
{
for (auto i: fresh) h256Set changeds;
appendFromNewBlock(i, changeds); for (auto const& h: _ir.first)
if (h != _ir.second)
appendFromNewBlock(h, changeds);
changeds.insert(ChainChangedFilter); changeds.insert(ChainChangedFilter);
} noteChanged(changeds);
x_stateDB.lock();
if (fresh.size()) // RESTART MINING
m_stateDB = db;
// LOCKS NEEDED?
Guard l(x_stateDB);
cwork << "preSTATE <== CHAIN"; cwork << "preSTATE <== CHAIN";
if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address())
{ {
@ -567,45 +518,57 @@ void Client::doWork()
m_postMine = m_preMine; m_postMine = m_preMine;
resyncStateNeeded = true; resyncStateNeeded = true;
changeds.insert(PendingChangedFilter); changeds.insert(PendingChangedFilter);
// TODO: Move transactions pending from m_postMine back to transaction queue.
m_postMine.commitToMine(m_bc);
m_farm.setWork(m_postMine.info());
}
} }
// returns TransactionReceipts, once for each transaction. void Client::noteChanged(h256Set const& _filters)
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++) Guard l(x_filtersWatches);
appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3()); if (_filters.size())
cnote << "noteChanged(" << filtersToString(_filters) << ")";
changeds.insert(PendingChangedFilter); // accrue all changes left in each filter into the watches.
for (auto& w: m_watches)
if (isMining()) if (_filters.count(w.second.id))
cnote << "Additional transaction ready: Restarting mining operation."; {
resyncStateNeeded = true; cwatch << "!!!" << w.first << (m_filters.count(w.second.id) ? w.second.id.abridged() : w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???");
if (auto h = m_host.lock()) if (m_filters.count(w.second.id)) // Normal filtering watch
h->noteNewTransactions(); 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();
} }
if (!changeds.empty()) void Client::doWork()
if (auto h = m_host.lock()) {
h->noteNewBlocks(); // TODO: Use condition variable rather than this rubbish.
if (resyncStateNeeded) Guard l(x_fakeSignalSystemState);
if (m_syncTransactionQueue)
{ {
ReadGuard l(x_localMiners); m_syncTransactionQueue = false;
for (auto& m: m_localMiners) syncTransactionQueue();
m.noteStateChange();
} }
cwork << "noteChanged" << changeds.size() << "items"; if (m_syncBlockQueue)
noteChanged(changeds); {
cworkout << "WORK"; m_syncBlockQueue = false;
syncBlockQueue();
}
checkWatchGarbage();
if (!stillGotWork) this_thread::sleep_for(chrono::milliseconds(20));
this_thread::sleep_for(chrono::milliseconds(100)); }
void Client::checkWatchGarbage()
{
if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5)) if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5))
{ {
// watches garbage collection // watches garbage collection

67
libethereum/Client.h

@ -42,6 +42,7 @@
#include "CommonNet.h" #include "CommonNet.h"
#include "Miner.h" #include "Miner.h"
#include "ABI.h" #include "ABI.h"
#include "Farm.h"
#include "ClientBase.h" #include "ClientBase.h"
namespace dev namespace dev
@ -122,7 +123,7 @@ struct ClientDetail: public LogChannel { static const char* name() { return " C
/** /**
* @brief Main API hub for interfacing with Ethereum. * @brief Main API hub for interfacing with Ethereum.
*/ */
class Client: public MinerHost, public ClientBase, Worker class Client: public ClientBase, Worker
{ {
friend class OldMiner; friend class OldMiner;
@ -132,8 +133,7 @@ public:
p2p::Host* _host, p2p::Host* _host,
std::string const& _dbPath = std::string(), std::string const& _dbPath = std::string(),
WithExisting _forceAction = WithExisting::Trust, WithExisting _forceAction = WithExisting::Trust,
u256 _networkId = 0, u256 _networkId = 0
int _miners = -1
); );
explicit Client( explicit Client(
@ -141,8 +141,7 @@ public:
std::shared_ptr<GasPricer> _gpForAdoption, // pass it in with new. std::shared_ptr<GasPricer> _gpForAdoption, // pass it in with new.
std::string const& _dbPath = std::string(), std::string const& _dbPath = std::string(),
WithExisting _forceAction = WithExisting::Trust, WithExisting _forceAction = WithExisting::Trust,
u256 _networkId = 0, u256 _networkId = 0
int _miners = -1
); );
/// Destructor. /// Destructor.
@ -191,31 +190,31 @@ public:
/// Are we allowed to GPU mine? /// Are we allowed to GPU mine?
bool turboMining() const { return m_turboMining; } bool turboMining() const { return m_turboMining; }
/// Enable/disable GPU mining. /// 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. /// Start mining.
/// NOT thread-safe - call it & stopMining only from a single thread /// 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 { if (m_turboMining) m_farm.startGPU(); else m_farm.startCPU(); }
/// Stop mining. /// Stop mining.
/// NOT thread-safe /// NOT thread-safe
void stopMining() override { { ReadGuard l(x_localMiners); for (auto& m: m_localMiners) m.stop(); } } void stopMining() override { m_farm.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; }
/// Are we mining now? /// Are we mining now?
bool isMining() const override { return m_farm.isMining(); }
/// The hashrate...
uint64_t hashrate() const override; uint64_t hashrate() const override;
/// Check the progress of the mining. /// Check the progress of the mining.
MineProgress miningProgress() const override; MiningProgress miningProgress() const override;
/// Get and clear the mining history. /// Get and clear the mining history.
std::list<MineInfo> miningHistory(); std::list<MineInfo> miningHistory();
/// Update to the latest transactions and get hash of the current block to be mined minus the /// 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. /// nonce (the 'work hash') and the difficulty to be met.
virtual std::pair<h256, u256> getWork() override; virtual std::pair<h256, u256> getWork() override;
/// Submit the proof for the proof-of-work.
/** @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; virtual bool submitWork(ProofOfWork::Solution const& _proof) override;
// Debug stuff: // Debug stuff:
@ -261,10 +260,23 @@ private:
/// Called when Worker is exiting. /// Called when Worker is exiting.
virtual void doneWorking(); virtual void doneWorking();
/// Overrides for being a mining host. /// Magically called when the chain has changed. An import route is provided.
virtual void setupState(State& _s); /// Called by either submitWork() or in our main thread through syncBlockQueue().
virtual bool turbo() const { return m_turboMining; } void onChainChanged(ImportRoute const& _ir);
virtual bool force() const { return m_forceMining; }
/// 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() { Guard l(x_fakeSignalSystemState); m_syncTransactionQueue = true; }
/// Magically called when m_tq needs syncing. Be nice and don't block.
void onBlockQueueReady() { Guard l(x_fakeSignalSystemState); m_syncBlockQueue = true; }
void checkWatchGarbage();
VersionChecker m_vc; ///< Dummy object to check & update the protocol version. VersionChecker m_vc; ///< Dummy object to check & update the protocol version.
CanonBlockChain m_bc; ///< Maintains block database. CanonBlockChain m_bc; ///< Maintains block database.
@ -278,17 +290,24 @@ private:
std::weak_ptr<EthereumHost> m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. std::weak_ptr<EthereumHost> m_host; ///< Our Ethereum Host. Don't do anything if we can't lock.
Farm<ProofOfWork> m_farm; ///< Our mining farm.
mutable Mutex x_remoteMiner; ///< The remote miner lock. mutable Mutex x_remoteMiner; ///< The remote miner lock.
RemoteMiner m_remoteMiner; ///< The remote miner. RemoteMiner m_remoteMiner; ///< The remote miner.
std::vector<LocalMiner> m_localMiners; ///< The in-process miners. Handler m_tqReady;
mutable SharedMutex x_localMiners; ///< The in-process miners lock. Handler m_bqReady;
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_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_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; 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)
mutable Mutex x_fakeSignalSystemState;
bool m_syncTransactionQueue = false;
bool m_syncBlockQueue = false;
}; };
} }

2
libethereum/ClientBase.h

@ -142,8 +142,6 @@ public:
/// TODO: consider moving it to a separate interface /// 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 startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::startMining")); }
virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::stopMining")); } 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 bool isMining() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::isMining")); }

12
libethereum/Farm.cpp

@ -1,12 +0,0 @@
#include "Farm.h"
Farm::Farm()
{
}
Farm::~Farm()
{
}

41
libethereum/Farm.h

@ -14,10 +14,9 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file Miner.h /** @file Farm.h
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>
* @date 2014 * @date 2015
*/ */
#pragma once #pragma once
@ -28,7 +27,8 @@
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Worker.h> #include <libdevcore/Worker.h>
#include <libethcore/Common.h> #include <libethcore/Common.h>
#include "Miner.h" #include <libethcore/Miner.h>
#include <libethcore/BlockInfo.h>
namespace dev namespace dev
{ {
@ -41,8 +41,8 @@ namespace eth
* Miners ask for work, then submit proofs * Miners ask for work, then submit proofs
* @threadsafe * @threadsafe
*/ */
template <class ProofOfWork> template <class PoW>
class Farm: public FarmFace class Farm: public FarmFace<PoW>
{ {
public: public:
/** /**
@ -65,13 +65,13 @@ public:
* @brief (Re)start miners for CPU only. * @brief (Re)start miners for CPU only.
* @returns true if started properly. * @returns true if started properly.
*/ */
bool startCPU() { return start<ProofOfWork::CPUMiner>(); } bool startCPU() { return start<PoW::CPUMiner>(); }
/** /**
* @brief (Re)start miners for GPU only. * @brief (Re)start miners for GPU only.
* @returns true if started properly. * @returns true if started properly.
*/ */
bool startGPU() { start<ProofOfWork::GPUMiner>(); } bool startGPU() { start<PoW::GPUMiner>(); }
/** /**
* @brief Stop all mining activities. * @brief Stop all mining activities.
@ -82,20 +82,24 @@ public:
m_miners.clear(); m_miners.clear();
} }
bool isMining() const
{
ReadGuard l(x_miners);
return !m_miners.empty();
}
/** /**
* @brief Get information on the progress of mining this work package. * @brief Get information on the progress of mining this work package.
* @return The progress with mining so far. * @return The progress with mining so far.
*/ */
MineProgress const& mineProgress() const { ReadGuard l(x_progress); return m_progress; } MiningProgress const& miningProgress() const { ReadGuard l(x_progress); return m_progress; }
protected:
// TO BE REIMPLEMENTED BY THE SUBCLASS
/** /**
* @brief Provides a valid header based upon that received previously with setWork(). * @brief Provides a valid header based upon that received previously with setWork().
* @param _bi The now-valid header. * @param _bi The now-valid header.
* @return true if the header was good and that the Farm should pause until more work is submitted. * @return true if the header was good and that the Farm should pause until more work is submitted.
*/ */
virtual bool submitHeader(BlockInfo const& _bi) = 0; void onSolutionFound(function<bool(Solution const&)> _handler) { m_onSolutionFound = _handler; }
private: private:
/** /**
@ -104,16 +108,15 @@ private:
* @param _wp The WorkPackage that the Solution is for. * @param _wp The WorkPackage that the Solution is for.
* @return true iff the solution was good (implying that mining should be . * @return true iff the solution was good (implying that mining should be .
*/ */
bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp, NewMiner* _m) override bool submitProof(Solution const& _s, WorkPackage const& _wp, Miner* _m) override
{ {
if (_wp.headerHash != m_work.headerHash) if (_wp.headerHash != m_work.headerHash)
return false; return false;
ProofOfWork::assignResult(_p, m_header); if (m_onSolutionFound && m_onSolutionFound(_s))
if (submitHeader(m_header))
{ {
ReadGuard l(x_miners); ReadGuard l(x_miners);
for (std::shared_ptr<NewMiner> const& m: m_miners) for (std::shared_ptr<Miner> const& m: m_miners)
if (m.get() != _m) if (m.get() != _m)
m->pause(); m->pause();
m_work.headerHash = h256(); m_work.headerHash = h256();
@ -139,14 +142,16 @@ private:
} }
mutable SharedMutex x_miners; mutable SharedMutex x_miners;
std::vector<std::shared_ptr<NewMiner>> m_miners; std::vector<std::shared_ptr<Miner>> m_miners;
mutable SharedMutex x_progress; mutable SharedMutex x_progress;
MineProgress m_progress; MiningProgress m_progress;
mutable SharedMutex x_work; mutable SharedMutex x_work;
WorkPackage m_work; WorkPackage m_work;
BlockInfo m_header; BlockInfo m_header;
function<bool(Solution const&)> m_onSolutionFound;
}; };
} }

14
libethereum/State.cpp

@ -856,20 +856,6 @@ void State::commitToMine(BlockChain const& _bc)
m_committedToMine = true; m_committedToMine = true;
} }
bool State::completeMine(ProofOfWork::Solution 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() void State::completeMine()
{ {
cdebug << "Completing mine!"; cdebug << "Completing mine!";

31
libethereum/State.h

@ -30,6 +30,7 @@
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h> #include <libethcore/BlockInfo.h>
#include <libethcore/ProofOfWork.h> #include <libethcore/ProofOfWork.h>
#include <libethcore/Miner.h>
#include <libethcore/Params.h> #include <libethcore/Params.h>
#include <libevm/ExtVMFace.h> #include <libevm/ExtVMFace.h>
#include "TransactionQueue.h" #include "TransactionQueue.h"
@ -162,30 +163,19 @@ public:
/// Pass in a solution to the proof-of-work. /// 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. /// @returns true iff the given nonce is a proof-of-work for this State's block.
bool completeMine(ProofOfWork::Solution const& _result); template <class PoW>
bool completeMine(typename PoW::Solution 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)
{ {
// Update difficulty according to timestamp. PoW::assignResult(_result, m_currentBlock);
m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock);
MineInfo ret; // if (!m_pow.verify(m_currentBlock))
typename ProofOfWork::Solution r; // return false;
std::tie(ret, r) = _pow->mine(m_currentBlock, _pow->defaultTimeout(), true);
if (!ret.completed) cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << PoW::verify(m_currentBlock);
m_currentBytes.clear();
else completeMine();
{
ProofOfWork::assignResult(r, m_currentBlock);
cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock);
}
return ret; return true;
} }
/** Commit to DB and build the final block if the previous call to mine()'s result is completion. /** 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. /// Debugging only. Good for checking the Trie is in shape.
void paranoia(std::string const& _when, bool _enforceRefs = false) const; void paranoia(std::string const& _when, bool _enforceRefs = false) const;
OverlayDB m_db; ///< Our overlay for the state tree. OverlayDB m_db; ///< Our overlay for the state tree.
SecureTrieDB<Address, OverlayDB> m_state; ///< Our state tree, as an OverlayDB DB. 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. Transactions m_transactions; ///< The current list of transactions that we've included in the state.

1
libethereum/TransactionQueue.cpp

@ -53,6 +53,7 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallb
if (_cb) if (_cb)
m_callbacks[h] = _cb; m_callbacks[h] = _cb;
ctxq << "Queued vaguely legit-looking transaction" << h.abridged(); ctxq << "Queued vaguely legit-looking transaction" << h.abridged();
m_onReady();
} }
catch (Exception const& _e) catch (Exception const& _e)
{ {

6
libethereum/TransactionQueue.h

@ -26,7 +26,7 @@
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Guards.h> #include <libdevcore/Guards.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include "libethcore/Common.h" #include <libethcore/Common.h>
#include "Transaction.h" #include "Transaction.h"
namespace dev namespace dev
@ -60,13 +60,15 @@ public:
void noteGood(std::pair<h256, Transaction> const& _t); void noteGood(std::pair<h256, Transaction> const& _t);
void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); } 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: private:
mutable boost::shared_mutex m_lock; ///< General lock. mutable boost::shared_mutex m_lock; ///< General lock.
std::set<h256> m_known; ///< Hashes of transactions in both sets. std::set<h256> m_known; ///< Hashes of transactions in both sets.
std::map<h256, Transaction> m_current; ///< Map of SHA3(tx) to tx. 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::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 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.
}; };
} }

Loading…
Cancel
Save