diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index 6cd2504b3..2118952eb 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -39,6 +39,8 @@ using namespace chrono; using namespace dev; using namespace eth; +#define ETH_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} + Ethasher* dev::eth::Ethasher::s_this = nullptr; Ethasher::~Ethasher() @@ -59,28 +61,35 @@ void Ethasher::killCache(h256 const& _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() ); - } + return light(_header.seedHash()); +} +void const* Ethasher::light(h256 const& _seedHash) +{ + RecursiveGuard l(x_this); if (!m_lights.count(_header.seedHash())) { - ethash_params p = params((unsigned)_header.number); - m_lights[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data()); + ethash_params p = params(_seedHash); + m_lights[_seedHash] = ethash_new_light(&p, _seedHash.data()); } - return m_lights[_header.seedHash()]; + return m_lights[_seedHash]; } -#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} +bytesConstRef Ethasher::full(BlockInfo const& _header, bytesRef _dest) +{ + return full(_header.seedHash(), _dest); +} -bytesConstRef Ethasher::full(BlockInfo const& _header) +bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest) { RecursiveGuard l(x_this); - if (!m_fulls.count(_header.seedHash())) + if (m_fulls.count(_seedHash) && _dest) + { + assert(m_fulls.size() <= _dest.size()); + m_fulls.at(_seedHash).copyTo(_dest); + return; + } + if (!m_fulls.count(_seedHash)) { // @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr. /* if (!m_fulls.empty()) @@ -93,9 +102,9 @@ bytesConstRef Ethasher::full(BlockInfo const& _header) boost::filesystem::create_directories(getDataDir("ethash")); } catch (...) {} - auto info = rlpList(c_ethashRevision, _header.seedHash()); + auto info = rlpList(c_ethashRevision, _seedHash); std::string oldMemoFile = getDataDir("ethash") + "/full"; - std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_header.seedHash().ref().cropped(0, 8)); + std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_seedHash.ref().cropped(0, 8)); if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info) { // memofile valid - rename. @@ -105,17 +114,26 @@ bytesConstRef Ethasher::full(BlockInfo const& _header) 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(_seedHash); + assert(!_dest || _dest.size() >= p.full_size); // must be big enough. + + bytesRef r = contentsNew(memoFile, _dest); + if (!r) { - 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()]); + // 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, &p, light(_seedHash)); + writeFile(memoFile, r); } + if (_dest) + return _dest; + m_fulls[_seedHash] = r; } - return m_fulls[_header.seedHash()]; + return m_fulls[_seedHash]; } ethash_params Ethasher::params(BlockInfo const& _header) @@ -123,44 +141,6 @@ 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; @@ -169,6 +149,27 @@ ethash_params Ethasher::params(unsigned _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) @@ -208,13 +209,18 @@ bool Ethasher::verify(BlockInfo const& _header) } Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) +{ + return eval(_header.seedHash(), _header.headerHash(WithoutNonce), _nonce); +} + +Ethasher::Result Ethasher::eval(h256 const& _seedHash, h256 const& _headerHash, 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); + if (Ethasher::get()->m_fulls.count(_seedHash)) + ethash_compute_full(&r, Ethasher::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce); else - ethash_compute_light(&r, Ethasher::get()->light(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); + ethash_compute_light(&r, Ethasher::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); return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; } diff --git a/libethcore/Ethasher.h b/libethcore/Ethasher.h index 32622929f..f57c2f4f6 100644 --- a/libethcore/Ethasher.h +++ b/libethcore/Ethasher.h @@ -52,12 +52,14 @@ public: using FullType = void const*; LightType light(BlockInfo const& _header); - bytesConstRef full(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); - void readFull(BlockInfo const& _header, void* _dest); - struct Result { h256 value; @@ -66,6 +68,7 @@ public: 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 @@ -103,6 +106,7 @@ private: RecursiveMutex x_this; std::map m_lights; std::map m_fulls; + std::map m_seedHashes; }; } diff --git a/libethcore/ProofOfWork.cpp b/libethcore/ProofOfWork.cpp index 1b3b55f4d..97488ee35 100644 --- a/libethcore/ProofOfWork.cpp +++ b/libethcore/ProofOfWork.cpp @@ -31,7 +31,7 @@ #include #include #include -#if ETH_ETHASHCL +#if ETH_ETHASHCL || !ETH_TRUE #include #endif #include "BlockInfo.h" @@ -134,10 +134,15 @@ public: }; */ -struct EthashCLHook: public ethash_cl_miner::search_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"; @@ -147,21 +152,23 @@ struct EthashCLHook: public ethash_cl_miner::search_hook if (!m_aborted) cwarn << "Couldn't abort. Abandoning OpenCL process."; m_aborted = m_abort = false; - m_found.clear(); } - vector fetchFound() { vector 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(_nonces, _nonces + _count); - return true; + 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 @@ -180,66 +187,53 @@ protected: private: Mutex x_all; - vector m_found; uint64_t m_total; uint64_t m_last; bool m_abort = false; bool m_aborted = true; + Ethash::GPUMiner* m_owner = nullptr; }; -EthashCL::EthashCL(): - m_hook(new EthashCLHook) +} } + +Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): + Miner(_ci), + m_hook(new EthashCLHook(this)) { } -EthashCL::~EthashCL() +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; } -std::pair EthashCL::mine(BlockInfo const& _header, unsigned _msTimeout, bool) +void Ethash::GPUMiner::kickOff(WorkPackage const& _work) { - if (!m_lastHeader || m_lastHeader.seedHash() != _header.seedHash()) + if (!m_miner || m_minerSeed != _work.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); + 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_lastHeader != _header) + if (m_lastWork.headerHash != _work.headerHash) { 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); + uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192); + m_miner->search(_work.headerHash, upper64OfBoundary, *m_hook); } - m_lastHeader = _header; - - MineInfo mi; - Solution proof; - mi.requirement = log2((double)(u256)_header.boundary()); - mi.best = 0; - - std::this_thread::sleep_for(chrono::milliseconds(_msTimeout)); + m_work = _work; +} - mi.hashes += m_hook->fetchTotal(); - auto found = m_hook->fetchFound(); - if (!found.empty()) - { - for (auto const& n: found) - { - auto result = Ethasher::eval(_header, n); - if (result.value < _header.boundary()) - { - mi.completed = true; - proof = Solution{n, result.mixHash}; - } - } - } - return std::make_pair(mi, proof); +void Ethash::GPUMiner::pause() +{ + m_hook->abort(); } #endif diff --git a/libethcore/ProofOfWork.h b/libethcore/ProofOfWork.h index 3ea177a9a..f66bc77c9 100644 --- a/libethcore/ProofOfWork.h +++ b/libethcore/ProofOfWork.h @@ -34,7 +34,6 @@ #define FAKE_DAGGER 1 class ethash_cl_miner; -struct ethash_cl_search_hook; namespace dev { @@ -52,8 +51,6 @@ struct MineInfo bool completed = false; }; -class EthashCLHook; - class Ethash { @@ -75,6 +72,7 @@ public: static unsigned instances() { return thread::hardware_concurrency(); } +protected: void kickOff(WorkPackage const& _work) override { stopWorking(); @@ -93,25 +91,29 @@ private: #if ETH_ETHASHCL || !ETH_TRUE -class GPUMiner: public NewMiner +class EthashCLHook; + +class GPUMiner: public Miner { -public: - GPUMiner(ConstructionInfo const& _ci): NewMiner(_ci) - { + friend class EthashCLHook; - } +public: + GPUMiner(ConstructionInfo const& _ci); static unsigned instances() { return 1; } - std::pair 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 m_miner; + void kickOff(WorkPackage const& _work) override; + void pause() override; + +private: + void report(uint64_t _nonce); + std::unique_ptr m_hook; + std::unique_ptr m_miner; + h256 m_minerSeed; + WorkPackage m_lastWork; ///< Work loaded into m_miner. + MineInfo m_info; }; #else