/* 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 #include "BlockInfo.h" #include "ProofOfWork.h" using namespace std; using namespace std::chrono; namespace dev { namespace eth { class Ethasher { public: Ethasher() {} static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; } bytes const& cache(BlockInfo const& _header) { RecursiveGuard l(x_this); if (!m_caches.count(_header.seedHash)) { try { boost::filesystem::create_directories(getDataDir() + "/ethashcache"); } catch (...) {} std::string memoFile = getDataDir() + "/ethashcache/" + toHex(_header.seedHash.ref().cropped(0, 4)) + ".cache"; m_caches[_header.seedHash] = contents(memoFile); if (m_caches[_header.seedHash].empty()) { ethash_params p = params((unsigned)_header.number); m_caches[_header.seedHash].resize(p.cache_size); ethash_prep_light(m_caches[_header.seedHash].data(), &p, _header.seedHash.data()); writeFile(memoFile, m_caches[_header.seedHash]); } } return m_caches[_header.seedHash]; } byte const* full(BlockInfo const& _header) { RecursiveGuard l(x_this); if (!m_fulls.count(_header.seedHash)) { if (!m_fulls.empty()) { delete [] m_fulls.begin()->second.data(); m_fulls.erase(m_fulls.begin()); } std::string memoFile = getDataDir() + "/ethashcache/" + toHex(_header.seedHash.ref().cropped(0, 4)) + ".full"; m_fulls[_header.seedHash] = contentsNew(memoFile); if (!m_fulls[_header.seedHash]) { ethash_params p = params((unsigned)_header.number); m_fulls[_header.seedHash] = bytesRef(new byte[p.full_size], p.full_size); auto c = cache(_header); ethash_prep_full(m_fulls[_header.seedHash].data(), &p, c.data()); writeFile(memoFile, m_fulls[_header.seedHash]); } } return m_fulls[_header.seedHash].data(); } static ethash_params params(BlockInfo const& _header) { return params((unsigned)_header.number); } static ethash_params params(unsigned _n) { ethash_params p; p.cache_size = ethash_get_cachesize(_n); p.full_size = ethash_get_datasize(_n); return p; } private: static Ethasher* s_this; RecursiveMutex x_this; std::map m_caches; std::map m_fulls; }; Ethasher* Ethasher::s_this = nullptr; bool Ethash::verify(BlockInfo const& _header) { bigint boundary = (bigint(1) << 256) / _header.difficulty; auto e = eval(_header, _header.nonce); return (u256)e.value <= boundary && e.mixHash == _header.mixHash; } Ethash::Result Ethash::eval(BlockInfo const& _header, Nonce const& _nonce) { auto p = Ethasher::params(_header); ethash_return_value r; ethash_compute_light(&r, Ethasher::get()->cache(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; } std::pair Ethash::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue, bool _turbo) { auto h = _header.headerHash(WithoutNonce); auto p = Ethasher::params(_header); auto d = Ethasher::get()->full(_header); std::pair ret; static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()))); uint64_t tryNonce = (uint64_t)(u64)(m_last = Nonce::random(s_eng)); bigint boundary = (bigint(1) << 256) / _header.difficulty; ret.first.requirement = log2((double)boundary); // 2^ 0 32 64 128 256 // [--------*-------------------------] // // evaluate until we run out of time auto startTime = std::chrono::steady_clock::now(); if (!_turbo) std::this_thread::sleep_for(std::chrono::milliseconds(_msTimeout * 90 / 100)); double best = 1e99; // high enough to be effectively infinity :) Proof result; ethash_return_value ethashReturn; unsigned hashCount = 0; for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; tryNonce++, hashCount++) { ethash_compute_full(ðashReturn, d, &p, h.data(), tryNonce); u256 val(h256(ethashReturn.result, h256::ConstructFromPointer)); best = std::min(best, log2((double)val)); if (val <= boundary) { ret.first.completed = true; result.mixHash = h256(ethashReturn.mix_hash, h256::ConstructFromPointer); result.nonce = u64(tryNonce); break; } } ret.first.hashes = hashCount; ret.first.best = best; ret.second = result; if (ret.first.completed) { BlockInfo test = _header; assignResult(result, test); assert(verify(test)); } return ret; } } }