This commit is contained in:
Dobromir Popov
2025-09-07 15:03:47 +03:00
parent 00cda24e71
commit 2d2653551b
132 changed files with 34281 additions and 5 deletions

View File

@@ -0,0 +1,20 @@
set(SOURCES
EthashAux.h EthashAux.cpp
Farm.cpp Farm.h
Miner.h Miner.cpp
)
include_directories(BEFORE ..)
add_library(ethcore ${SOURCES})
target_link_libraries(ethcore PUBLIC devcore ethash PRIVATE hwmon)
if(ETHASHCL)
target_link_libraries(ethcore PRIVATE ethash-cl)
endif()
if(ETHASHCUDA)
target_link_libraries(ethcore PUBLIC ethash-cuda)
endif()
if(ETHASHCPU)
target_link_libraries(ethcore PUBLIC ethash-cpu)
endif()

View File

@@ -0,0 +1,44 @@
/*
This file is part of progminer.
progminer 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.
progminer 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 progminer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EthashAux.h"
#include <ethash/ethash.hpp>
#include <ethash/progpow.hpp>
using namespace dev;
using namespace eth;
Result EthashAux::eval(int epoch, h256 const& _headerHash, uint64_t _nonce) noexcept
{
auto headerHash = ethash::hash256_from_bytes(_headerHash.data());
auto& context = ethash::get_global_epoch_context(epoch);
auto result = ethash::hash(context, headerHash, _nonce);
h256 mix{reinterpret_cast<byte*>(result.mix_hash.bytes), h256::ConstructFromPointer};
h256 final{reinterpret_cast<byte*>(result.final_hash.bytes), h256::ConstructFromPointer};
return {final, mix};
}
Result EthashAux::eval(int epoch, int _block_number, h256 const& _headerHash, uint64_t _nonce) noexcept
{
auto headerHash = ethash::hash256_from_bytes(_headerHash.data());
auto& context = ethash::get_global_epoch_context(epoch);
auto result = progpow::hash(context, _block_number, headerHash, _nonce);
h256 mix{reinterpret_cast<byte*>(result.mix_hash.bytes), h256::ConstructFromPointer};
h256 final{reinterpret_cast<byte*>(result.final_hash.bytes), h256::ConstructFromPointer};
return {final, mix};
}

View File

@@ -0,0 +1,84 @@
/*
This file is part of progminer.
progminer 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.
progminer 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 progminer. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <libdevcore/Common.h>
#include <libdevcore/Exceptions.h>
#include <libdevcore/Worker.h>
#include <ethash/ethash.hpp>
namespace dev
{
namespace eth
{
struct Result
{
h256 value;
h256 mixHash;
};
class EthashAux
{
public:
static Result eval(int epoch, h256 const& _headerHash, uint64_t _nonce) noexcept;
static Result eval(int epoch, int _block_number, h256 const& _headerHash, uint64_t _nonce) noexcept;
};
struct EpochContext
{
int epochNumber;
int lightNumItems;
size_t lightSize;
const ethash_hash512* lightCache;
int dagNumItems;
uint64_t dagSize;
};
struct WorkPackage
{
WorkPackage() = default;
explicit operator bool() const { return header != h256(); }
std::string job; // Job identifier can be anything. Not necessarily a hash
h256 boundary;
h256 header; ///< When h256() means "pause until notified a new work package is available".
h256 seed;
int epoch = -1;
int block = -1;
uint64_t startNonce = 0;
uint16_t exSizeBytes = 0;
std::string algo = "ethash";
};
struct Solution
{
uint64_t nonce; // Solution found nonce
h256 mixHash; // Mix hash
WorkPackage work; // WorkPackage this solution refers to
std::chrono::steady_clock::time_point tstamp; // Timestamp of found solution
unsigned midx; // Originating miner Id
};
} // namespace eth
} // namespace dev

686
zano/libethcore/Farm.cpp Normal file
View File

@@ -0,0 +1,686 @@
/*
This file is part of progminer.
progminer 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.
progminer 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 progminer. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libethcore/Farm.h>
#if ETH_ETHASHCL
#include <libethash-cl/CLMiner.h>
#endif
#if ETH_ETHASHCUDA
#include <libethash-cuda/CUDAMiner.h>
#endif
#if ETH_ETHASHCPU
#include <libethash-cpu/CPUMiner.h>
#endif
namespace dev
{
namespace eth
{
Farm* Farm::m_this = nullptr;
Farm::Farm(std::map<std::string, DeviceDescriptor>& _DevicesCollection,
FarmSettings _settings, CUSettings _CUSettings, CLSettings _CLSettings, CPSettings _CPSettings)
: m_Settings(std::move(_settings)),
m_CUSettings(std::move(_CUSettings)),
m_CLSettings(std::move(_CLSettings)),
m_CPSettings(std::move(_CPSettings)),
m_io_strand(g_io_service),
m_collectTimer(g_io_service),
m_DevicesCollection(_DevicesCollection)
{
m_this = this;
// Init HWMON if needed
if (m_Settings.hwMon)
{
m_telemetry.hwmon = true;
#if defined(__linux)
bool need_sysfsh = false;
#else
bool need_adlh = false;
#endif
bool need_nvmlh = false;
// Scan devices collection to identify which hw monitors to initialize
for (auto it = m_DevicesCollection.begin(); it != m_DevicesCollection.end(); it++)
{
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::Cuda)
{
need_nvmlh = true;
continue;
}
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::OpenCL)
{
if (it->second.clPlatformType == ClPlatformTypeEnum::Nvidia)
{
need_nvmlh = true;
continue;
}
if (it->second.clPlatformType == ClPlatformTypeEnum::Amd)
{
#if defined(__linux)
need_sysfsh = true;
#else
need_adlh = true;
#endif
continue;
}
}
}
#if defined(__linux)
if (need_sysfsh)
sysfsh = wrap_amdsysfs_create();
if (sysfsh)
{
// Build Pci identification mapping as done in miners.
for (int i = 0; i < sysfsh->sysfs_gpucount; i++)
{
std::ostringstream oss;
std::string uniqueId;
oss << std::setfill('0') << std::setw(2) << std::hex
<< (unsigned int)sysfsh->sysfs_pci_bus_id[i] << ":" << std::setw(2)
<< (unsigned int)(sysfsh->sysfs_pci_device_id[i]) << ".0";
uniqueId = oss.str();
map_amdsysfs_handle[uniqueId] = i;
}
}
#else
if (need_adlh)
adlh = wrap_adl_create();
if (adlh)
{
// Build Pci identification as done in miners.
for (int i = 0; i < adlh->adl_gpucount; i++)
{
std::ostringstream oss;
std::string uniqueId;
oss << std::setfill('0') << std::setw(2) << std::hex
<< (unsigned int)adlh->devs[adlh->phys_logi_device_id[i]].iBusNumber << ":"
<< std::setw(2)
<< (unsigned int)(adlh->devs[adlh->phys_logi_device_id[i]].iDeviceNumber)
<< ".0";
uniqueId = oss.str();
map_adl_handle[uniqueId] = i;
}
}
#endif
if (need_nvmlh)
nvmlh = wrap_nvml_create();
if (nvmlh)
{
// Build Pci identification as done in miners.
for (int i = 0; i < nvmlh->nvml_gpucount; i++)
{
std::ostringstream oss;
std::string uniqueId;
oss << std::setfill('0') << std::setw(2) << std::hex
<< (unsigned int)nvmlh->nvml_pci_bus_id[i] << ":" << std::setw(2)
<< (unsigned int)(nvmlh->nvml_pci_device_id[i] >> 3) << ".0";
uniqueId = oss.str();
map_nvml_handle[uniqueId] = i;
}
}
}
// Initialize nonce_scrambler
shuffle();
// Start data collector timer
// It should work for the whole lifetime of Farm
// regardless it's mining state
m_collectTimer.expires_from_now(boost::posix_time::milliseconds(m_collectInterval));
m_collectTimer.async_wait(
m_io_strand.wrap(boost::bind(&Farm::collectData, this, boost::asio::placeholders::error)));
}
Farm::~Farm()
{
// Stop data collector (before monitors !!!)
m_collectTimer.cancel();
// Deinit HWMON
#if defined(__linux)
if (sysfsh)
wrap_amdsysfs_destroy(sysfsh);
#else
if (adlh)
wrap_adl_destroy(adlh);
#endif
if (nvmlh)
wrap_nvml_destroy(nvmlh);
// Stop mining (if needed)
if (m_isMining.load(std::memory_order_relaxed))
stop();
}
/**
* @brief Randomizes the nonce scrambler
*/
void Farm::shuffle()
{
// Given that all nonces are equally likely to solve the problem
// we could reasonably always start the nonce search ranges
// at a fixed place, but that would be boring. Provide a once
// per run randomized start place, without creating much overhead.
random_device engine;
m_nonce_scrambler = uniform_int_distribution<uint64_t>()(engine);
}
void Farm::setWork(WorkPackage const& _newWp)
{
// Set work to each miner giving it's own starting nonce
Guard l(x_minerWork);
// Retrieve appropriate EpochContext
if (m_currentWp.epoch != _newWp.epoch)
{
ethash::epoch_context _ec = ethash::get_global_epoch_context(_newWp.epoch);
m_currentEc.epochNumber = _newWp.epoch;
m_currentEc.lightNumItems = _ec.light_cache_num_items;
m_currentEc.lightSize = ethash::get_light_cache_size(_ec.light_cache_num_items);
m_currentEc.dagNumItems = ethash::calculate_full_dataset_num_items(_newWp.epoch);
m_currentEc.dagSize = ethash::get_full_dataset_size(m_currentEc.dagNumItems);
m_currentEc.lightCache = _ec.light_cache;
for (auto const& miner : m_miners)
miner->setEpoch(m_currentEc);
}
m_currentWp = _newWp;
// Check if we need to shuffle per work (ergodicity == 2)
if (m_Settings.ergodicity == 2 && m_currentWp.exSizeBytes == 0)
shuffle();
uint64_t _startNonce;
if (m_currentWp.exSizeBytes > 0)
{
// Equally divide the residual segment among miners
_startNonce = m_currentWp.startNonce;
m_nonce_segment_with =
(unsigned int)log2(pow(2, 64 - (m_currentWp.exSizeBytes * 4)) / m_miners.size());
}
else
{
// Get the randomly selected nonce
_startNonce = m_nonce_scrambler;
}
for (unsigned int i = 0; i < m_miners.size(); i++)
{
m_currentWp.startNonce = _startNonce + ((uint64_t)i << m_nonce_segment_with);
m_miners.at(i)->setWork(m_currentWp);
}
}
/**
* @brief Start a number of miners.
*/
bool Farm::start()
{
// Prevent recursion
if (m_isMining.load(std::memory_order_relaxed))
return true;
Guard l(x_minerWork);
// Start all subscribed miners if none yet
if (!m_miners.size())
{
for (auto it = m_DevicesCollection.begin(); it != m_DevicesCollection.end(); it++)
{
TelemetryAccountType minerTelemetry;
#if ETH_ETHASHCUDA
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::Cuda)
{
minerTelemetry.prefix = "cu";
m_miners.push_back(std::shared_ptr<Miner>(
new CUDAMiner(m_miners.size(), m_CUSettings, it->second)));
}
#endif
#if ETH_ETHASHCL
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::OpenCL)
{
minerTelemetry.prefix = "cl";
m_miners.push_back(std::shared_ptr<Miner>(
new CLMiner(m_miners.size(), m_CLSettings, it->second)));
}
#endif
#if ETH_ETHASHCPU
if (it->second.subscriptionType == DeviceSubscriptionTypeEnum::Cpu)
{
minerTelemetry.prefix = "cp";
m_miners.push_back(std::shared_ptr<Miner>(
new CPUMiner(m_miners.size(), m_CPSettings, it->second)));
}
#endif
if (minerTelemetry.prefix.empty())
continue;
m_telemetry.miners.push_back(minerTelemetry);
m_miners.back()->startWorking();
}
// Initialize DAG Load mode
Miner::setDagLoadInfo(m_Settings.dagLoadMode, (unsigned int)m_miners.size());
m_isMining.store(true, std::memory_order_relaxed);
}
else
{
for (auto const& miner : m_miners)
miner->startWorking();
m_isMining.store(true, std::memory_order_relaxed);
}
return m_isMining.load(std::memory_order_relaxed);
}
/**
* @brief Stop all mining activities.
*/
void Farm::stop()
{
// Avoid re-entering if not actually mining.
// This, in fact, is also called by destructor
if (isMining())
{
{
Guard l(x_minerWork);
for (auto const& miner : m_miners)
{
miner->triggerStopWorking();
miner->kick_miner();
}
m_miners.clear();
m_isMining.store(false, std::memory_order_relaxed);
}
}
}
/**
* @brief Pauses the whole collection of miners
*/
void Farm::pause()
{
// Signal each miner to suspend mining
Guard l(x_minerWork);
m_paused.store(true, std::memory_order_relaxed);
for (auto const& m : m_miners)
m->pause(MinerPauseEnum::PauseDueToFarmPaused);
}
/**
* @brief Returns whether or not this farm is paused for any reason
*/
bool Farm::paused()
{
return m_paused.load(std::memory_order_relaxed);
}
/**
* @brief Resumes from a pause condition
*/
void Farm::resume()
{
// Signal each miner to resume mining
// Note ! Miners may stay suspended if other reasons
Guard l(x_minerWork);
m_paused.store(false, std::memory_order_relaxed);
for (auto const& m : m_miners)
m->resume(MinerPauseEnum::PauseDueToFarmPaused);
}
/**
* @brief Stop all mining activities and Starts them again
*/
void Farm::restart()
{
if (m_onMinerRestart)
m_onMinerRestart();
}
/**
* @brief Stop all mining activities and Starts them again (async post)
*/
void Farm::restart_async()
{
g_io_service.post(m_io_strand.wrap(boost::bind(&Farm::restart, this)));
}
/**
* @brief Spawn a reboot script (reboot.bat/reboot.sh)
* @return false if no matching file was found
*/
bool Farm::reboot(const std::vector<std::string>& args)
{
#if defined(_WIN32)
const char* filename = "reboot.bat";
#else
const char* filename = "reboot.sh";
#endif
return spawn_file_in_bin_dir(filename, args);
}
/**
* @brief Account solutions for miner and for farm
*/
void Farm::accountSolution(unsigned _minerIdx, SolutionAccountingEnum _accounting)
{
if (_accounting == SolutionAccountingEnum::Accepted)
{
m_telemetry.farm.solutions.accepted++;
m_telemetry.farm.solutions.tstamp = std::chrono::steady_clock::now();
m_telemetry.miners.at(_minerIdx).solutions.accepted++;
m_telemetry.miners.at(_minerIdx).solutions.tstamp = std::chrono::steady_clock::now();
return;
}
if (_accounting == SolutionAccountingEnum::Wasted)
{
m_telemetry.farm.solutions.wasted++;
m_telemetry.farm.solutions.tstamp = std::chrono::steady_clock::now();
m_telemetry.miners.at(_minerIdx).solutions.wasted++;
m_telemetry.miners.at(_minerIdx).solutions.tstamp = std::chrono::steady_clock::now();
return;
}
if (_accounting == SolutionAccountingEnum::Rejected)
{
m_telemetry.farm.solutions.rejected++;
m_telemetry.farm.solutions.tstamp = std::chrono::steady_clock::now();
m_telemetry.miners.at(_minerIdx).solutions.rejected++;
m_telemetry.miners.at(_minerIdx).solutions.tstamp = std::chrono::steady_clock::now();
return;
}
if (_accounting == SolutionAccountingEnum::Failed)
{
m_telemetry.farm.solutions.failed++;
m_telemetry.farm.solutions.tstamp = std::chrono::steady_clock::now();
m_telemetry.miners.at(_minerIdx).solutions.failed++;
m_telemetry.miners.at(_minerIdx).solutions.tstamp = std::chrono::steady_clock::now();
return;
}
}
/**
* @brief Gets the solutions account for the whole farm
*/
SolutionAccountType Farm::getSolutions()
{
return m_telemetry.farm.solutions;
}
/**
* @brief Gets the solutions account for single miner
*/
SolutionAccountType Farm::getSolutions(unsigned _minerIdx)
{
try
{
return m_telemetry.miners.at(_minerIdx).solutions;
}
catch (const std::exception&)
{
return SolutionAccountType();
}
}
/**
* @brief Provides the description of segments each miner is working on
* @return a JsonObject
*/
Json::Value Farm::get_nonce_scrambler_json()
{
Json::Value jRes;
jRes["start_nonce"] = toHex(m_nonce_scrambler, HexPrefix::Add);
jRes["device_width"] = m_nonce_segment_with;
jRes["device_count"] = (uint64_t)m_miners.size();
return jRes;
}
void Farm::setTStartTStop(unsigned tstart, unsigned tstop)
{
m_Settings.tempStart = tstart;
m_Settings.tempStop = tstop;
}
void Farm::submitProof(Solution const& _s)
{
g_io_service.post(m_io_strand.wrap(boost::bind(&Farm::submitProofAsync, this, _s)));
}
void Farm::submitProofAsync(Solution const& _s)
{
#ifdef DEV_BUILD
const bool dbuild = true;
#else
const bool dbuild = false;
#endif
if (!m_Settings.noEval || dbuild)
{
Result r = EthashAux::eval(_s.work.epoch, _s.work.block, _s.work.header, _s.nonce);
if (r.value > _s.work.boundary)
{
accountSolution(_s.midx, SolutionAccountingEnum::Failed);
cwarn << "GPU " << _s.midx
<< " gave incorrect result. Lower overclocking values if it happens frequently.";
return;
}
if (dbuild && (_s.mixHash != r.mixHash))
cwarn << "GPU " << _s.midx << " mix missmatch";
m_onSolutionFound(Solution{_s.nonce, r.mixHash, _s.work, _s.tstamp, _s.midx});
}
else
m_onSolutionFound(_s);
#ifdef DEV_BUILD
if (g_logOptions & LOG_SUBMIT)
cnote << "Submit time: "
<< std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - _s.tstamp)
.count()
<< " us.";
#endif
}
// Collects data about hashing and hardware status
void Farm::collectData(const boost::system::error_code& ec)
{
if (ec)
return;
// Reset hashrate (it will accumulate from miners)
float farm_hr = 0.0f;
// Process miners
for (auto const& miner : m_miners)
{
int minerIdx = miner->Index();
float hr = (miner->paused() ? 0.0f : miner->RetrieveHashRate());
farm_hr += hr;
m_telemetry.miners.at(minerIdx).hashrate = hr;
m_telemetry.miners.at(minerIdx).paused = miner->paused();
if (m_Settings.hwMon)
{
HwMonitorInfo hwInfo = miner->hwmonInfo();
unsigned int tempC = 0, fanpcnt = 0, powerW = 0;
if (hwInfo.deviceType == HwMonitorInfoType::NVIDIA && nvmlh)
{
int devIdx = hwInfo.deviceIndex;
if (devIdx == -1 && !hwInfo.devicePciId.empty())
{
if (map_nvml_handle.find(hwInfo.devicePciId) != map_nvml_handle.end())
{
devIdx = map_nvml_handle[hwInfo.devicePciId];
miner->setHwmonDeviceIndex(devIdx);
}
else
{
// This will prevent further tries to map
miner->setHwmonDeviceIndex(-2);
}
}
if (devIdx >= 0)
{
wrap_nvml_get_tempC(nvmlh, devIdx, &tempC);
wrap_nvml_get_fanpcnt(nvmlh, devIdx, &fanpcnt);
if (m_Settings.hwMon == 2)
wrap_nvml_get_power_usage(nvmlh, devIdx, &powerW);
}
}
else if (hwInfo.deviceType == HwMonitorInfoType::AMD)
{
#if defined(__linux)
if (sysfsh)
{
int devIdx = hwInfo.deviceIndex;
if (devIdx == -1 && !hwInfo.devicePciId.empty())
{
if (map_amdsysfs_handle.find(hwInfo.devicePciId) !=
map_amdsysfs_handle.end())
{
devIdx = map_amdsysfs_handle[hwInfo.devicePciId];
miner->setHwmonDeviceIndex(devIdx);
}
else
{
// This will prevent further tries to map
miner->setHwmonDeviceIndex(-2);
}
}
if (devIdx >= 0)
{
wrap_amdsysfs_get_tempC(sysfsh, devIdx, &tempC);
wrap_amdsysfs_get_fanpcnt(sysfsh, devIdx, &fanpcnt);
if (m_Settings.hwMon == 2)
wrap_amdsysfs_get_power_usage(sysfsh, devIdx, &powerW);
}
}
#else
if (adlh) // Windows only for AMD
{
int devIdx = hwInfo.deviceIndex;
if (devIdx == -1 && !hwInfo.devicePciId.empty())
{
if (map_adl_handle.find(hwInfo.devicePciId) != map_adl_handle.end())
{
devIdx = map_adl_handle[hwInfo.devicePciId];
miner->setHwmonDeviceIndex(devIdx);
}
else
{
// This will prevent further tries to map
miner->setHwmonDeviceIndex(-2);
}
}
if (devIdx >= 0)
{
wrap_adl_get_tempC(adlh, devIdx, &tempC);
wrap_adl_get_fanpcnt(adlh, devIdx, &fanpcnt);
if (m_Settings.hwMon == 2)
wrap_adl_get_power_usage(adlh, devIdx, &powerW);
}
}
#endif
}
// If temperature control has been enabled call
// check threshold
if (m_Settings.tempStop)
{
bool paused = miner->pauseTest(MinerPauseEnum::PauseDueToOverHeating);
if (!paused && (tempC >= m_Settings.tempStop))
miner->pause(MinerPauseEnum::PauseDueToOverHeating);
if (paused && (tempC <= m_Settings.tempStart))
miner->resume(MinerPauseEnum::PauseDueToOverHeating);
}
m_telemetry.miners.at(minerIdx).sensors.tempC = tempC;
m_telemetry.miners.at(minerIdx).sensors.fanP = fanpcnt;
m_telemetry.miners.at(minerIdx).sensors.powerW = powerW / ((double)1000.0);
}
m_telemetry.farm.hashrate = farm_hr;
miner->TriggerHashRateUpdate();
}
// Resubmit timer for another loop
m_collectTimer.expires_from_now(boost::posix_time::milliseconds(m_collectInterval));
m_collectTimer.async_wait(
m_io_strand.wrap(boost::bind(&Farm::collectData, this, boost::asio::placeholders::error)));
}
bool Farm::spawn_file_in_bin_dir(const char* filename, const std::vector<std::string>& args)
{
std::string fn = boost::dll::program_location().parent_path().string() +
"/" + // boost::filesystem::path::preferred_separator
filename;
try
{
if (!boost::filesystem::exists(fn))
return false;
/* anything in the file */
if (!boost::filesystem::file_size(fn))
return false;
#if defined(__linux)
struct stat sb;
if (stat(fn.c_str(), &sb) != 0)
return false;
/* just check if any exec flag is set.
still execution can fail (not the uid, not in the group, selinux, ...)
*/
if ((sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
return false;
#endif
/* spawn it (no wait,...) - fire and forget! */
boost::process::spawn(fn, args);
return true;
}
catch (...)
{
}
return false;
}
} // namespace eth
} // namespace dev

