You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

240 lines
6.4 KiB

/*
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 ProofOfWork.cpp
* @author Gav Wood <i@gavwood.com>
* @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
#include <libethash-cl/ethash_cl_miner.h>
#define ETHASH_REVISION REVISION
#define ETHASH_DATASET_BYTES_INIT DATASET_BYTES_INIT
#define ETHASH_DATASET_BYTES_GROWTH DATASET_BYTES_GROWTH
#define ETHASH_CACHE_BYTES_INIT CACHE_BYTES_INIT
#define ETHASH_CACHE_BYTES_GROWTH CACHE_BYTES_GROWTH
#define ETHASH_DAGSIZE_BYTES_INIT DAGSIZE_BYTES_INIT
#define ETHASH_DAG_GROWTH DAG_GROWTH
#define ETHASH_EPOCH_LENGTH EPOCH_LENGTH
#define ETHASH_MIX_BYTES MIX_BYTES
#define ETHASH_HASH_BYTES HASH_BYTES
#define ETHASH_DATASET_PARENTS DATASET_PARENTS
#define ETHASH_CACHE_ROUNDS CACHE_ROUNDS
#define ETHASH_ACCESSES ACCESSES
#undef REVISION
#undef DATASET_BYTES_INIT
#undef DATASET_BYTES_GROWTH
#undef CACHE_BYTES_INIT
#undef CACHE_BYTES_GROWTH
#undef DAGSIZE_BYTES_INIT
#undef DAG_GROWTH
#undef EPOCH_LENGTH
#undef MIX_BYTES
#undef HASH_BYTES
#undef DATASET_PARENTS
#undef CACHE_ROUNDS
#undef ACCESSES
#endif
#include "BlockInfo.h"
#include "Ethasher.h"
#include "ProofOfWork.h"
using namespace std;
using namespace std::chrono;
namespace dev
{
namespace eth
{
bool EthashCPU::verify(BlockInfo const& _header)
{
return Ethasher::verify(_header);
}
std::pair<MineInfo, EthashCPU::Proof> EthashCPU::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue, bool _turbo)
{
Ethasher::Miner m(_header);
std::pair<MineInfo, Proof> 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 = u256((bigint(1) << 256) / _header.difficulty);
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();
if (!_turbo)
std::this_thread::sleep_for(std::chrono::milliseconds(_msTimeout * 90 / 100));
double best = 1e99; // high enough to be effectively infinity :)
Proof result;
unsigned hashCount = 0;
for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; tryNonce++, hashCount++)
{
h256 val(m.mine(tryNonce));
best = std::min<double>(best, log2((double)(u256)val));
if (val <= boundary)
{
ret.first.completed = true;
assert(Ethasher::eval(_header, (Nonce)(u64)tryNonce).value == val);
result.mixHash = m.lastMixHash();
result.nonce = u64(tryNonce);
BlockInfo test = _header;
assignResult(result, test);
assert(verify(test));
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;
}
#if ETH_ETHASHCL
/*
struct ethash_cl_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;
};
class ethash_cl_miner
{
public:
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);
};
*/
struct EthashCLHook: public ethash_cl_search_hook
{
virtual bool found(uint64_t const* _nonces, uint32_t _count)
{
Guard l(x_all);
for (unsigned i = 0; i < _count; ++i)
found.push_back((Nonce)(u64)_nonces[i]);
if (abort)
{
aborted = true;
return true;
}
return false;
}
virtual bool searched(uint64_t _startNonce, uint32_t _count)
{
Guard l(x_all);
total += _count;
last = _startNonce + _count;
if (abort)
{
aborted = true;
return true;
}
return false;
}
vector<Nonce> fetchFound() { vector<Nonce> ret; Guard l(x_all); std::swap(ret, found); return ret; }
uint64_t fetchTotal() { Guard l(x_all); auto ret = total; total = 0; return ret; }
Mutex x_all;
vector<Nonce> found;
uint64_t total;
uint64_t last;
bool abort = false;
bool aborted = false;
};
EthashCL::EthashCL():
m_miner(new ethash_cl_miner),
m_hook(new EthashCLHook)
{
}
EthashCL::~EthashCL()
{
m_hook->abort = true;
for (unsigned timeout = 0; timeout < 100 && !m_hook->aborted; ++timeout)
std::this_thread::sleep_for(chrono::milliseconds(30));
if (!m_hook->aborted)
cwarn << "Couldn't abort. Abandoning OpenCL process.";
}
bool EthashCL::verify(BlockInfo const& _header)
{
return Ethasher::verify(_header);
}
std::pair<MineInfo, Ethash::Proof> EthashCL::mine(BlockInfo const& _header, unsigned _msTimeout, bool, bool)
{
if (m_lastHeader.seedHash() != _header.seedHash())
{
m_miner->init(Ethasher::params(_header), _header.seedHash().data());
// TODO: reinit probably won't work when seed changes.
}
if (m_lastHeader != _header)
{
static std::random_device s_eng;
uint64_t tryNonce = (uint64_t)(u64)(m_last = Nonce::random(s_eng));
m_miner->search(_header.headerHash(WithoutNonce).data(), tryNonce, *m_hook);
}
m_lastHeader = _header;
std::this_thread::sleep_for(chrono::milliseconds(_msTimeout));
auto found = m_hook->fetchFound();
if (!found.empty())
{
h256 mixHash; // ?????
return std::make_pair(MineInfo{0.0, 1e99, 0, true}, EthashCL::Proof((Nonce)(u64)found[0], mixHash));
}
return std::make_pair(MineInfo{0.0, 1e99, 0, false}, EthashCL::Proof());
}
#endif
}
}