diff options
author | fireice-uk <fireice-uk@users.noreply.github.com> | 2017-11-09 22:43:08 +0000 |
---|---|---|
committer | psychocrypt <psychocrypt@users.noreply.github.com> | 2017-11-09 23:43:08 +0100 |
commit | d35893d926c74893d7c85d1b87b24ffa55744649 (patch) | |
tree | b2c1186e3ad96696592e1572e553514af04048b4 /xmrstak | |
parent | b3237bab587f937e90a2ff0d06aba5e3630a8047 (diff) | |
download | xmr-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.hpp | 3 | ||||
-rw-r--r-- | xmrstak/backend/globalStates.hpp | 5 | ||||
-rw-r--r-- | xmrstak/cli/cli-miner.cpp | 220 | ||||
-rw-r--r-- | xmrstak/config.tpl | 35 | ||||
-rw-r--r-- | xmrstak/jconf.cpp | 163 | ||||
-rw-r--r-- | xmrstak/jconf.hpp | 31 | ||||
-rw-r--r-- | xmrstak/misc/executor.cpp | 420 | ||||
-rw-r--r-- | xmrstak/misc/executor.hpp | 43 | ||||
-rw-r--r-- | xmrstak/net/jpsock.cpp | 31 | ||||
-rw-r--r-- | xmrstak/net/jpsock.hpp | 42 | ||||
-rw-r--r-- | xmrstak/net/msgstruct.hpp | 50 | ||||
-rw-r--r-- | xmrstak/net/socket.cpp | 67 |
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; } |