313
zano/libethcore/Farm.h Normal file
View File

@@ -0,0 +1,313 @@
/*
This file is part of progminer.
progminer 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.
progminer 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 progminer. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <atomic>
#include <list>
#include <thread>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/dll.hpp>
#include <boost/filesystem.hpp>
#include <boost/process.hpp>
#include <jsoncpp/json/json.h>
#include <libdevcore/Common.h>
#include <libdevcore/Worker.h>
#include <libethcore/Miner.h>
#include <libhwmon/wrapnvml.h>
#if defined(__linux)
#include <libhwmon/wrapamdsysfs.h>
#include <sys/stat.h>
#else
#include <libhwmon/wrapadl.h>
#endif
extern boost::asio::io_service g_io_service;
namespace dev
{
namespace eth
{
struct FarmSettings
{
unsigned dagLoadMode = 0; // 0 = Parallel; 1 = Serialized
bool noEval = false; // Whether or not to re-evaluate solutions
unsigned hwMon = 0; // 0 - No monitor; 1 - Temp and Fan; 2 - Temp Fan Power
unsigned ergodicity = 0; // 0=default, 1=per session, 2=per job
unsigned tempStart = 40; // Temperature threshold to restart mining (if paused)
unsigned tempStop = 0; // Temperature threshold to pause mining (overheating)
};
/**
* @brief A collective of Miners.
* Miners ask for work, then submit proofs
* @threadsafe
*/
class Farm : public FarmFace
{
public:
unsigned tstart = 0, tstop = 0;
Farm(std::map<std::string, DeviceDescriptor>& _DevicesCollection,
FarmSettings _settings, CUSettings _CUSettings, CLSettings _CLSettings,
CPSettings _CPSettings);
~Farm();
static Farm& f() { return *m_this; }
/**
* @brief Randomizes the nonce scrambler
*/
void shuffle();
/**
* @brief Sets the current mining mission.
* @param _wp The work package we wish to be mining.
*/
void setWork(WorkPackage const& _newWp);
/**
* @brief Start a number of miners.
*/
bool start();
/**
* @brief All mining activities to a full stop.
* Implies all mining threads are stopped.
*/
void stop();
/**
* @brief Signals all miners to suspend mining
*/
void pause();
/**
* @brief Whether or not the whole farm has been paused
*/
bool paused();
/**
* @brief Signals all miners to resume mining
*/
void resume();
/**
* @brief Stop all mining activities and Starts them again
*/
void restart();
/**
* @brief Stop all mining activities and Starts them again (async post)
*/
void restart_async();
/**
* @brief Returns whether or not the farm has been started
*/
bool isMining() const { return m_isMining.load(std::memory_order_relaxed); }
/**
* @brief Spawn a reboot script (reboot.bat/reboot.sh)
* @return false if no matching file was found
*/
bool reboot(const std::vector<std::string>& args);
/**
* @brief Get information on the progress of mining this work package.
* @return The progress with mining so far.
*/
TelemetryType& Telemetry() { return m_telemetry; }
/**
* @brief Gets current hashrate
*/
float HashRate() { return m_telemetry.farm.hashrate; };
/**
* @brief Gets the collection of pointers to miner instances
*/
std::vector<std::shared_ptr<Miner>> getMiners() { return m_miners; }
/**
* @brief Gets the number of miner instances
*/
unsigned getMinersCount() { return (unsigned)m_miners.size(); };
/**
* @brief Gets the pointer to a miner instance
*/
std::shared_ptr<Miner> getMiner(unsigned index)
{
try
{
return m_miners.at(index);
}
catch (const std::exception&)
{
return nullptr;
}
}
/**
* @brief Accounts a solution to a miner and, as a consequence, to
* the whole farm
*/
void accountSolution(unsigned _minerIdx, SolutionAccountingEnum _accounting) override;
/**
* @brief Gets the solutions account for the whole farm
*/
SolutionAccountType getSolutions();
/**
* @brief Gets the solutions account for single miner
*/
SolutionAccountType getSolutions(unsigned _minerIdx);
using SolutionFound = std::function<void(const Solution&)>;
using MinerRestart = std::function<void()>;
/**
* @brief Provides a valid header based upon that received previously with setWork().
* @param _bi The now-valid header.
* @return true if the header was good and that the Farm should pause until more work is
* submitted.
*/
void onSolutionFound(SolutionFound const& _handler) { m_onSolutionFound = _handler; }
void onMinerRestart(MinerRestart const& _handler) { m_onMinerRestart = _handler; }
/**
* @brief Gets the actual start nonce of the segment picked by the farm
*/
uint64_t get_nonce_scrambler() override { return m_nonce_scrambler; }
/**
* @brief Gets the actual width of each subsegment assigned to miners
*/
unsigned get_segment_width() override { return m_nonce_segment_with; }
/**
* @brief Sets the actual start nonce of the segment picked by the farm
*/
void set_nonce_scrambler(uint64_t n) { m_nonce_scrambler = n; }
/**
* @brief Sets the actual width of each subsegment assigned to miners
*/
void set_nonce_segment_width(unsigned n)
{
if (!m_currentWp.exSizeBytes)
m_nonce_segment_with = n;
}
/**
* @brief Provides the description of segments each miner is working on
* @return a JsonObject
*/
Json::Value get_nonce_scrambler_json();
void setTStartTStop(unsigned tstart, unsigned tstop);
unsigned get_tstart() override { return m_Settings.tempStart; }
unsigned get_tstop() override { return m_Settings.tempStop; }
unsigned get_ergodicity() override { return m_Settings.ergodicity; }
/**
* @brief Called from a Miner to note a WorkPackage has a solution.
* @param _s The solution.
*/
void submitProof(Solution const& _s) override;
bool getNoEval() { return m_Settings.noEval; }
private:
std::atomic<bool> m_paused = {false};
// Async submits solution serializing execution
// in Farm's strand
void submitProofAsync(Solution const& _s);
// Collects data about hashing and hardware status
void collectData(const boost::system::error_code& ec);
/**
* @brief Spawn a file - must be located in the directory of progminer binary
* @return false if file was not found or it is not executeable
*/
bool spawn_file_in_bin_dir(const char* filename, const std::vector<std::string>& args);
mutable Mutex x_minerWork;
std::vector<std::shared_ptr<Miner>> m_miners; // Collection of miners
WorkPackage m_currentWp;
EpochContext m_currentEc;
std::atomic<bool> m_isMining = {false};
TelemetryType m_telemetry; // Holds progress and status info for farm and miners
SolutionFound m_onSolutionFound;
MinerRestart m_onMinerRestart;
FarmSettings m_Settings; // Own Farm Settings
CUSettings m_CUSettings; // Cuda settings passed to CUDA Miner instantiator
CLSettings m_CLSettings; // OpenCL settings passed to CL Miner instantiator
CPSettings m_CPSettings; // CPU settings passed to CPU Miner instantiator
boost::asio::io_service::strand m_io_strand;
boost::asio::deadline_timer m_collectTimer;
static const int m_collectInterval = 5000;
string m_pool_addresses;
// StartNonce (non-NiceHash Mode) and
// segment width assigned to each GPU as exponent of 2
// considering an average block time of 15 seconds
// a single device GPU should need a speed of 286 Mh/s
// before it consumes the whole 2^32 segment
uint64_t m_nonce_scrambler;
unsigned int m_nonce_segment_with = 32;
// Wrappers for hardware monitoring libraries and their mappers
wrap_nvml_handle* nvmlh = nullptr;
std::map<string, int> map_nvml_handle = {};
#if defined(__linux)
wrap_amdsysfs_handle* sysfsh = nullptr;
std::map<string, int> map_amdsysfs_handle = {};
#else
wrap_adl_handle* adlh = nullptr;
std::map<string, int> map_adl_handle = {};
#endif
static Farm* m_this;
std::map<std::string, DeviceDescriptor>& m_DevicesCollection;
};
} // namespace eth
} // namespace dev

