/* 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 Ethash.cpp * @author Gav Wood * @date 2014 */ #include "Ethash.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if ETH_ETHASHCL || !ETH_TRUE #include #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; } Ethash::WorkPackage Ethash::package(BlockInfo const& _bi) { WorkPackage ret; ret.boundary = _bi.boundary(); ret.headerHash = _bi.headerHash(WithoutNonce); ret.seedHash = _bi.seedHash(); return ret; } void Ethash::prep(BlockInfo const& _header) { EthashAux::full(_header); } bool Ethash::preVerify(BlockInfo const& _header) { if (_header.number >= ETHASH_EPOCH_LENGTH * 2048) return false; h256 boundary = u256((bigint(1) << 256) / _header.difficulty); return ethash_quick_check_difficulty( _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_header.nonce, _header.mixHash.data(), boundary.data()); } bool Ethash::verify(BlockInfo const& _header) { bool pre = preVerify(_header); #if !ETH_DEBUG if (!pre) return false; #endif auto result = EthashAux::eval(_header); bool slow = result.value <= _header.boundary() && result.mixHash == _header.mixHash; #if ETH_DEBUG || !ETH_TRUE if (!pre && slow) { cwarn << "WARNING: evaluated result gives true whereas ethash_quick_check_difficulty gives false."; cwarn << "headerHash:" << _header.headerHash(WithoutNonce); cwarn << "nonce:" << _header.nonce; cwarn << "mixHash:" << _header.mixHash; cwarn << "difficulty:" << _header.difficulty; cwarn << "boundary:" << _header.boundary(); cwarn << "result.value:" << result.value; cwarn << "result.mixHash:" << result.mixHash; } #endif return slow; } void Ethash::CPUMiner::workLoop() { auto tid = std::this_thread::get_id(); static std::mt19937_64 s_eng((time(0) + std::hash()(tid))); uint64_t tryNonce = (uint64_t)(u64)Nonce::random(s_eng); ethash_return_value ethashReturn; WorkPackage w = work(); auto p = EthashAux::params(w.seedHash); void const* dagPointer = EthashAux::full(w.seedHash).data(); uint8_t const* headerHashPointer = w.headerHash.data(); h256 boundary = w.boundary; unsigned hashCount = 1; for (; !shouldStop(); tryNonce++, hashCount++) { ethash_compute_full(ðashReturn, dagPointer, &p, headerHashPointer, tryNonce); h256 value = h256(ethashReturn.result, h256::ConstructFromPointer); if (value <= boundary && submitProof(Solution{(Nonce)(u64)tryNonce, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)})) break; if (!(hashCount % 1000)) accumulateHashes(1000); } } #if ETH_ETHASHCL || !ETH_TRUE class EthashCLHook: public ethash_cl_miner::search_hook { public: EthashCLHook(Ethash::GPUMiner* _owner): m_owner(_owner) {} void abort() { Guard l(x_all); if (m_aborted) return; // cdebug << "Attempting to abort"; m_abort = true; for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout) std::this_thread::sleep_for(chrono::milliseconds(30)); // if (!m_aborted) // cwarn << "Couldn't abort. Abandoning OpenCL process."; } void reset() { m_aborted = m_abort = false; } protected: virtual bool found(uint64_t const* _nonces, uint32_t _count) override { // dev::operator <<(std::cerr << "Found nonces: ", vector(_nonces, _nonces + _count)) << std::endl; for (uint32_t i = 0; i < _count; ++i) { if (m_owner->report(_nonces[i])) { m_aborted = true; return true; } } return false; } virtual bool searched(uint64_t _startNonce, uint32_t _count) override { Guard l(x_all); // std::cerr << "Searched " << _count << " from " << _startNonce << std::endl; m_owner->accumulateHashes(_count); m_last = _startNonce + _count; if (m_abort) { m_aborted = true; return true; } return false; } private: Mutex x_all; uint64_t m_last; bool m_abort = false; bool m_aborted = true; Ethash::GPUMiner* m_owner = nullptr; }; Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): Miner(_ci), m_hook(new EthashCLHook(this)) { } Ethash::GPUMiner::~GPUMiner() { pause(); delete m_miner; delete m_hook; } bool Ethash::GPUMiner::report(uint64_t _nonce) { Nonce n = (Nonce)(u64)_nonce; Result r = EthashAux::eval(work().seedHash, work().headerHash, n); if (r.value < work().boundary) return submitProof(Solution{n, r.mixHash}); return false; } void Ethash::GPUMiner::kickOff() { m_hook->reset(); startWorking(); } void Ethash::GPUMiner::workLoop() { // take local copy of work since it may end up being overwritten by kickOff/pause. WorkPackage w = work(); if (!m_miner || m_minerSeed != w.seedHash) { m_minerSeed = w.seedHash; delete m_miner; m_miner = new ethash_cl_miner; auto p = EthashAux::params(m_minerSeed); auto cb = [&](void* d) { EthashAux::full(m_minerSeed, bytesRef((byte*)d, p.full_size)); }; m_miner->init(p, cb, 32); } uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192); m_miner->search(w.headerHash.data(), upper64OfBoundary, *m_hook); } void Ethash::GPUMiner::pause() { m_hook->abort(); stopWorking(); } #endif } }