Compare commits

..

47 Commits

Author SHA1 Message Date
SChernykh 9a5be0a321 p2pool v2.1 2022-05-31 18:20:21 +02:00
SChernykh c203f6eb0a Fixed a new cppcheck issue 2022-05-31 16:51:09 +02:00
SChernykh b713f4e702 CI: added Linux aarch64 build 2022-05-31 16:38:28 +02:00
SChernykh 111087d96a Fixed block reward share calculation 2022-05-28 22:27:46 +02:00
SChernykh 9e7666d12f Fixed data races 2022-05-28 21:46:50 +02:00
SChernykh cb147773b5 Updated cppcheck workflow on Linux 2022-05-26 21:19:01 +02:00
SChernykh d84bef8f14 Added block reward share percent to miner api 2022-05-26 11:08:16 +02:00
SChernykh 32bb318af4 Fixed cppcheck workflow on Windows 2022-05-25 20:05:17 +02:00
SChernykh 055b5e2256 Updated dependencies 2022-05-25 12:54:40 +02:00
SChernykh c1754d122f Update RandomX 2022-05-24 15:42:37 +02:00
SChernykh 6d121122f8 Fixed compiler warnings in tests 2022-05-24 15:18:09 +02:00
SChernykh bfa8334aeb Fixed executable stack 2022-05-23 21:41:17 +02:00
SChernykh 0e222afd6a Exit if server startup fails 2022-05-23 15:50:25 +02:00
SChernykh b191962d32 Handle startup errors without calling abort() 2022-05-23 09:37:11 +02:00
SChernykh 1cf13e142a SideChain: fixed alternative chain detection 2022-05-16 15:59:48 +02:00
SChernykh 8f1f81749d StratumServer: reset cumulative hashes too 2022-05-16 14:36:11 +02:00
SChernykh 72d70eba16 SideChain: added "synchronized" log message 2022-05-16 01:48:37 +02:00
SChernykh ae3612f595 Fixed worker diff display when autodiff is off 2022-05-15 18:13:36 +02:00
SChernykh 37b6457054 Show worker hashrate only when autodiff is on 2022-05-15 01:15:21 +02:00
SChernykh 9579be5c7d Optimized SideChain::miner_count() 2022-05-12 22:18:08 +02:00
SChernykh d05d47fa75 Fixed compilation 2022-05-12 16:10:55 +02:00
SChernykh bd88c04e9e SideChain: mutex locks refactoring 2022-05-12 15:24:02 +02:00
SChernykh eb3a3872eb Fixed missing blocks download logic 2022-05-12 08:49:34 +02:00
SChernykh 5cea7be405 Allow only one pending get_miner_data 2022-05-12 01:09:59 +02:00
SChernykh 516c382fe4 Update keccak.cpp 2022-05-11 16:13:00 +02:00
SChernykh 710e55422a Ignore duplicate get_miner_data 2022-05-11 16:10:10 +02:00
SChernykh 3406cf78b4 Don't return stale chain tip to other peers 2022-05-11 15:07:54 +02:00
SChernykh 8fa354796c Reload peer list when there are no connections 2022-05-11 14:09:28 +02:00
SChernykh 6c6ef1c1b8 Restart ZMQ connection if it looks dead 2022-05-11 13:50:47 +02:00
SChernykh c3c326c680 Suppress MSVC warnings 2022-05-11 10:55:36 +02:00
SChernykh 92e20485b2 Log: don't allow side effects in LOG() calls 2022-05-10 21:41:53 +02:00
SChernykh edd782ac23 Fixed custom worker names 2022-05-10 14:01:10 +02:00
SChernykh a4d84e318d TCPServer: refactored Client constructor 2022-05-09 19:13:34 +02:00
SChernykh 08ee599f79 Use the same compilation flags for tests 2022-05-09 17:57:31 +02:00
SChernykh 134f2d68a3 Fixed data races 2022-05-09 16:10:55 +02:00
SChernykh d23c46ff84 Added bsr() function to util 2022-05-09 13:02:06 +02:00
SChernykh 820c5db5e8 Log: check for logrotate only once per loop 2022-05-09 09:58:43 +02:00
SChernykh 990916ab62 StratumServer: added autodiff
Autodiff is enabled by default, target time is 30 seconds. It can be disabled with `--no-autodiff` command line option.
2022-05-08 19:03:00 +02:00
SChernykh d4e362cd76 TCPServer: optimized Client struct size 2022-05-07 12:35:31 +02:00
SChernykh 1dd06cc509 Removed std::string to avoid memory allocation 2022-05-07 11:00:29 +02:00
SChernykh 2e2bd1d137 Added log padding 2022-05-06 20:04:01 +02:00
SChernykh 0818d91f4f StratumServer: added console command workers 2022-05-06 13:19:56 +02:00
SChernykh 6e1f34b168 Termux build instructions 2022-05-05 15:53:04 +02:00
SChernykh 1a35177633 Fixed unsafe usage of log::Stream buffers 2022-05-05 14:38:56 +02:00
SChernykh c45689b02b Fixed pthread_cancel detection 2022-05-03 21:03:06 +02:00
SChernykh 8f27d940e3 Don't ban localhost 2022-05-03 13:11:55 +02:00
SChernykh 6b4640c413 Fixes for Termux 2022-05-03 07:32:47 +02:00
57 changed files with 1086 additions and 432 deletions
+44
View File
@@ -53,6 +53,50 @@ jobs:
name: p2pool-${{ matrix.config.os }}
path: build/p2pool
build-ubuntu-aarch64:
runs-on: ubuntu-latest
steps:
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y git build-essential cmake gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++
make -j$(nproc)
- name: Build libzmq
run: |
cd external/src/libzmq
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j$(nproc)
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DCMAKE_STRIP=/usr/bin/aarch64-linux-gnu-strip -DSTATIC_LIBS=ON -DARCH_ID=aarch64
make -j$(nproc)
- name: Archive binary
uses: actions/upload-artifact@v2
with:
name: p2pool-ubuntu-latest-aarch64
path: build/p2pool
build-windows-msys2:
runs-on: windows-latest
+22 -6
View File
@@ -12,14 +12,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install cppcheck
run: sudo apt update && sudo apt install cppcheck
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Checkout cppcheck
uses: actions/checkout@v3
with:
repository: danmar/cppcheck
path: cppcheck-main
- name: Build cppcheck
run: |
cd cppcheck-main
make -j$(nproc) cppcheck
- name: Run cppcheck
run: |
cd cppcheck
@@ -30,14 +38,22 @@ jobs:
runs-on: windows-latest
steps:
- name: Install cppcheck
run: choco install cppcheck
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Checkout cppcheck
uses: actions/checkout@v3
with:
repository: danmar/cppcheck
path: cppcheck-main
- name: Build cppcheck
run: |
cd cppcheck-main
& "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\msbuild.exe" /m /p:Configuration=Release /p:Platform=x64
- name: Setup cmake
uses: lukka/get-cmake@latest
+26 -3
View File
@@ -2,11 +2,12 @@ cmake_minimum_required(VERSION 2.8.12)
project(p2pool)
option(STATIC_BINARY "Build static binary" OFF)
option(STATIC_LIBS "Link libuv and libzmq statically" OFF)
option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned off, p2pool will rely on monerod for verifying RandomX hashes" ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
if (${CMAKE_VERSION} VERSION_GREATER "3.5.2")
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.6.0")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
endif()
@@ -100,7 +101,7 @@ if (WIN32)
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(LIBS ${LIBS} pthread)
elseif (NOT APPLE)
set(LIBS ${LIBS} pthread gss dl)
set(LIBS ${LIBS} pthread dl)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
@@ -133,9 +134,31 @@ endif()
add_definitions(/DZMQ_STATIC)
include(CheckSymbolExists)
set(CMAKE_REQUIRED_FLAGS "${GENERAL_FLAGS}")
check_symbol_exists(pthread_cancel pthread.h HAVE_PTHREAD_CANCEL)
if (HAVE_PTHREAD_CANCEL)
add_definitions(/DHAVE_PTHREAD_CANCEL)
endif()
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("int main(){ return __builtin_clzll(1);}" HAVE_BUILTIN_CLZLL)
check_cxx_source_compiles("#include <intrin.h>\n#pragma intrinsic(_BitScanReverse64)\nint main(){unsigned long r;_BitScanReverse64(&r,1);return r;}" HAVE_BITSCANREVERSE64)
if (HAVE_BUILTIN_CLZLL)
add_definitions(/DHAVE_BUILTIN_CLZLL)
endif()
if (HAVE_BITSCANREVERSE64)
add_definitions(/DHAVE_BITSCANREVERSE64)
endif()
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
if (STATIC_BINARY)
if (STATIC_BINARY OR STATIC_LIBS)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME})
if (WITH_RANDOMX)
+12
View File
@@ -271,6 +271,18 @@ cmake ..
make
```
### Android (Termux)
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally in Termux:
```
pkg install git build-essential cmake libuv libzmq
git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool
mkdir build && cd build
cmake ..
make -j$(nproc)
```
## Donations
If you'd like to support further development of Monero P2Pool, you're welcome to send any amount of XMR to the following address:
+17 -14
View File
@@ -6,14 +6,15 @@ set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(GENERAL_FLAGS "-pthread")
set(WARNING_FLAGS "-Wall -Wextra -Werror")
set(OPTIMIZATION_FLAGS "-Ofast -s")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
if (WIN32)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
@@ -25,26 +26,28 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
endif()
endif()
elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
set(GENERAL_FLAGS "")
set(WARNING_FLAGS "/Wall /WX /sdl")
set(SECURITY_FLAGS "/GS /guard:cf")
set(OPTIMIZATION_FLAGS "/O2 /Oi /Ob2 /Ot /DNDEBUG /GL")
set(CMAKE_C_FLAGS_DEBUG "${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
set(CMAKE_CXX_FLAGS_DEBUG "${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
set(CMAKE_C_FLAGS_DEBUG "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
set(CMAKE_CXX_FLAGS_DEBUG "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
set(CMAKE_C_FLAGS_RELEASE "${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
set(CMAKE_CXX_FLAGS_RELEASE "${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
set(CMAKE_C_FLAGS_RELEASE "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
set(CMAKE_CXX_FLAGS_RELEASE "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
set(GENERAL_FLAGS "-pthread")
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal -Werror")
set(OPTIMIZATION_FLAGS "-Ofast -funroll-loops -fmerge-all-constants")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
endif()
+2 -2
View File
@@ -1,9 +1,9 @@
../src/
../external/src/
../external/src/cryptonote/
../external/src/libuv/
../external/src/libuv/include/
../external/src/cppzmq/
../external/src/libzmq/
../external/src/libzmq/include/
../external/src/llhttp/
../external/src/RandomX/src/
../external/src/rapidjson/include
+1 -1
View File
@@ -1,5 +1,5 @@
@echo off
"C:\Program Files\cppcheck\cppcheck.exe" --project=..\build\p2pool.vcxproj --project-configuration="Release|x64" -DZMQ_STATIC --platform=win64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" --includes-file=includes.txt --suppressions-list=suppressions.txt --output-file=errors_full.txt
"..\cppcheck-main\bin\cppcheck.exe" --project=..\build\p2pool.vcxproj --project-configuration="Release|x64" -DSIZE_MAX=UINT64_MAX -DRAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN --platform=win64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" --includes-file=includes.txt --suppressions-list=suppressions.txt --output-file=errors_full.txt
findstr /V /C:"external\src" errors_full.txt > errors_filtered.txt
for /f %%i in ("errors_filtered.txt") do set size=%%~zi
if %size% gtr 0 (
+2 -2
View File
@@ -1,6 +1,6 @@
#!/bin/bash
cppcheck ../src -DZMQ_STATIC --platform=unix64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" -I ../src/ -I ../external/src/ -I ../external/src/cryptonote/ -I ../external/src/libuv/ -I ../external/src/cppzmq/ -I ../external/src/libzmq/ -I ../external/src/llhttp/ -I ../external/src/RandomX/src/ -I ../external/src/rapidjson/include -I ../external/src/robin-hood-hashing/src/include --suppressions-list=suppressions.txt --output-file=errors_full.txt
grep -v 'external' errors_full.txt | grep -v 'unmatchedSuppression' > errors_filtered.txt
../cppcheck-main/cppcheck ../src -DSIZE_MAX=UINT64_MAX -DRAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN --platform=unix64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" --includes-file=includes.txt --suppressions-list=suppressions.txt --output-file=errors_full.txt
grep -v 'external' errors_full.txt > errors_filtered.txt
if [ -s errors_filtered.txt ]; then
cat errors_filtered.txt
exit 1
-3
View File
@@ -1,6 +1,3 @@
missingIncludeSystem
unusedFunction
useStlAlgorithm
functionStatic
functionConst
unmatchedSuppression
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -2
View File
@@ -115,7 +115,7 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
m_extraNonceOffsetInTemplate = b.m_extraNonceOffsetInTemplate;
m_numTransactionHashes = b.m_numTransactionHashes;
m_prevId = b.m_prevId;
m_height = b.m_height;
m_height = b.m_height.load();
m_difficulty = b.m_difficulty;
m_seedHash = b.m_seedHash;
m_timestamp = b.m_timestamp;
@@ -575,7 +575,6 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_rewards.clear();
m_mempoolTxs.clear();
m_mempoolTxsOrder.clear();
m_shares.clear();
}
#if TEST_MEMPOOL_PICKING_ALGORITHM
+2 -3
View File
@@ -50,12 +50,11 @@ public:
void update_tx_keys();
FORCEINLINE uint64_t height() const { return m_height; }
FORCEINLINE uint64_t timestamp() const { return m_timestamp; }
FORCEINLINE difficulty_type difficulty() const { return m_difficulty; }
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
FORCEINLINE uint64_t final_reward() const { return m_finalReward; }
FORCEINLINE const std::vector<MinerShare>& shares() const { return m_shares; }
private:
p2pool* m_pool;
@@ -83,7 +82,7 @@ private:
size_t m_numTransactionHashes;
hash m_prevId;
uint64_t m_height;
std::atomic<uint64_t> m_height;
difficulty_type m_difficulty;
hash m_seedHash;
+17 -1
View File
@@ -19,22 +19,25 @@
#ifdef _MSC_VER
#pragma warning(disable : 4005 4061 4365 4464 4625 4626 4668 4710 4711 4804 4820 5039 5045 5220)
#pragma warning(disable : 4005 4061 4365 4464 4625 4626 4668 4710 4711 4804 4820 5039 5045 5220 5246)
#define FORCEINLINE __forceinline
#define NOINLINE __declspec(noinline)
#define LIKELY(expression) expression
#define MSVC_PRAGMA(...) __pragma(__VA_ARGS__)
#elif __GNUC__
#define FORCEINLINE __attribute__((always_inline)) inline
#define NOINLINE __attribute__((noinline))
#define LIKELY(expression) __builtin_expect(expression, 1)
#define MSVC_PRAGMA(...)
#else
#define FORCEINLINE inline
#define NOINLINE
#define LIKELY(expression) expression
#define MSVC_PRAGMA(...)
#endif
@@ -198,11 +201,16 @@ struct difficulty_type
FORCEINLINE bool operator==(const difficulty_type& other) const { return (lo == other.lo) && (hi == other.hi); }
FORCEINLINE bool operator!=(const difficulty_type& other) const { return (lo != other.lo) || (hi != other.hi); }
FORCEINLINE bool operator==(uint64_t other) const { return (lo == other) && (hi == 0); }
FORCEINLINE bool operator!=(uint64_t other) const { return (lo != other) || (hi != 0); }
friend std::ostream& operator<<(std::ostream& s, const difficulty_type& d);
friend std::istream& operator>>(std::istream& s, difficulty_type& d);
FORCEINLINE double to_double() const { return static_cast<double>(hi) * 18446744073709551616.0 + static_cast<double>(lo); }
FORCEINLINE bool empty() const { return (lo == 0) && (hi == 0); }
// Finds a 64-bit target for mining (target = 2^64 / difficulty) and rounds up the result of division
// Because of that, there's a very small chance that miners will find a hash that meets the target but is still wrong (hash * difficulty >= 2^256)
// A proper difficulty check is in check_pow()
@@ -309,6 +317,14 @@ struct raw_ip
}
FORCEINLINE bool operator!=(const raw_ip& other) const { return !operator==(other); }
FORCEINLINE bool is_localhost() const
{
static constexpr raw_ip localhost_ipv4 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01 };
static constexpr raw_ip localhost_ipv6 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
return (*this == localhost_ipv4) || (*this == localhost_ipv6);
}
};
static_assert(sizeof(raw_ip) == 16, "struct raw_ip has invalid size");
+15 -2
View File
@@ -27,6 +27,10 @@
#include "side_chain.h"
#include <iostream>
#ifdef HAVE_PTHREAD_CANCEL
#include <pthread.h>
#endif
static constexpr char log_category_prefix[] = "ConsoleCommands ";
namespace p2pool {
@@ -45,7 +49,7 @@ ConsoleCommands::~ConsoleCommands()
#ifdef _WIN32
TerminateThread(reinterpret_cast<HANDLE>(m_worker->native_handle()), 0);
#else
#elif defined HAVE_PTHREAD_CANCEL
pthread_cancel(m_worker->native_handle());
#endif
@@ -72,7 +76,7 @@ typedef struct cmd {
cmdfunc *func;
} cmd;
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_showpeers, do_showbans, do_outpeers, do_inpeers, do_exit;
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_showpeers, do_showworkers, do_showbans, do_outpeers, do_inpeers, do_exit;
#ifdef WITH_RANDOMX
static cmdfunc do_start_mining, do_stop_mining;
@@ -85,6 +89,7 @@ static cmd cmds[] = {
{ STRCONST("addpeers"), "<peeraddr>", "add peer", do_addpeers },
{ STRCONST("droppeers"), "", "disconnect all peers", do_droppeers },
{ STRCONST("peers"), "", "show all peers", do_showpeers },
{ STRCONST("workers"), "", "show all connected workers", do_showworkers },
{ STRCONST("bans"), "", "show all banned IPs", do_showbans },
{ STRCONST("outpeers"), "", "set maximum number of outgoing connections", do_outpeers },
{ STRCONST("inpeers"), "", "set maximum number of incoming connections", do_inpeers },
@@ -154,6 +159,14 @@ static int do_showpeers(p2pool* m_pool, const char* /* args */)
return 0;
}
static int do_showworkers(p2pool* m_pool, const char* /* args */)
{
if (m_pool->stratum_server()) {
m_pool->stratum_server()->show_workers();
}
return 0;
}
static int do_showbans(p2pool* m_pool, const char* /* args */)
{
if (m_pool->stratum_server()) {
+1
View File
@@ -238,6 +238,7 @@ private:
struct DerivationEntry
{
hash derivation;
// cppcheck-suppress unusedStructMember
uint8_t view_tag;
};
+1 -1
View File
@@ -72,7 +72,7 @@ JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, C
m_request.reserve(std::max<size_t>(len + 128, log::Stream::BUF_SIZE + 1));
m_request.resize(log::Stream::BUF_SIZE + 1);
log::Stream s(m_request.data());
log::Stream s(m_request.data(), m_request.size());
s << "POST " << uri << " HTTP/1.1\nContent-Type: application/json\nContent-Length: " << len << "\n\n";
m_request.resize(s.m_pos);
+1
View File
@@ -71,6 +71,7 @@ private:
static void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf);
void on_read(const char* data, size_t size);
// cppcheck-suppress functionConst
void close();
static void on_close(uv_handle_t* handle);
+24 -57
View File
@@ -48,7 +48,7 @@ NOINLINE void keccakf(uint64_t* st)
bc[3] = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
bc[4] = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
#define X(i) { \
#define THETA(i) { \
const uint64_t t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); \
st[i + 0 ] ^= t; \
st[i + 5] ^= t; \
@@ -57,9 +57,11 @@ NOINLINE void keccakf(uint64_t* st)
st[i + 20] ^= t; \
}
X(0); X(1); X(2); X(3); X(4);
#undef X
THETA(0);
THETA(1);
THETA(2);
THETA(3);
THETA(4);
// Rho Pi
const uint64_t t = st[1];
@@ -89,60 +91,25 @@ NOINLINE void keccakf(uint64_t* st)
st[10] = ROTL64(t, 1);
// Chi
// unrolled loop, where only last iteration is different
int j = 0;
bc[0] = st[j + 0];
bc[1] = st[j + 1];
#define CHI(j) { \
const uint64_t st0 = st[j ]; \
const uint64_t st1 = st[j + 1]; \
const uint64_t st2 = st[j + 2]; \
const uint64_t st3 = st[j + 3]; \
const uint64_t st4 = st[j + 4]; \
st[j ] ^= ~st1 & st2; \
st[j + 1] ^= ~st2 & st3; \
st[j + 2] ^= ~st3 & st4; \
st[j + 3] ^= ~st4 & st0; \
st[j + 4] ^= ~st0 & st1; \
}
st[j + 0] ^= (~st[j + 1]) & st[j + 2];
st[j + 1] ^= (~st[j + 2]) & st[j + 3];
st[j + 2] ^= (~st[j + 3]) & st[j + 4];
st[j + 3] ^= (~st[j + 4]) & bc[0];
st[j + 4] ^= (~bc[0]) & bc[1];
CHI( 0);
CHI( 5);
CHI(10);
CHI(15);
CHI(20);
j = 5;
bc[0] = st[j + 0];
bc[1] = st[j + 1];
st[j + 0] ^= (~st[j + 1]) & st[j + 2];
st[j + 1] ^= (~st[j + 2]) & st[j + 3];
st[j + 2] ^= (~st[j + 3]) & st[j + 4];
st[j + 3] ^= (~st[j + 4]) & bc[0];
st[j + 4] ^= (~bc[0]) & bc[1];
j = 10;
bc[0] = st[j + 0];
bc[1] = st[j + 1];
st[j + 0] ^= (~st[j + 1]) & st[j + 2];
st[j + 1] ^= (~st[j + 2]) & st[j + 3];
st[j + 2] ^= (~st[j + 3]) & st[j + 4];
st[j + 3] ^= (~st[j + 4]) & bc[0];
st[j + 4] ^= (~bc[0]) & bc[1];
j = 15;
bc[0] = st[j + 0];
bc[1] = st[j + 1];
st[j + 0] ^= (~st[j + 1]) & st[j + 2];
st[j + 1] ^= (~st[j + 2]) & st[j + 3];
st[j + 2] ^= (~st[j + 3]) & st[j + 4];
st[j + 3] ^= (~st[j + 4]) & bc[0];
st[j + 4] ^= (~bc[0]) & bc[1];
j = 20;
bc[0] = st[j + 0];
bc[1] = st[j + 1];
bc[2] = st[j + 2];
bc[3] = st[j + 3];
bc[4] = st[j + 4];
st[j + 0] ^= (~bc[1]) & bc[2];
st[j + 1] ^= (~bc[2]) & bc[3];
st[j + 2] ^= (~bc[3]) & bc[4];
st[j + 3] ^= (~bc[4]) & bc[0];
st[j + 4] ^= (~bc[0]) & bc[1];
// Iota
st[0] ^= keccakf_rndc[round];
}
@@ -173,7 +140,7 @@ NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen)
temp[rsiz - 1] |= 0x80;
for (int i = 0; i < rsizw; i++) {
st[i] ^= ((uint64_t*)temp)[i];
st[i] ^= reinterpret_cast<uint64_t*>(temp)[i];
}
keccakf(st);
+7 -10
View File
@@ -194,15 +194,6 @@ private:
fwrite(p, 1, size, (severity == 1) ? stdout : stderr);
#endif
// Reopen the log file if it's been moved (logrotate support)
if (m_logFile.is_open()) {
struct stat buf;
if (stat(log_file_name, &buf) != 0) {
m_logFile.close();
m_logFile.open(log_file_name, std::ios::app | std::ios::binary);
}
}
if (m_logFile.is_open()) {
if (c) {
strip_colors(p, size);
@@ -232,6 +223,13 @@ private:
// Flush the log file only after all pending log lines have been written
if (m_logFile.is_open()) {
m_logFile.flush();
// Reopen the log file if it's been moved (logrotate support)
struct stat buf;
if (stat(log_file_name, &buf) != 0) {
m_logFile.close();
m_logFile.open(log_file_name, std::ios::app | std::ios::binary);
}
}
} while (!stopped);
}
@@ -278,7 +276,6 @@ static Worker worker;
#endif // P2POOL_LOG_DISABLE
// cppcheck-suppress uninitMemberVar
NOINLINE Writer::Writer(Severity severity) : Stream(m_stackBuf)
{
m_buf[0] = static_cast<char>(severity);
+109 -11
View File
@@ -35,8 +35,10 @@ struct Stream
{
enum params : int { BUF_SIZE = 1024 - 1 };
explicit FORCEINLINE Stream(char* buf) : m_pos(0), m_numberWidth(1), m_buf(buf), m_bufSize(BUF_SIZE) {}
FORCEINLINE Stream(char* buf, size_t size) : m_pos(0), m_numberWidth(1), m_buf(buf), m_bufSize(static_cast<int>(size) - 1) {}
template<size_t N>
explicit FORCEINLINE Stream(char (&buf)[N]) : m_pos(0), m_numberWidth(1), m_buf(buf), m_bufSize(N - 1) {}
FORCEINLINE Stream(void* buf, size_t size) : m_pos(0), m_numberWidth(1), m_buf(reinterpret_cast<char*>(buf)), m_bufSize(static_cast<int>(size) - 1) {}
template<typename T>
struct Entry
@@ -182,7 +184,10 @@ INT_ENTRY(unsigned long)
template<typename T, int base>
struct BasedValue
{
explicit FORCEINLINE BasedValue(T value) : m_value(value) {}
explicit FORCEINLINE BasedValue(T value) : m_value(value)
{
static_assert(std::is_integral<T>::value, "Must be an integer type here");
}
T m_value;
};
@@ -190,7 +195,7 @@ struct BasedValue
template<typename T, int base>
struct Stream::Entry<BasedValue<T, base>>
{
static FORCEINLINE void put(BasedValue<T, base>&& data, Stream* wrapper)
static FORCEINLINE void put(BasedValue<T, base> data, Stream* wrapper)
{
wrapper->writeInt<T, base>(data.m_value);
}
@@ -302,6 +307,7 @@ struct Hashrate
{
FORCEINLINE Hashrate() : m_data(0), m_valid(false) {}
explicit FORCEINLINE Hashrate(uint64_t data) : m_data(data), m_valid(true) {}
FORCEINLINE Hashrate(uint64_t data, bool valid) : m_data(data), m_valid(valid) {}
uint64_t m_data;
bool m_valid;
@@ -309,7 +315,7 @@ struct Hashrate
template<> struct log::Stream::Entry<Hashrate>
{
static NOINLINE void put(Hashrate&& value, Stream* wrapper)
static NOINLINE void put(const Hashrate& value, Stream* wrapper)
{
if (!value.m_valid) {
return;
@@ -354,7 +360,7 @@ struct XMRAmount
template<> struct log::Stream::Entry<XMRAmount>
{
static NOINLINE void put(XMRAmount&& value, Stream* wrapper)
static NOINLINE void put(XMRAmount value, Stream* wrapper)
{
constexpr uint64_t denomination = 1000000000000ULL;
@@ -372,8 +378,7 @@ template<> struct log::Stream::Entry<XMRAmount>
template<> struct log::Stream::Entry<NetworkType>
{
// cppcheck-suppress constParameter
static NOINLINE void put(const NetworkType& value, Stream* wrapper)
static NOINLINE void put(NetworkType value, Stream* wrapper)
{
switch (value) {
case NetworkType::Invalid: *wrapper << "invalid"; break;
@@ -384,6 +389,68 @@ template<> struct log::Stream::Entry<NetworkType>
}
};
struct Duration
{
explicit FORCEINLINE Duration(uint64_t data) : m_data(data) {}
uint64_t m_data;
};
template<> struct log::Stream::Entry<Duration>
{
static NOINLINE void put(Duration value, Stream* wrapper)
{
const uint64_t uptime = value.m_data;
const int64_t s = uptime % 60;
const int64_t m = (uptime / 60) % 60;
const int64_t h = (uptime / 3600) % 24;
const int64_t d = uptime / 86400;
if (d > 0) {
*wrapper << d << "d ";
}
*wrapper << h << "h " << m << "m " << s << 's';
}
};
template<typename T>
struct PadRight
{
FORCEINLINE PadRight(const T& value, int len) : m_value(value), m_len(len) {}
const T& m_value;
int m_len;
// Declare it to make compiler happy
PadRight(const PadRight&);
private:
PadRight& operator=(const PadRight&) = delete;
PadRight& operator=(PadRight&&) = delete;
};
template<typename T> FORCEINLINE PadRight<T> pad_right(const T& value, int len) { return PadRight<T>(value, len); }
template<typename T>
struct log::Stream::Entry<PadRight<T>>
{
static NOINLINE void put(PadRight<T>&& data, Stream* wrapper)
{
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf);
s << data.m_value;
const int len = std::min<int>(data.m_len, log::Stream::BUF_SIZE);
if (s.m_pos < len) {
memset(buf + s.m_pos, ' ', static_cast<size_t>(len) - s.m_pos);
s.m_pos = len;
}
wrapper->writeBuf(buf, s.m_pos);
}
};
void put_rawip(const raw_ip& value, Stream* wrapper);
template<> struct log::Stream::Entry<raw_ip>
@@ -402,16 +469,47 @@ namespace {
#define CONCAT(a, b) CONCAT2(a, b)
#define CONCAT2(a, b) a##b
// This is to check that LOG() call doesn't modify variables in scope, making program behavior dependent on the log level:
//
// int some_func(int& n) { return ++n; }
// ...
// LOGINFO(1, "Some important value: " << some_func(n));
//
// will not compile because the dummy lambda capture uses const-qualified copies of all variables.
//
// The check is "free": compiler will remove it entirely in release builds.
struct DummyStream
{
template<typename T>
FORCEINLINE DummyStream& operator<<(const T&)
{
return *this;
}
};
#define SIDE_EFFECT_CHECK(level, ...) \
do { \
if (0) { \
MSVC_PRAGMA(warning(suppress:26444)) \
[=]() { \
log::DummyStream x; \
x << level << __VA_ARGS__; \
}; \
} \
} while (0)
#ifdef P2POOL_LOG_DISABLE
#define LOGINFO(level, ...)
#define LOGWARN(level, ...)
#define LOGERR(level, ...)
#define LOGINFO(level, ...) SIDE_EFFECT_CHECK(level, __VA_ARGS__)
#define LOGWARN(level, ...) SIDE_EFFECT_CHECK(level, __VA_ARGS__)
#define LOGERR(level, ...) SIDE_EFFECT_CHECK(level, __VA_ARGS__)
#else
#define LOG(level, severity, ...) \
do { \
SIDE_EFFECT_CHECK(level, __VA_ARGS__); \
if (level <= log::GLOBAL_LOG_LEVEL) { \
log::Writer CONCAT(log_wrapper_, __LINE__)(severity); \
CONCAT(log_wrapper_, __LINE__) << log::Gray() << log_category_prefix; \
+8 -2
View File
@@ -44,7 +44,8 @@ void p2pool_usage()
"--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 1000)\n"
"--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 1000)\n"
"--start-mining N Start built-in miner using N threads (any value between 1 and 64)\n"
"--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d.\n"
"--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d\n"
"--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum\n"
"--help Show this help message\n\n"
"Example command line:\n\n"
"%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",
@@ -84,10 +85,15 @@ int main(int argc, char* argv[])
memory_tracking_start();
p2pool::init_crypto_cache();
{
try {
p2pool::p2pool pool(argc, argv);
result = pool.run();
}
catch (...) {
result = 1;
}
p2pool::destroy_crypto_cache();
memory_tracking_stop();
+2
View File
@@ -236,7 +236,9 @@ NOINLINE void operator delete(void* p, size_t) noexcept { p2pool::free_hook(p);
NOINLINE void operator delete[](void* p, size_t) noexcept { p2pool::free_hook(p); }
#else
// cppcheck-suppress functionStatic
void memory_tracking_start() {}
// cppcheck-suppress functionStatic
void memory_tracking_stop() {}
namespace p2pool {
+5 -1
View File
@@ -24,6 +24,7 @@
#include "randomx.h"
#include "params.h"
#include "p2pool_api.h"
#include "side_chain.h"
#include <thread>
static constexpr char log_category_prefix[] = "Miner ";
@@ -101,8 +102,10 @@ void Miner::on_block(const BlockTemplate& block)
m_totalHashes += hash_count;
if (m_pool->api() && m_pool->params().m_localStats) {
const double block_reward_share_percent = m_pool->side_chain().get_reward_share(m_pool->params().m_wallet) * 100.0;
m_pool->api()->set(p2pool_api::Category::LOCAL, "miner",
[cur_ts, hash_count, dt, this](log::Stream& s)
[cur_ts, hash_count, dt, block_reward_share_percent, this](log::Stream& s)
{
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
const double time_running = static_cast<double>(duration_cast<milliseconds>(cur_ts - m_startTimestamp).count()) / 1e3;
@@ -111,6 +114,7 @@ void Miner::on_block(const BlockTemplate& block)
<< ",\"total_hashes\":" << m_totalHashes
<< ",\"time_running\":" << time_running
<< ",\"shares_found\":" << m_sharesFound.load()
<< ",\"block_reward_share_percent\":" << block_reward_share_percent
<< ",\"threads\":" << m_threads
<< "}";
});
+103 -44
View File
@@ -37,6 +37,8 @@ static const char* seed_nodes_mini[] = { "seeds-mini.p2pool.io", "" };
static constexpr int DEFAULT_BACKLOG = 16;
static constexpr uint64_t DEFAULT_BAN_TIME = 600;
static constexpr size_t SEND_BUF_MIN_SIZE = 256;
#include "tcp_server.inl"
namespace p2pool {
@@ -75,7 +77,7 @@ P2PServer::P2PServer(p2pool* pool)
int err = uv_async_init(&m_loop, &m_broadcastAsync, on_broadcast);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
return;
panic();
}
m_broadcastAsync.data = this;
m_broadcastQueue.reserve(2);
@@ -149,6 +151,7 @@ void P2PServer::clear_cached_blocks()
WriteLock lock(m_cachedBlocksLock);
// cppcheck-suppress identicalConditionAfterEarlyExit
if (!m_cachedBlocks) {
return;
}
@@ -288,6 +291,7 @@ void P2PServer::update_peer_connections()
if (!has_good_peers && ((m_timerCounter % 30) == 0)) {
LOGERR(1, "no connections to other p2pool nodes, check your monerod/p2pool/network/firewall setup!!!");
load_peer_list();
if (m_peerListMonero.empty()) {
load_monerod_peer_list();
}
@@ -310,9 +314,14 @@ void P2PServer::update_peer_list()
client->m_nextOutgoingPeerListRequest = cur_time + (60 + (get_random64() % 61));
const bool result = send(client,
[](void* buf)
[](void* buf, size_t buf_size)
{
LOGINFO(5, "sending PEER_LIST_REQUEST");
if (buf_size < SEND_BUF_MIN_SIZE) {
return 0;
}
*reinterpret_cast<uint8_t*>(buf) = static_cast<uint8_t>(MessageId::PEER_LIST_REQUEST);
return 1;
});
@@ -406,8 +415,13 @@ void P2PServer::save_peer_list()
void P2PServer::load_peer_list()
{
size_t old_size;
{
MutexLock lock(m_peerListLock);
old_size = m_peerList.size();
}
std::string saved_list;
const size_t old_size = m_peerList.size();
// Load peers from seed nodes if we're on the default or mini sidechain
auto load_from_seed_nodes = [&saved_list](const char** nodes, int p2p_port) {
@@ -417,7 +431,7 @@ void P2PServer::load_peer_list()
addrinfo hints{};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
hints.ai_flags = AI_ADDRCONFIG;
addrinfo* result;
const int err = getaddrinfo(nodes[i], nullptr, &hints, &result);
@@ -426,7 +440,9 @@ void P2PServer::load_peer_list()
const char* addr_str;
char addr_str_buf[64];
char buf[log::Stream::BUF_SIZE + 1];
char buf[128];
buf[0] = '\0';
log::Stream s(buf);
if (r->ai_family == AF_INET6) {
@@ -443,7 +459,7 @@ void P2PServer::load_peer_list()
}
if (s.m_pos) {
LOGINFO(4, "added " << static_cast<char*>(buf) << " from " << nodes[i]);
LOGINFO(4, "added " << static_cast<const char*>(buf) << " from " << nodes[i]);
if (!saved_list.empty()) {
saved_list += ',';
}
@@ -542,33 +558,35 @@ void P2PServer::load_monerod_peer_list()
JSONRPCRequest::call(params.m_host.c_str(), params.m_rpcPort, "/get_peer_list",
[this](const char* data, size_t size)
{
constexpr char err_str[] = "/get_peer_list RPC request returned invalid JSON ";
#define ERR_STR "/get_peer_list RPC request returned invalid JSON "
using namespace rapidjson;
Document doc;
if (doc.Parse(data, size).HasParseError()) {
LOGWARN(4, err_str << "(parse error)");
LOGWARN(4, ERR_STR "(parse error)");
return;
}
if (!doc.IsObject()) {
LOGWARN(4, err_str << "(not an object)");
LOGWARN(4, ERR_STR "(not an object)");
return;
}
if (!doc.HasMember("white_list")) {
LOGWARN(4, err_str << "('white_list' not found)");
LOGWARN(4, ERR_STR "('white_list' not found)");
return;
}
const auto& white_list = doc["white_list"];
if (!white_list.IsArray()) {
LOGWARN(4, err_str << "('white_list' is not an array)");
LOGWARN(4, ERR_STR "('white_list' is not an array)");
return;
}
#undef ERR_STR
const int port = m_pool->side_chain().is_mini() ? DEFAULT_P2P_PORT_MINI : DEFAULT_P2P_PORT;
const SizeType n = white_list.Size();
@@ -780,7 +798,8 @@ void P2PServer::on_broadcast()
}
for (Broadcast* data : broadcast_queue) {
send(client, [client, data](void* buf) {
send(client, [client, data](void* buf, size_t buf_size) -> size_t
{
uint8_t* p0 = reinterpret_cast<uint8_t*>(buf);
uint8_t* p = p0;
@@ -798,9 +817,14 @@ void P2PServer::on_broadcast()
if (send_pruned) {
LOGINFO(6, "sending BLOCK_BROADCAST (pruned) to " << log::Gray() << static_cast<char*>(client->m_addrString));
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
const uint32_t len = static_cast<uint32_t>(data->pruned_blob.size());
if (buf_size < SEND_BUF_MIN_SIZE + 1 + sizeof(uint32_t) + len) {
return 0;
}
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
memcpy(p, &len, sizeof(uint32_t));
p += sizeof(uint32_t);
@@ -811,9 +835,14 @@ void P2PServer::on_broadcast()
}
else {
LOGINFO(5, "sending BLOCK_BROADCAST (full) to " << log::Gray() << static_cast<char*>(client->m_addrString));
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
const uint32_t len = static_cast<uint32_t>(data->blob.size());
if (buf_size < SEND_BUF_MIN_SIZE + 1 + sizeof(uint32_t) + len) {
return 0;
}
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
memcpy(p, &len, sizeof(uint32_t));
p += sizeof(uint32_t);
@@ -837,25 +866,12 @@ uint64_t P2PServer::get_random64()
void P2PServer::print_status()
{
const int64_t uptime = seconds_since_epoch() - m_pool->start_time();
const int64_t s = uptime % 60;
const int64_t m = (uptime / 60) % 60;
const int64_t h = (uptime / 3600) % 24;
const int64_t d = uptime / 86400;
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s1(buf);
if (d > 0) {
s1 << d << "d ";
}
s1 << h << "h " << m << "m " << s << 's';
MutexLock lock(m_peerListLock);
LOGINFO(0, "status" <<
"\nConnections = " << m_numConnections << " (" << m_numIncomingConnections << " incoming)" <<
"\nConnections = " << m_numConnections.load() << " (" << m_numIncomingConnections.load() << " incoming)" <<
"\nPeer list size = " << m_peerList.size() <<
"\nUptime = " << log::const_buf(buf, s1.m_pos)
"\nUptime = " << log::Duration(seconds_since_epoch() - m_pool->start_time())
);
}
@@ -863,11 +879,16 @@ void P2PServer::show_peers()
{
MutexLock lock(m_clientsListLock);
size_t n = 0;
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if (client->m_listenPort >= 0) {
LOGINFO(0, (client->m_isIncoming ? "I " : "O ") << client->m_pingTime << " ms\t" << static_cast<char*>(client->m_addrString));
++n;
}
}
LOGINFO(0, "Total: " << n << " peers");
}
void P2PServer::on_timer()
@@ -980,12 +1001,17 @@ void P2PServer::download_missing_blocks()
}
const bool result = send(client,
[&id](void* buf)
[&id](void* buf, size_t buf_size) -> size_t
{
LOGINFO(5, "sending BLOCK_REQUEST for id = " << id);
if (buf_size < SEND_BUF_MIN_SIZE) {
return 0;
}
uint8_t* p0 = reinterpret_cast<uint8_t*>(buf);
uint8_t* p = p0;
LOGINFO(5, "sending BLOCK_REQUEST for id = " << id);
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_REQUEST);
memcpy(p, id.h, HASH_SIZE);
@@ -1012,6 +1038,7 @@ void P2PServer::check_zmq()
if (cur_time >= last_active + 300) {
const uint64_t dt = static_cast<uint64_t>(cur_time - last_active);
LOGERR(1, "no ZMQ messages received from monerod in the last " << dt << " seconds, check your monerod/p2pool/network/firewall setup!!!");
m_pool->restart_zmq();
}
}
@@ -1348,12 +1375,17 @@ bool P2PServer::P2PClient::send_handshake_challenge()
m_handshakeChallenge = owner->get_random64();
return owner->send(this,
[this, owner](void* buf)
[this, owner](void* buf, size_t buf_size) -> size_t
{
LOGINFO(5, "sending HANDSHAKE_CHALLENGE");
if (buf_size < SEND_BUF_MIN_SIZE) {
return 0;
}
uint8_t* p0 = reinterpret_cast<uint8_t*>(buf);
uint8_t* p = p0;
LOGINFO(5, "sending HANDSHAKE_CHALLENGE");
*(p++) = static_cast<uint8_t>(MessageId::HANDSHAKE_CHALLENGE);
uint64_t k = m_handshakeChallenge;
@@ -1465,12 +1497,17 @@ void P2PServer::P2PClient::send_handshake_solution(const uint8_t (&challenge)[CH
}
const bool result = work->server->send(work->client,
[work](void* buf)
[work](void* buf, size_t buf_size) -> size_t
{
LOGINFO(5, "sending HANDSHAKE_SOLUTION");
if (buf_size < SEND_BUF_MIN_SIZE) {
return 0;
}
uint8_t* p0 = reinterpret_cast<uint8_t*>(buf);
uint8_t* p = p0;
LOGINFO(5, "sending HANDSHAKE_SOLUTION");
*(p++) = static_cast<uint8_t>(MessageId::HANDSHAKE_SOLUTION);
memcpy(p, work->solution.h, HASH_SIZE);
@@ -1615,8 +1652,14 @@ bool P2PServer::P2PClient::on_handshake_solution(const uint8_t* buf)
}
return m_owner->send(this,
[this](void* buf)
[this](void* buf, size_t buf_size) -> size_t
{
LOGINFO(5, "sending LISTEN_PORT and BLOCK_REQUEST for the chain tip");
if (buf_size < SEND_BUF_MIN_SIZE) {
return 0;
}
uint8_t* p0 = reinterpret_cast<uint8_t*>(buf);
uint8_t* p = p0;
on_after_handshake(p);
@@ -1679,15 +1722,21 @@ bool P2PServer::P2PClient::on_block_request(const uint8_t* buf)
}
return server->send(this,
[&blob](void* buf)
[&blob](void* buf, size_t buf_size) -> size_t
{
LOGINFO(5, "sending BLOCK_RESPONSE");
const uint32_t len = static_cast<uint32_t>(blob.size());
if (buf_size < SEND_BUF_MIN_SIZE + 1 + sizeof(uint32_t) + len) {
return 0;
}
uint8_t* p0 = reinterpret_cast<uint8_t*>(buf);
uint8_t* p = p0;
LOGINFO(5, "sending BLOCK_RESPONSE");
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_RESPONSE);
const uint32_t len = static_cast<uint32_t>(blob.size());
memcpy(p, &len, sizeof(uint32_t));
p += sizeof(uint32_t);
@@ -1841,12 +1890,17 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
}
return server->send(this,
[&peers, num_selected_peers](void* buf)
[&peers, num_selected_peers](void* buf, size_t buf_size) -> size_t
{
LOGINFO(5, "sending PEER_LIST_RESPONSE");
if (buf_size < SEND_BUF_MIN_SIZE + 2 + num_selected_peers * 19) {
return 0;
}
uint8_t* p0 = reinterpret_cast<uint8_t*>(buf);
uint8_t* p = p0;
LOGINFO(5, "sending PEER_LIST_RESPONSE");
*(p++) = static_cast<uint8_t>(MessageId::PEER_LIST_RESPONSE);
*(p++) = static_cast<uint8_t>(num_selected_peers);
@@ -2002,12 +2056,17 @@ void P2PServer::P2PClient::post_handle_incoming_block(const uint32_t reset_count
}
const bool result = m_owner->send(this,
[&id](void* buf)
[&id](void* buf, size_t buf_size) -> size_t
{
LOGINFO(5, "sending BLOCK_REQUEST for id = " << id);
if (buf_size < SEND_BUF_MIN_SIZE + 1 + HASH_SIZE) {
return 0;
}
uint8_t* p0 = reinterpret_cast<uint8_t*>(buf);
uint8_t* p = p0;
LOGINFO(5, "sending BLOCK_REQUEST for id = " << id);
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_REQUEST);
memcpy(p, id.h, HASH_SIZE);
+3 -3
View File
@@ -133,7 +133,7 @@ public:
void print_status() override;
void show_peers();
size_t peer_list_size() const { return m_peerList.size(); }
size_t peer_list_size() const { MutexLock lock(m_peerListLock); return m_peerList.size(); }
uint32_t max_outgoing_peers() const { return m_maxOutgoingPeers; }
uint32_t max_incoming_peers() const { return m_maxIncomingPeers; }
@@ -181,7 +181,7 @@ private:
uint64_t m_peerId;
uv_mutex_t m_peerListLock;
mutable uv_mutex_t m_peerListLock;
struct Peer
{
@@ -194,7 +194,7 @@ private:
std::vector<Peer> m_peerList;
std::vector<Peer> m_peerListMonero;
uint64_t m_peerListLastSaved;
std::atomic<uint64_t> m_peerListLastSaved;
struct Broadcast
{
+62 -9
View File
@@ -35,6 +35,7 @@
#include "crypto.h"
#include "p2pool_api.h"
#include "pool_block.h"
#include "keccak.h"
#include <thread>
#include <fstream>
@@ -60,13 +61,13 @@ p2pool::p2pool(int argc, char* argv[])
if (!m_params->m_wallet.valid()) {
LOGERR(1, "Invalid wallet address. Try \"p2pool --help\".");
panic();
throw std::exception();
}
bool is_v6;
if (!resolve_host(m_params->m_host, is_v6)) {
LOGERR(1, "resolve_host failed for " << m_params->m_host);
panic();
throw std::exception();
}
hash pub, sec, eph_public_key;
@@ -75,7 +76,7 @@ p2pool::p2pool(int argc, char* argv[])
uint8_t view_tag;
if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key, view_tag)) {
LOGERR(1, "Invalid wallet address: get_eph_public_key failed");
panic();
throw std::exception();
}
const NetworkType type = m_params->m_wallet.type();
@@ -90,24 +91,31 @@ p2pool::p2pool(int argc, char* argv[])
int err = uv_async_init(uv_default_loop_checked(), &m_submitBlockAsync, on_submit_block);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
throw std::exception();
}
m_submitBlockAsync.data = this;
err = uv_async_init(uv_default_loop_checked(), &m_blockTemplateAsync, on_update_block_template);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
throw std::exception();
}
m_blockTemplateAsync.data = this;
err = uv_async_init(uv_default_loop_checked(), &m_stopAsync, on_stop);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
throw std::exception();
}
m_stopAsync.data = this;
err = uv_async_init(uv_default_loop_checked(), &m_restartZMQAsync, on_restart_zmq);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
throw std::exception();
}
m_restartZMQAsync.data = this;
uv_rwlock_init_checked(&m_mainchainLock);
uv_rwlock_init_checked(&m_minerDataLock);
uv_mutex_init_checked(&m_foundBlocksLock);
@@ -120,7 +128,7 @@ p2pool::p2pool(int argc, char* argv[])
if (m_params->m_localStats && !m_api) {
LOGERR(1, "--local-api and --stratum-api command line parameters can't be used without --data-api");
panic();
throw std::exception();
}
m_sideChain = new SideChain(this, type, m_params->m_mini ? "mini" : nullptr);
@@ -294,7 +302,8 @@ void p2pool::handle_miner_data(MinerData& data)
WriteLock lock(m_mainchainLock);
for (uint64_t h = data.height; h && (h + BLOCK_HEADERS_REQUIRED > data.height); --h) {
if (m_mainchainByHeight.find(h) == m_mainchainByHeight.end()) {
auto it = m_mainchainByHeight.find(h);
if ((it == m_mainchainByHeight.end()) || it->second.difficulty.empty()) {
LOGWARN(3, "Mainchain data for height " << h << " is missing, requesting it from monerod again");
missing_heights.push_back(h);
}
@@ -438,6 +447,7 @@ void p2pool::on_stop(uv_async_t* async)
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_submitBlockAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_blockTemplateAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_stopAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_restartZMQAsync), nullptr);
uv_stop(uv_default_loop());
}
@@ -632,7 +642,14 @@ void p2pool::download_block_headers(uint64_t current_height)
if (parse_block_headers_range(data, size) == current_height - start_height) {
update_median_timestamp();
if (m_serversStarted.exchange(1) == 0) {
m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this);
try {
m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this);
}
catch (const std::exception& e) {
LOGERR(1, "Couldn't start ZMQ reader: exception " << e.what());
panic();
}
m_stratumServer = new StratumServer(this);
m_p2pServer = new P2PServer(this);
#ifdef WITH_RANDOMX
@@ -895,6 +912,8 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
void p2pool::get_miner_data()
{
m_getMinerDataPending = true;
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_miner_data\"}",
[this](const char* data, size_t size)
{
@@ -907,11 +926,22 @@ void p2pool::get_miner_data()
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_miner_data();
}
else {
m_getMinerDataPending = false;
}
});
}
void p2pool::parse_get_miner_data_rpc(const char* data, size_t size)
{
hash h;
keccak(reinterpret_cast<const uint8_t*>(data), static_cast<int>(size), h.h, HASH_SIZE);
if (h == m_getMinerDataHash) {
LOGWARN(4, "Received a duplicate get_miner_data RPC response, ignoring it");
return;
}
m_getMinerDataHash = h;
rapidjson::Document doc;
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
@@ -1376,6 +1406,29 @@ void p2pool::stop()
uv_async_send(&m_stopAsync);
}
void p2pool::restart_zmq()
{
if (!is_main_thread()) {
uv_async_send(&m_restartZMQAsync);
return;
}
if (!m_getMinerDataPending) {
get_miner_data();
}
delete m_ZMQReader;
m_ZMQReader = nullptr;
try {
m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this);
m_zmqLastActive = seconds_since_epoch();
}
catch (const std::exception& e) {
LOGERR(1, "Couldn't restart ZMQ reader: exception " << e.what());
}
}
int p2pool::run()
{
if (!m_params->ok()) {
+6
View File
@@ -95,6 +95,7 @@ public:
uint64_t zmq_last_active() const { return m_zmqLastActive; }
uint64_t start_time() const { return m_startTime; }
void restart_zmq();
private:
p2pool(const p2pool&) = delete;
@@ -103,6 +104,7 @@ private:
static void on_submit_block(uv_async_t* async) { reinterpret_cast<p2pool*>(async->data)->submit_block(); }
static void on_update_block_template(uv_async_t* async) { reinterpret_cast<p2pool*>(async->data)->update_block_template(); }
static void on_stop(uv_async_t*);
static void on_restart_zmq(uv_async_t* async) { reinterpret_cast<p2pool*>(async->data)->restart_zmq(); }
void submit_block() const;
@@ -197,8 +199,12 @@ private:
std::atomic<uint64_t> m_zmqLastActive;
uint64_t m_startTime;
uv_async_t m_restartZMQAsync;
ZMQReader* m_ZMQReader = nullptr;
hash m_getMinerDataHash;
bool m_getMinerDataPending = false;
};
} // namespace p2pool
-2
View File
@@ -131,12 +131,10 @@ void p2pool_api::dump_to_file()
{
MutexLock lock(m_dumpDataLock);
data = std::move(m_dumpData);
// cppcheck-suppress accessMoved
m_dumpData.clear();
}
for (auto& it : data) {
// cppcheck-suppress uninitvar
DumpFileWork* work = new DumpFileWork{ {}, {}, {}, it.first, std::move(it.second) };
work->open_req.data = work;
work->write_req.data = work;
+1
View File
@@ -34,6 +34,7 @@ public:
LOCAL,
};
// cppcheck-suppress functionConst
void on_stop();
template<typename T>
+6 -1
View File
@@ -125,10 +125,15 @@ Params::Params(int argc, char* argv[])
ok = true;
}
if (strcmp(argv[i], "--no-autodiff") == 0) {
m_autoDiff = false;
ok = true;
}
if (!ok) {
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
p2pool_usage();
panic();
throw std::exception();
}
}
+1
View File
@@ -48,6 +48,7 @@ struct Params
uint32_t m_maxIncomingPeers = 1000;
uint32_t m_minerThreads = 0;
bool m_mini = false;
bool m_autoDiff = true;
};
} // namespace p2pool
-1
View File
@@ -187,7 +187,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
const int sidechain_hash_offset = static_cast<int>((data - data_begin) + outputs_blob_size_diff);
READ_BUF(m_sidechainId.h, HASH_SIZE);
// cppcheck-suppress duplicateExpression
if (static_cast<uint64_t>(data - tx_extra_begin) != tx_extra_size) return __LINE__;
EXPECT_BYTE(0);
-2
View File
@@ -180,7 +180,6 @@ void RandomX_Hasher::set_seed(const hash& seed)
}
{
// cppcheck-suppress unreadVariable
ON_SCOPE_LEAVE([this]() { uv_rwlock_wrunlock(&m_cacheLock); });
if (m_stopped.load()) {
@@ -327,7 +326,6 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*
{
// First try to use the dataset if it's ready
if (uv_rwlock_tryrdlock(&m_datasetLock) == 0) {
// cppcheck-suppress unreadVariable
ON_SCOPE_LEAVE([this]() { uv_rwlock_rdunlock(&m_datasetLock); });
if (m_stopped.load()) {
+115 -53
View File
@@ -61,6 +61,7 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
: m_pool(pool)
, m_networkType(type)
, m_chainTip{ nullptr }
, m_seenWalletsLastPruneTime(0)
, m_poolName(pool_name ? pool_name : "default")
, m_targetBlockTime(10)
, m_minDifficulty(MIN_DIFFICULTY, 0)
@@ -78,12 +79,12 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
panic();
}
uv_mutex_init_checked(&m_sidechainLock);
uv_rwlock_init_checked(&m_sidechainLock);
uv_mutex_init_checked(&m_seenWalletsLock);
uv_mutex_init_checked(&m_seenBlocksLock);
uv_rwlock_init_checked(&m_curDifficultyLock);
m_difficultyData.reserve(m_chainWindowSize);
m_tmpShares.reserve(m_chainWindowSize * 2);
m_tmpRewards.reserve(m_chainWindowSize * 2);
LOGINFO(1, "generating consensus ID");
@@ -162,16 +163,18 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
SideChain::~SideChain()
{
uv_mutex_destroy(&m_sidechainLock);
uv_rwlock_destroy(&m_sidechainLock);
uv_mutex_destroy(&m_seenWalletsLock);
uv_mutex_destroy(&m_seenBlocksLock);
for (auto& it : m_blocksById) {
uv_rwlock_destroy(&m_curDifficultyLock);
for (const auto& it : m_blocksById) {
delete it.second;
}
}
void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares)
void SideChain::fill_sidechain_data(PoolBlock& block, const Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares) const
{
MutexLock lock(m_sidechainLock);
ReadLock lock(m_sidechainLock);
block.m_minerWallet = *w;
block.m_txkeySec = txkeySec;
@@ -206,7 +209,11 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
}
for (uint64_t i = 0, n = std::min<uint64_t>(UNCLE_BLOCK_DEPTH, tip->m_sidechainHeight + 1); i < n; ++i) {
for (PoolBlock* uncle : m_blocksByHeight[tip->m_sidechainHeight - i]) {
auto it = m_blocksByHeight.find(tip->m_sidechainHeight - i);
if (it == m_blocksByHeight.end()) {
continue;
}
for (const PoolBlock* uncle : it->second) {
// Only add verified and valid blocks
if (!uncle || !uncle->m_verified || uncle->m_invalid) {
continue;
@@ -221,16 +228,13 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
bool same_chain = false;
do {
tmp = tip;
while (tmp->m_sidechainHeight > uncle->m_sidechainHeight) {
while (tmp && (tmp->m_sidechainHeight > uncle->m_sidechainHeight)) {
tmp = get_parent(tmp);
if (!tmp) {
break;
}
}
if (!tmp || (tmp->m_sidechainHeight < uncle->m_sidechainHeight)) {
break;
}
PoolBlock* tmp2 = uncle;
const PoolBlock* tmp2 = uncle;
for (size_t j = 0; (j < UNCLE_BLOCK_DEPTH) && tmp && tmp2 && (tmp->m_sidechainHeight + UNCLE_BLOCK_DEPTH >= block.m_sidechainHeight); ++j) {
if (tmp->m_parent == tmp2->m_parent) {
same_chain = true;
@@ -263,7 +267,7 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
block.m_uncles.erase(std::unique(block.m_uncles.begin(), block.m_uncles.end()), block.m_uncles.end());
}
block.m_difficulty = m_curDifficulty;
block.m_difficulty = difficulty();
block.m_cumulativeDifficulty = tip->m_cumulativeDifficulty + block.m_difficulty;
for (const hash& uncle_id : block.m_uncles) {
@@ -395,9 +399,10 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
return false;
}
bool too_low_diff = (block.m_difficulty < m_curDifficulty);
const difficulty_type expected_diff = difficulty();
bool too_low_diff = (block.m_difficulty < expected_diff);
{
MutexLock lock(m_sidechainLock);
ReadLock lock(m_sidechainLock);
if (m_blocksById.find(block.m_sidechainId) != m_blocksById.end()) {
LOGINFO(4, "add_external_block: block " << block.m_sidechainId << " is already added");
return true;
@@ -423,7 +428,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
LOGINFO(4, "add_external_block: height = " << block.m_sidechainHeight << ", id = " << block.m_sidechainId << ", mainchain height = " << block.m_txinGenHeight);
if (too_low_diff) {
LOGWARN(4, "add_external_block: block has too low difficulty " << block.m_difficulty << ", expected >= ~" << m_curDifficulty << ". Ignoring it.");
LOGWARN(4, "add_external_block: block has too low difficulty " << block.m_difficulty << ", expected >= ~" << expected_diff << ". Ignoring it.");
return true;
}
@@ -479,7 +484,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
missing_blocks.clear();
{
MutexLock lock(m_sidechainLock);
WriteLock lock(m_sidechainLock);
if (!block.m_parent.empty() && (m_blocksById.find(block.m_parent) == m_blocksById.end())) {
missing_blocks.push_back(block.m_parent);
}
@@ -520,8 +525,12 @@ void SideChain::add_block(const PoolBlock& block)
);
PoolBlock* new_block = new PoolBlock(block);
{
MutexLock lock(m_seenWalletsLock);
m_seenWallets[new_block->m_minerWallet.spend_public_key()] = new_block->m_localTimestamp;
}
MutexLock lock(m_sidechainLock);
WriteLock lock(m_sidechainLock);
auto result = m_blocksById.insert({ new_block->m_sidechainId, new_block });
if (!result.second) {
@@ -551,13 +560,11 @@ void SideChain::add_block(const PoolBlock& block)
else {
verify_loop(new_block);
}
m_seenWallets[new_block->m_minerWallet.spend_public_key()] = new_block->m_localTimestamp;
}
PoolBlock* SideChain::find_block(const hash& id)
PoolBlock* SideChain::find_block(const hash& id) const
{
MutexLock lock(m_sidechainLock);
ReadLock lock(m_sidechainLock);
auto it = m_blocksById.find(id);
if (it != m_blocksById.end()) {
@@ -569,20 +576,25 @@ PoolBlock* SideChain::find_block(const hash& id)
void SideChain::watch_mainchain_block(const ChainMain& data, const hash& possible_id)
{
MutexLock lock(m_sidechainLock);
WriteLock lock(m_sidechainLock);
m_watchBlock = data;
m_watchBlockSidechainId = possible_id;
}
bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob)
bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob) const
{
MutexLock lock(m_sidechainLock);
ReadLock lock(m_sidechainLock);
const PoolBlock* block = nullptr;
// Empty hash means we return current sidechain tip
if (id == hash()) {
if (id.empty()) {
block = m_chainTip;
// Don't return stale chain tip
if (block && (block->m_txinGenHeight + 2 < m_pool->miner_data().height)) {
return false;
}
}
else {
auto it = m_blocksById.find(id);
@@ -602,11 +614,11 @@ bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob)
return true;
}
bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob)
bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob) const
{
blob.clear();
MutexLock lock(m_sidechainLock);
ReadLock lock(m_sidechainLock);
auto it = m_blocksById.find(block->m_sidechainId);
if (it != m_blocksById.end()) {
@@ -630,11 +642,14 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
return true;
}
if (!get_shares(block, m_tmpShares) || !split_reward(total_reward, m_tmpShares, m_tmpRewards) || (m_tmpRewards.size() != m_tmpShares.size())) {
std::vector<MinerShare> tmpShares;
std::vector<uint64_t> tmpRewards;
if (!get_shares(block, tmpShares) || !split_reward(total_reward, tmpShares, tmpRewards) || (tmpRewards.size() != tmpShares.size())) {
return false;
}
const size_t n = m_tmpShares.size();
const size_t n = tmpShares.size();
blob.reserve(n * 39 + 64);
@@ -647,12 +662,12 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
hash eph_public_key;
for (size_t i = 0; i < n; ++i) {
writeVarint(m_tmpRewards[i], blob);
writeVarint(tmpRewards[i], blob);
blob.emplace_back(tx_type);
uint8_t view_tag;
if (!m_tmpShares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key, view_tag)) {
if (!tmpShares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key, view_tag)) {
LOGWARN(6, "get_eph_public_key failed at index " << i);
}
blob.insert(blob.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
@@ -661,21 +676,23 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
blob.emplace_back(view_tag);
}
block->m_outputs.emplace_back(m_tmpRewards[i], eph_public_key, tx_type, view_tag);
block->m_outputs.emplace_back(tmpRewards[i], eph_public_key, tx_type, view_tag);
}
return true;
}
void SideChain::print_status()
void SideChain::print_status() const
{
std::vector<hash> blocks_in_window;
blocks_in_window.reserve(m_chainWindowSize * 9 / 8);
MutexLock lock(m_sidechainLock);
const difficulty_type diff = difficulty();
ReadLock lock(m_sidechainLock);
uint64_t rem;
uint64_t pool_hashrate = udiv128(m_curDifficulty.hi, m_curDifficulty.lo, m_targetBlockTime, &rem);
uint64_t pool_hashrate = udiv128(diff.hi, diff.lo, m_targetBlockTime, &rem);
difficulty_type network_diff = m_pool->miner_data().difficulty;
uint64_t network_hashrate = udiv128(network_diff.hi, network_diff.lo, 120, &rem);
@@ -736,7 +753,11 @@ void SideChain::print_status()
if (tip) {
std::sort(blocks_in_window.begin(), blocks_in_window.end());
for (uint64_t i = 0; (i < m_chainWindowSize) && (i <= tip_height); ++i) {
for (PoolBlock* block : m_blocksByHeight[tip_height - i]) {
auto it = m_blocksByHeight.find(tip_height - i);
if (it == m_blocksByHeight.end()) {
continue;
}
for (const PoolBlock* block : it->second) {
if (!std::binary_search(blocks_in_window.begin(), blocks_in_window.end(), block->m_sidechainId)) {
LOGINFO(4, "orphan block at height " << log::Gray() << block->m_sidechainHeight << log::NoColor() << ": " << log::Gray() << block->m_sidechainId);
++total_orphans;
@@ -747,7 +768,7 @@ void SideChain::print_status()
}
}
Wallet w = m_pool->params().m_wallet;
const Wallet& w = m_pool->params().m_wallet;
hash eph_public_key;
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
@@ -811,6 +832,38 @@ void SideChain::print_status()
);
}
double SideChain::get_reward_share(const Wallet& w) const
{
uint64_t reward = 0;
uint64_t total_reward = 0;
{
ReadLock lock(m_sidechainLock);
const PoolBlock* tip = m_chainTip;
if (tip) {
hash eph_public_key;
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
const PoolBlock::TxOutput& out = tip->m_outputs[i];
if (!reward) {
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
reward = out.m_reward;
}
}
else {
uint8_t view_tag;
if (w.get_eph_public_key(tip->m_txkeySec, i, eph_public_key, view_tag) && (out.m_ephPublicKey == eph_public_key)) {
reward = out.m_reward;
}
}
}
total_reward += out.m_reward;
}
}
}
return total_reward ? (static_cast<double>(reward) / static_cast<double>(total_reward)) : 0.0;
}
difficulty_type SideChain::total_hashes() const
{
const PoolBlock* tip = m_chainTip;
@@ -821,16 +874,19 @@ uint64_t SideChain::miner_count()
{
const uint64_t cur_time = seconds_since_epoch();
MutexLock lock(m_sidechainLock);
MutexLock lock(m_seenWalletsLock);
// Delete wallets that weren't seen for more than 72 hours and return how many remain
for (auto it = m_seenWallets.begin(); it != m_seenWallets.end();) {
if (it->second + 72 * 60 * 60 <= cur_time) {
it = m_seenWallets.erase(it);
}
else {
++it;
// Every 5 minutes, delete wallets that weren't seen for more than 72 hours
if (m_seenWalletsLastPruneTime + 5 * 60 <= cur_time) {
for (auto it = m_seenWallets.begin(); it != m_seenWallets.end();) {
if (it->second + 72 * 60 * 60 < cur_time) {
it = m_seenWallets.erase(it);
}
else {
++it;
}
}
m_seenWalletsLastPruneTime = cur_time;
}
return m_seenWallets.size();
@@ -1405,10 +1461,13 @@ void SideChain::update_chain_tip(PoolBlock* block)
difficulty_type diff;
if (get_difficulty(block, m_difficultyData, diff)) {
m_chainTip = block;
m_curDifficulty = diff;
{
WriteLock lock(m_curDifficultyLock);
m_curDifficulty = diff;
}
LOGINFO(2, "new chain tip: next height = " << log::Gray() << block->m_sidechainHeight + 1 << log::NoColor() <<
", next difficulty = " << log::Gray() << m_curDifficulty << log::NoColor() <<
", next difficulty = " << log::Gray() << diff << log::NoColor() <<
", main chain height = " << log::Gray() << block->m_txinGenHeight);
block->m_wantBroadcast = true;
@@ -1419,6 +1478,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
StratumServer* s = m_pool->stratum_server();
if (s && is_alternative) {
s->reset_share_counters();
LOGINFO(0, log::LightCyan() << "SYNCHRONIZED");
}
}
prune_old_blocks();
@@ -1471,7 +1531,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
}
}
PoolBlock* SideChain::get_parent(const PoolBlock* block)
PoolBlock* SideChain::get_parent(const PoolBlock* block) const
{
if (block) {
auto it = m_blocksById.find(block->m_parent);
@@ -1491,13 +1551,15 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
}
if (!block) {
// Switching from an empty to a non-empty chain
is_alternative = true;
return true;
}
// If these two blocks are on the same chain, they must have a common ancestor
const PoolBlock* block_ancestor = block;
while (block_ancestor->m_sidechainHeight > candidate->m_sidechainHeight) {
while (block_ancestor && (block_ancestor->m_sidechainHeight > candidate->m_sidechainHeight)) {
const hash& id = block_ancestor->m_parent;
block_ancestor = get_parent(block_ancestor);
if (!block_ancestor) {
@@ -1701,11 +1763,11 @@ void SideChain::prune_old_blocks()
}
}
void SideChain::get_missing_blocks(std::vector<hash>& missing_blocks)
void SideChain::get_missing_blocks(std::vector<hash>& missing_blocks) const
{
missing_blocks.clear();
MutexLock lock(m_sidechainLock);
ReadLock lock(m_sidechainLock);
for (auto& b : m_blocksById) {
if (b.second->m_verified) {
+14 -11
View File
@@ -43,21 +43,22 @@ public:
SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr);
~SideChain();
void fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares);
void fill_sidechain_data(PoolBlock& block, const Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares) const;
bool block_seen(const PoolBlock& block);
void unsee_block(const PoolBlock& block);
bool add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks);
void add_block(const PoolBlock& block);
void get_missing_blocks(std::vector<hash>& missing_blocks);
void get_missing_blocks(std::vector<hash>& missing_blocks) const;
PoolBlock* find_block(const hash& id);
PoolBlock* find_block(const hash& id) const;
void watch_mainchain_block(const ChainMain& data, const hash& possible_id);
bool get_block_blob(const hash& id, std::vector<uint8_t>& blob);
bool get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob);
bool get_block_blob(const hash& id, std::vector<uint8_t>& blob) const;
bool get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob) const;
void print_status();
void print_status() const;
double get_reward_share(const Wallet& w) const;
// Consensus ID can be used to spawn independent P2Pools with their own sidechains
// It's never sent over the network to avoid revealing it to the possible man in the middle
@@ -65,7 +66,7 @@ public:
const std::vector<uint8_t>& consensus_id() const { return m_consensusId; }
uint64_t chain_window_size() const { return m_chainWindowSize; }
NetworkType network_type() const { return m_networkType; }
const difficulty_type& difficulty() const { return m_curDifficulty; }
FORCEINLINE difficulty_type difficulty() const { ReadLock lock(m_curDifficultyLock); return m_curDifficulty; }
difficulty_type total_hashes() const;
uint64_t block_time() const { return m_targetBlockTime; }
uint64_t miner_count();
@@ -88,7 +89,7 @@ private:
void verify_loop(PoolBlock* block);
void verify(PoolBlock* block);
void update_chain_tip(PoolBlock* block);
PoolBlock* get_parent(const PoolBlock* block);
PoolBlock* get_parent(const PoolBlock* block) const;
// Checks if "candidate" has longer (higher difficulty) chain than "block"
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative);
@@ -98,13 +99,14 @@ private:
bool load_config(const std::string& filename);
bool check_config();
mutable uv_mutex_t m_sidechainLock;
mutable uv_rwlock_t m_sidechainLock;
std::atomic<PoolBlock*> m_chainTip;
std::map<uint64_t, std::vector<PoolBlock*>> m_blocksByHeight;
unordered_map<hash, PoolBlock*> m_blocksById;
uv_mutex_t m_seenWalletsLock;
unordered_map<hash, uint64_t> m_seenWallets;
std::vector<MinerShare> m_tmpShares;
std::vector<uint64_t> m_tmpRewards;
uint64_t m_seenWalletsLastPruneTime;
uv_mutex_t m_seenBlocksLock;
unordered_set<hash> m_seenBlocks;
@@ -121,6 +123,7 @@ private:
std::vector<uint8_t> m_consensusId;
std::string m_consensusIdDisplayStr;
mutable uv_rwlock_t m_curDifficultyLock;
difficulty_type m_curDifficulty;
ChainMain m_watchBlock;
+231 -84
View File
@@ -27,9 +27,11 @@ static constexpr char log_category_prefix[] = "StratumServer ";
static constexpr int DEFAULT_BACKLOG = 128;
static constexpr uint64_t DEFAULT_BAN_TIME = 600;
static constexpr uint64_t MIN_DIFF = 1000;
static constexpr uint64_t AUTO_DIFF_TARGET_TIME = 30;
// Use short target format (4 bytes) for diff <= 4 million
static constexpr uint64_t TARGET_4_BYTES_LIMIT = std::numeric_limits<uint64_t>::max() / 4000000;
static constexpr uint64_t TARGET_4_BYTES_LIMIT = std::numeric_limits<uint64_t>::max() / 4000000 + 1;
#include "tcp_server.inl"
@@ -38,6 +40,7 @@ namespace p2pool {
StratumServer::StratumServer(p2pool* pool)
: TCPServer(StratumClient::allocate)
, m_pool(pool)
, m_autoDiff(pool->params().m_autoDiff)
, m_extraNonce(0)
, m_rng(RandomDeviceSeed::instance)
, m_cumulativeHashes(0)
@@ -57,7 +60,6 @@ StratumServer::StratumServer(p2pool* pool)
uv_mutex_init_checked(&m_blobsQueueLock);
uv_mutex_init_checked(&m_rngLock);
uv_mutex_init_checked(&m_submittedSharesPoolLock);
uv_rwlock_init_checked(&m_hashrateDataLock);
m_submittedSharesPool.resize(10);
@@ -68,7 +70,7 @@ StratumServer::StratumServer(p2pool* pool)
const int err = uv_async_init(&m_loop, &m_blobsAsync, on_blobs_ready);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
return;
panic();
}
m_blobsAsync.data = this;
m_blobsQueue.reserve(2);
@@ -84,7 +86,6 @@ StratumServer::~StratumServer()
uv_mutex_destroy(&m_blobsQueueLock);
uv_mutex_destroy(&m_rngLock);
uv_mutex_destroy(&m_submittedSharesPoolLock);
uv_rwlock_destroy(&m_hashrateDataLock);
for (SubmittedShare* share : m_submittedSharesPool) {
@@ -101,6 +102,7 @@ void StratumServer::on_block(const BlockTemplate& block)
LOGINFO(4, "no clients connected");
return;
}
m_extraNonce.exchange(num_connections);
BlobsData* blobs_data = new BlobsData{};
@@ -112,9 +114,7 @@ void StratumServer::on_block(const BlockTemplate& block)
// Even if they do, they'll be added to the beginning of the list and will get their block template in on_login()
// We'll iterate through the list backwards so when we get to the beginning and run out of extra_nonce values, it'll be only new clients left
blobs_data->m_numClientsExpected = num_connections;
m_extraNonce.exchange(blobs_data->m_numClientsExpected);
blobs_data->m_blobSize = block.get_hashing_blobs(0, blobs_data->m_numClientsExpected, blobs_data->m_blobs, blobs_data->m_height, difficulty, sidechain_difficulty, blobs_data->m_seedHash, nonce_offset, blobs_data->m_templateId);
blobs_data->m_blobSize = block.get_hashing_blobs(0, num_connections, blobs_data->m_blobs, blobs_data->m_height, difficulty, sidechain_difficulty, blobs_data->m_seedHash, nonce_offset, blobs_data->m_templateId);
// Integrity checks
if (blobs_data->m_blobSize < 76) {
@@ -178,11 +178,13 @@ void StratumServer::on_block(const BlockTemplate& block)
}
}
bool StratumServer::get_custom_user(const char* s, std::string& user)
template<size_t N>
static bool get_custom_user(const char* s, char (&user)[N])
{
user.clear();
size_t len = 0;
// Find first of '+' or '.', drop non-printable characters
while (s && (user.length() < 64)) {
while (s && (len < N - 1)) {
const char c = *s;
if (!c) {
break;
@@ -192,15 +194,16 @@ bool StratumServer::get_custom_user(const char* s, std::string& user)
}
// Limit to printable ASCII characters
if (c >= ' ' && c <= '~') {
user += c;
user[len++] = c;
}
++s;
}
user[len] = '\0';
return !user.empty();
return (len > 0);
}
bool StratumServer::get_custom_diff(const char* s, difficulty_type& diff)
static bool get_custom_diff(const char* s, difficulty_type& diff)
{
const char* diff_str = nullptr;
@@ -219,8 +222,8 @@ bool StratumServer::get_custom_diff(const char* s, difficulty_type& diff)
if (diff_str) {
const uint64_t t = strtoull(diff_str + 1, nullptr, 10);
if (t) {
// Don't let clients set difficulty less than 1000
diff = { std::max<uint64_t>(t + 1, 1000), 0 };
// Don't let clients set difficulty less than MIN_DIFF
diff = { std::max<uint64_t>(t + 1, MIN_DIFF), 0 };
return true;
}
}
@@ -250,16 +253,15 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
}
if (get_custom_user(login, client->m_customUser)) {
LOGINFO(5, "client " << log::Gray() << static_cast<char*>(client->m_addrString) << " set custom user " << client->m_customUser);
const char* s = client->m_customUser;
LOGINFO(5, "client " << log::Gray() << static_cast<char*>(client->m_addrString) << " set custom user " << s);
}
uint32_t job_id;
{
MutexLock lock(client->m_jobsLock);
job_id = ++client->m_perConnectionJobId;
job_id = client->m_perConnectionJobId++;
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE];
saved_job.job_id = job_id;
saved_job.extra_nonce = extra_nonce;
saved_job.template_id = template_id;
@@ -267,7 +269,7 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
}
const bool result = send(client,
[client, id, &hashing_blob, job_id, blob_size, target, height, &seed_hash](void* buf)
[client, id, &hashing_blob, job_id, blob_size, target, height, &seed_hash](void* buf, size_t buf_size)
{
do {
client->m_rpcId = static_cast<uint32_t>(static_cast<StratumServer*>(client->m_owner)->get_random64());
@@ -280,7 +282,7 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
target_hex.m_size -= sizeof(uint32_t);
}
log::Stream s(reinterpret_cast<char*>(buf));
log::Stream s(buf, buf_size);
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"result\":{\"id\":\"";
s << log::Hex(client->m_rpcId) << "\",\"job\":{\"blob\":\"";
s << log::hex_buf(hashing_blob, blob_size) << "\",\"job_id\":\"";
@@ -307,6 +309,11 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
job_id = (job_id << 4) + d;
}
if (!job_id) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " invalid params ('job_id' can't be 0)");
return false;
}
uint32_t nonce = 0;
for (int i = static_cast<int>(sizeof(uint32_t)) - 1; i >= 0; --i) {
@@ -335,9 +342,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
bool found = false;
{
MutexLock lock(client->m_jobsLock);
const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE];
if (saved_job.job_id == job_id) {
template_id = saved_job.template_id;
extra_nonce = saved_job.extra_nonce;
@@ -353,33 +358,33 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
if (!block.get_difficulties(template_id, mainchain_diff, sidechain_diff)) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
return send(client,
[id](void* buf)
[id](void* buf, size_t buf_size)
{
log::Stream s(reinterpret_cast<char*>(buf));
log::Stream s(buf, buf_size);
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Stale share\"}}\n";
return s.m_pos;
});
}
if (mainchain_diff.check_pow(resultHash)) {
const std::string& s = client->m_customUser;
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (!s.empty() ? " user " : "") << s << " found a mainchain block, submitting it");
const char* s = client->m_customUser;
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << " found a mainchain block, submitting it");
m_pool->submit_block_async(template_id, nonce, extra_nonce);
block.update_tx_keys();
}
SubmittedShare* share;
{
MutexLock lock(m_submittedSharesPoolLock);
if (!m_submittedSharesPool.empty()) {
share = m_submittedSharesPool.back();
m_submittedSharesPool.pop_back();
}
else {
share = new SubmittedShare{};
}
if (!m_submittedSharesPool.empty()) {
share = m_submittedSharesPool.back();
m_submittedSharesPool.pop_back();
}
else {
share = new SubmittedShare{};
}
if (target >= TARGET_4_BYTES_LIMIT) {
target = (target >> 32) << 32;
}
share->m_req.data = share;
@@ -395,9 +400,16 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
share->m_target = target;
share->m_resultHash = resultHash;
share->m_sidechainDifficulty = sidechain_diff;
share->m_timestamp = seconds_since_epoch();
uint64_t rem;
share->m_hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 1;
share->m_highEnoughDifficulty = sidechain_diff.check_pow(resultHash);
update_auto_diff(client, share->m_timestamp, share->m_hashes);
// If this share is below sidechain difficulty, process it in this thread because it'll be quick
if (!share->m_sidechainDifficulty.check_pow(share->m_resultHash)) {
if (!share->m_highEnoughDifficulty) {
on_share_found(&share->m_req);
on_after_share_found(&share->m_req, 0);
return true;
@@ -419,9 +431,9 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a share with invalid job id");
const bool result = send(client,
[id](void* buf)
[id](void* buf, size_t buf_size)
{
log::Stream s(reinterpret_cast<char*>(buf));
log::Stream s(buf, buf_size);
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Invalid job id\"}}\n";
return s.m_pos;
});
@@ -441,8 +453,53 @@ void StratumServer::print_status()
print_stratum_status();
}
void StratumServer::show_workers()
{
const uint64_t cur_time = seconds_since_epoch();
const difficulty_type pool_diff = m_pool->side_chain().difficulty();
MutexLock lock(m_clientsListLock);
int addr_len = 0;
for (const StratumClient* c = static_cast<StratumClient*>(m_connectedClientsList->m_next); c != m_connectedClientsList; c = static_cast<StratumClient*>(c->m_next)) {
addr_len = std::max(addr_len, static_cast<int>(strlen(c->m_addrString)));
}
size_t n = 0;
LOGINFO(0, log::pad_right("IP:port", addr_len + 8)
<< log::pad_right("uptime", 20)
<< log::pad_right("difficulty", 20)
<< log::pad_right("hashrate", 15)
<< "name"
);
for (const StratumClient* c = static_cast<StratumClient*>(m_connectedClientsList->m_next); c != m_connectedClientsList; c = static_cast<StratumClient*>(c->m_next)) {
difficulty_type diff;
if (c->m_customDiff != 0) {
diff = c->m_customDiff;
}
else if (m_autoDiff && (c->m_autoDiff != 0)) {
diff = c->m_autoDiff;
}
else {
diff = pool_diff;
}
LOGINFO(0, log::pad_right(static_cast<const char*>(c->m_addrString), addr_len + 8)
<< log::pad_right(log::Duration(cur_time - c->m_connectedTime), 20)
<< log::pad_right(diff, 20)
<< log::pad_right(log::Hashrate(c->m_autoDiff.lo / AUTO_DIFF_TARGET_TIME, m_autoDiff && (c->m_autoDiff != 0)), 15)
<< (c->m_rpcId ? c->m_customUser : "not logged in")
);
++n;
}
LOGINFO(0, "Total: " << n << " workers");
}
void StratumServer::reset_share_counters()
{
m_cumulativeHashes = 0;
m_cumulativeHashesAtLastShare = 0;
m_cumulativeFoundSharesDiff = 0.0;
m_totalFoundShares = 0;
@@ -495,10 +552,83 @@ void StratumServer::print_stratum_status() const
"\nShares found = " << m_totalFoundShares <<
"\nAverage effort = " << average_effort << '%' <<
"\nCurrent effort = " << static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double() << '%' <<
"\nConnections = " << m_numConnections << " (" << m_numIncomingConnections << " incoming)"
"\nConnections = " << m_numConnections.load() << " (" << m_numIncomingConnections.load() << " incoming)"
);
}
// Compresses 64-bit hashes value into 16-bit value (5 bits for shift, 11 bits for data)
namespace {
enum HashValue : uint64_t {
bits = 11,
mask = (1 << bits) - 1,
};
static constexpr FORCEINLINE uint64_t hash_uncompress(uint64_t h)
{
return (h & HashValue::mask) << (h >> HashValue::bits);
};
enum HashMaxValue : uint64_t {
value = hash_uncompress(std::numeric_limits<uint16_t>::max())
};
static FORCEINLINE uint16_t hash_compress(uint64_t h)
{
if (h <= HashValue::mask) {
return static_cast<uint16_t>(h);
}
if (h >= HashMaxValue::value) {
return std::numeric_limits<uint16_t>::max();
}
const uint64_t shift = bsr(h) - (HashValue::bits - 1);
return static_cast<uint16_t>((shift << HashValue::bits) | (h >> shift));
}
}
void StratumServer::update_auto_diff(StratumClient* client, const uint64_t timestamp, const uint64_t hashes)
{
const uint16_t hashes_compressed = hash_compress(hashes);
client->m_autoDiffWindowHashes += hash_uncompress(hashes_compressed);
const uint32_t k = client->m_autoDiffIndex++;
constexpr uint32_t N = StratumClient::AUTO_DIFF_SIZE;
StratumClient::AutoDiffData& auto_diff_data = client->m_autoDiffData[k % N];
if (k >= N) {
client->m_autoDiffWindowHashes -= hash_uncompress(auto_diff_data.m_hashes);
}
const uint16_t t1 = auto_diff_data.m_timestamp;
const uint16_t t2 = static_cast<uint16_t>(timestamp);
auto_diff_data.m_timestamp = t2;
auto_diff_data.m_hashes = hashes_compressed;
if (k >= N) {
// Full window
const uint64_t dt = t2 - t1;
client->m_autoDiff.lo = std::max<uint64_t>((client->m_autoDiffWindowHashes * AUTO_DIFF_TARGET_TIME) / (dt ? dt : 1), MIN_DIFF);
client->m_autoDiff.hi = 0;
}
else if (k >= 10) {
// Partial window
const uint64_t h0 = hash_uncompress(client->m_autoDiffData[0].m_hashes);
const uint64_t dt = client->m_autoDiffData[k].m_timestamp - client->m_autoDiffData[0].m_timestamp;
client->m_autoDiff.lo = std::max<uint64_t>(((client->m_autoDiffWindowHashes - h0) * AUTO_DIFF_TARGET_TIME) / (dt ? dt : 1), MIN_DIFF);
client->m_autoDiff.hi = 0;
}
else if (k == 0) {
// First share, fix auto diff to current difficulty until we have at least 10 shares
client->m_autoDiff.lo = hashes;
client->m_autoDiff.hi = 0;
}
}
void StratumServer::on_blobs_ready()
{
std::vector<BlobsData*> blobs_queue;
@@ -555,14 +685,32 @@ void StratumServer::on_blobs_ready()
if (client->m_customDiff.lo) {
target = std::max(target, client->m_customDiff.target());
}
else if (m_autoDiff) {
if (client->m_autoDiff.lo) {
const uint32_t k = client->m_autoDiffIndex;
const uint16_t elapsed_time = static_cast<uint16_t>(cur_time) - client->m_autoDiffData[(k - 1) % StratumClient::AUTO_DIFF_SIZE].m_timestamp;
if (elapsed_time > AUTO_DIFF_TARGET_TIME * 5) {
// More than 500% effort, reduce the auto diff by 1/8 every time until the share is found
client->m_autoDiff.lo = std::max<uint64_t>(client->m_autoDiff.lo - client->m_autoDiff.lo / 8, MIN_DIFF);
}
target = std::max(target, client->m_autoDiff.target());
}
else {
// Not enough shares from the client yet, cut diff in half every 16 seconds
const uint64_t num_halvings = (cur_time - client->m_connectedTime) / 16;
constexpr uint64_t max_target = (std::numeric_limits<uint64_t>::max() / MIN_DIFF) + 1;
for (uint64_t i = 0; (i < num_halvings) && (target < max_target); ++i) {
target *= 2;
}
target = std::min<uint64_t>(target, max_target);
}
}
uint32_t job_id;
{
MutexLock lock3(client->m_jobsLock);
job_id = ++client->m_perConnectionJobId;
job_id = client->m_perConnectionJobId++;
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE];
saved_job.job_id = job_id;
saved_job.extra_nonce = extra_nonce;
saved_job.template_id = data->m_templateId;
@@ -570,7 +718,7 @@ void StratumServer::on_blobs_ready()
}
const bool result = send(client,
[data, target, hashing_blob, &job_id](void* buf)
[data, target, hashing_blob, &job_id](void* buf, size_t buf_size)
{
log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t));
@@ -579,7 +727,7 @@ void StratumServer::on_blobs_ready()
target_hex.m_size -= sizeof(uint32_t);
}
log::Stream s(reinterpret_cast<char*>(buf));
log::Stream s(buf, buf_size);
s << "{\"jsonrpc\":\"2.0\",\"method\":\"job\",\"params\":{\"blob\":\"";
s << log::hex_buf(hashing_blob, data->m_blobSize) << "\",\"job_id\":\"";
s << log::Hex(job_id) << "\",\"target\":\"";
@@ -597,8 +745,9 @@ void StratumServer::on_blobs_ready()
}
}
if (numClientsProcessed != m_numConnections) {
LOGWARN(1, "client list is broken, expected " << m_numConnections << ", got " << numClientsProcessed << " clients");
const uint32_t num_connections = m_numConnections;
if (numClientsProcessed != num_connections) {
LOGWARN(1, "client list is broken, expected " << num_connections << ", got " << numClientsProcessed << " clients");
}
}
@@ -638,26 +787,23 @@ void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp)
void StratumServer::on_share_found(uv_work_t* req)
{
bkg_jobs_tracker.start("StratumServer::on_share_found");
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
if (share->m_highEnoughDifficulty) {
bkg_jobs_tracker.start("StratumServer::on_share_found");
}
StratumClient* client = share->m_client;
StratumServer* server = share->m_server;
p2pool* pool = server->m_pool;
uint64_t target = share->m_target;
if (target >= TARGET_4_BYTES_LIMIT) {
target = (target >> 32) << 32;
}
uint64_t rem;
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
const uint64_t target = share->m_target;
const uint64_t hashes = share->m_hashes;
if (pool->stopped()) {
LOGWARN(0, "p2pool is shutting down, but a share was found. Trying to process it anyway!");
}
if (share->m_sidechainDifficulty.check_pow(share->m_resultHash)) {
if (share->m_highEnoughDifficulty) {
uint8_t blob[128];
uint64_t height;
difficulty_type difficulty;
@@ -698,8 +844,8 @@ void StratumServer::on_share_found(uv_work_t* req)
server->m_cumulativeFoundSharesDiff += diff;
++server->m_totalFoundShares;
const std::string& s = client->m_customUser;
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", client " << static_cast<char*>(client->m_addrString) << (!s.empty() ? " user " : "") << s << ", effort " << effort << '%');
const char* s = client->m_customUser;
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << ", effort " << effort << '%');
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
}
@@ -707,7 +853,7 @@ void StratumServer::on_share_found(uv_work_t* req)
const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t));
if (LIKELY(value < target)) {
const uint64_t timestamp = seconds_since_epoch();
const uint64_t timestamp = share->m_timestamp;
server->update_hashrate_data(hashes, timestamp);
server->api_update_local_stats(timestamp);
share->m_result = SubmittedShare::Result::OK;
@@ -721,16 +867,11 @@ void StratumServer::on_share_found(uv_work_t* req)
void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
{
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
if (share->m_highEnoughDifficulty) {
bkg_jobs_tracker.stop("StratumServer::on_share_found");
}
ON_SCOPE_LEAVE(
[share]()
{
{
MutexLock lock(share->m_server->m_submittedSharesPoolLock);
share->m_server->m_submittedSharesPool.push_back(share);
}
bkg_jobs_tracker.stop("StratumServer::on_share_found");
});
ON_SCOPE_LEAVE([share]() { share->m_server->m_submittedSharesPool.push_back(share); });
StratumClient* client = share->m_client;
StratumServer* server = share->m_server;
@@ -739,9 +880,9 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
if ((client->m_resetCounter.load() == share->m_clientResetCounter) && (client->m_rpcId == share->m_rpcId)) {
const bool result = server->send(client,
[share](void* buf)
[share](void* buf, size_t buf_size)
{
log::Stream s(reinterpret_cast<char*>(buf));
log::Stream s(buf, buf_size);
switch (share->m_result) {
case SubmittedShare::Result::STALE:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Stale share\"}}\n";
@@ -777,28 +918,34 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
StratumServer::StratumClient::StratumClient()
: m_rpcId(0)
, m_perConnectionJobId(0)
, m_connectedTime(0)
, m_jobs{}
, m_perConnectionJobId(0)
, m_autoDiffData{}
, m_autoDiffWindowHashes(0)
, m_autoDiffIndex(0)
, m_customDiff{}
, m_autoDiff{}
, m_customUser{}
{
uv_mutex_init_checked(&m_jobsLock);
}
StratumServer::StratumClient::~StratumClient()
{
uv_mutex_destroy(&m_jobsLock);
}
void StratumServer::StratumClient::reset()
{
Client::reset();
m_rpcId = 0;
m_connectedTime = 0;
memset(m_jobs, 0, sizeof(m_jobs));
m_perConnectionJobId = 0;
m_connectedTime = 0;
for (int i = 0; i < JOBS_SIZE; ++i) {
m_jobs[i].job_id = 0;
}
m_autoDiffWindowHashes = 0;
m_autoDiffIndex = 0;
m_customDiff = {};
m_customUser.clear();
m_autoDiff = {};
m_customUser[0] = '\0';
}
bool StratumServer::StratumClient::on_connect()
+23 -9
View File
@@ -39,7 +39,7 @@ public:
struct StratumClient : public Client
{
StratumClient();
~StratumClient();
FORCEINLINE ~StratumClient() {}
static Client* allocate() { return new StratumClient(); }
@@ -52,20 +52,32 @@ public:
bool process_submit(rapidjson::Document& doc, uint32_t id);
uint32_t m_rpcId;
uint32_t m_perConnectionJobId;
uint64_t m_connectedTime;
uv_mutex_t m_jobsLock;
enum {
JOBS_SIZE = 4,
AUTO_DIFF_SIZE = 64,
};
struct SavedJob {
uint32_t job_id;
uint32_t extra_nonce;
uint32_t template_id;
uint64_t target;
} m_jobs[4];
} m_jobs[JOBS_SIZE];
struct AutoDiffData {
uint16_t m_timestamp;
uint16_t m_hashes;
} m_autoDiffData[AUTO_DIFF_SIZE];
uint64_t m_autoDiffWindowHashes;
uint32_t m_autoDiffIndex;
uint32_t m_perConnectionJobId;
difficulty_type m_customDiff;
std::string m_customUser;
difficulty_type m_autoDiff;
char m_customUser[32];
};
bool on_login(StratumClient* client, uint32_t id, const char* login);
@@ -73,19 +85,19 @@ public:
uint64_t get_random64();
void print_status() override;
void show_workers();
void reset_share_counters();
private:
void print_stratum_status() const;
static bool get_custom_diff(const char* s, difficulty_type& diff);
static bool get_custom_user(const char* s, std::string& user);
void update_auto_diff(StratumClient* client, const uint64_t timestamp, const uint64_t hashes);
static void on_share_found(uv_work_t* req);
static void on_after_share_found(uv_work_t* req, int status);
p2pool* m_pool;
bool m_autoDiff;
struct BlobsData
{
@@ -125,6 +137,9 @@ private:
uint64_t m_target;
hash m_resultHash;
difficulty_type m_sidechainDifficulty;
uint64_t m_timestamp;
uint64_t m_hashes;
bool m_highEnoughDifficulty;
enum class Result {
STALE,
@@ -135,7 +150,6 @@ private:
} m_result;
};
uv_mutex_t m_submittedSharesPoolLock;
std::vector<SubmittedShare*> m_submittedSharesPool;
struct HashrateData
+10 -10
View File
@@ -70,6 +70,8 @@ public:
void init_addr_string(bool is_v6, const sockaddr_storage* peer_addr);
alignas(8) char m_readBuf[READ_BUF_SIZE];
TCPServer* m_owner;
// Used to maintain connected clients list
@@ -77,19 +79,17 @@ public:
Client* m_next;
uv_tcp_t m_socket;
uv_connect_t m_connectRequest;
bool m_isV6;
bool m_isIncoming;
bool m_readBufInUse;
uint32_t m_numRead;
raw_ip m_addr;
int m_port;
char m_addrString[64];
bool m_readBufInUse;
char m_readBuf[READ_BUF_SIZE];
uint32_t m_numRead;
std::atomic<uint32_t> m_resetCounter{ 0 };
std::atomic<uint32_t> m_resetCounter;
};
struct WriteBuf
@@ -105,14 +105,14 @@ public:
struct SendCallbackBase
{
virtual ~SendCallbackBase() {}
virtual size_t operator()(void*) = 0;
virtual size_t operator()(void*, size_t) = 0;
};
template<typename T>
struct SendCallback : public SendCallbackBase
{
explicit FORCEINLINE SendCallback(T&& callback) : m_callback(std::move(callback)) {}
size_t operator()(void* buf) override { return m_callback(buf); }
size_t operator()(void* buf, size_t buf_size) override { return m_callback(buf, buf_size); }
private:
SendCallback& operator=(SendCallback&&) = delete;
@@ -155,8 +155,8 @@ protected:
uv_mutex_t m_clientsListLock;
std::vector<Client*> m_preallocatedClients;
Client* m_connectedClientsList;
uint32_t m_numConnections;
uint32_t m_numIncomingConnections;
std::atomic<uint32_t> m_numConnections;
std::atomic<uint32_t> m_numIncomingConnections;
uv_mutex_t m_bansLock;
unordered_map<raw_ip, std::chrono::steady_clock::time_point> m_bans;
+43 -13
View File
@@ -28,8 +28,8 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
, m_finished(0)
, m_listenPort(-1)
, m_loopStopped{false}
, m_numConnections(0)
, m_numIncomingConnections(0)
, m_numConnections{ 0 }
, m_numIncomingConnections{ 0 }
{
int err = uv_loop_init(&m_loop);
if (err) {
@@ -37,10 +37,18 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
panic();
}
uv_async_init(&m_loop, &m_dropConnectionsAsync, on_drop_connections);
err = uv_async_init(&m_loop, &m_dropConnectionsAsync, on_drop_connections);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
}
m_dropConnectionsAsync.data = this;
uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
err = uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
}
m_shutdownAsync.data = this;
uv_mutex_init_checked(&m_clientsListLock);
@@ -64,6 +72,7 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
// cppcheck-suppress functionStatic
TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::~TCPServer()
{
if (m_finished.load() == 0) {
@@ -322,6 +331,10 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connect_failed(bool, const raw
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::is_banned(const raw_ip& ip)
{
if (ip.is_localhost()) {
return false;
}
const auto cur_time = std::chrono::steady_clock::now();
MutexLock lock(m_bansLock);
@@ -372,8 +385,11 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer_nolock(Client* cl
return false;
}
client->m_connectRequest.data = client;
err = uv_tcp_connect(&client->m_connectRequest, &client->m_socket, addr, on_connect);
uv_connect_t* connect_request = reinterpret_cast<uv_connect_t*>(client->m_readBuf);
memset(connect_request, 0, sizeof(uv_connect_t));
connect_request->data = client;
err = uv_tcp_connect(connect_request, &client->m_socket, addr, on_connect);
if (err) {
LOGERR(1, "failed to initiate tcp connection, error " << uv_err_name(err));
m_pendingConnections.erase(client->m_addr);
@@ -488,13 +504,17 @@ template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::print_status()
{
LOGINFO(0, "status" <<
"\nConnections = " << m_numConnections << " (" << m_numIncomingConnections << " incoming)"
"\nConnections = " << m_numConnections.load() << " (" << m_numIncomingConnections.load() << " incoming)"
);
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::ban(const raw_ip& ip, uint64_t seconds)
{
if (ip.is_localhost()) {
return;
}
const auto ban_time = std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
MutexLock lock(m_bansLock);
@@ -548,7 +568,7 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
// callback_buf is used in only 1 thread, so it's safe
static uint8_t callback_buf[WRITE_BUF_SIZE];
const size_t bytes_written = callback(callback_buf);
const size_t bytes_written = callback(callback_buf, sizeof(callback_buf));
if (bytes_written > WRITE_BUF_SIZE) {
LOGERR(0, "send callback wrote " << bytes_written << " bytes, expected no more than " << WRITE_BUF_SIZE << " bytes");
@@ -808,10 +828,21 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_client_nolock(uv_stream_t*
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::Client()
: m_owner(nullptr)
, m_prev(nullptr)
, m_next(nullptr)
, m_socket{}
, m_isV6(false)
, m_isIncoming(false)
, m_readBufInUse(false)
, m_numRead(0)
, m_addr{}
, m_port(0)
, m_addrString{}
, m_resetCounter{ 0 }
{
Client::reset();
m_readBuf[0] = '\0';
m_readBuf[READ_BUF_SIZE - 1] = '\0';
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
@@ -823,14 +854,13 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::reset()
m_prev = nullptr;
m_next = nullptr;
memset(&m_socket, 0, sizeof(m_socket));
memset(&m_connectRequest, 0, sizeof(m_connectRequest));
m_isV6 = false;
m_isIncoming = false;
m_readBufInUse = false;
m_numRead = 0;
m_addr = {};
m_port = -1;
m_addrString[0] = '\0';
m_readBufInUse = false;
m_numRead = 0;
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
+41 -2
View File
@@ -32,7 +32,7 @@ namespace p2pool {
#define STR2(X) STR(X)
#define STR(X) #X
const char* VERSION = "v2.0 (built"
const char* VERSION = "v2.1 (built"
#if defined(__clang__)
" with clang/" __clang_version__
#elif defined(__GNUC__)
@@ -349,7 +349,7 @@ 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_V4MAPPED | AI_ADDRCONFIG;
hints.ai_flags = AI_ADDRCONFIG;
addrinfo* r = nullptr;
const int err = getaddrinfo(host.c_str(), nullptr, &hints, &r);
@@ -384,4 +384,43 @@ bool resolve_host(std::string& host, bool& is_v6)
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<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;
}
} // namespace p2pool
+16
View File
@@ -203,6 +203,22 @@ FORCEINLINE uint64_t seconds_since_epoch()
return duration_cast<seconds>(steady_clock::now().time_since_epoch()).count();
}
uint64_t bsr_reference(uint64_t x);
#ifdef HAVE_BUILTIN_CLZLL
#define bsr(x) (63 - __builtin_clzll(x))
#elif defined HAVE_BITSCANREVERSE64
#pragma intrinsic(_BitScanReverse64)
FORCEINLINE uint64_t bsr(uint64_t x)
{
unsigned long index;
_BitScanReverse64(&index, x);
return index;
}
#else
#define bsr bsr_reference
#endif
} // namespace p2pool
namespace robin_hood {
+1 -2
View File
@@ -89,7 +89,6 @@ Wallet::Wallet(const Wallet& w)
operator=(w);
}
// cppcheck-suppress operatorEqVarError
Wallet& Wallet::operator=(const Wallet& w)
{
if (this == &w) {
@@ -119,7 +118,7 @@ bool Wallet::decode(const char* address)
static_assert(last_block_size_index >= 0, "Check ADDRESS_LENGTH");
uint8_t data[static_cast<size_t>(num_full_blocks) * sizeof(uint64_t) + last_block_size_index];
uint8_t data[static_cast<size_t>(num_full_blocks) * sizeof(uint64_t) + last_block_size_index] = {};
int data_index = 0;
for (int i = 0; i <= num_full_blocks; ++i) {
+7 -9
View File
@@ -49,13 +49,13 @@ ZMQReader::ZMQReader(const char* address, uint32_t zmq_port, MinerCallbackHandle
if (!m_publisherPort) {
LOGERR(1, "failed to to bind ZMQ publisher port, aborting");
panic();
throw zmq::error_t(EFSM);
}
const int err = uv_thread_create(&m_worker, run_wrapper, this);
if (err) {
LOGERR(1, "failed to start ZMQ thread, error " << uv_err_name(err));
panic();
throw zmq::error_t(EFSM);
}
}
@@ -71,8 +71,7 @@ ZMQReader::~ZMQReader()
uv_thread_join(&m_worker);
}
catch (const std::exception& e) {
LOGERR(1, "exception " << e.what() << ", aborting");
panic();
LOGERR(1, "exception " << e.what());
}
}
@@ -104,7 +103,7 @@ void ZMQReader::run()
zmq_msg_t message;
int rc = zmq_msg_init(&message);
if (rc != 0) {
throw zmq::error_t();
throw zmq::error_t(errno);
}
LOGINFO(1, "worker thread ready");
@@ -112,7 +111,7 @@ void ZMQReader::run()
do {
rc = zmq_msg_recv(&message, m_subscriber, 0);
if (rc < 0) {
throw zmq::error_t();
throw zmq::error_t(errno);
}
if (m_finished.load()) {
@@ -125,8 +124,7 @@ void ZMQReader::run()
zmq_msg_close(&message);
}
catch (const std::exception& e) {
LOGERR(1, "exception " << e.what() << ", aborting");
panic();
LOGERR(1, "exception " << e.what());
}
}
@@ -196,7 +194,7 @@ void ZMQReader::parse(char* data, size_t size)
using namespace rapidjson;
Document doc;
if (doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(value, end - value).HasParseError()) {
if (doc.Parse<kParseCommentsFlag | kParseTrailingCommasFlag>(value, end - value).HasParseError()) {
LOGWARN(1, "ZeroMQ message failed to parse, skipping it");
return;
}
+16 -49
View File
@@ -3,12 +3,7 @@ project(p2pool_tests)
option(STATIC_LIBS "Use locally built libuv and libzmq static libs" OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.6.0")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool_tests)
@@ -21,48 +16,7 @@ add_subdirectory(../external/src/RandomX RandomX)
set(LIBS ${LIBS} randomx)
add_definitions(-DWITH_RANDOMX)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(WARNING_FLAGS "")
set(OPTIMIZATION_FLAGS "-Ofast -s")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
if (WIN32)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
else()
if (STATIC_LINUX_BINARY)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
else()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
endif()
endif()
elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
set(WARNING_FLAGS "")
set(SECURITY_FLAGS "/GS /guard:cf")
set(OPTIMIZATION_FLAGS "/O2 /Oi /Ob2 /Ot /DNDEBUG /GL")
set(CMAKE_C_FLAGS_DEBUG "${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
set(CMAKE_CXX_FLAGS_DEBUG "${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
set(CMAKE_C_FLAGS_RELEASE "${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
set(CMAKE_CXX_FLAGS_RELEASE "${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
set(WARNING_FLAGS "")
set(OPTIMIZATION_FLAGS "-Ofast -funroll-loops -fmerge-all-constants")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
endif()
include(cmake/flags.cmake)
set(HEADERS
)
@@ -74,7 +28,7 @@ set(SOURCES
src/keccak_tests.cpp
src/main.cpp
src/pool_block_tests.cpp
src/varint_tests.cpp
src/util_tests.cpp
src/wallet_tests.cpp
../external/src/cryptonote/crypto-ops-data.c
../external/src/cryptonote/crypto-ops.c
@@ -160,6 +114,19 @@ endif()
add_definitions(/DZMQ_STATIC /DP2POOL_LOG_DISABLE)
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("int main(){ return __builtin_clzll(1);}" HAVE_BUILTIN_CLZLL)
check_cxx_source_compiles("#include <intrin.h>\n#pragma intrinsic(_BitScanReverse64)\nint main(){unsigned long r;_BitScanReverse64(&r,1);return r;}" HAVE_BITSCANREVERSE64)
if (HAVE_BUILTIN_CLZLL)
add_definitions(/DHAVE_BUILTIN_CLZLL)
endif()
if (HAVE_BITSCANREVERSE64)
add_definitions(/DHAVE_BITSCANREVERSE64)
endif()
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} ${LIBS})
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/crypto_tests.txt" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
+53
View File
@@ -0,0 +1,53 @@
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(GENERAL_FLAGS "-pthread")
set(WARNING_FLAGS "-Wall -Wextra")
set(OPTIMIZATION_FLAGS "-Ofast -s")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
if (WIN32)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
else()
if (STATIC_BINARY)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
else()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
endif()
endif()
elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
set(GENERAL_FLAGS "")
set(WARNING_FLAGS "/Wall /sdl")
set(SECURITY_FLAGS "/GS /guard:cf")
set(OPTIMIZATION_FLAGS "/O2 /Oi /Ob2 /Ot /DNDEBUG /GL")
set(CMAKE_C_FLAGS_DEBUG "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
set(CMAKE_CXX_FLAGS_DEBUG "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
set(CMAKE_C_FLAGS_RELEASE "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
set(CMAKE_CXX_FLAGS_RELEASE "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
set(GENERAL_FLAGS "-pthread")
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal")
set(OPTIMIZATION_FLAGS "-Ofast -funroll-loops -fmerge-all-constants")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
endif()
+1 -1
View File
@@ -55,7 +55,7 @@ TEST(hash, empty)
hash h;
ASSERT_EQ(h.empty(), true);
for (int i = 0; i < HASH_SIZE; ++i) {
for (size_t i = 0; i < HASH_SIZE; ++i) {
hash h2;
h2.h[i] = 1;
ASSERT_EQ(h2.empty(), false);
@@ -21,7 +21,7 @@
namespace p2pool {
TEST(varint, read_write)
TEST(util, varint)
{
std::vector<uint8_t> v;
v.reserve(16);
@@ -82,4 +82,12 @@ TEST(varint, read_write)
ASSERT_EQ(readVarint(buf2, buf2 + 1, check), nullptr);
}
TEST(util, bsr)
{
for (uint64_t i = 0, x = 1; i <= 63; ++i, x <<= 1) {
ASSERT_EQ(bsr(x), i);
ASSERT_EQ(bsr_reference(x), i);
}
}
}