Files
mines/zano/progminer/main.cpp

1386 lines
55 KiB
C++

/*
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 <CLI/CLI.hpp>
#include <progminer/buildinfo.h>
#include <condition_variable>
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
#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
#include <libpoolprotocols/PoolManager.h>
#if API_CORE
#include <libapicore/ApiServer.h>
#include <regex>
#endif
#if defined(__linux__) || defined(__APPLE__)
#include <execinfo.h>
#elif defined(_WIN32)
#include <Windows.h>
#endif
using namespace std;
using namespace dev;
using namespace dev::eth;
// Global vars
bool g_running = false;
bool g_exitOnError = false; // Whether or not progminer should exit on mining threads errors
condition_variable g_shouldstop;
boost::asio::io_service g_io_service; // The IO service itself
struct MiningChannel : public LogChannel
{
static const char* name() { return EthGreen " m"; }
static const int verbosity = 2;
};
#define minelog clog(MiningChannel)
#if ETH_DBUS
#include <progminer/DBusInt.h>
#endif
class MinerCLI
{
public:
enum class OperationMode
{
None,
Simulation,
Mining
};
MinerCLI() : m_cliDisplayTimer(g_io_service), m_io_strand(g_io_service)
{
// Initialize display timer as sleeper
m_cliDisplayTimer.expires_from_now(boost::posix_time::pos_infin);
m_cliDisplayTimer.async_wait(m_io_strand.wrap(boost::bind(
&MinerCLI::cliDisplayInterval_elapsed, this, boost::asio::placeholders::error)));
// Start io_service in it's own thread
m_io_thread = std::thread{boost::bind(&boost::asio::io_service::run, &g_io_service)};
// Io service is now live and running
// All components using io_service should post to reference of g_io_service
// and should not start/stop or even join threads (which heavily time consuming)
}
virtual ~MinerCLI()
{
m_cliDisplayTimer.cancel();
g_io_service.stop();
m_io_thread.join();
}
void cliDisplayInterval_elapsed(const boost::system::error_code& ec)
{
if (!ec && g_running)
{
string logLine =
PoolManager::p().isConnected() ? Farm::f().Telemetry().str() : "Not connected";
minelog << logLine;
#if ETH_DBUS
dbusint.send(Farm::f().Telemetry().str());
#endif
// Resubmit timer
m_cliDisplayTimer.expires_from_now(boost::posix_time::seconds(m_cliDisplayInterval));
m_cliDisplayTimer.async_wait(m_io_strand.wrap(boost::bind(
&MinerCLI::cliDisplayInterval_elapsed, this, boost::asio::placeholders::error)));
}
}
static void signalHandler(int sig)
{
dev::setThreadName("main");
switch (sig)
{
#if defined(__linux__) || defined(__APPLE__)
#define BACKTRACE_MAX_FRAMES 100
case SIGSEGV:
static bool in_handler = false;
if (!in_handler)
{
int j, nptrs;
void* buffer[BACKTRACE_MAX_FRAMES];
char** symbols;
in_handler = true;
dev::setThreadName("main");
cerr << "SIGSEGV encountered ...\n";
cerr << "stack trace:\n";
nptrs = backtrace(buffer, BACKTRACE_MAX_FRAMES);
cerr << "backtrace() returned " << nptrs << " addresses\n";
symbols = backtrace_symbols(buffer, nptrs);
if (symbols == NULL)
{
perror("backtrace_symbols()");
exit(EXIT_FAILURE); // Also exit 128 ??
}
for (j = 0; j < nptrs; j++)
cerr << symbols[j] << "\n";
free(symbols);
in_handler = false;
}
exit(128);
#undef BACKTRACE_MAX_FRAMES
#endif
case (999U):
// Compiler complains about the lack of
// a case statement in Windows
// this makes it happy.
break;
default:
cnote << "Got interrupt ...";
g_running = false;
g_shouldstop.notify_all();
break;
}
}
#if API_CORE
static void ParseBind(
const std::string& inaddr, std::string& outaddr, int& outport, bool advertise_negative_port)
{
std::regex pattern("([\\da-fA-F\\.\\:]*)\\:([\\d\\-]*)");
std::smatch matches;
if (std::regex_match(inaddr, matches, pattern))
{
// Validate Ip address
boost::system::error_code ec;
outaddr = boost::asio::ip::address::from_string(matches[1], ec).to_string();
if (ec)
throw std::invalid_argument("Invalid Ip Address");
// Parse port ( Let exception throw )
outport = std::stoi(matches[2]);
if (advertise_negative_port)
{
if (outport < -65535 || outport > 65535 || outport == 0)
throw std::invalid_argument(
"Invalid port number. Allowed non zero values in range [-65535 .. 65535]");
}
else
{
if (outport < 1 || outport > 65535)
throw std::invalid_argument(
"Invalid port number. Allowed non zero values in range [1 .. 65535]");
}
}
else
{
throw std::invalid_argument("Invalid syntax");
}
}
#endif
bool validateArgs(int argc, char** argv)
{
std::queue<string> warnings;
CLI::App app("Progminer - GPU Ethash miner");
bool bhelp = false;
string shelpExt;
app.set_help_flag();
app.add_flag("-h,--help", bhelp, "Show help");
app.add_option("-H,--help-ext", shelpExt, "Show extended help")
->default_val("")
->check(CLI::IsMember({
"con", "test",
#if ETH_ETHASHCL
"cl",
#endif
#if ETH_ETHASHCUDA
"cu",
#endif
#if ETH_ETHASHCPU
"cp",
#endif
#if API_CORE
"api",
#endif
"misc", "env"
}));
bool version = false;
app.add_option("--ergodicity", m_FarmSettings.ergodicity)->default_val(0)->check(CLI::Range(0, 2));
app.add_flag("-V,--version", version, "Show program version");
app.add_option("-v,--verbosity", g_logOptions)->default_val(0)->check(CLI::Range(LOG_NEXT - 1));
app.add_option("--farm-recheck", m_PoolSettings.getWorkPollInterval)->default_val(500)->check(CLI::Range(1, 99999));
app.add_option("--farm-retries", m_PoolSettings.connectionMaxRetries)->default_val(5)->check(CLI::Range(0, 99999));
app.add_option("--work-timeout", m_PoolSettings.noWorkTimeout)->default_val(180)
->check(CLI::Range(100000, 1000000));
app.add_option("--response-timeout", m_PoolSettings.noResponseTimeout)->default_val(2)
->check(CLI::Range(2, 999));
app.add_flag("-R,--report-hashrate,--report-hr", m_PoolSettings.reportHashrate, "");
app.add_option("--display-interval", m_cliDisplayInterval)->default_val(5)
->check(CLI::Range(1, 1800));
app.add_option("--HWMON", m_FarmSettings.hwMon)->default_val(0)->check(CLI::Range(0, 2));
app.add_flag("--exit", g_exitOnError, "");
vector<string> pools;
app.add_option("-P,--pool", pools, "");
app.add_option("--failover-timeout", m_PoolSettings.poolFailoverTimeout)->default_val(0)
->check(CLI::Range(0, 999));
app.add_flag("--nocolor", g_logNoColor, "");
app.add_flag("--syslog", g_logSyslog, "");
app.add_flag("--stdout", g_logStdout, "");
#if API_CORE
app.add_option("--api-bind", m_api_bind)->default_val("127.0.0.1")
->check([this](const string& bind_arg) -> string {
try
{
MinerCLI::ParseBind(bind_arg, this->m_api_address, this->m_api_port, true);
}
catch (const std::exception& ex)
{
throw CLI::ValidationError("--api-bind", ex.what());
}
// not sure what to return, and the documentation doesn't say either.
// https://github.com/CLIUtils/CLI11/issues/144
return string("");
});
app.add_option("--api-port", m_api_port)->default_val(0)->check(CLI::Range(-65535, 65535));
app.add_option("--api-password", m_api_password, "");
#endif
#if ETH_ETHASHCL || ETH_ETHASHCUDA || ETH_ETHASH_CPU
app.add_flag("--list-devices", m_shouldListDevices, "");
#endif
#if ETH_ETHASHCL
app.add_option("--opencl-device,--opencl-devices,--cl-devices", m_CLSettings.devices, "");
app.add_option("--cl-global-work", m_CLSettings.globalWorkSize)->default_val(8192);
app.add_option("--cl-local-work", m_CLSettings.localWorkSize)->default_val(128)->check(CLI::IsMember({64, 128, 256}));
#endif
#if ETH_ETHASHCUDA
app.add_option("--cuda-devices,--cu-devices", m_CUSettings.devices, "");
app.add_option("--cuda-grid-size,--cu-grid-size", m_CUSettings.gridSize)->default_val(8192)
->check(CLI::Range(1, 131072));
app.add_option("--cuda-block-size,--cu-block-size", m_CUSettings.blockSize)->default_val(128)->check(CLI::IsMember({32, 64, 128, 256, 512}));
app.add_option("--cuda-parallel-hash,--cu-parallel-hash", m_CUSettings.parallelHash)->default_val(4)->check(CLI::IsMember({1, 2, 4, 8}));
string sched = "sync";
app.add_option("--cuda-schedule,--cu-schedule", sched)->default_val("sync")->check(CLI::IsMember({"auto", "spin", "yield", "sync"}));
app.add_option("--cuda-streams,--cu-streams", m_CUSettings.streams)->default_val(2)
->check(CLI::Range(1, 99));
#endif
#if ETH_ETHASHCPU
app.add_option("--cpu-devices,--cp-devices", m_CPSettings.devices, "");
#endif
app.add_flag("--noeval", m_FarmSettings.noEval, "");
app.add_option("-L,--dag-load-mode", m_FarmSettings.dagLoadMode)->default_val(0)->check(CLI::Range(1));
bool cl_miner = false;
app.add_flag("-G,--opencl", cl_miner, "");
bool cuda_miner = false;
app.add_flag("-U,--cuda", cuda_miner, "");
bool cpu_miner = false;
#if ETH_ETHASHCPU
app.add_flag("--cpu", cpu_miner, "");
#endif
auto sim_opt = app.add_option("-Z,--simulation,-M,--benchmark", m_PoolSettings.benchmarkBlock)->default_val(0);
app.add_option("--diff", m_PoolSettings.benchmarkDiff, "")
->check(CLI::Range(0.00001, 10000.0));
app.add_option("--tstop", m_FarmSettings.tempStop)->default_val(95)->check(CLI::Range(30, 100));
app.add_option("--tstart", m_FarmSettings.tempStart)->default_val(40)->check(CLI::Range(30, 100));
// Exception handling is held at higher level
app.parse(argc, argv);
if (bhelp)
{
help();
return false;
}
else if (!shelpExt.empty())
{
helpExt(shelpExt);
return false;
}
else if (version)
{
return false;
}
if (cl_miner)
m_minerType = MinerType::CL;
else if (cuda_miner)
m_minerType = MinerType::CUDA;
else if (cpu_miner)
m_minerType = MinerType::CPU;
else
m_minerType = MinerType::Mixed;
/*
Operation mode Simulation do not require pool definitions
Operation mode Stratum or GetWork do need at least one
*/
if (sim_opt->count())
{
m_mode = OperationMode::Simulation;
pools.clear();
m_PoolSettings.connections.push_back(
std::shared_ptr<URI>(new URI("simulation://localhost:0", true)));
}
else
{
m_mode = OperationMode::Mining;
}
if (!m_shouldListDevices && m_mode != OperationMode::Simulation)
{
if (!pools.size())
throw std::invalid_argument(
"At least one pool definition required. See -P argument.");
for (size_t i = 0; i < pools.size(); i++)
{
std::string url = pools.at(i);
if (url == "exit")
{
if (i == 0)
throw std::invalid_argument(
"'exit' failover directive can't be the first in -P arguments list.");
else
url = "stratum+tcp://-:x@exit:0";
}
try
{
std::shared_ptr<URI> uri = std::shared_ptr<URI>(new URI(url));
if (uri->SecLevel() != dev::SecureLevel::NONE &&
uri->HostNameType() != dev::UriHostNameType::Dns && !getenv("SSL_NOVERIFY"))
{
warnings.push(
"You have specified host " + uri->Host() + " with encryption enabled.");
warnings.push("Certificate validation will likely fail");
}
m_PoolSettings.connections.push_back(uri);
}
catch (const std::exception& _ex)
{
string what = _ex.what();
throw std::runtime_error("Bad URI : " + what);
}
}
}
#if ETH_ETHASHCUDA
if (sched == "auto")
m_CUSettings.schedule = 0;
else if (sched == "spin")
m_CUSettings.schedule = 1;
else if (sched == "yield")
m_CUSettings.schedule = 2;
else if (sched == "sync")
m_CUSettings.schedule = 4;
#endif
if (m_FarmSettings.tempStop)
{
// If temp threshold set HWMON at least to 1
m_FarmSettings.hwMon = std::max((unsigned int)m_FarmSettings.hwMon, 1U);
if (m_FarmSettings.tempStop <= m_FarmSettings.tempStart)
{
std::string what = "-tstop must be greater than -tstart";
throw std::invalid_argument(what);
}
}
// Output warnings if any
if (warnings.size())
{
while (warnings.size())
{
cout << warnings.front() << endl;
warnings.pop();
}
cout << endl;
}
return true;
}
void execute()
{
#if ETH_ETHASHCL
if (m_minerType == MinerType::CL || m_minerType == MinerType::Mixed)
CLMiner::enumDevices(m_DevicesCollection);
#endif
#if ETH_ETHASHCUDA
if (m_minerType == MinerType::CUDA || m_minerType == MinerType::Mixed)
CUDAMiner::enumDevices(m_DevicesCollection);
#endif
#if ETH_ETHASHCPU
if (m_minerType == MinerType::CPU)
CPUMiner::enumDevices(m_DevicesCollection);
#endif
// Can't proceed without any GPU
if (!m_DevicesCollection.size())
throw std::runtime_error("No usable mining devices found");
// If requested list detected devices and exit
if (m_shouldListDevices)
{
cout << setw(4) << " Id ";
cout << setiosflags(ios::left) << setw(10) << "Pci Id ";
cout << setw(5) << "Type ";
cout << setw(30) << "Name ";
#if ETH_ETHASHCUDA
if (m_minerType == MinerType::CUDA || m_minerType == MinerType::Mixed)
{
cout << setw(5) << "CUDA ";
cout << setw(4) << "SM ";
}
#endif
#if ETH_ETHASHCL
if (m_minerType == MinerType::CL || m_minerType == MinerType::Mixed)
cout << setw(5) << "CL ";
#endif
cout << resetiosflags(ios::left) << setw(13) << "Total Memory"
<< " ";
#if ETH_ETHASHCL
if (m_minerType == MinerType::CL || m_minerType == MinerType::Mixed)
{
cout << resetiosflags(ios::left) << setw(13) << "Cl Max Alloc"
<< " ";
cout << resetiosflags(ios::left) << setw(13) << "Cl Max W.Grp"
<< " ";
}
#endif
cout << resetiosflags(ios::left) << endl;
cout << setw(4) << "--- ";
cout << setiosflags(ios::left) << setw(10) << "--------- ";
cout << setw(5) << "---- ";
cout << setw(30) << "----------------------------- ";
#if ETH_ETHASHCUDA
if (m_minerType == MinerType::CUDA || m_minerType == MinerType::Mixed)
{
cout << setw(5) << "---- ";
cout << setw(4) << "--- ";
}
#endif
#if ETH_ETHASHCL
if (m_minerType == MinerType::CL || m_minerType == MinerType::Mixed)
cout << setw(5) << "---- ";
#endif
cout << resetiosflags(ios::left) << setw(13) << "------------"
<< " ";
#if ETH_ETHASHCL
if (m_minerType == MinerType::CL || m_minerType == MinerType::Mixed)
{
cout << resetiosflags(ios::left) << setw(13) << "------------"
<< " ";
cout << resetiosflags(ios::left) << setw(13) << "------------"
<< " ";
}
#endif
cout << resetiosflags(ios::left) << endl;
std::map<string, DeviceDescriptor>::iterator it = m_DevicesCollection.begin();
while (it != m_DevicesCollection.end())
{
auto i = std::distance(m_DevicesCollection.begin(), it);
cout << setw(3) << i << " ";
cout << setiosflags(ios::left) << setw(10) << it->first;
cout << setw(5);
switch (it->second.type)
{
case DeviceTypeEnum::Cpu:
cout << "Cpu";
break;
case DeviceTypeEnum::Gpu:
cout << "Gpu";
break;
case DeviceTypeEnum::Accelerator:
cout << "Acc";
break;
default:
break;
}
cout << setw(30) << (it->second.name).substr(0, 28);
#if ETH_ETHASHCUDA
if (m_minerType == MinerType::CUDA || m_minerType == MinerType::Mixed)
{
cout << setw(5) << (it->second.cuDetected ? "Yes" : "");
cout << setw(4) << it->second.cuCompute;
}
#endif
#if ETH_ETHASHCL
if (m_minerType == MinerType::CL || m_minerType == MinerType::Mixed)
cout << setw(5) << (it->second.clDetected ? "Yes" : "");
#endif
cout << resetiosflags(ios::left) << setw(13)
<< getFormattedMemory((double)it->second.totalMemory) << " ";
#if ETH_ETHASHCL
if (m_minerType == MinerType::CL || m_minerType == MinerType::Mixed)
{
cout << resetiosflags(ios::left) << setw(13)
<< getFormattedMemory((double)it->second.clMaxMemAlloc) << " ";
cout << resetiosflags(ios::left) << setw(13)
<< getFormattedMemory((double)it->second.clMaxWorkGroup) << " ";
}
#endif
cout << resetiosflags(ios::left) << endl;
it++;
}
return;
}
// Subscribe devices with appropriate Miner Type
// Use CUDA first when available then, as second, OpenCL
// Apply discrete subscriptions (if any)
#if ETH_ETHASHCUDA
if (m_CUSettings.devices.size() &&
(m_minerType == MinerType::CUDA || m_minerType == MinerType::Mixed))
{
for (auto index : m_CUSettings.devices)
{
if (index < m_DevicesCollection.size())
{
auto it = m_DevicesCollection.begin();
std::advance(it, index);
if (!it->second.cuDetected)
throw std::runtime_error("Can't CUDA subscribe a non-CUDA device.");
it->second.subscriptionType = DeviceSubscriptionTypeEnum::Cuda;
}
}
}
#endif
#if ETH_ETHASHCL
if (m_CLSettings.devices.size() &&
(m_minerType == MinerType::CL || m_minerType == MinerType::Mixed))
{
for (auto index : m_CLSettings.devices)
{
if (index < m_DevicesCollection.size())
{
auto it = m_DevicesCollection.begin();
std::advance(it, index);
if (!it->second.clDetected)
throw std::runtime_error("Can't OpenCL subscribe a non-OpenCL device.");
if (it->second.subscriptionType != DeviceSubscriptionTypeEnum::None)
throw std::runtime_error(
"Can't OpenCL subscribe a CUDA subscribed device.");
it->second.subscriptionType = DeviceSubscriptionTypeEnum::OpenCL;
}
}
}
#endif
#if ETH_ETHASHCPU
if (m_CPSettings.devices.size() && (m_minerType == MinerType::CPU))
{
for (auto index : m_CPSettings.devices)
{
if (index < m_DevicesCollection.size())
{
auto it = m_DevicesCollection.begin();
std::advance(it, index);
it->second.subscriptionType = DeviceSubscriptionTypeEnum::Cpu;
}
}
}
#endif
// Subscribe all detected devices
#if ETH_ETHASHCUDA
if (!m_CUSettings.devices.size() &&
(m_minerType == MinerType::CUDA || m_minerType == MinerType::Mixed))
{
for (auto it = m_DevicesCollection.begin(); it != m_DevicesCollection.end(); it++)
{
if (!it->second.cuDetected ||
it->second.subscriptionType != DeviceSubscriptionTypeEnum::None)
continue;
it->second.subscriptionType = DeviceSubscriptionTypeEnum::Cuda;
}
}
#endif
#if ETH_ETHASHCL
if (!m_CLSettings.devices.size() &&
(m_minerType == MinerType::CL || m_minerType == MinerType::Mixed))
{
for (auto it = m_DevicesCollection.begin(); it != m_DevicesCollection.end(); it++)
{
if (!it->second.clDetected ||
it->second.subscriptionType != DeviceSubscriptionTypeEnum::None)
continue;
it->second.subscriptionType = DeviceSubscriptionTypeEnum::OpenCL;
}
}
#endif
#if ETH_ETHASHCPU
if (!m_CPSettings.devices.size() &&
(m_minerType == MinerType::CPU))
{
for (auto it = m_DevicesCollection.begin(); it != m_DevicesCollection.end(); it++)
{
it->second.subscriptionType = DeviceSubscriptionTypeEnum::Cpu;
}
}
#endif
// Count of subscribed devices
int subscribedDevices = 0;
for (auto it = m_DevicesCollection.begin(); it != m_DevicesCollection.end(); it++)
{
if (it->second.subscriptionType != DeviceSubscriptionTypeEnum::None)
subscribedDevices++;
}
// If no OpenCL and/or CUDA devices subscribed then throw error
if (!subscribedDevices)
throw std::runtime_error("No mining device selected. Aborting ...");
// Enable
g_running = true;
// Signal traps
#if defined(__linux__) || defined(__APPLE__)
signal(SIGSEGV, MinerCLI::signalHandler);
#endif
signal(SIGINT, MinerCLI::signalHandler);
signal(SIGTERM, MinerCLI::signalHandler);
// Initialize Farm
new Farm(m_DevicesCollection, m_FarmSettings, m_CUSettings, m_CLSettings, m_CPSettings);
// Run Miner
doMiner();
}
void help()
{
cout << "Progminer - GPU ethash miner" << endl
<< "minimal usage : progminer [DEVICES_TYPE] [OPTIONS] -P... [-P...]" << endl
<< endl
<< "Devices type options :" << endl
<< endl
<< " By default progminer will try to use all devices types" << endl
<< " it can detect. Optionally you can limit this behavior" << endl
<< " setting either of the following options" << endl
#if ETH_ETHASHCL
<< " -G,--opencl Mine/Benchmark using OpenCL only" << endl
#endif
#if ETH_ETHASHCUDA
<< " -U,--cuda Mine/Benchmark using CUDA only" << endl
#endif
#if ETH_ETHASHCPU
<< " --cpu Mine/Benchmark using CPU only" << endl
#endif
<< endl
<< "Connection options :" << endl
<< endl
<< " -P,--pool Stratum pool or http (getWork) connection as URL" << endl
<< " "
"scheme://[user[.workername][:password]@]hostname:port[/...]"
<< endl
<< " For an explication and some samples about" << endl
<< " how to fill in this value please use" << endl
<< " progminer --help-ext con" << endl
<< endl
<< "Common Options :" << endl
<< endl
<< " -h,--help Displays this help text and exits" << endl
<< " -H,--help-ext TEXT {'con','test',"
#if ETH_ETHASHCL
<< "cl,"
#endif
#if ETH_ETHASHCUDA
<< "cu,"
#endif
#if ETH_ETHASHCPU
<< "cp,"
#endif
#if API_CORE
<< "api,"
#endif
<< "'misc','env'}" << endl
<< " Display help text about one of these contexts:" << endl
<< " 'con' Connections and their definitions" << endl
<< " 'test' Benchmark/Simulation options" << endl
#if ETH_ETHASHCL
<< " 'cl' Extended OpenCL options" << endl
#endif
#if ETH_ETHASHCUDA
<< " 'cu' Extended CUDA options" << endl
#endif
#if ETH_ETHASHCPU
<< " 'cp' Extended CPU options" << endl
#endif
#if API_CORE
<< " 'api' API and Http monitoring interface" << endl
#endif
<< " 'misc' Other miscellaneous options" << endl
<< " 'env' Using environment variables" << endl
<< " -V,--version Show program version and exits" << endl
<< endl;
}
void helpExt(std::string ctx)
{
// Help text for benchmarking options
if (ctx == "test")
{
cout << "Benchmarking / Simulation options :" << endl
<< endl
<< " When playing with benchmark or simulation no connection specification "
"is"
<< endl
<< " needed ie. you can omit any -P argument." << endl
<< endl
<< " -M,--benchmark UINT [0 ..] Default not set" << endl
<< " Mining test. Used to test hashing speed." << endl
<< " Specify the block number to test on." << endl
<< endl
<< " --diff FLOAT [>0.0] Default " << m_PoolSettings.benchmarkDiff
<< endl
<< " Mining test. Used to test hashing speed." << endl
<< " Specify the difficulty level to test on." << endl
<< endl
<< " -Z,--simulation UINT [0 ..] Default not set" << endl
<< " Mining test. Used to test hashing speed." << endl
<< " Specify the block number to test on." << endl
<< endl;
}
// Help text for API interfaces options
if (ctx == "api")
{
cout << "API Interface Options :" << endl
<< endl
<< " Progminer provide an interface for monitor and or control" << endl
<< " Please note that information delivered by API interface" << endl
<< " may depend on value of --HWMON" << endl
<< " A single endpoint is used to accept both HTTP or plain tcp" << endl
<< " requests." << endl
<< endl
<< " --api-bind TEXT Default not set" << endl
<< " Set the API address:port the miner should listen "
"on. "
<< endl
<< " Use negative port number for readonly mode" << endl
<< " --api-port INT [1 .. 65535] Default not set" << endl
<< " Set the API port, the miner should listen on all "
"bound"
<< endl
<< " addresses. Use negative numbers for readonly mode"
<< endl
<< " --api-password TEXT Default not set" << endl
<< " Set the password to protect interaction with API "
"server. "
<< endl
<< " If not set, any connection is granted access. " << endl
<< " Be advised passwords are sent unencrypted over "
"plain "
"TCP!!"
<< endl;
}
if (ctx == "cl")
{
cout << "OpenCL Extended Options :" << endl
<< endl
<< " Use this extended OpenCL arguments to fine tune the performance." << endl
<< " Be advised default values are best generic findings by developers" << endl
<< endl
<< " --cl-devices UINT {} Default not set" << endl
<< " Space separated list of device indexes to use" << endl
<< " eg --cl-devices 0 2 3" << endl
<< " If not set all available CL devices will be used"
<< endl
<< " --cl-global-work UINT Default = " << m_CLSettings.globalWorkSizeMultiplier << endl
<< " Set the global work size multiplier" << endl
<< " Value will be adjusted to nearest power of 2" << endl
<< " --cl-local-work UINT {64,128,256} Default = " << m_CLSettings.localWorkSize << endl
<< " Set the local work size multiplier" << endl;
}
if (ctx == "cu")
{
cout << "CUDA Extended Options :" << endl
<< endl
<< " Use this extended CUDA arguments to fine tune the performance." << endl
<< " Be advised default values are best generic findings by developers" << endl
<< endl
<< " --cu-grid-size INT [1 .. 131072] Default = " << m_CUSettings.gridSize << endl
<< " Set the grid size" << endl
<< " --cu-block-size UINT {32,64,128,256} Default = " << m_CUSettings.blockSize << endl
<< " Set the block size" << endl
<< " --cu-devices UINT {} Default not set" << endl
<< " Space separated list of device indexes to use" << endl
<< " eg --cu-devices 0 2 3" << endl
<< " If not set all available CUDA devices will be used"
<< endl
<< " --cu-parallel-hash UINT {1,2,4,8} Default = " << m_CUSettings.parallelHash << endl
<< " Set the number of parallel hashes per kernel" << endl
<< " --cu-streams INT [1 .. 99] Default = " << m_CUSettings.streams << endl
<< " Set the number of streams per GPU" << endl
<< " --cu-schedule TEXT Default = 'sync'" << endl
<< " Set the CUDA scheduler mode. Can be one of" << endl
<< " 'auto' Uses a heuristic based on the number of "
"active "
<< endl
<< " CUDA contexts in the process (C) and the "
"number"
<< endl
<< " of logical processors in the system (P)"
<< endl
<< " If C > P then 'yield' else 'spin'" << endl
<< " 'spin' Instructs CUDA to actively spin when "
"waiting"
<< endl
<< " for results from the device" << endl
<< " 'yield' Instructs CUDA to yield its thread when "
"waiting for"
<< endl
<< " for results from the device" << endl
<< " 'sync' Instructs CUDA to block the CPU thread on "
"a "
<< endl
<< " synchronize primitive when waiting for "
"results"
<< endl
<< " from the device" << endl
<< endl;
}
if (ctx == "cp")
{
cout << "CPU Extended Options :" << endl
<< endl
<< " Use this extended CPU arguments"
<< endl
<< endl
<< " --cp-devices UINT {} Default not set" << endl
<< " Space separated list of device indexes to use" << endl
<< " eg --cp-devices 0 2 3" << endl
<< " If not set all available CPUs will be used" << endl
<< endl;
}
if (ctx == "misc")
{
cout << "Miscellaneous Options :" << endl
<< endl
<< " This set of options is valid for mining mode independently from" << endl
<< " OpenCL or CUDA or Mixed mining mode." << endl
<< endl
<< " --display-interval INT[1 .. 1800] Default = 5" << endl
<< " Statistic display interval in seconds" << endl
<< " --farm-recheck INT[1 .. 99999] Default = 500" << endl
<< " Set polling interval for new work in getWork mode"
<< endl
<< " Value expressed in milliseconds" << endl
<< " It has no meaning in stratum mode" << endl
<< " --farm-retries INT[1 .. 99999] Default = 3" << endl
<< " Set number of reconnection retries to same pool"
<< endl
<< " --failover-timeout INT[0 .. ] Default not set" << endl
<< " Sets the number of minutes progminer can stay" << endl
<< " connected to a fail-over pool before trying to" << endl
<< " reconnect to the primary (the first) connection."
<< endl
<< " before switching to a fail-over connection" << endl
<< " --work-timeout INT[180 .. 99999] Default = 180" << endl
<< " If no new work received from pool after this" << endl
<< " amount of time the connection is dropped" << endl
<< " Value expressed in seconds." << endl
<< " --response-timeout INT[2 .. 999] Default = 2" << endl
<< " If no response from pool to a stratum message " << endl
<< " after this amount of time the connection is dropped"
<< endl
<< " -R,--report-hr FLAG Notify pool of effective hashing rate" << endl
<< " --HWMON INT[0 .. 2] Default = 0" << endl
<< " GPU hardware monitoring level. Can be one of:" << endl
<< " 0 No monitoring" << endl
<< " 1 Monitor temperature and fan percentage" << endl
<< " 2 As 1 plus monitor power drain" << endl
<< " --exit FLAG Stop progminer whenever an error is encountered"
<< endl
<< " --ergodicity INT[0 .. 2] Default = 0" << endl
<< " Sets how progminer chooses the nonces segments to"
<< endl
<< " search on." << endl
<< " 0 A search segment is picked at startup" << endl
<< " 1 A search segment is picked on every pool "
"connection"
<< endl
<< " 2 A search segment is picked on every new job" << endl
<< endl
<< " --nocolor FLAG Monochrome display log lines" << endl
<< " --syslog FLAG Use syslog appropriate output (drop timestamp "
"and"
<< endl
<< " channel prefix)" << endl
<< " --stdout FLAG Log to stdout instead of stderr" << endl
<< " --noeval FLAG By-pass host software re-evaluation of GPUs"
<< endl
<< " found nonces. Trims some ms. from submission" << endl
<< " time but it may increase rejected solution rate."
<< endl
<< " --list-devices FLAG Lists the detected OpenCL/CUDA devices and "
"exits"
<< endl
<< " Must be combined with -G or -U or -X flags" << endl
<< " -L,--dag-load-mode INT[0 .. 1] Default = 0" << endl
<< " Set DAG load mode. Can be one of:" << endl
<< " 0 Parallel load mode (each GPU independently)" << endl
<< " 1 Sequential load mode (one GPU after another)" << endl
<< endl
<< " --tstart UINT[30 .. 100] Default = 0" << endl
<< " Suspend mining on GPU which temperature is above"
<< endl
<< " this threshold. Implies --HWMON 1" << endl
<< " If not set or zero no temp control is performed"
<< endl
<< " --tstop UINT[30 .. 100] Default = 40" << endl
<< " Resume mining on previously overheated GPU when "
"temp"
<< endl
<< " drops below this threshold. Implies --HWMON 1" << endl
<< " Must be lower than --tstart" << endl
<< " -v,--verbosity INT[0 .. 255] Default = 0 " << endl
<< " Set output verbosity level. Use the sum of :" << endl
<< " 1 to log stratum json messages" << endl
<< " 2 to log found solutions per GPU" << endl
#ifdef DEV_BUILD
<< " 32 to log socket (dis)connections" << endl
<< " 64 to log time for job switches" << endl
<< " 128 to log time for solution submissions" << endl
<< " 256 to log kernel compile diagnostics" << endl
#endif
<< endl;
}
if (ctx == "env")
{
cout << "Environment variables :" << endl
<< endl
<< " If you need or do feel more comfortable you can set the following" << endl
<< " environment variables. Please respect letter casing." << endl
<< endl
<< " NO_COLOR Set to any value to disable colored output." << endl
<< " Acts the same as --nocolor command line argument"
<< endl
<< " SYSLOG Set to any value to strip timestamp, colors and "
"channel"
<< endl
<< " from output log." << endl
<< " Acts the same as --syslog command line argument"
<< endl
#ifndef _WIN32
<< " SSL_CERT_FILE Set to the full path to of your CA certificates "
"file"
<< endl
<< " if it is not in standard path :" << endl
<< " /etc/ssl/certs/ca-certificates.crt." << endl
#endif
<< " SSL_NOVERIFY set to any value to to disable the verification "
"chain "
"for"
<< endl
<< " certificates. WARNING ! Disabling certificate "
"validation"
<< endl
<< " declines every security implied in connecting to a "
"secured"
<< endl
<< " SSL/TLS remote endpoint." << endl
<< " USE AT YOU OWN RISK AND ONLY IF YOU KNOW WHAT "
"YOU'RE "
"DOING"
<< endl;
}
if (ctx == "con")
{
cout << "Connections specifications :" << endl
<< endl
<< " Whether you need to connect to a stratum pool or to make use of "
"getWork "
"polling"
<< endl
<< " mode (generally used to solo mine) you need to specify the connection "
"making use"
<< endl
<< " of -P command line argument filling up the URL. The URL is in the form "
":"
<< endl
<< " " << endl
<< " scheme://[user[.workername][:password]@]hostname:port[/...]." << endl
<< " " << endl
<< " where 'scheme' can be any of :" << endl
<< " " << endl
<< " getwork for http getWork mode" << endl
<< " stratum for tcp stratum mode" << endl
<< " stratums for tcp encrypted stratum mode" << endl
<< " stratumss for tcp encrypted stratum mode with strong TLS 1.2 "
"validation"
<< endl
<< endl
<< " Example 1: -P getwork://127.0.0.1:8545" << endl
<< " Example 2: "
"-P stratums://0x012345678901234567890234567890123.miner1@ethermine.org:5555"
<< endl
<< " Example 3: "
"-P stratum://0x012345678901234567890234567890123.miner1@nanopool.org:9999/"
"john.doe%40gmail.com"
<< endl
<< " Example 4: "
"-P stratum://0x012345678901234567890234567890123@nanopool.org:9999/miner1/"
"john.doe%40gmail.com"
<< endl
<< endl
<< " Please note: if your user or worker or password do contain characters"
<< endl
<< " which may impair the correct parsing (namely any of . : @ # ?) you have to"
<< endl
<< " enclose those values in backticks( ` ASCII 096) or Url Encode them" << endl
<< " Also note that backtick has a special meaning in *nix environments thus"
<< endl
<< " you need to further escape those backticks with backslash." << endl
<< endl
<< " Example : -P stratums://\\`account.121\\`.miner1:x@ethermine.org:5555"
<< endl
<< " Example : -P stratums://account%2e121.miner1:x@ethermine.org:5555" << endl
<< " (In Windows backslashes are not needed)" << endl
<< endl
<< endl
<< " Common url encoded chars are " << endl
<< " . (dot) %2e" << endl
<< " : (column) %3a" << endl
<< " @ (at sign) %40" << endl
<< " ? (question) %3f" << endl
<< " # (number) %23" << endl
<< " / (slash) %2f" << endl
<< " + (plus) %2b" << endl
<< endl
<< " You can add as many -P arguments as you want. Every -P specification"
<< endl
<< " after the first one behaves as fail-over connection. When also the" << endl
<< " the fail-over disconnects progminer passes to the next connection" << endl
<< " available and so on till the list is exhausted. At that moment" << endl
<< " progminer restarts the connection cycle from the first one." << endl
<< " An exception to this behavior is ruled by the --failover-timeout" << endl
<< " command line argument. See 'progminer -H misc' for details." << endl
<< endl
<< " The special notation '-P exit' stops the failover loop." << endl
<< " When progminer reaches this kind of connection it simply quits." << endl
<< endl
<< " When using stratum mode progminer tries to auto-detect the correct" << endl
<< " flavour provided by the pool. Should be fine in 99% of the cases." << endl
<< " Nevertheless you might want to fine tune the stratum flavour by" << endl
<< " any of of the following valid schemes :" << endl
<< endl
<< " " << URI::KnownSchemes(ProtocolFamily::STRATUM) << endl
<< endl
<< " where a scheme is made up of two parts, the stratum variant + the tcp "
"transport protocol"
<< endl
<< endl
<< " Stratum variants :" << endl
<< endl
<< " stratum Stratum" << endl
<< " stratum1 Eth Proxy compatible" << endl
<< " stratum2 EthereumStratum 1.0.0 (nicehash)" << endl
<< " stratum3 EthereumStratum 2.0.0" << endl
<< endl
<< " Transport variants :" << endl
<< endl
<< " tcp Unencrypted tcp connection" << endl
<< " tls Encrypted tcp connection (including deprecated TLS 1.1)"
<< endl
<< " tls12 Encrypted tcp connection with TLS 1.2" << endl
<< " ssl Encrypted tcp connection with TLS 1.2" << endl
<< endl;
}
}
private:
void doMiner()
{
new PoolManager(m_PoolSettings);
if (m_mode != OperationMode::Simulation)
for (auto conn : m_PoolSettings.connections)
cnote << "Configured pool " << conn->Host() + ":" + to_string(conn->Port());
#if API_CORE
ApiServer api(m_api_address, m_api_port, m_api_password);
if (m_api_port)
api.start();
#endif
// Start PoolManager
PoolManager::p().start();
// Initialize display timer as sleeper with proper interval
m_cliDisplayTimer.expires_from_now(boost::posix_time::seconds(m_cliDisplayInterval));
m_cliDisplayTimer.async_wait(m_io_strand.wrap(boost::bind(
&MinerCLI::cliDisplayInterval_elapsed, this, boost::asio::placeholders::error)));
// Stay in non-busy wait till signals arrive
unique_lock<mutex> clilock(m_climtx);
while (g_running)
g_shouldstop.wait(clilock);
#if API_CORE
// Stop Api server
if (api.isRunning())
api.stop();
#endif
if (PoolManager::p().isRunning())
PoolManager::p().stop();
cnote << "Terminated!";
return;
}
// Global boost's io_service
std::thread m_io_thread; // The IO service thread
boost::asio::deadline_timer m_cliDisplayTimer; // The timer which ticks display lines
boost::asio::io_service::strand m_io_strand; // A strand to serialize posts in
// multithreaded environment
// Physical Mining Devices descriptor
std::map<std::string, DeviceDescriptor> m_DevicesCollection = {};
// Mining options
MinerType m_minerType = MinerType::Mixed;
OperationMode m_mode = OperationMode::None;
bool m_shouldListDevices = false;
FarmSettings m_FarmSettings; // Operating settings for Farm
PoolSettings m_PoolSettings; // Operating settings for PoolManager
CLSettings m_CLSettings; // Operating settings for CL Miners
CUSettings m_CUSettings; // Operating settings for CUDA Miners
CPSettings m_CPSettings; // Operating settings for CPU Miners
//// -- Pool manager related params
//std::vector<std::shared_ptr<URI>> m_poolConns;
// -- CLI Interface related params
unsigned m_cliDisplayInterval =
5; // Display stats/info on cli interface every this number of seconds
// -- CLI Flow control
mutex m_climtx;
#if API_CORE
// -- API and Http interfaces related params
string m_api_bind; // API interface binding address in form <address>:<port>
string m_api_address = "0.0.0.0"; // API interface binding address (Default any)
int m_api_port = 0; // API interface binding port
string m_api_password; // API interface write protection password
#endif
#if ETH_DBUS
DBusInt dbusint;
#endif
};
int main(int argc, char** argv)
{
// Return values
// 0 - Normal exit
// 1 - Invalid/Insufficient command line arguments
// 2 - Runtime error
// 3 - Other exceptions
// 4 - Possible corruption
#if defined(_WIN32)
// Need to change the code page from the default OEM code page (437) so that
// UTF-8 characters are displayed correctly in the console
SetConsoleOutputCP(CP_UTF8);
#endif
// Always out release version
auto* bi = progminer_get_buildinfo();
cout << endl
<< endl
<< "progminer " << bi->project_version << endl
<< "Build: " << bi->system_name << "/" << bi->build_type << "/" << bi->compiler_id << endl
<< endl;
if (argc < 2)
{
cerr << "No arguments specified. " << endl
<< "Try 'progminer --help' to get a list of arguments." << endl
<< endl;
return 1;
}
try
{
MinerCLI cli;
try
{
// Set env vars controlling GPU driver behavior.
setenv("GPU_MAX_HEAP_SIZE", "100");
setenv("GPU_MAX_ALLOC_PERCENT", "100");
setenv("GPU_SINGLE_ALLOC_PERCENT", "100");
// Argument validation either throws exception
// or returns false which means do not continue
if (!cli.validateArgs(argc, argv))
return 0;
if (getenv("SYSLOG"))
g_logSyslog = true;
if (g_logSyslog || (getenv("NO_COLOR")))
g_logNoColor = true;
#if defined(_WIN32)
if (!g_logNoColor)
{
g_logNoColor = true;
// Set output mode to handle virtual terminal sequences
// Only works on Windows 10, but most users should use it anyway
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut != INVALID_HANDLE_VALUE)
{
DWORD dwMode = 0;
if (GetConsoleMode(hOut, &dwMode))
{
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (SetConsoleMode(hOut, dwMode))
g_logNoColor = false;
}
}
}
#endif
cli.execute();
cout << endl << endl;
return 0;
}
catch (std::invalid_argument& ex1)
{
cerr << "Error: " << ex1.what() << endl
<< "Try progminer --help to get an explained list of arguments." << endl
<< endl;
return 1;
}
catch (std::runtime_error& ex2)
{
cerr << "Error: " << ex2.what() << endl << endl;
return 2;
}
catch (std::exception& ex3)
{
cerr << "Error: " << ex3.what() << endl << endl;
return 3;
}
catch (...)
{
cerr << "Error: Unknown failure occurred. Possible memory corruption." << endl << endl;
return 4;
}
}
catch (const std::exception& ex)
{
cerr << "Could not initialize CLI interface " << endl
<< "Error: " << ex.what() << endl
<< endl;
return 4;
}
}