2021-08-22 10:20:59 +00:00
|
|
|
/*
|
|
|
|
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
2022-03-30 12:42:26 +00:00
|
|
|
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
2021-08-22 10:20:59 +00:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, version 3.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "uv_util.h"
|
2021-08-24 09:42:41 +00:00
|
|
|
#include <map>
|
|
|
|
#include <thread>
|
2021-08-22 10:20:59 +00:00
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
#include <sched.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static constexpr char log_category_prefix[] = "Util ";
|
|
|
|
|
|
|
|
namespace p2pool {
|
|
|
|
|
2021-10-02 15:06:48 +00:00
|
|
|
#define STR2(X) STR(X)
|
|
|
|
#define STR(X) #X
|
|
|
|
|
2022-10-31 16:39:33 +00:00
|
|
|
const char* VERSION = "v2.5 (built"
|
2021-10-02 15:06:48 +00:00
|
|
|
#if defined(__clang__)
|
|
|
|
" with clang/" __clang_version__
|
|
|
|
#elif defined(__GNUC__)
|
|
|
|
" with GCC/" STR2(__GNUC__) "." STR2(__GNUC_MINOR__) "." STR2(__GNUC_PATCHLEVEL__)
|
|
|
|
#elif defined(_MSC_VER)
|
|
|
|
" with MSVC/" STR2(_MSC_VER)
|
|
|
|
#endif
|
|
|
|
" on " __DATE__ ")";
|
|
|
|
|
|
|
|
#undef STR2
|
|
|
|
#undef STR
|
|
|
|
|
2021-08-22 10:20:59 +00:00
|
|
|
MinerCallbackHandler::~MinerCallbackHandler() {}
|
|
|
|
|
|
|
|
void panic()
|
|
|
|
{
|
|
|
|
p2pool::log::stop();
|
|
|
|
do {
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (IsDebuggerPresent()) {
|
|
|
|
__debugbreak();
|
|
|
|
}
|
|
|
|
#endif
|
2021-09-22 15:52:23 +00:00
|
|
|
abort();
|
2021-08-22 10:20:59 +00:00
|
|
|
} while (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void make_thread_background()
|
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
SetThreadPriorityBoost(GetCurrentThread(), true);
|
|
|
|
SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN);
|
|
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
|
2022-04-05 17:05:15 +00:00
|
|
|
#elif !defined(__APPLE__) && !defined(__FreeBSD__)
|
2021-08-22 10:20:59 +00:00
|
|
|
sched_param param;
|
|
|
|
param.sched_priority = 0;
|
|
|
|
if (sched_setscheduler(0, SCHED_IDLE, ¶m) != 0) {
|
|
|
|
sched_setscheduler(0, SCHED_BATCH, ¶m);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
NOINLINE bool difficulty_type::check_pow(const hash& pow_hash) const
|
|
|
|
{
|
|
|
|
const uint64_t* a = reinterpret_cast<const uint64_t*>(pow_hash.h);
|
|
|
|
|
|
|
|
uint64_t result[6] = {};
|
|
|
|
uint64_t product[6] = {};
|
|
|
|
|
|
|
|
if (LIKELY(hi == 0)) {
|
|
|
|
for (int i = 3; i >= 0; --i) {
|
|
|
|
product[0] = umul128(a[i], lo, &product[1]);
|
|
|
|
|
|
|
|
uint64_t carry = 0;
|
|
|
|
for (int k = i, l = 0; k < 5; ++k, ++l) {
|
2021-08-28 09:50:20 +00:00
|
|
|
uint64_t t = result[k] + product[l];
|
|
|
|
const uint64_t next_carry = static_cast<uint64_t>(t < result[k]);
|
|
|
|
t += carry;
|
|
|
|
carry = next_carry | static_cast<uint64_t>(t < result[k]);
|
2021-08-22 10:20:59 +00:00
|
|
|
result[k] = t;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result[4]) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const uint64_t* b = reinterpret_cast<const uint64_t*>(this);
|
|
|
|
|
|
|
|
for (int i = 3; i >= 0; --i) {
|
|
|
|
for (int j = 1; j >= 0; --j) {
|
|
|
|
product[0] = umul128(a[i], b[j], &product[1]);
|
|
|
|
|
|
|
|
uint64_t carry = 0;
|
|
|
|
for (int k = i + j, l = 0; k < 6; ++k, ++l) {
|
2021-08-28 09:50:20 +00:00
|
|
|
uint64_t t = result[k] + product[l];
|
|
|
|
const uint64_t next_carry = static_cast<uint64_t>(t < result[k]);
|
|
|
|
t += carry;
|
|
|
|
carry = next_carry | static_cast<uint64_t>(t < result[k]);
|
2021-08-22 10:20:59 +00:00
|
|
|
result[k] = t;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result[4] || result[5]) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
difficulty_type operator+(const difficulty_type& a, const difficulty_type& b)
|
|
|
|
{
|
|
|
|
difficulty_type result = a;
|
|
|
|
result += b;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-09-03 16:04:54 +00:00
|
|
|
std::ostream& operator<<(std::ostream& s, const difficulty_type& d)
|
|
|
|
{
|
|
|
|
char buf[log::Stream::BUF_SIZE + 1];
|
|
|
|
log::Stream s1(buf);
|
|
|
|
s1 << d << '\0';
|
|
|
|
s << buf;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::istream& operator>>(std::istream& s, difficulty_type& diff)
|
|
|
|
{
|
|
|
|
diff.lo = 0;
|
|
|
|
diff.hi = 0;
|
|
|
|
|
|
|
|
bool found_number = false;
|
|
|
|
char c;
|
|
|
|
while (s.good() && !s.eof()) {
|
|
|
|
s.read(&c, 1);
|
|
|
|
if (!s.good() || s.eof()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ('0' <= c && c <= '9') {
|
|
|
|
found_number = true;
|
|
|
|
const uint32_t digit = static_cast<uint32_t>(c - '0');
|
|
|
|
uint64_t hi;
|
|
|
|
diff.lo = umul128(diff.lo, 10, &hi) + digit;
|
|
|
|
if (diff.lo < digit) {
|
|
|
|
++hi;
|
|
|
|
}
|
|
|
|
diff.hi = diff.hi * 10 + hi;
|
|
|
|
}
|
|
|
|
else if (found_number) {
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2021-09-05 20:28:57 +00:00
|
|
|
std::ostream& operator<<(std::ostream& s, const hash& h)
|
|
|
|
{
|
|
|
|
char buf[log::Stream::BUF_SIZE + 1];
|
|
|
|
log::Stream s1(buf);
|
|
|
|
s1 << h << '\0';
|
|
|
|
s << buf;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::istream& operator>>(std::istream& s, hash& h)
|
|
|
|
{
|
|
|
|
memset(h.h, 0, HASH_SIZE);
|
|
|
|
|
|
|
|
bool found_number = false;
|
|
|
|
uint32_t index = 0;
|
|
|
|
char c;
|
|
|
|
while (s.good() && !s.eof()) {
|
|
|
|
s.read(&c, 1);
|
|
|
|
if (!s.good() || s.eof()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
uint8_t digit;
|
|
|
|
if (from_hex(c, digit)) {
|
|
|
|
found_number = true;
|
|
|
|
h.h[index >> 1] = (h.h[index >> 1] << 4) | digit;
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
else if (found_number) {
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2022-07-14 07:02:08 +00:00
|
|
|
void uv_cond_init_checked(uv_cond_t* cond)
|
|
|
|
{
|
|
|
|
const int result = uv_cond_init(cond);
|
|
|
|
if (result) {
|
|
|
|
LOGERR(1, "failed to create conditional variable, error " << uv_err_name(result));
|
|
|
|
panic();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-22 10:20:59 +00:00
|
|
|
void uv_mutex_init_checked(uv_mutex_t* mutex)
|
|
|
|
{
|
|
|
|
const int result = uv_mutex_init(mutex);
|
|
|
|
if (result) {
|
|
|
|
LOGERR(1, "failed to create mutex, error " << uv_err_name(result));
|
|
|
|
panic();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void uv_rwlock_init_checked(uv_rwlock_t* lock)
|
|
|
|
{
|
|
|
|
const int result = uv_rwlock_init(lock);
|
|
|
|
if (result) {
|
|
|
|
LOGERR(1, "failed to create rwlock, error " << uv_err_name(result));
|
|
|
|
panic();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-03 18:24:15 +00:00
|
|
|
void uv_async_init_checked(uv_loop_t* loop, uv_async_t* async, uv_async_cb async_cb)
|
|
|
|
{
|
|
|
|
const int err = uv_async_init(loop, async, async_cb);
|
|
|
|
if (err) {
|
|
|
|
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
|
|
|
panic();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-25 10:17:14 +00:00
|
|
|
uv_loop_t* uv_default_loop_checked()
|
|
|
|
{
|
2021-10-31 13:19:49 +00:00
|
|
|
if (!is_main_thread()) {
|
2021-08-25 10:17:14 +00:00
|
|
|
LOGERR(1, "uv_default_loop() can only be used by the main thread. Fix the code!");
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (IsDebuggerPresent()) {
|
|
|
|
__debugbreak();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return uv_default_loop();
|
|
|
|
}
|
|
|
|
|
2021-08-24 09:42:41 +00:00
|
|
|
struct BackgroundJobTracker::Impl
|
|
|
|
{
|
|
|
|
Impl() { uv_mutex_init_checked(&m_lock); }
|
|
|
|
~Impl() { uv_mutex_destroy(&m_lock); }
|
|
|
|
|
|
|
|
void start(const char* name)
|
|
|
|
{
|
|
|
|
MutexLock lock(m_lock);
|
|
|
|
|
|
|
|
auto it = m_jobs.insert({ name, 1 });
|
|
|
|
if (!it.second) {
|
|
|
|
++it.first->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void stop(const char* name)
|
|
|
|
{
|
|
|
|
MutexLock lock(m_lock);
|
|
|
|
|
|
|
|
auto it = m_jobs.find(name);
|
|
|
|
if (it == m_jobs.end()) {
|
|
|
|
LOGWARN(1, "background job " << name << " is not running, but stop() was called");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
--it->second;
|
|
|
|
if (it->second <= 0) {
|
|
|
|
m_jobs.erase(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void wait()
|
|
|
|
{
|
|
|
|
do {
|
|
|
|
bool is_empty = true;
|
|
|
|
{
|
|
|
|
MutexLock lock(m_lock);
|
|
|
|
is_empty = m_jobs.empty();
|
|
|
|
for (const auto& job : m_jobs) {
|
|
|
|
LOGINFO(1, "waiting for " << job.second << " \"" << job.first << "\" jobs to finish");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_empty) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
|
|
|
} while (1);
|
|
|
|
}
|
|
|
|
|
2021-08-25 09:31:35 +00:00
|
|
|
void print_status()
|
|
|
|
{
|
|
|
|
MutexLock lock(m_lock);
|
|
|
|
|
|
|
|
if (m_jobs.empty()) {
|
|
|
|
LOGINFO(0, "no background jobs running");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buf[log::Stream::BUF_SIZE + 1];
|
|
|
|
log::Stream s(buf);
|
|
|
|
for (const auto& job : m_jobs) {
|
|
|
|
s << '\n' << job.first << " (" << job.second << ')';
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGINFO(0, "background jobs running:" << log::const_buf(buf, s.m_pos));
|
|
|
|
}
|
|
|
|
|
2021-08-24 09:42:41 +00:00
|
|
|
uv_mutex_t m_lock;
|
2021-08-24 11:22:56 +00:00
|
|
|
std::map<std::string, int32_t> m_jobs;
|
2021-08-24 09:42:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
BackgroundJobTracker::BackgroundJobTracker() : m_impl(new Impl())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
BackgroundJobTracker::~BackgroundJobTracker()
|
|
|
|
{
|
|
|
|
delete m_impl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BackgroundJobTracker::start(const char* name)
|
|
|
|
{
|
|
|
|
m_impl->start(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BackgroundJobTracker::stop(const char* name)
|
|
|
|
{
|
|
|
|
m_impl->stop(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BackgroundJobTracker::wait()
|
|
|
|
{
|
|
|
|
m_impl->wait();
|
|
|
|
}
|
|
|
|
|
2021-08-25 09:31:35 +00:00
|
|
|
void BackgroundJobTracker::print_status()
|
|
|
|
{
|
|
|
|
m_impl->print_status();
|
|
|
|
}
|
|
|
|
|
2021-08-24 09:42:41 +00:00
|
|
|
BackgroundJobTracker bkg_jobs_tracker;
|
2021-10-31 13:19:49 +00:00
|
|
|
|
|
|
|
static thread_local bool main_thread = false;
|
|
|
|
void set_main_thread() { main_thread = true; }
|
|
|
|
bool is_main_thread() { return main_thread; }
|
2021-08-24 09:42:41 +00:00
|
|
|
|
2022-09-04 16:10:14 +00:00
|
|
|
bool disable_resolve_host = false;
|
|
|
|
|
2021-09-08 18:25:39 +00:00
|
|
|
bool resolve_host(std::string& host, bool& is_v6)
|
|
|
|
{
|
2022-09-04 16:10:14 +00:00
|
|
|
if (disable_resolve_host) {
|
|
|
|
LOGERR(1, "resolve_host was called with DNS disabled for host " << host);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-09-08 18:25:39 +00:00
|
|
|
addrinfo hints{};
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
2022-05-02 19:24:51 +00:00
|
|
|
hints.ai_flags = AI_ADDRCONFIG;
|
2021-09-08 18:25:39 +00:00
|
|
|
|
|
|
|
addrinfo* r = nullptr;
|
|
|
|
const int err = getaddrinfo(host.c_str(), nullptr, &hints, &r);
|
|
|
|
if ((err == 0) && r) {
|
|
|
|
const char* addr_str = nullptr;
|
|
|
|
char addr_str_buf[64];
|
|
|
|
|
|
|
|
void* addr;
|
|
|
|
if (r->ai_family == AF_INET6) {
|
|
|
|
addr = &reinterpret_cast<sockaddr_in6*>(r->ai_addr)->sin6_addr;
|
|
|
|
is_v6 = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
addr = &reinterpret_cast<sockaddr_in*>(r->ai_addr)->sin_addr;
|
|
|
|
is_v6 = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr_str = inet_ntop(r->ai_family, addr, addr_str_buf, sizeof(addr_str_buf));
|
|
|
|
if (addr_str) {
|
|
|
|
LOGINFO(5, log::LightCyan() << host << log::NoColor() << " resolved to " << log::Gray() << addr_str);
|
|
|
|
host = addr_str;
|
|
|
|
}
|
|
|
|
freeaddrinfo(r);
|
|
|
|
}
|
|
|
|
else {
|
2021-09-18 08:03:06 +00:00
|
|
|
LOGWARN(3, "getaddrinfo failed for " << host << ": " << gai_strerror(err));
|
2021-09-08 18:25:39 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-03-17 15:14:29 +00:00
|
|
|
RandomDeviceSeed RandomDeviceSeed::instance;
|
|
|
|
|
2022-05-09 09:25:52 +00:00
|
|
|
struct BSR8
|
|
|
|
{
|
|
|
|
uint8_t data[256];
|
|
|
|
|
|
|
|
static constexpr BSR8 init() {
|
|
|
|
BSR8 result = { 55 };
|
|
|
|
|
|
|
|
for (int i = 1; i < 256; ++i) {
|
|
|
|
int x = i;
|
|
|
|
result.data[i] = 63;
|
|
|
|
while (x < 0x80) {
|
|
|
|
--result.data[i];
|
|
|
|
x <<= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static constexpr BSR8 bsr8_table = BSR8::init();
|
|
|
|
|
|
|
|
NOINLINE uint64_t bsr_reference(uint64_t x)
|
|
|
|
{
|
|
|
|
uint32_t y = static_cast<uint32_t>(x);
|
|
|
|
|
|
|
|
uint64_t n0 = (x == y) ? 0 : 32;
|
|
|
|
y = static_cast<uint32_t>(x >> n0);
|
|
|
|
n0 ^= 32;
|
|
|
|
|
|
|
|
const uint64_t n1 = (y & 0xFFFF0000UL) ? 0 : 16;
|
|
|
|
y <<= n1;
|
|
|
|
|
|
|
|
const uint64_t n2 = (y & 0xFF000000UL) ? 0 : 8;
|
|
|
|
y <<= n2;
|
|
|
|
|
|
|
|
return bsr8_table.data[y >> 24] - n0 - n1 - n2;
|
|
|
|
}
|
|
|
|
|
2022-08-31 14:37:33 +00:00
|
|
|
bool str_to_ip(bool is_v6, const char* ip, raw_ip& result)
|
|
|
|
{
|
|
|
|
sockaddr_storage addr;
|
|
|
|
|
|
|
|
if (is_v6) {
|
|
|
|
sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(&addr);
|
|
|
|
const int err = uv_ip6_addr(ip, 0, addr6);
|
|
|
|
if (err) {
|
|
|
|
LOGERR(1, "failed to parse IPv6 address " << ip << ", error " << uv_err_name(err));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
memcpy(result.data, &addr6->sin6_addr, sizeof(in6_addr));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(&addr);
|
|
|
|
const int err = uv_ip4_addr(ip, 0, addr4);
|
|
|
|
if (err) {
|
|
|
|
LOGERR(1, "failed to parse IPv4 address " << ip << ", error " << uv_err_name(err));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
result = {};
|
|
|
|
result.data[10] = 0xFF;
|
|
|
|
result.data[11] = 0xFF;
|
|
|
|
memcpy(result.data + 12, &addr4->sin_addr, sizeof(in_addr));
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_localhost(const std::string& host)
|
|
|
|
{
|
|
|
|
if (host.empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (host.compare("localhost") == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (host.find_first_not_of("0123456789.:") != std::string::npos) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
raw_ip addr;
|
|
|
|
if (!str_to_ip(host.find(':') != std::string::npos, host.c_str(), addr)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return addr.is_localhost();
|
|
|
|
}
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create)
|
|
|
|
{
|
|
|
|
UV_LoopUserData* data = reinterpret_cast<UV_LoopUserData*>(loop->data);
|
|
|
|
|
|
|
|
if (!data && create) {
|
|
|
|
data = new UV_LoopUserData(loop);
|
|
|
|
loop->data = data;
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2021-08-22 10:20:59 +00:00
|
|
|
} // namespace p2pool
|