summaryrefslogtreecommitdiffstats
path: root/xmrstak
diff options
context:
space:
mode:
authorfireice-uk <fireice-uk@users.noreply.github.com>2017-11-09 22:43:08 +0000
committerpsychocrypt <psychocrypt@users.noreply.github.com>2017-11-09 23:43:08 +0100
commitd35893d926c74893d7c85d1b87b24ffa55744649 (patch)
treeb2c1186e3ad96696592e1572e553514af04048b4 /xmrstak
parentb3237bab587f937e90a2ff0d06aba5e3630a8047 (diff)
downloadxmr-stak-d35893d926c74893d7c85d1b87b24ffa55744649.zip
xmr-stak-d35893d926c74893d7c85d1b87b24ffa55744649.tar.gz
Multi-pool final version (#90)
* Multi-pool first draft * Fix wspace from new IDE * Better TLS error message * Fix TLS bug * Don't put dev pool on stats + pool change-back * bug fixes * Error message work * fix win build * add per-pool nicehash setting * Fix bugs * rm debug msg * Multipool guided setup * Support TLS and Nicehash in config * prelim jconf changes * final multipool changes * increase default retry_time to 30, fix mac erro * rm debug dev pool settings * Fix another source of connect runaway
Diffstat (limited to 'xmrstak')
-rw-r--r--xmrstak/backend/cpu/jconf.hpp3
-rw-r--r--xmrstak/backend/globalStates.hpp5
-rw-r--r--xmrstak/cli/cli-miner.cpp220
-rw-r--r--xmrstak/config.tpl35
-rw-r--r--xmrstak/jconf.cpp163
-rw-r--r--xmrstak/jconf.hpp31
-rw-r--r--xmrstak/misc/executor.cpp420
-rw-r--r--xmrstak/misc/executor.hpp43
-rw-r--r--xmrstak/net/jpsock.cpp31
-rw-r--r--xmrstak/net/jpsock.hpp42
-rw-r--r--xmrstak/net/msgstruct.hpp50
-rw-r--r--xmrstak/net/socket.cpp67
12 files changed, 719 insertions, 391 deletions
diff --git a/xmrstak/backend/cpu/jconf.hpp b/xmrstak/backend/cpu/jconf.hpp
index 3c7da49..f843ed4 100644
--- a/xmrstak/backend/cpu/jconf.hpp
+++ b/xmrstak/backend/cpu/jconf.hpp
@@ -31,9 +31,6 @@ public:
bool GetThreadConfig(size_t id, thd_cfg &cfg);
bool NeedsAutoconf();
-
-
-
private:
jconf();
static jconf* oInst;
diff --git a/xmrstak/backend/globalStates.hpp b/xmrstak/backend/globalStates.hpp
index 1c28d5c..9fdda2a 100644
--- a/xmrstak/backend/globalStates.hpp
+++ b/xmrstak/backend/globalStates.hpp
@@ -6,6 +6,7 @@
#include <atomic>
+constexpr static size_t invalid_pool_id = (-1);
namespace xmrstak
{
@@ -15,7 +16,7 @@ struct pool_data
uint32_t iSavedNonce;
size_t pool_id;
- pool_data() : iSavedNonce(0), pool_id(0)
+ pool_data() : iSavedNonce(0), pool_id(invalid_pool_id)
{
}
};
@@ -46,7 +47,7 @@ struct globalStates
std::atomic<uint64_t> iConsumeCnt;
std::atomic<uint32_t> iGlobalNonce;
uint64_t iThreadCount;
- size_t pool_id;
+ size_t pool_id = invalid_pool_id;
private:
globalStates() : iThreadCount(0)
diff --git a/xmrstak/cli/cli-miner.cpp b/xmrstak/cli/cli-miner.cpp
index 81639ef..5c16c1b 100644
--- a/xmrstak/cli/cli-miner.cpp
+++ b/xmrstak/cli/cli-miner.cpp
@@ -49,7 +49,6 @@
#include <openssl/err.h>
#endif
-
#ifdef _WIN32
# define strcasecmp _stricmp
#endif // _WIN32
@@ -90,6 +89,170 @@ void help()
cout<<"Brought to by fireice_uk and psychocrypt under GPLv3."<<endl;
}
+bool read_yes_no(const char* str)
+{
+ std::string tmp;
+ do
+ {
+ std::cout << str << std::endl;
+ std::cin >> tmp;
+ std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
+ }
+ while(tmp != "y" && tmp != "n" && tmp != "yes" && tmp != "no");
+
+ return tmp == "y" || tmp == "yes";
+}
+
+inline const char* bool_to_str(bool v)
+{
+ return v ? "true" : "false";
+}
+
+std::string get_multipool_entry(bool& final)
+{
+ std::cout<<std::endl<<"- Next Pool:"<<std::endl<<std::endl;
+
+ std::string pool;
+ if(xmrstak::params::inst().currency == "monero")
+ std::cout<<"- Pool address: e.g. pool.usxmrpool.com:3333"<<std::endl;
+ else
+ std::cout<<"- Pool address: e.g. mine.aeon-pool.com:5555"<<std::endl;
+ std::cin >> pool;
+
+ std::string userName;
+ std::cout<<"- Username (wallet address or pool login):"<<std::endl;
+ std::cin >> userName;
+
+ std::string passwd;
+ std::cin.clear(); std::cin.ignore(INT_MAX,'\n');
+ std::cout<<"- Password (mostly empty or x):"<<std::endl;
+ getline(std::cin, passwd);
+
+ bool tls = read_yes_no("- Does this pool port support TLS/SSL? Use no if unknown. (y/N)");
+ bool nicehash = read_yes_no("- Do you want to use nicehash on this pool? (y/n)");
+
+ int64_t pool_weight;
+ std::cout << "- Please enter a weight for this pool: "<<std::endl;
+ while(!(std::cin >> pool_weight) || pool_weight <= 0)
+ {
+ std::cin.clear();
+ std::cin.ignore(INT_MAX, '\n');
+ std::cout << "Invalid weight. Try 1, 10, 100, etc:" << std::endl;
+ }
+
+ final = !read_yes_no("- Do you want to add another pool? (y/n)");
+
+ return "\t{\"pool_address\" : \"" + pool +"\", \"wallet_address\" : \"" + userName + "\", \"pool_password\" : \"" +
+ passwd + "\", \"use_nicehash\" : " + bool_to_str(nicehash) + ", \"use_tls\" : " + bool_to_str(tls) +
+ ", \"tls_fingerprint\" : \"\", \"pool_weight\" : " + std::to_string(pool_weight) + " },\n";
+}
+
+void do_guided_config(bool userSetPasswd)
+{
+ using namespace xmrstak;
+
+ // load the template of the backend config into a char variable
+ const char *tpl =
+ #include "../config.tpl"
+ ;
+
+ configEditor configTpl{};
+ configTpl.set(std::string(tpl));
+ std::cout<<"Please enter:"<<std::endl;
+ auto& currency = params::inst().currency;
+ if(currency.empty())
+ {
+ std::string tmp;
+#if defined(CONF_NO_AEON)
+ tmp = "monero";
+#elif defined(CONF_NO_MONERO)
+ tmp = "aeon";
+#endif
+ while(tmp != "monero" && tmp != "aeon")
+ {
+ std::cout<<"- Currency: 'monero' or 'aeon'"<<std::endl;
+ std::cin >> tmp;
+ std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
+ }
+ currency = tmp;
+ }
+
+ auto& pool = params::inst().poolURL;
+ bool userSetPool = true;
+ if(pool.empty())
+ {
+ userSetPool = false;
+ if(currency == "monero")
+ std::cout<<"- Pool address: e.g. pool.usxmrpool.com:3333"<<std::endl;
+ else
+ std::cout<<"- Pool address: e.g. mine.aeon-pool.com:5555"<<std::endl;
+ std::cin >> pool;
+ }
+
+ auto& userName = params::inst().poolUsername;
+ if(userName.empty())
+ {
+ std::cout<<"- Username (wallet address or pool login):"<<std::endl;
+ std::cin >> userName;
+ }
+
+ auto& passwd = params::inst().poolPasswd;
+ if(passwd.empty() && (!userSetPasswd))
+ {
+ // clear everything from stdin to allow an empty password
+ std::cin.clear(); std::cin.ignore(INT_MAX,'\n');
+ std::cout<<"- Password (mostly empty or x):"<<std::endl;
+ getline(std::cin, passwd);
+ }
+
+ bool tls = read_yes_no("- Does this pool port support TLS/SSL? Use no if unknown. (y/N)");
+ bool nicehash = read_yes_no("- Do you want to use nicehash on this pool? (y/n)");
+
+ bool multipool;
+ if(!userSetPool)
+ multipool = read_yes_no("- Do you want to use multiple pools? (y/n)");
+ else
+ multipool = false;
+
+ int64_t pool_weight;
+ if(multipool)
+ {
+ std::cout << "Pool weight is a number telling the miner how important the pool is." << std::endl;
+ std::cout << "Miner will mine mostly at the pool with the highest weight, unless the pool fails." << std::endl;
+ std::cout << "Weight must be an integer larger than 0." << std::endl;
+ std::cout << "- Please enter a weight for this pool: "<<std::endl;
+
+ while(!(std::cin >> pool_weight) || pool_weight <= 0)
+ {
+ std::cin.clear();
+ std::cin.ignore(INT_MAX, '\n');
+ std::cout << "Invalid weight. Try 1, 10, 100, etc:" << std::endl;
+ }
+ }
+ else
+ pool_weight = 1;
+
+ std::string pool_table;
+ pool_table += "\t{\"pool_address\" : \"" + pool +"\", \"wallet_address\" : \"" + userName + "\", \"pool_password\" : \"" +
+ passwd + "\", \"use_nicehash\" : " + bool_to_str(nicehash) + ", \"use_tls\" : " + bool_to_str(tls) +
+ ", \"tls_fingerprint\" : \"\", \"pool_weight\" : " + std::to_string(pool_weight) + " },\n";
+
+ if(multipool)
+ {
+ bool final;
+ do
+ {
+ pool_table += get_multipool_entry(final);
+ }
+ while(!final);
+ }
+
+ configTpl.replace("POOLCONF", pool_table);
+ configTpl.replace("CURRENCY", currency);
+ configTpl.write(params::inst().configFile);
+ std::cout<<"Configuration stored in file '"<<params::inst().configFile<<"'"<<std::endl;
+}
+
int main(int argc, char *argv[])
{
#ifndef CONF_NO_TLS
@@ -244,60 +407,7 @@ int main(int argc, char *argv[])
// check if we need a guided start
if(!configEditor::file_exist(params::inst().configFile))
- {
- // load the template of the backend config into a char variable
- const char *tpl =
- #include "../config.tpl"
- ;
- configEditor configTpl{};
- configTpl.set(std::string(tpl));
- std::cout<<"Please enter:"<<std::endl;
- auto& currency = params::inst().currency;
- if(currency.empty())
- {
- std::string tmp;
-#if defined(CONF_NO_AEON)
- tmp = "monero";
-#elif defined(CONF_NO_MONERO)
- tmp = "aeon";
-#endif
- while(!xmrstak::strcmp_i(tmp, "monero") && !xmrstak::strcmp_i(tmp, "aeon"))
- {
- std::cout<<"- currency: 'monero' or 'aeon'"<<std::endl;
- std::cin >> tmp;
- }
- currency = tmp;
- }
- auto& pool = params::inst().poolURL;
- if(pool.empty())
- {
- if(xmrstak::strcmp_i(currency, "monero"))
- std::cout<<"- pool address: e.g. pool.usxmrpool.com:3333"<<std::endl;
- else
- std::cout<<"- pool address: e.g. mine.aeon-pool.com:5555"<<std::endl;
- std::cin >> pool;
- }
- auto& userName = params::inst().poolUsername;
- if(userName.empty())
- {
- std::cout<<"- user name (wallet address or pool login):"<<std::endl;
- std::cin >> userName;
- }
- auto& passwd = params::inst().poolPasswd;
- if(passwd.empty() && (!userSetPasswd))
- {
- // clear everything from stdin to allow an empty password
- std::cin.clear(); std::cin.ignore(INT_MAX,'\n');
- std::cout<<"- password (mostly empty or x):"<<std::endl;
- getline(std::cin, passwd);
- }
- configTpl.replace("POOLURL", pool);
- configTpl.replace("POOLUSER", userName);
- configTpl.replace("POOLPASSWD", passwd);
- configTpl.replace("CURRENCY", currency);
- configTpl.write(params::inst().configFile);
- std::cout<<"Configuration stored in file '"<<params::inst().configFile<<"'"<<std::endl;
- }
+ do_guided_config(userSetPasswd);
if(!jconf::inst()->parse_config(params::inst().configFile.c_str()))
{
diff --git a/xmrstak/config.tpl b/xmrstak/config.tpl
index 3308354..c06d121 100644
--- a/xmrstak/config.tpl
+++ b/xmrstak/config.tpl
@@ -1,14 +1,19 @@
R"===(
/*
- * pool_address - Pool address should be in the form "pool.supportxmr.com:3333". Only stratum pools are supported.
- * wallet_address - Your wallet, or pool login.
- * pool_password - Can be empty in most cases or "x".
+ * pool_address - Pool address should be in the form "pool.supportxmr.com:3333". Only stratum pools are supported.
+ * wallet_address - Your wallet, or pool login.
+ * pool_password - Can be empty in most cases or "x".
+ * use_nicehash - Limit the nonce to 3 bytes as required by nicehash.
+ * use_tls - This option will make us connect using Transport Layer Security.
+ * tls_fingerprint - Server's SHA256 fingerprint. If this string is non-empty then we will check the server's cert against it.
+ * pool_weight - Pool weight is a number telling the miner how important the pool is. Miner will mine mostly at the pool
+ * with the highest weight, unless the pool fails. Weight must be an integer larger than 0.
*
* We feature pools up to 1MH/s. For a more complete list see M5M400's pool list at www.moneropools.com
*/
-"pool_address" : "POOLURL",
-"wallet_address" : "POOLUSER",
-"pool_password" : "POOLPASSWD",
+"pool_list" :
+[
+POOLCONF],
/*
* currency to mine
@@ -31,7 +36,7 @@ R"===(
* don't mine while the connection is lost, so your computer's power usage goes down to idle.
*/
"call_timeout" : 10,
-"retry_time" : 10,
+"retry_time" : 30,
"giveup_limit" : 0,
/*
@@ -111,25 +116,13 @@ R"===(
"use_slow_memory" : "warn",
/*
- * NiceHash mode
- * nicehash_nonce - Limit the nonce to 3 bytes as required by nicehash. This cuts all the safety margins, and
- * if a block isn't found within 30 minutes then you might run into nonce collisions. Number
- * of threads in this mode is hard-limited to 32.
- */
-"nicehash_nonce" : false,
-
-/*
* TLS Settings
* If you need real security, make sure tls_secure_algo is enabled (otherwise MITM attack can downgrade encryption
* to trivially breakable stuff like DES and MD5), and verify the server's fingerprint through a trusted channel.
*
- * use_tls - This option will make us connect using Transport Layer Security.
* tls_secure_algo - Use only secure algorithms. This will make us quit with an error if we can't negotiate a secure algo.
- * tls_fingerprint - Server's SHA256 fingerprint. If this string is non-empty then we will check the server's cert against it.
*/
-"use_tls" : false,
"tls_secure_algo" : true,
-"tls_fingerprint" : "",
/*
* Daemon mode
@@ -145,7 +138,7 @@ R"===(
* each output line immediately. This can cause delays when running in background.
* Set this option to true to flush stdout after each line, so it can be read immediately.
*/
- "flush_stdout" : false,
+"flush_stdout" : false,
/*
* Output file
@@ -172,4 +165,4 @@ R"===(
"prefer_ipv4" : true,
)==="
- \ No newline at end of file
+
diff --git a/xmrstak/jconf.cpp b/xmrstak/jconf.cpp
index c82865d..ec1233a 100644
--- a/xmrstak/jconf.cpp
+++ b/xmrstak/jconf.cpp
@@ -32,6 +32,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <math.h>
+#include <vector>
+#include <numeric>
+#include <algorithm>
#ifdef _WIN32
#define strcasecmp _stricmp
@@ -47,9 +51,9 @@ using namespace rapidjson;
* This enum needs to match index in oConfigValues, otherwise we will get a runtime error
*/
enum configEnum {
- bTlsMode, bTlsSecureAlgo, sTlsFingerprint, sPoolAddr, sWalletAddr, sPoolPwd,sCurrency,
- iCallTimeout, iNetRetry, iGiveUpLimit, iVerboseLevel, iAutohashTime,bFlushStdout,
- bDaemonMode, sOutputFile, iHttpdPort, bPreferIpv4, bNiceHashMode, bAesOverride, sUseSlowMem };
+ aPoolList, bTlsSecureAlgo, sCurrency, iCallTimeout, iNetRetry, iGiveUpLimit, iVerboseLevel, iAutohashTime,
+ bFlushStdout, bDaemonMode, sOutputFile, iHttpdPort, bPreferIpv4, bAesOverride, sUseSlowMem
+};
struct configVal {
configEnum iName;
@@ -60,12 +64,8 @@ struct configVal {
// Same order as in configEnum, as per comment above
// kNullType means any type
configVal oConfigValues[] = {
- { bTlsMode, "use_tls", kTrueType },
+ { aPoolList, "pool_list", kArrayType },
{ bTlsSecureAlgo, "tls_secure_algo", kTrueType },
- { sTlsFingerprint, "tls_fingerprint", kStringType },
- { sPoolAddr, "pool_address", kStringType },
- { sWalletAddr, "wallet_address", kStringType },
- { sPoolPwd, "pool_password", kStringType },
{ sCurrency, "currency", kStringType },
{ iCallTimeout, "call_timeout", kNumberType },
{ iNetRetry, "retry_time", kNumberType },
@@ -77,7 +77,6 @@ configVal oConfigValues[] = {
{ sOutputFile, "output_file", kStringType },
{ iHttpdPort, "httpd_port", kNumberType },
{ bPreferIpv4, "prefer_ipv4", kTrueType },
- { bNiceHashMode, "nicehash_nonce", kTrueType },
{ bAesOverride, "aes_override", kNullType },
{ sUseSlowMem, "use_slow_memory", kStringType }
};
@@ -113,44 +112,54 @@ jconf::jconf()
prv = new opaque_private();
}
-bool jconf::GetTlsSetting()
+uint64_t jconf::GetPoolCount()
{
- return prv->configValues[bTlsMode]->GetBool();
-}
-
-bool jconf::TlsSecureAlgos()
-{
- return prv->configValues[bTlsSecureAlgo]->GetBool();
-}
-
-const char* jconf::GetTlsFingerprint()
-{
- return prv->configValues[sTlsFingerprint]->GetString();
-}
-
-const char* jconf::GetPoolAddress()
-{
- auto& poolURL = xmrstak::params::inst().poolURL;
- if(poolURL.empty())
- poolURL = prv->configValues[sPoolAddr]->GetString();
- return poolURL.c_str();
+ if(prv->configValues[aPoolList]->IsArray())
+ return prv->configValues[aPoolList]->Size();
+ else
+ return 0;
}
-const char* jconf::GetPoolPwd()
+bool jconf::GetPoolConfig(size_t id, pool_cfg& cfg)
{
- auto& poolPasswd = xmrstak::params::inst().poolPasswd;
- if(poolPasswd.empty())
- poolPasswd = prv->configValues[sPoolPwd]->GetString();
- return poolPasswd.c_str();
+ if(id >= GetPoolCount())
+ return false;
+ typedef const Value* cval;
+ cval jaddr, jlogin, jpasswd, jnicehash, jtls, jtlsfp, jwt;
+ const Value& oThdConf = prv->configValues[aPoolList]->GetArray()[id];
+
+ /* We already checked presence and types */
+ jaddr = GetObjectMember(oThdConf, "pool_address");
+ jlogin = GetObjectMember(oThdConf, "wallet_address");
+ jpasswd = GetObjectMember(oThdConf, "pool_password");
+ jnicehash = GetObjectMember(oThdConf, "use_nicehash");
+ jtls = GetObjectMember(oThdConf, "use_tls");
+ jtlsfp = GetObjectMember(oThdConf, "tls_fingerprint");
+ jwt = GetObjectMember(oThdConf, "pool_weight");
+
+ cfg.sPoolAddr = jaddr->GetString();
+ cfg.sWalletAddr = jlogin->GetString();
+ cfg.sPasswd = jpasswd->GetString();
+ cfg.nicehash = jnicehash->GetBool();
+ cfg.tls = jtls->GetBool();
+ cfg.tls_fingerprint = jtlsfp->GetString();
+ cfg.raw_weight = jwt->GetUint64();
+
+ size_t dlt = wt_max - wt_min;
+ if(dlt != 0)
+ {
+ /* Normalise weights between 0 and 9.9 */
+ cfg.weight = double(cfg.raw_weight - wt_min) * 9.9;
+ cfg.weight /= dlt;
+ }
+ else /* Special case - user selected same weights for everything */
+ cfg.weight = 0.0;
}
-const char* jconf::GetWalletAddress()
+bool jconf::TlsSecureAlgos()
{
- auto& poolUsername = xmrstak::params::inst().poolUsername;
- if(poolUsername.empty())
- poolUsername = prv->configValues[sWalletAddr]->GetString();
- return poolUsername.c_str();
+ return prv->configValues[bTlsSecureAlgo]->GetBool();
}
const std::string jconf::GetCurrency()
@@ -237,12 +246,6 @@ const char* jconf::GetOutputFile()
return prv->configValues[sOutputFile]->GetString();
}
-bool jconf::NiceHashMode()
-{
- return prv->configValues[bNiceHashMode]->GetBool();
-}
-
-
void jconf::cpuid(uint32_t eax, int32_t ecx, int32_t val[4])
{
memset(val, 0, sizeof(int32_t)*4);
@@ -385,6 +388,60 @@ bool jconf::parse_config(const char* sFilename)
}
}
+ size_t pool_cnt = prv->configValues[aPoolList]->Size();
+ if(pool_cnt == 0)
+ {
+ printer::inst()->print_msg(L0, "Invalid config file. pool_list must not be empty.");
+ return false;
+ }
+
+ std::vector<size_t> pool_weights;
+ pool_weights.reserve(pool_cnt);
+
+ const char* aPoolValues[] = { "pool_address", "wallet_address", "pool_password", "use_nicehash", "use_tls", "tls_fingerprint", "pool_weight" };
+ Type poolValTypes[] = { kStringType, kStringType, kStringType, kTrueType, kTrueType, kStringType, kNumberType };
+
+ constexpr size_t pvcnt = sizeof(aPoolValues)/sizeof(aPoolValues[0]);
+ for(uint32_t i=0; i < pool_cnt; i++)
+ {
+ const Value& oThdConf = prv->configValues[aPoolList]->GetArray()[i];
+
+ if(!oThdConf.IsObject())
+ {
+ printer::inst()->print_msg(L0, "Invalid config file. pool_list must contain objects.");
+ return false;
+ }
+
+ for(uint32_t j=0; j < pvcnt; j++)
+ {
+ const Value* v;
+ if((v = GetObjectMember(oThdConf, aPoolValues[j])) == nullptr)
+ {
+ printer::inst()->print_msg(L0, "Invalid config file. Pool %u does not have the value %s.", i, aPoolValues[j]);
+ return false;
+ }
+
+ if(!checkType(v->GetType(), poolValTypes[j]))
+ {
+ printer::inst()->print_msg(L0, "Invalid config file. Value %s for pool %u has unexpected type.", aPoolValues[j], i);
+ return false;
+ }
+ }
+
+ const Value* jwt = GetObjectMember(oThdConf, "pool_weight");
+ size_t wt;
+ if(!jwt->IsUint64() || (wt = jwt->GetUint64()) == 0)
+ {
+ printer::inst()->print_msg(L0, "Invalid pool list for pool %u. Pool weight needs to be an integer larger than zero.", i);
+ return false;
+ }
+
+ pool_weights.emplace_back(wt);
+ }
+
+ wt_max = *std::max_element(pool_weights.begin(), pool_weights.end());
+ wt_min = *std::min_element(pool_weights.begin(), pool_weights.end());
+
if(!prv->configValues[iCallTimeout]->IsUint64() ||
!prv->configValues[iNetRetry]->IsUint64() ||
!prv->configValues[iGiveUpLimit]->IsUint64())
@@ -394,6 +451,13 @@ bool jconf::parse_config(const char* sFilename)
return false;
}
+ if(prv->configValues[iCallTimeout]->GetUint64() < 2 || prv->configValues[iNetRetry]->GetUint64() < 2)
+ {
+ printer::inst()->print_msg(L0,
+ "Invalid config file. call_timeout and retry_time need to be larger than 1 second.");
+ return false;
+ }
+
if(!prv->configValues[iVerboseLevel]->IsUint64() || !prv->configValues[iAutohashTime]->IsUint64())
{
printer::inst()->print_msg(L0,
@@ -417,15 +481,6 @@ bool jconf::parse_config(const char* sFilename)
}
#endif // CONF_NO_TLS
- /* \todo check in the cpu backend if we have more than 32 worker
- * keep in mined that we have change the why how the nonce is calculated (reverse thread index)
- if(NiceHashMode() && GetThreadCount() >= 32)
- {
- printer::inst()->print_msg(L0, "You need to use less than 32 threads in NiceHash mode.");
- return false;
- }
- */
-
if(prv->configValues[bAesOverride]->IsBool())
bHaveAes = prv->configValues[bAesOverride]->GetBool();
diff --git a/xmrstak/jconf.hpp b/xmrstak/jconf.hpp
index 48b47b5..b68ef38 100644
--- a/xmrstak/jconf.hpp
+++ b/xmrstak/jconf.hpp
@@ -19,12 +19,23 @@ public:
};
bool parse_config(const char* sFilename = xmrstak::params::inst().configFile.c_str());
-
- struct thd_cfg {
- bool bDoubleMode;
- bool bNoPrefetch;
- long long iCpuAff;
+
+ struct pool_cfg {
+ const char* sPoolAddr;
+ const char* sWalletAddr;
+ const char* sPasswd;
+ bool nicehash;
+ bool tls;
+ const char* tls_fingerprint;
+ size_t raw_weight;
+ double weight;
};
+
+ size_t wt_max;
+ size_t wt_min;
+
+ uint64_t GetPoolCount();
+ bool GetPoolConfig(size_t id, pool_cfg& cfg);
enum slow_mem_cfg {
always_use,
@@ -34,13 +45,8 @@ public:
unknown_value
};
- bool GetTlsSetting();
bool TlsSecureAlgos();
- const char* GetTlsFingerprint();
-
- const char* GetPoolAddress();
- const char* GetPoolPwd();
- const char* GetWalletAddress();
+
const std::string GetCurrency();
bool IsCurrencyMonero();
@@ -59,9 +65,6 @@ public:
bool PreferIpv4();
-
- bool NiceHashMode();
-
inline bool HaveHardwareAes() { return bHaveAes; }
static void cpuid(uint32_t eax, int32_t ecx, int32_t val[4]);
diff --git a/xmrstak/misc/executor.cpp b/xmrstak/misc/executor.cpp
index b469dc2..931e2aa 100644
--- a/xmrstak/misc/executor.cpp
+++ b/xmrstak/misc/executor.cpp
@@ -39,6 +39,7 @@
#include <string>
#include <cmath>
#include <algorithm>
+#include <functional>
#include <assert.h>
#include <time.h>
@@ -58,23 +59,17 @@ void executor::push_timed_event(ex_event&& ev, size_t sec)
void executor::ex_clock_thd()
{
- size_t iSwitchPeriod = sec_to_ticks(iDevDonatePeriod);
- size_t iDevPortion = (size_t)floor(((double)iSwitchPeriod) * fDevDonationLevel);
-
- //No point in bothering with less than 10 sec
- if(iDevPortion < sec_to_ticks(10))
- iDevPortion = 0;
-
- //Add 2 seconds to compensate for connect
- if(iDevPortion != 0)
- iDevPortion += sec_to_ticks(2);
-
+ size_t tick = 0;
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(size_t(iTickTime)));
push_event(ex_event(EV_PERF_TICK));
+ //Eval pool choice every fourth tick
+ if((tick++ & 0x03) == 0)
+ push_event(ex_event(EV_EVAL_POOL_CHOICE));
+
// Service timed events
std::unique_lock<std::mutex> lck(timed_event_mutex);
std::list<timed_event>::iterator ev = lTimedEvents.begin();
@@ -90,49 +85,190 @@ void executor::ex_clock_thd()
ev++;
}
lck.unlock();
+ }
+}
+
+bool executor::get_live_pools(std::vector<jpsock*>& eval_pools, bool is_dev)
+{
+ size_t limit = jconf::inst()->GetGiveUpLimit();
+ size_t wait = jconf::inst()->GetNetRetry();
- if(iDevPortion == 0)
+ if(limit == 0 || is_dev) limit = (-1); //No limit = limit of 2^64-1
+
+ size_t pool_count = 0;
+ size_t over_limit = 0;
+ for(jpsock& pool : pools)
+ {
+ if(pool.is_dev_pool() != is_dev)
continue;
- iSwitchPeriod--;
- if(iSwitchPeriod == 0)
- {
- push_event(ex_event(EV_SWITCH_POOL, usr_pool_id));
- iSwitchPeriod = sec_to_ticks(iDevDonatePeriod);
- }
- else if(iSwitchPeriod == iDevPortion)
+ // Only eval live pools
+ size_t num, dtime;
+ if(pool.get_disconnects(num, dtime))
+ set_timestamp();
+
+ if(dtime == 0 || (dtime >= wait && num <= limit))
+ eval_pools.emplace_back(&pool);
+
+ pool_count++;
+ if(num > limit)
+ over_limit++;
+ }
+
+ if(eval_pools.size() == 0)
+ {
+ if(!is_dev)
{
- push_event(ex_event(EV_SWITCH_POOL, dev_pool_id));
+ if(xmrstak::globalStates::inst().pool_id != invalid_pool_id)
+ {
+ printer::inst()->print_msg(L0, "All pools are dead. Idling...");
+ auto work = xmrstak::miner_work();
+ xmrstak::pool_data dat;
+ xmrstak::globalStates::inst().switch_work(work, dat);
+ }
+
+ if(over_limit == pool_count)
+ {
+ printer::inst()->print_msg(L0, "All pools are over give up limit. Exitting.");
+ exit(0);
+ }
+
+ return false;
}
+ else
+ return get_live_pools(eval_pools, false);
}
+
+ return true;
}
-void executor::sched_reconnect()
+/*
+ * This event is called by the timer and whenever something relevant happens.
+ * The job here is to decide if we want to connect, disconnect, or switch jobs (or do nothing)
+ */
+void executor::eval_pool_choice()
{
- iReconnectAttempts++;
- size_t iLimit = jconf::inst()->GetGiveUpLimit();
- if(iLimit != 0 && iReconnectAttempts > iLimit)
+ std::vector<jpsock*> eval_pools;
+ eval_pools.reserve(pools.size());
+
+ bool dev_time = is_dev_time();
+ if(!get_live_pools(eval_pools, dev_time))
+ return;
+
+ size_t running = 0;
+ for(jpsock* pool : eval_pools)
{
- printer::inst()->print_msg(L0, "Give up limit reached. Exitting.");
- exit(0);
+ if(pool->is_running())
+ running++;
}
- long long unsigned int rt = jconf::inst()->GetNetRetry();
- printer::inst()->print_msg(L1, "Pool connection lost. Waiting %lld s before retry (attempt %llu).",
- rt, int_port(iReconnectAttempts));
+ // Special case - if we are without a pool, connect to all find a live pool asap
+ if(running == 0)
+ {
+ if(dev_time)
+ printer::inst()->print_msg(L1, "Fast-connecting to dev pool ...");
- auto work = xmrstak::miner_work();
- xmrstak::pool_data dat;
+ for(jpsock* pool : eval_pools)
+ {
+ if(pool->can_connect())
+ {
+ if(!dev_time)
+ printer::inst()->print_msg(L1, "Fast-connecting to %s pool ...", pool->get_pool_addr());
+ std::string error;
+ if(!pool->connect(error))
+ log_socket_error(pool, std::move(error));
+ }
+ }
+
+ return;
+ }
+
+ std::sort(eval_pools.begin(), eval_pools.end(), [](jpsock* a, jpsock* b) { return b->get_pool_weight(true) < a->get_pool_weight(true); });
+ jpsock* goal = eval_pools[0];
- xmrstak::globalStates::inst().switch_work(work, dat);
+ if(goal->get_pool_id() != xmrstak::globalStates::inst().pool_id)
+ {
+ if(!goal->is_running() && goal->can_connect())
+ {
+ if(dev_time)
+ printer::inst()->print_msg(L1, "Connecting to dev pool ...");
+ else
+ printer::inst()->print_msg(L1, "Connecting to %s pool ...", goal->get_pool_addr());
+
+ std::string error;
+ if(!goal->connect(error))
+ log_socket_error(goal, std::move(error));
+ return;
+ }
+
+ if(goal->is_logged_in())
+ {
+ pool_job oPoolJob;
+ if(!goal->get_current_job(oPoolJob))
+ {
+ goal->disconnect();
+ return;
+ }
- push_timed_event(ex_event(EV_RECONNECT, usr_pool_id), rt);
+ size_t prev_pool_id = current_pool_id;
+ current_pool_id = goal->get_pool_id();
+ on_pool_have_job(current_pool_id, oPoolJob);
+
+ jpsock* prev_pool = pick_pool_by_id(prev_pool_id);
+ if(prev_pool == nullptr || (!prev_pool->is_dev_pool() && !goal->is_dev_pool()))
+ reset_stats();
+
+ if(goal->is_dev_pool() && (prev_pool != nullptr && !prev_pool->is_dev_pool()))
+ last_usr_pool_id = prev_pool_id;
+ else
+ last_usr_pool_id = invalid_pool_id;
+
+ return;
+ }
+ }
+ else
+ {
+ /* All is good - but check if we can do better */
+ std::sort(eval_pools.begin(), eval_pools.end(), [](jpsock* a, jpsock* b) { return b->get_pool_weight(false) < a->get_pool_weight(false); });
+ jpsock* goal2 = eval_pools[0];
+
+ if(goal->get_pool_id() != goal2->get_pool_id())
+ {
+ if(!goal2->is_running() && goal2->can_connect())
+ {
+ printer::inst()->print_msg(L1, "Background-connect to %s pool ...", goal2->get_pool_addr());
+ std::string error;
+ if(!goal2->connect(error))
+ log_socket_error(goal2, std::move(error));
+ return;
+ }
+ }
+ }
+
+ if(!dev_time)
+ {
+ for(jpsock& pool : pools)
+ {
+ if(goal->is_logged_in() && pool.is_running() && pool.get_pool_id() != goal->get_pool_id())
+ pool.disconnect(true);
+
+ if(pool.is_dev_pool() && pool.is_running())
+ pool.disconnect(true);
+ }
+ }
}
-void executor::log_socket_error(std::string&& sError)
+void executor::log_socket_error(jpsock* pool, std::string&& sError)
{
+ std::string pool_name;
+ pool_name.reserve(128);
+ pool_name.append("[").append(pool->get_pool_addr()).append("] ");
+ sError.insert(0, pool_name);
+
vSocketLog.emplace_back(std::move(sError));
printer::inst()->print_msg(L1, "SOCKET ERROR - %s", vSocketLog.back().msg.c_str());
+
+ push_event(ex_event(EV_EVAL_POOL_CHOICE));
}
void executor::log_result_error(std::string&& sError)
@@ -172,70 +308,48 @@ jpsock* executor::pick_pool_by_id(size_t pool_id)
if(pool_id == invalid_pool_id)
return nullptr;
- if(pool_id == dev_pool_id)
- return dev_pool;
- else
- return usr_pool;
+ for(jpsock& pool : pools)
+ if(pool.get_pool_id() == pool_id)
+ return &pool;
+
+ return nullptr;
}
void executor::on_sock_ready(size_t pool_id)
{
jpsock* pool = pick_pool_by_id(pool_id);
+
+ if(pool->is_dev_pool())
+ printer::inst()->print_msg(L1, "Dev pool connected. Logging in...");
+ else
+ printer::inst()->print_msg(L1, "Pool %s connected. Logging in...", pool->get_pool_addr());
- if(pool_id == dev_pool_id)
- {
- if(::jconf::inst()->IsCurrencyMonero())
- {
- if(!pool->cmd_login("", ""))
- pool->disconnect();
- }
- else
- {
- if(!pool->cmd_login("WmsvqXDu7Fw5eAEZr1euJH3ycad55NxFd82PfhLR9Zi1Nq5S74zk63EA8fyMS8BQNR94os9N9aah87inKkumNJ7G2d7qTpRLN", "x"))
- pool->disconnect();
- }
-
- current_pool_id = dev_pool_id;
- printer::inst()->print_msg(L1, "Dev pool logged in. Switching work.");
- return;
- }
-
- printer::inst()->print_msg(L1, "Connected. Logging in...");
-
- if (!pool->cmd_login(jconf::inst()->GetWalletAddress(), jconf::inst()->GetPoolPwd()))
+ if(!pool->cmd_login())
{
if(!pool->have_sock_error())
{
- log_socket_error(pool->get_call_error());
+ log_socket_error(pool, pool->get_call_error());
pool->disconnect();
}
}
- else
- {
- iReconnectAttempts = 0;
- reset_stats();
- }
}
-void executor::on_sock_error(size_t pool_id, std::string&& sError)
+void executor::on_sock_error(size_t pool_id, std::string&& sError, bool silent)
{
jpsock* pool = pick_pool_by_id(pool_id);
- if(pool_id == dev_pool_id)
- {
- pool->disconnect();
-
- if(current_pool_id != dev_pool_id)
- return;
+ pool->disconnect();
+
+ if(pool_id == current_pool_id)
+ current_pool_id = invalid_pool_id;
- printer::inst()->print_msg(L1, "Dev pool connection error. Switching work.");
- on_switch_pool(usr_pool_id);
+ if(silent)
return;
- }
- log_socket_error(std::move(sError));
- pool->disconnect();
- sched_reconnect();
+ if(!pool->is_dev_pool())
+ log_socket_error(pool, std::move(sError));
+ else
+ printer::inst()->print_msg(L1, "Dev pool socket error - mining on user pool...");
}
void executor::on_pool_have_job(size_t pool_id, pool_job& oPoolJob)
@@ -245,9 +359,8 @@ void executor::on_pool_have_job(size_t pool_id, pool_job& oPoolJob)
jpsock* pool = pick_pool_by_id(pool_id);
- xmrstak::miner_work oWork(oPoolJob.sJobID, oPoolJob.bWorkBlob, oPoolJob.iWorkLen, oPoolJob.iTarget,
- pool_id != dev_pool_id && ::jconf::inst()->NiceHashMode(), pool_id);
-
+ xmrstak::miner_work oWork(oPoolJob.sJobID, oPoolJob.bWorkBlob, oPoolJob.iWorkLen, oPoolJob.iTarget, pool->is_nicehash(), pool_id);
+
xmrstak::pool_data dat;
dat.iSavedNonce = oPoolJob.iSavedNonce;
dat.pool_id = pool_id;
@@ -261,7 +374,7 @@ void executor::on_pool_have_job(size_t pool_id, pool_job& oPoolJob)
prev_pool->save_nonce(dat.iSavedNonce);
}
- if(pool_id == dev_pool_id)
+ if(pool->is_dev_pool())
return;
if(iPoolDiff != pool->get_current_diff())
@@ -270,17 +383,22 @@ void executor::on_pool_have_job(size_t pool_id, pool_job& oPoolJob)
printer::inst()->print_msg(L2, "Difficulty changed. Now: %llu.", int_port(iPoolDiff));
}
- if(dat.pool_id == pool_id)
- printer::inst()->print_msg(L3, "New block detected.");
+ if(dat.pool_id != pool_id)
+ {
+ if(dat.pool_id == invalid_pool_id)
+ printer::inst()->print_msg(L2, "Pool logged in.");
+ else
+ printer::inst()->print_msg(L2, "Pool switched.");
+ }
else
- printer::inst()->print_msg(L3, "Pool switched.");
+ printer::inst()->print_msg(L3, "New block detected.");
}
void executor::on_miner_result(size_t pool_id, job_result& oResult)
{
jpsock* pool = pick_pool_by_id(pool_id);
- if(pool_id == dev_pool_id)
+ if(pool->is_dev_pool())
{
//Ignore errors silently
if(pool->is_running() && pool->is_logged_in())
@@ -331,64 +449,6 @@ void executor::on_miner_result(size_t pool_id, job_result& oResult)
}
}
-void executor::on_reconnect(size_t pool_id)
-{
- jpsock* pool = pick_pool_by_id(pool_id);
-
- std::string error;
- if(pool_id == dev_pool_id)
- return;
-
- printer::inst()->print_msg(L1, "Connecting to pool %s ...", jconf::inst()->GetPoolAddress());
-
- if(!pool->connect(jconf::inst()->GetPoolAddress(), error))
- {
- log_socket_error(std::move(error));
- sched_reconnect();
- }
-}
-
-void executor::on_switch_pool(size_t pool_id)
-{
- if(pool_id == current_pool_id)
- return;
-
- jpsock* pool = pick_pool_by_id(pool_id);
- if(pool_id == dev_pool_id)
- {
- std::string error;
-
- // If it fails, it fails, we carry on on the usr pool
- // as we never receive further events
- printer::inst()->print_msg(L1, "Connecting to dev pool...");
- std::string dev_pool_addr;
- if(::jconf::inst()->IsCurrencyMonero())
- dev_pool_addr = jconf::inst()->GetTlsSetting() ? "donate.xmr-stak.net:6666" : "donate.xmr-stak.net:3333";
- else
- dev_pool_addr = jconf::inst()->GetTlsSetting() ? "mine.aeon-pool.com:443" : "mine.aeon-pool.com:5555";
- if(!pool->connect(dev_pool_addr.c_str(), error))
- printer::inst()->print_msg(L1, "Error connecting to dev pool. Staying with user pool.");
- }
- else
- {
- printer::inst()->print_msg(L1, "Switching back to user pool.");
-
- current_pool_id = pool_id;
- pool_job oPoolJob;
-
- if(!pool->get_current_job(oPoolJob))
- {
- pool->disconnect();
- return;
- }
-
- on_pool_have_job(current_pool_id, oPoolJob);
-
- if(dev_pool->is_running())
- push_timed_event(ex_event(EV_DEV_POOL_EXIT), 5);
- }
-}
-
void executor::ex_main()
{
assert(1000 % iTickTime == 0);
@@ -406,15 +466,36 @@ void executor::ex_main()
telem = new xmrstak::telemetry(pvThreads->size());
- current_pool_id = usr_pool_id;
- usr_pool = new jpsock(usr_pool_id, jconf::inst()->GetTlsSetting());
- dev_pool = new jpsock(dev_pool_id, jconf::inst()->GetTlsSetting());
+ set_timestamp();
+ size_t pc = jconf::inst()->GetPoolCount();
+ bool tls = true;
+ for(size_t i=0; i < pc; i++)
+ {
+ jconf::pool_cfg cfg;
+ jconf::inst()->GetPoolConfig(i, cfg);
+ if(!cfg.tls) tls = false;
+ pools.emplace_back(i+1, cfg.sPoolAddr, cfg.sWalletAddr, cfg.sPasswd, cfg.weight, false, cfg.tls, cfg.tls_fingerprint, cfg.nicehash);
+ }
+
+ if(jconf::inst()->IsCurrencyMonero())
+ {
+ if(tls)
+ pools.emplace_front(0, "donate.xmr-stak.net:6666", "", "", 0.0, true, true, "", false);
+ else
+ pools.emplace_front(0, "donate.xmr-stak.net:3333", "", "", 0.0, true, false, "", false);
+ }
+ else
+ {
+ if(tls)
+ pools.emplace_front(0, "donate.xmr-stak.net:7777", "", "", 0.0, true, true, "", false);
+ else
+ pools.emplace_front(0, "donate.xmr-stak.net:4444", "", "", 0.0, true, false, "", false);
+ }
ex_event ev;
std::thread clock_thd(&executor::ex_clock_thd, this);
- //This will connect us to the pool for the first time
- push_event(ex_event(EV_RECONNECT, usr_pool_id));
+ eval_pool_choice();
// Place the default success result at position 0, it needs to
// be here even if our first result is a failure
@@ -435,7 +516,7 @@ void executor::ex_main()
break;
case EV_SOCK_ERROR:
- on_sock_error(ev.iPoolId, std::move(ev.sSocketError));
+ on_sock_error(ev.iPoolId, std::move(ev.oSocketError.sSocketError), ev.oSocketError.silent);
break;
case EV_POOL_HAVE_JOB:
@@ -446,16 +527,8 @@ void executor::ex_main()
on_miner_result(ev.iPoolId, ev.oJobResult);
break;
- case EV_RECONNECT:
- on_reconnect(ev.iPoolId);
- break;
-
- case EV_SWITCH_POOL:
- on_switch_pool(ev.iPoolId);
- break;
-
- case EV_DEV_POOL_EXIT:
- dev_pool->disconnect();
+ case EV_EVAL_POOL_CHOICE:
+ eval_pool_choice();
break;
case EV_PERF_TICK:
@@ -486,7 +559,7 @@ void executor::ex_main()
if(normal && fHighestHps < fHps)
fHighestHps = fHps;
}
- break;
+ break;
case EV_USR_HASHRATE:
case EV_USR_RESULTS:
@@ -672,11 +745,13 @@ void executor::connection_report(std::string& out)
out.reserve(512);
- jpsock* pool = pick_pool_by_id(dev_pool_id + 1);
+ jpsock* pool = pick_pool_by_id(current_pool_id);
+ if(pool != nullptr && pool->is_dev_pool())
+ pool = pick_pool_by_id(last_usr_pool_id);
out.append("CONNECTION REPORT\n");
- out.append("Pool address : ").append(jconf::inst()->GetPoolAddress()).append(1, '\n');
- if (pool->is_running() && pool->is_logged_in())
+ out.append("Pool address : ").append(pool != nullptr ? pool->get_pool_addr() : "<not connected>").append(1, '\n');
+ if(pool != nullptr && pool->is_running() && pool->is_logged_in())
out.append("Connected since : ").append(time_format(date, sizeof(date), tPoolConnTime)).append(1, '\n');
else
out.append("Connected since : <not connected>\n");
@@ -833,9 +908,12 @@ void executor::http_connection_report(std::string& out)
snprintf(buffer, sizeof(buffer), sHtmlCommonHeader, "Connection Report", "Connection Report");
out.append(buffer);
- jpsock* pool = pick_pool_by_id(dev_pool_id + 1);
+ jpsock* pool = pick_pool_by_id(current_pool_id);
+ if(pool != nullptr && pool->is_dev_pool())
+ pool = pick_pool_by_id(last_usr_pool_id);
+
const char* cdate = "not connected";
- if (pool->is_running() && pool->is_logged_in())
+ if (pool != nullptr && pool->is_running() && pool->is_logged_in())
cdate = time_format(date, sizeof(date), tPoolConnTime);
size_t n_calls = iPoolCallTimes.size();
@@ -848,7 +926,7 @@ void executor::http_connection_report(std::string& out)
}
snprintf(buffer, sizeof(buffer), sHtmlConnectionBodyHigh,
- jconf::inst()->GetPoolAddress(),
+ pool != nullptr ? pool->get_pool_addr() : "not connected",
cdate, ping_time);
out.append(buffer);
@@ -918,10 +996,12 @@ void executor::http_json_report(std::string& out)
for(size_t i=1; i < ln; i++)
iTotalRes += vMineResults[i].count;
- jpsock* pool = pick_pool_by_id(dev_pool_id + 1);
+ jpsock* pool = pick_pool_by_id(current_pool_id);
+ if(pool != nullptr && pool->is_dev_pool())
+ pool = pick_pool_by_id(last_usr_pool_id);
size_t iConnSec = 0;
- if(pool->is_running() && pool->is_logged_in())
+ if(pool != nullptr && pool->is_running() && pool->is_logged_in())
{
using namespace std::chrono;
iConnSec = duration_cast<seconds>(system_clock::now() - tPoolConnTime).count();
@@ -973,7 +1053,7 @@ void executor::http_json_report(std::string& out)
int_port(iPoolDiff), int_port(iGoodRes), int_port(iTotalRes), fAvgResTime, int_port(iPoolHashes),
int_port(iTopDiff[0]), int_port(iTopDiff[1]), int_port(iTopDiff[2]), int_port(iTopDiff[3]), int_port(iTopDiff[4]),
int_port(iTopDiff[5]), int_port(iTopDiff[6]), int_port(iTopDiff[7]), int_port(iTopDiff[8]), int_port(iTopDiff[9]),
- res_error.c_str(), jconf::inst()->GetPoolAddress(), int_port(iConnSec), int_port(iPoolPing), cn_error.c_str());
+ res_error.c_str(), pool != nullptr ? pool->get_pool_addr() : "not connected", int_port(iConnSec), int_port(iPoolPing), cn_error.c_str());
out = std::string(bigbuf.get(), bigbuf.get() + bb_len);
}
diff --git a/xmrstak/misc/executor.hpp b/xmrstak/misc/executor.hpp
index d705c27..11c8206 100644
--- a/xmrstak/misc/executor.hpp
+++ b/xmrstak/misc/executor.hpp
@@ -5,12 +5,14 @@
#include "xmrstak/backend/iBackend.hpp"
#include "xmrstak/misc/environment.hpp"
#include "xmrstak/net/msgstruct.hpp"
+#include "xmrstak/donate-level.hpp"
#include <atomic>
#include <array>
#include <list>
+#include <vector>
#include <future>
-
+#include <chrono>
class jpsock;
@@ -42,10 +44,6 @@ public:
void push_timed_event(ex_event&& ev, size_t sec);
void log_result_error(std::string&& sError);
- constexpr static size_t invalid_pool_id = 0;
- constexpr static size_t dev_pool_id = 1;
- constexpr static size_t usr_pool_id = 2;
-
private:
struct timed_event
{
@@ -55,6 +53,8 @@ private:
timed_event(ex_event&& ev, size_t ticks) : event(std::move(ev)), ticks_left(ticks) {}
};
+ inline void set_timestamp() { dev_timestamp = get_timestamp(); };
+
// In miliseconds, has to divide a second (1000ms) into an integer number
constexpr static size_t iTickTime = 500;
@@ -62,6 +62,17 @@ private:
// We will divide up this period according to the config setting
constexpr static size_t iDevDonatePeriod = 100 * 60;
+ inline bool is_dev_time()
+ {
+ //Add 2 seconds to compensate for connect
+ constexpr size_t dev_portion = double(iDevDonatePeriod) * fDevDonationLevel + 2;
+
+ if(dev_portion < 12) //No point in bothering with less than 10s
+ return false;
+
+ return (get_timestamp() - dev_timestamp) % iDevDonatePeriod >= (iDevDonatePeriod - dev_portion);
+ };
+
std::list<timed_event> lTimedEvents;
std::mutex timed_event_mutex;
thdq<ex_event> oEventQ;
@@ -69,15 +80,14 @@ private:
xmrstak::telemetry* telem;
std::vector<xmrstak::iBackend*>* pvThreads;
- size_t current_pool_id;
+ size_t current_pool_id = invalid_pool_id;
+ size_t last_usr_pool_id = invalid_pool_id;
+ size_t dev_timestamp;
- jpsock* usr_pool;
- jpsock* dev_pool;
+ std::list<jpsock> pools;
jpsock* pick_pool_by_id(size_t pool_id);
- bool is_dev_time;
-
executor();
void ex_main();
@@ -101,8 +111,6 @@ private:
std::promise<void> httpReady;
std::mutex httpMutex;
- size_t iReconnectAttempts = 0;
-
struct sck_error_log
{
std::chrono::system_clock::time_point time;
@@ -171,17 +179,16 @@ private:
double fHighestHps = 0.0;
- void log_socket_error(std::string&& sError);
+ void log_socket_error(jpsock* pool, std::string&& sError);
void log_result_ok(uint64_t iActualDiff);
- void sched_reconnect();
-
void on_sock_ready(size_t pool_id);
- void on_sock_error(size_t pool_id, std::string&& sError);
+ void on_sock_error(size_t pool_id, std::string&& sError, bool silent);
void on_pool_have_job(size_t pool_id, pool_job& oPoolJob);
void on_miner_result(size_t pool_id, job_result& oResult);
- void on_reconnect(size_t pool_id);
- void on_switch_pool(size_t pool_id);
+ void connect_to_pools(std::list<jpsock*>& eval_pools);
+ bool get_live_pools(std::vector<jpsock*>& eval_pools, bool is_dev);
+ void eval_pool_choice();
inline size_t sec_to_ticks(size_t sec) { return sec * (1000 / iTickTime); }
};
diff --git a/xmrstak/net/jpsock.cpp b/xmrstak/net/jpsock.cpp
index 68b495d..f6f5c1f 100644
--- a/xmrstak/net/jpsock.cpp
+++ b/xmrstak/net/jpsock.cpp
@@ -94,7 +94,9 @@ struct jpsock::opq_json_val
opq_json_val(const Value* val) : val(val) {}
};
-jpsock::jpsock(size_t id, bool tls) : pool_id(id)
+jpsock::jpsock(size_t id, const char* sAddr, const char* sLogin, const char* sPassword, double pool_weight, bool dev_pool, bool tls, const char* tls_fp, bool nicehash) :
+ net_addr(sAddr), usr_login(sLogin), usr_pass(sPassword), tls_fp(tls_fp), pool_id(id), pool_weight(pool_weight), pool(dev_pool), nicehash(nicehash),
+ connect_time(0), connect_attempts(0), disconnect_time(0), quiet_close(false)
{
sock_init();
@@ -189,7 +191,7 @@ bool jpsock::set_socket_error_strerr(const char* a, int res)
void jpsock::jpsock_thread()
{
jpsock_thd_main();
- executor::inst()->push_event(ex_event(std::move(sSocketError), pool_id));
+ executor::inst()->push_event(ex_event(std::move(sSocketError), quiet_close, pool_id));
// If a call is wating, send an error to end it
bool bCallWaiting = false;
@@ -206,11 +208,16 @@ void jpsock::jpsock_thread()
if(bCallWaiting)
call_cond.notify_one();
- bRunning = false;
bLoggedIn = false;
+ if(bHaveSocketError && !quiet_close)
+ disconnect_time = get_timestamp();
+ else
+ disconnect_time = 0;
+
std::unique_lock<std::mutex>(job_mutex);
memset(&oCurrentJob, 0, sizeof(oCurrentJob));
+ bRunning = false;
}
bool jpsock::jpsock_thd_main()
@@ -417,15 +424,18 @@ bool jpsock::process_pool_job(const opq_json_val* params)
return true;
}
-bool jpsock::connect(const char* sAddr, std::string& sConnectError)
+bool jpsock::connect(std::string& sConnectError)
{
bHaveSocketError = false;
sSocketError.clear();
iJobDiff = 0;
-
- if(sck->set_hostname(sAddr))
+ connect_attempts++;
+
+ if(sck->set_hostname(net_addr.c_str()))
{
bRunning = true;
+ disconnect_time = 0;
+ connect_time = get_timestamp();
oRecvThd = new std::thread(&jpsock::jpsock_thread, this);
return true;
}
@@ -434,8 +444,9 @@ bool jpsock::connect(const char* sAddr, std::string& sConnectError)
return false;
}
-void jpsock::disconnect()
+void jpsock::disconnect(bool quiet)
{
+ quiet_close = quiet;
sck->close(false);
if(oRecvThd != nullptr)
@@ -446,6 +457,7 @@ void jpsock::disconnect()
}
sck->close(true);
+ quiet_close = false;
}
bool jpsock::cmd_ret_wait(const char* sPacket, opq_json_val& poResult)
@@ -493,12 +505,12 @@ bool jpsock::cmd_ret_wait(const char* sPacket, opq_json_val& poResult)
return bSuccess;
}
-bool jpsock::cmd_login(const char* sLogin, const char* sPassword)
+bool jpsock::cmd_login()
{
char cmd_buffer[1024];
snprintf(cmd_buffer, sizeof(cmd_buffer), "{\"method\":\"login\",\"params\":{\"login\":\"%s\",\"pass\":\"%s\",\"agent\":\"" AGENTID_STR "\"},\"id\":1}\n",
- sLogin, sPassword);
+ usr_login.c_str(), usr_pass.c_str());
opq_json_val oResult(nullptr);
@@ -541,6 +553,7 @@ bool jpsock::cmd_login(const char* sLogin, const char* sPassword)
}
bLoggedIn = true;
+ connect_attempts = 0;
return true;
}
diff --git a/xmrstak/net/jpsock.hpp b/xmrstak/net/jpsock.hpp
index e94eaad..db8cd0e 100644
--- a/xmrstak/net/jpsock.hpp
+++ b/xmrstak/net/jpsock.hpp
@@ -26,20 +26,37 @@ class base_socket;
class jpsock
{
public:
- jpsock(size_t id, bool tls);
+ jpsock(size_t id, const char* sAddr, const char* sLogin, const char* sPassword, double pool_weight, bool dev_pool, bool tls, const char* tls_fp, bool nicehash);
~jpsock();
- bool connect(const char* sAddr, std::string& sConnectError);
- void disconnect();
+ bool connect(std::string& sConnectError);
+ void disconnect(bool quiet = false);
- bool cmd_login(const char* sLogin, const char* sPassword);
+ bool cmd_login();
bool cmd_submit(const char* sJobId, uint32_t iNonce, const uint8_t* bResult);
static bool hex2bin(const char* in, unsigned int len, unsigned char* out);
static void bin2hex(const unsigned char* in, unsigned int len, char* out);
+ inline double get_pool_weight(bool gross_weight)
+ {
+ double ret = pool_weight;
+ if(gross_weight && bRunning)
+ ret += 10.0;
+ if(gross_weight && bLoggedIn)
+ ret += 10.0;
+ return ret;
+ }
+
+ inline size_t can_connect() { return get_timestamp() != connect_time; }
inline bool is_running() { return bRunning; }
inline bool is_logged_in() { return bLoggedIn; }
+ inline bool is_dev_pool() { return pool; }
+ inline size_t get_pool_id() { return pool_id; }
+ inline bool get_disconnects(size_t& att, size_t& time) { att = connect_attempts; time = disconnect_time != 0 ? get_timestamp() - disconnect_time + 1 : 0; return pool && usr_login[0]; }
+ inline const char* get_pool_addr() { return net_addr.c_str(); }
+ inline const char* get_tls_fp() { return tls_fp.c_str(); }
+ inline bool is_nicehash() { return nicehash; }
std::string&& get_call_error();
bool have_sock_error() { return bHaveSocketError; }
@@ -53,8 +70,6 @@ public:
void save_nonce(uint32_t nonce);
bool get_current_job(pool_job& job);
- size_t pool_id;
-
bool set_socket_error(const char* a);
bool set_socket_error(const char* a, const char* b);
bool set_socket_error(const char* a, size_t len);
@@ -62,8 +77,23 @@ public:
bool set_socket_error_strerr(const char* a, int res);
private:
+ std::string net_addr;
+ std::string usr_login;
+ std::string usr_pass;
+ std::string tls_fp;
+
+ size_t pool_id;
+ double pool_weight;
+ bool pool;
+ bool nicehash;
+
+ size_t connect_time = 0;
+ std::atomic<size_t> connect_attempts;
+ std::atomic<size_t> disconnect_time;
+
std::atomic<bool> bRunning;
std::atomic<bool> bLoggedIn;
+ std::atomic<bool> quiet_close;
uint8_t* bJsonRecvMem;
uint8_t* bJsonParseMem;
diff --git a/xmrstak/net/msgstruct.hpp b/xmrstak/net/msgstruct.hpp
index 82b59c1..01aa76c 100644
--- a/xmrstak/net/msgstruct.hpp
+++ b/xmrstak/net/msgstruct.hpp
@@ -39,11 +39,33 @@ struct job_result
}
};
+struct sock_err
+{
+ std::string sSocketError;
+ bool silent;
+
+ sock_err() {}
+ sock_err(std::string&& err, bool silent) : sSocketError(std::move(err)), silent(silent) { }
+ sock_err(sock_err&& from) : sSocketError(std::move(from.sSocketError)), silent(from.silent) {}
+
+ sock_err& operator=(sock_err&& from)
+ {
+ assert(this != &from);
+ sSocketError = std::move(from.sSocketError);
+ silent = from.silent;
+ return *this;
+ }
+
+ ~sock_err() { }
+
+ sock_err(sock_err const&) = delete;
+ sock_err& operator=(sock_err const&) = delete;
+};
enum ex_event_name { EV_INVALID_VAL, EV_SOCK_READY, EV_SOCK_ERROR,
- EV_POOL_HAVE_JOB, EV_MINER_HAVE_RESULT, EV_PERF_TICK, EV_RECONNECT,
- EV_SWITCH_POOL, EV_DEV_POOL_EXIT, EV_USR_HASHRATE, EV_USR_RESULTS, EV_USR_CONNSTAT,
- EV_HASHRATE_LOOP, EV_HTML_HASHRATE, EV_HTML_RESULTS, EV_HTML_CONNSTAT, EV_HTML_JSON };
+ EV_POOL_HAVE_JOB, EV_MINER_HAVE_RESULT, EV_PERF_TICK, EV_EVAL_POOL_CHOICE,
+ EV_USR_HASHRATE, EV_USR_RESULTS, EV_USR_CONNSTAT, EV_HASHRATE_LOOP,
+ EV_HTML_HASHRATE, EV_HTML_RESULTS, EV_HTML_CONNSTAT, EV_HTML_JSON };
/*
This is how I learned to stop worrying and love c++11 =).
@@ -64,11 +86,11 @@ struct ex_event
{
pool_job oPoolJob;
job_result oJobResult;
- std::string sSocketError;
+ sock_err oSocketError;
};
ex_event() { iName = EV_INVALID_VAL; iPoolId = 0;}
- ex_event(std::string&& err, size_t id) : iName(EV_SOCK_ERROR), iPoolId(id), sSocketError(std::move(err)) { }
+ ex_event(std::string&& err, bool silent, size_t id) : iName(EV_SOCK_ERROR), iPoolId(id), oSocketError(std::move(err), silent) { }
ex_event(job_result dat, size_t id) : iName(EV_MINER_HAVE_RESULT), iPoolId(id), oJobResult(dat) {}
ex_event(pool_job dat, size_t id) : iName(EV_POOL_HAVE_JOB), iPoolId(id), oPoolJob(dat) {}
ex_event(ex_event_name ev, size_t id = 0) : iName(ev), iPoolId(id) {}
@@ -85,7 +107,7 @@ struct ex_event
switch(iName)
{
case EV_SOCK_ERROR:
- new (&sSocketError) std::string(std::move(from.sSocketError));
+ new (&oSocketError) sock_err(std::move(from.oSocketError));
break;
case EV_MINER_HAVE_RESULT:
oJobResult = from.oJobResult;
@@ -103,7 +125,7 @@ struct ex_event
assert(this != &from);
if(iName == EV_SOCK_ERROR)
- sSocketError.~basic_string();
+ oSocketError.~sock_err();
iName = from.iName;
iPoolId = from.iPoolId;
@@ -111,8 +133,8 @@ struct ex_event
switch(iName)
{
case EV_SOCK_ERROR:
- new (&sSocketError) std::string();
- sSocketError = std::move(from.sSocketError);
+ new (&oSocketError) sock_err();
+ oSocketError = std::move(from.oSocketError);
break;
case EV_MINER_HAVE_RESULT:
oJobResult = from.oJobResult;
@@ -130,6 +152,14 @@ struct ex_event
~ex_event()
{
if(iName == EV_SOCK_ERROR)
- sSocketError.~basic_string();
+ oSocketError.~sock_err();
}
};
+
+#include <chrono>
+//Get steady_clock timestamp - misc helper function
+inline size_t get_timestamp()
+{
+ using namespace std::chrono;
+ return time_point_cast<seconds>(steady_clock::now()).time_since_epoch().count();
+};
diff --git a/xmrstak/net/socket.cpp b/xmrstak/net/socket.cpp
index b93376e..7079205 100644
--- a/xmrstak/net/socket.cpp
+++ b/xmrstak/net/socket.cpp
@@ -185,7 +185,15 @@ void tls_socket::print_error()
char *buf = nullptr;
size_t len = BIO_get_mem_data(err_bio, &buf);
- pCallback->set_socket_error(buf, len);
+ if(buf == nullptr)
+ {
+ if(jconf::inst()->TlsSecureAlgos())
+ pCallback->set_socket_error("Unknown TLS error. Secure TLS maybe unspported, try setting tls_secure_algo to false.");
+ else
+ pCallback->set_socket_error("Unknown TLS error.");
+ }
+ else
+ pCallback->set_socket_error(buf, len);
BIO_free(err_bio);
}
@@ -290,41 +298,42 @@ bool tls_socket::connect()
return false;
}
- if(pCallback->pool_id != executor::dev_pool_id)
+ //Base64 encode digest
+ BIO *bmem, *b64;
+ b64 = BIO_new(BIO_f_base64());
+ bmem = BIO_new(BIO_s_mem());
+
+ BIO_puts(bmem, "SHA256:");
+ b64 = BIO_push(b64, bmem);
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ BIO_write(b64, md, dlen);
+ BIO_flush(b64);
+
+ const char* conf_md = pCallback->get_tls_fp();
+ char *b64_md = nullptr;
+ size_t b64_len = BIO_get_mem_data(bmem, &b64_md);
+
+ if(strlen(conf_md) == 0)
{
- //Base64 encode digest
- BIO *bmem, *b64;
- b64 = BIO_new(BIO_f_base64());
- bmem = BIO_new(BIO_s_mem());
-
- BIO_puts(bmem, "SHA256:");
- b64 = BIO_push(b64, bmem);
- BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
- BIO_write(b64, md, dlen);
- BIO_flush(b64);
-
- const char* conf_md = jconf::inst()->GetTlsFingerprint();
- char *b64_md = nullptr;
- size_t b64_len = BIO_get_mem_data(bmem, &b64_md);
-
- if(strlen(conf_md) == 0)
- {
- printer::inst()->print_msg(L1, "Server fingerprint: %.*s", (int)b64_len, b64_md);
- }
- else if(strncmp(b64_md, conf_md, b64_len) != 0)
+ if(!pCallback->is_dev_pool())
+ printer::inst()->print_msg(L1, "TLS fingerprint [%s] %.*s", pCallback->get_pool_addr(), (int)b64_len, b64_md);
+ }
+ else if(strncmp(b64_md, conf_md, b64_len) != 0)
+ {
+ if(!pCallback->is_dev_pool())
{
- printer::inst()->print_msg(L0, "FINGERPRINT FAILED CHECK: %.*s was given, %s was configured",
- (int)b64_len, b64_md, conf_md);
-
- pCallback->set_socket_error("FINGERPRINT FAILED CHECK");
- BIO_free_all(b64);
- X509_free(cert);
- return false;
+ printer::inst()->print_msg(L0, "FINGERPRINT FAILED CHECK [%s] %.*s was given, %s was configured",
+ pCallback->get_pool_addr(), (int)b64_len, b64_md, conf_md);
}
+ pCallback->set_socket_error("FINGERPRINT FAILED CHECK");
BIO_free_all(b64);
+ X509_free(cert);
+ return false;
}
+ BIO_free_all(b64);
+
X509_free(cert);
return true;
}
OpenPOWER on IntegriCloud