/* 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 . */ /** @file ProofOfWork.cpp * @author Gav Wood * @date 2014 */ #include #include #include #include #include #include #include #include #include #include #include #include #if ETH_ETHASHCL || !ETH_TRUE #include #endif #include "BlockInfo.h" #include "Ethasher.h" #include "ProofOfWork.h" using namespace std; using namespace std::chrono; 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 ret; auto tid = std::this_thread::get_id(); static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()) + std::hash()(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(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(_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 } }