207
zano/libethcore/Miner.cpp Normal file
View File

@@ -0,0 +1,207 @@
/*
This file is part of ethereum.
progminer 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.
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 progminer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Miner.h"
namespace dev
{
namespace eth
{
unsigned Miner::s_dagLoadMode = 0;
unsigned Miner::s_dagLoadIndex = 0;
unsigned Miner::s_minersCount = 0;
FarmFace* FarmFace::m_this = nullptr;
DeviceDescriptor Miner::getDescriptor()
{
return m_deviceDescriptor;
}
void Miner::setWork(WorkPackage const& _work)
{
{
boost::mutex::scoped_lock l(x_work);
// Void work if this miner is paused
if (paused())
m_work.header = h256();
else
m_work = _work;
#ifdef DEV_BUILD
m_workSwitchStart = std::chrono::steady_clock::now();
#endif
}
kick_miner();
}
void Miner::pause(MinerPauseEnum what)
{
boost::mutex::scoped_lock l(x_pause);
m_pauseFlags.set(what);
m_work.header = h256();
kick_miner();
}
bool Miner::paused()
{
boost::mutex::scoped_lock l(x_pause);
return m_pauseFlags.any();
}
bool Miner::pauseTest(MinerPauseEnum what)
{
boost::mutex::scoped_lock l(x_pause);
return m_pauseFlags.test(what);
}
std::string Miner::pausedString()
{
boost::mutex::scoped_lock l(x_pause);
std::string retVar;
if (m_pauseFlags.any())
{
for (int i = 0; i < MinerPauseEnum::Pause_MAX; i++)
{
if (m_pauseFlags[(MinerPauseEnum)i])
{
if (!retVar.empty())
retVar.append("; ");
if (i == MinerPauseEnum::PauseDueToOverHeating)
retVar.append("Overheating");
else if (i == MinerPauseEnum::PauseDueToAPIRequest)
retVar.append("Api request");
else if (i == MinerPauseEnum::PauseDueToFarmPaused)
retVar.append("Farm suspended");
else if (i == MinerPauseEnum::PauseDueToInsufficientMemory)
retVar.append("Insufficient GPU memory");
else if (i == MinerPauseEnum::PauseDueToInitEpochError)
retVar.append("Epoch initialization error");
}
}
}
return retVar;
}
void Miner::resume(MinerPauseEnum fromwhat)
{
boost::mutex::scoped_lock l(x_pause);
m_pauseFlags.reset(fromwhat);
//if (!m_pauseFlags.any())
//{
// // TODO Push most recent job from farm ?
// // If we do not push a new job the miner will stay idle
// // till a new job arrives
//}
}
float Miner::RetrieveHashRate() noexcept
{
return m_hashRate.load(std::memory_order_relaxed);
}
void Miner::TriggerHashRateUpdate() noexcept
{
bool b = false;
if (m_hashRateUpdate.compare_exchange_weak(b, true, std::memory_order_relaxed))
return;
// GPU didn't respond to last trigger, assume it's dead.
// This can happen on CUDA if:
// runtime of --cuda-grid-size * --cuda-streams exceeds time of m_collectInterval
m_hashRate = 0.0;
}
bool Miner::initEpoch()
{
// When loading of DAG is sequential wait for
// this instance to become current
if (s_dagLoadMode == DAG_LOAD_MODE_SEQUENTIAL)
{
while (s_dagLoadIndex < m_index)
{
boost::system_time const timeout =
boost::get_system_time() + boost::posix_time::seconds(3);
boost::mutex::scoped_lock l(x_work);
m_dag_loaded_signal.timed_wait(l, timeout);
}
if (shouldStop())
return false;
}
// Run the internal initialization
// specific for miner
bool result = initEpoch_internal();
// Advance to next miner or reset to zero for
// next run if all have processed
if (s_dagLoadMode == DAG_LOAD_MODE_SEQUENTIAL)
{
s_dagLoadIndex = (m_index + 1);
if (s_minersCount == s_dagLoadIndex)
s_dagLoadIndex = 0;
else
m_dag_loaded_signal.notify_all();
}
return result;
}
WorkPackage Miner::work() const
{
boost::mutex::scoped_lock l(x_work);
return m_work;
}
void Miner::updateHashRate(uint32_t _groupSize, uint32_t _increment) noexcept
{
m_groupCount += _increment;
bool b = true;
if (!m_hashRateUpdate.compare_exchange_weak(b, false, std::memory_order_relaxed))
return;
using namespace std::chrono;
auto t = steady_clock::now();
auto us = duration_cast<microseconds>(t - m_hashTime).count();
m_hashTime = t;
m_hashRate.store(
us ? (float(m_groupCount * _groupSize) * 1.0e6f) / us : 0.0f, std::memory_order_relaxed);
m_groupCount = 0;
}
bool Miner::dropThreadPriority()
{
#if defined(__linux__)
// Non Posix hack to lower compile thread's priority. Under POSIX
// the nice value is a process attribute, under Linux it's a thread
// attribute
return nice(5) != -1;
#elif defined(WIN32)
return SetThreadPriority(m_compileThread->native_handle(), THREAD_PRIORITY_BELOW_NORMAL);
#else
return false;
#endif
}
} // namespace eth
} // namespace dev

485
zano/libethcore/Miner.h Normal file
View File

@@ -0,0 +1,485 @@
/*
This file is part of progminer.
progminer 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.
progminer 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 progminer. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <bitset>
#include <list>
#include <numeric>
#include <string>
#include "EthashAux.h"
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
#include <libdevcore/Worker.h>
#include <boost/asio.hpp>
#include <boost/format.hpp>
#include <boost/thread.hpp>
#define DAG_LOAD_MODE_PARALLEL 0
#define DAG_LOAD_MODE_SEQUENTIAL 1
using namespace std;
extern boost::asio::io_service g_io_service;
namespace dev
{
namespace eth
{
enum class DeviceTypeEnum
{
Unknown,
Cpu,
Gpu,
Accelerator
};
enum class DeviceSubscriptionTypeEnum
{
None,
OpenCL,
Cuda,
Cpu
};
enum class MinerType
{
Mixed,
CL,
CUDA,
CPU
};
enum class HwMonitorInfoType
{
UNKNOWN,
NVIDIA,
AMD,
CPU
};
enum class ClPlatformTypeEnum
{
Unknown,
Amd,
Clover,
Nvidia
};
enum class SolutionAccountingEnum
{
Accepted,
Rejected,
Wasted,
Failed
};
struct MinerSettings
{
vector<unsigned> devices;
};
// Holds settings for CUDA Miner
struct CUSettings : public MinerSettings
{
unsigned streams = 2;
unsigned schedule = 4;
unsigned gridSize = 256;
unsigned blockSize = 512;
unsigned parallelHash = 4;
};
// Holds settings for OpenCL Miner
struct CLSettings : public MinerSettings
{
bool noBinary = false;
unsigned globalWorkSize = 0;
unsigned globalWorkSizeMultiplier = 32768;
unsigned localWorkSize = 256;
};
// Holds settings for CPU Miner
struct CPSettings : public MinerSettings
{
};
struct SolutionAccountType
{
unsigned accepted = 0;
unsigned rejected = 0;
unsigned wasted = 0;
unsigned failed = 0;
std::chrono::steady_clock::time_point tstamp = std::chrono::steady_clock::now();
string str()
{
string _ret = "A" + to_string(accepted);
if (wasted)
_ret.append(":W" + to_string(wasted));
if (rejected)
_ret.append(":R" + to_string(rejected));
if (failed)
_ret.append(":F" + to_string(failed));
return _ret;
};
};
struct HwSensorsType
{
int tempC = 0;
int fanP = 0;
double powerW = 0.0;
string str()
{
string _ret = to_string(tempC) + "C " + to_string(fanP) + "%";
if (powerW)
_ret.append(boost::str(boost::format("%f") % powerW));
return _ret;
};
};
struct TelemetryAccountType
{
string prefix = "";
float hashrate = 0.0f;
bool paused = false;
HwSensorsType sensors;
SolutionAccountType solutions;
};
struct DeviceDescriptor
{
DeviceTypeEnum type = DeviceTypeEnum::Unknown;
DeviceSubscriptionTypeEnum subscriptionType = DeviceSubscriptionTypeEnum::None;
string uniqueId; // For GPUs this is the PCI ID
size_t totalMemory; // Total memory available by device
string name; // Device Name
bool clDetected; // For OpenCL detected devices
string clName;
unsigned int clPlatformId;
string clPlatformName;
ClPlatformTypeEnum clPlatformType = ClPlatformTypeEnum::Unknown;
string clPlatformVersion;
unsigned int clPlatformVersionMajor;
unsigned int clPlatformVersionMinor;
unsigned int clDeviceOrdinal;
unsigned int clDeviceIndex;
string clDeviceVersion;
unsigned int clDeviceVersionMajor;
unsigned int clDeviceVersionMinor;
string clBoardName;
size_t clMaxMemAlloc;
size_t clMaxWorkGroup;
unsigned int clMaxComputeUnits;
string clNvCompute;
unsigned int clNvComputeMajor;
unsigned int clNvComputeMinor;
bool cuDetected; // For CUDA detected devices
string cuName;
unsigned int cuDeviceOrdinal;
unsigned int cuDeviceIndex;
string cuCompute;
unsigned int cuComputeMajor;
unsigned int cuComputeMinor;
int cpCpuNumer; // For CPU
};
struct HwMonitorInfo
{
HwMonitorInfoType deviceType = HwMonitorInfoType::UNKNOWN;
string devicePciId;
int deviceIndex = -1;
};
/// Pause mining
enum MinerPauseEnum
{
PauseDueToOverHeating,
PauseDueToAPIRequest,
PauseDueToFarmPaused,
PauseDueToInsufficientMemory,
PauseDueToInitEpochError,
Pause_MAX // Must always be last as a placeholder of max count
};
/// Keeps track of progress for farm and miners
struct TelemetryType
{
bool hwmon = false;
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
TelemetryAccountType farm;
std::vector<TelemetryAccountType> miners;
std::string str()
{
std::stringstream _ret;
/*
Output is formatted as
Run <h:mm> <Solutions> <Speed> [<miner> ...]
where
- Run h:mm Duration of the batch
- Solutions Detailed solutions (A+R+F) per farm
- Speed Actual hashing rate
each <miner> reports
- speed Actual speed at the same level of
magnitude for farm speed
- sensors Values of sensors (temp, fan, power)
- solutions Optional (LOG_PER_GPU) Solutions detail per GPU
*/
/*
Calculate duration
*/
auto duration = std::chrono::steady_clock::now() - start;
auto hours = std::chrono::duration_cast<std::chrono::hours>(duration);
int hoursSize = (hours.count() > 9 ? (hours.count() > 99 ? 3 : 2) : 1);
duration -= hours;
auto minutes = std::chrono::duration_cast<std::chrono::minutes>(duration);
_ret << EthGreen << setw(hoursSize) << hours.count() << ":" << setfill('0') << setw(2)
<< minutes.count() << EthReset << EthWhiteBold << " " << farm.solutions.str()
<< EthReset << " ";
/*
Github : @AndreaLanfranchi
I whish I could simply make use of getFormattedHashes but in this case
this would be misleading as total hashrate could be of a different order
of magnitude than the hashrate expressed by single devices.
Thus I need to set the vary same scaling index on the farm and on devices
*/
static string suffixes[] = {"h", "Kh", "Mh", "Gh"};
float hr = farm.hashrate;
int magnitude = 0;
while (hr > 1000.0f && magnitude <= 3)
{
hr /= 1000.0f;
magnitude++;
}
_ret << EthTealBold << std::fixed << std::setprecision(2) << hr << " "
<< suffixes[magnitude] << EthReset << " - ";
int i = -1; // Current miner index
int m = miners.size() - 1; // Max miner index
for (TelemetryAccountType miner : miners)
{
i++;
hr = miner.hashrate;
if (hr > 0.0f)
hr /= pow(1000.0f, magnitude);
_ret << (miner.paused ? EthRed : "") << miner.prefix << i << " " << EthTeal
<< std::fixed << std::setprecision(2) << hr << EthReset;
if (hwmon)
_ret << " " << EthTeal << miner.sensors.str() << EthReset;
// Eventually push also solutions per single GPU
if (g_logOptions & LOG_PER_GPU)
_ret << " " << EthTeal << miner.solutions.str() << EthReset;
// Separator if not the last miner index
if (i < m)
_ret << ", ";
}
return _ret.str();
};
};
/**
* @brief Class for hosting one or more Miners.
* @warning Must be implemented in a threadsafe manner since it will be called from multiple
* miner threads.
*/
class FarmFace
{
public:
FarmFace() { m_this = this; }
static FarmFace& f() { return *m_this; };
virtual ~FarmFace() = default;
virtual unsigned get_tstart() = 0;
virtual unsigned get_tstop() = 0;
virtual unsigned get_ergodicity() = 0;
/**
* @brief Called from a Miner to note a WorkPackage has a solution.
* @param _p The solution.
* @return true iff the solution was good (implying that mining should be .
*/
virtual void submitProof(Solution const& _p) = 0;
virtual void accountSolution(unsigned _minerIdx, SolutionAccountingEnum _accounting) = 0;
virtual uint64_t get_nonce_scrambler() = 0;
virtual unsigned get_segment_width() = 0;
private:
static FarmFace* m_this;
};
/**
* @brief A miner - a member and adoptee of the Farm.
* @warning Not threadsafe. It is assumed Farm will synchronise calls to/from this class.
*/
class Miner : public Worker
{
public:
Miner(std::string const& _name, unsigned _index)
: Worker(_name + std::to_string(_index)), m_index(_index)
{}
~Miner() override = default;
// Sets basic info for eventual serialization of DAG load
static void setDagLoadInfo(unsigned _mode, unsigned _devicecount)
{
s_dagLoadMode = _mode;
s_dagLoadIndex = 0;
s_minersCount = _devicecount;
};
/**
* @brief Gets the device descriptor assigned to this instance
*/
DeviceDescriptor getDescriptor();
/**
* @brief Assigns hashing work to this instance
*/
void setWork(WorkPackage const& _work);
/**
* @brief Assigns Epoch context to this instance
*/
void setEpoch(EpochContext const& _ec) { m_epochContext = _ec; }
unsigned Index() { return m_index; };
HwMonitorInfo hwmonInfo() { return m_hwmoninfo; }
void setHwmonDeviceIndex(int i) { m_hwmoninfo.deviceIndex = i; }
/**
* @brief Kick an asleep miner.
*/
virtual void kick_miner() = 0;
/**
* @brief Pauses mining setting a reason flag
*/
void pause(MinerPauseEnum what);
/**
* @brief Whether or not this miner is paused for any reason
*/
bool paused();
/**
* @brief Checks if the given reason for pausing is currently active
*/
bool pauseTest(MinerPauseEnum what);
/**
* @brief Returns the human readable reason for this miner being paused
*/
std::string pausedString();
/**
* @brief Cancels a pause flag.
* @note Miner can be paused for multiple reasons at a time.
*/
void resume(MinerPauseEnum fromwhat);
/**
* @brief Retrieves currrently collected hashrate
*/
float RetrieveHashRate() noexcept;
void TriggerHashRateUpdate() noexcept;
protected:
/**
* @brief Initializes miner's device.
*/
virtual bool initDevice() = 0;
/**
* @brief Initializes miner to current (or changed) epoch.
*/
bool initEpoch();
/**
* @brief Miner's specific initialization to current (or changed) epoch.
*/
virtual bool initEpoch_internal() = 0;
/**
* @brief Returns current workpackage this miner is working on
*/
WorkPackage work() const;
void updateHashRate(uint32_t _groupSize, uint32_t _increment) noexcept;
bool dropThreadPriority();
static unsigned s_minersCount; // Total Number of Miners
static unsigned s_dagLoadMode; // Way dag should be loaded
static unsigned s_dagLoadIndex; // In case of serialized load of dag this is the index of miner
// which should load next
const unsigned m_index = 0; // Ordinal index of the Instance (not the device)
DeviceDescriptor m_deviceDescriptor; // Info about the device
EpochContext m_epochContext;
#ifdef DEV_BUILD
std::chrono::steady_clock::time_point m_workSwitchStart;
#endif
HwMonitorInfo m_hwmoninfo;
mutable boost::mutex x_work;
mutable boost::mutex x_pause;
boost::condition_variable m_new_work_signal;
boost::condition_variable m_dag_loaded_signal;
uint64_t m_nextProgpowPeriod = 0;
boost::thread* m_compileThread = nullptr;
private:
bitset<MinerPauseEnum::Pause_MAX> m_pauseFlags;
WorkPackage m_work;
std::chrono::steady_clock::time_point m_hashTime = std::chrono::steady_clock::now();
std::atomic<float> m_hashRate = {0.0};
uint64_t m_groupCount = 0;
atomic<bool> m_hashRateUpdate = {false};
};
} // namespace eth
} // namespace dev