/* * This file is part of the Monero P2Pool * Copyright (c) 2021-2022 SChernykh * * 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 . */ #include "common.h" #include "util.h" #include "uv_util.h" #include #include #ifndef _WIN32 #include #endif static constexpr char log_category_prefix[] = "Util "; namespace p2pool { #define STR2(X) STR(X) #define STR(X) #X const char* VERSION = "v2.1 (built" #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 MinerCallbackHandler::~MinerCallbackHandler() {} void panic() { p2pool::log::stop(); do { #ifdef _WIN32 if (IsDebuggerPresent()) { __debugbreak(); } #endif abort(); } while (true); } void make_thread_background() { #ifdef _WIN32 SetThreadPriorityBoost(GetCurrentThread(), true); SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); #elif !defined(__APPLE__) && !defined(__FreeBSD__) 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(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) { uint64_t t = result[k] + product[l]; const uint64_t next_carry = static_cast(t < result[k]); t += carry; carry = next_carry | static_cast(t < result[k]); result[k] = t; } if (result[4]) { return false; } } } else { const uint64_t* b = reinterpret_cast(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) { uint64_t t = result[k] + product[l]; const uint64_t next_carry = static_cast(t < result[k]); t += carry; carry = next_carry | static_cast(t < result[k]); 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; } 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(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; } 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; } 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(); } } uv_loop_t* uv_default_loop_checked() { if (!is_main_thread()) { 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(); } 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); } 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)); } uv_mutex_t m_lock; std::map m_jobs; }; 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(); } void BackgroundJobTracker::print_status() { m_impl->print_status(); } BackgroundJobTracker bkg_jobs_tracker; static thread_local bool main_thread = false; void set_main_thread() { main_thread = true; } bool is_main_thread() { return main_thread; } bool resolve_host(std::string& host, bool& is_v6) { addrinfo hints{}; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG; 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(r->ai_addr)->sin6_addr; is_v6 = true; } else { addr = &reinterpret_cast(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 { LOGWARN(3, "getaddrinfo failed for " << host << ": " << gai_strerror(err)); return false; } return true; } RandomDeviceSeed RandomDeviceSeed::instance; 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(x); uint64_t n0 = (x == y) ? 0 : 32; y = static_cast(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; } UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create) { UV_LoopUserData* data = reinterpret_cast(loop->data); if (!data && create) { data = new UV_LoopUserData(loop); loop->data = data; } return data; } } // namespace p2pool