Compare commits

...
50 Commits
Author SHA1 Message Date
SChernykh d40bb61da8 p2pool v2.3 2022-08-30 18:01:45 +02:00
SChernykh 4733d42932 TCPServer: more informative error messages 2022-08-27 10:52:02 +02:00
SChernykh e6030dcc3f Update googletest 2022-08-26 15:50:31 +02:00
SChernykh 6615fa67f5 API: fixed error messages 2022-08-26 09:39:44 +02:00
SChernykh dc4b88d08e API: use unique temp file names 2022-08-25 09:52:58 +02:00
SChernykh cc2abe3c30 Update rapidjson 2022-08-24 18:18:31 +02:00
SChernykh 70640b65ea Updated dependencies 2022-08-24 18:14:47 +02:00
SChernykh 4599d81a59 API: save to temp file first and then rename it 2022-08-24 17:28:53 +02:00
SChernykh 3ea5b575b7 JSON RPC: added more error checks 2022-08-24 15:17:23 +02:00
SChernykh b9eb66e2b3 Added a test for mini chain 2022-08-23 21:37:55 +02:00
SChernykh bde5b19c77 CI: added sync test 2022-08-23 19:49:41 +02:00
SChernykh cc60ab3d63 StratumServer: added scoring system for clients
Ban clients only after a few bad shares without good shares compensating them.
2022-08-22 11:07:11 +02:00
SChernykh 42f7e6f486 CI: fixed MSVC analysis 2022-08-19 22:16:48 +02:00
SChernykh 5f68b5923d CI: added Ubuntu 22.04 2022-08-18 16:43:19 +02:00
SChernykh 85abbdd699 Fixed NixOS files 2022-08-18 16:07:59 +02:00
SChernykh 3802886722 Fixed "unknown command" message 2022-08-17 19:37:18 +02:00
SChernykh d4e27a27cb Tweaked SideChain::get_missing_blocks() 2022-08-17 16:44:40 +02:00
SChernykh e882b91b06 Reduced lock contention in Cache::get_derivation() 2022-08-16 18:15:41 +02:00
SChernykh 4c8dbee79d More fine-grained locks in crypto cache 2022-08-15 18:39:28 +02:00
SChernykh 1438175477 CI: macos-10.15 is deprecated 2022-08-15 16:09:41 +02:00
SChernykh 30861bbf91 Speedup SideChain::get_outputs_blob() 2022-08-15 13:48:21 +02:00
SChernykh ea6a19a260 Fixed data race 2022-08-06 11:15:09 +02:00
SChernykh fc81fa8745 JSON RPC: added more error checking 2022-08-06 10:03:10 +02:00
SChernykh 948436e57e Fixed coverity issue 2022-08-05 15:27:39 +02:00
SChernykh 3dde71f1ff Fixed processing of long console commands 2022-08-05 09:09:14 +02:00
SChernykhandGitHub be0e220fe4 Merge pull request #190 from bladedoyle/monero_18
support monero v0.18.0.0 and minor fixes
2022-07-18 11:38:48 +02:00
Blade Doyle 2e9a209a78 support monero v0.18.0.0 and minor fixes 2022-07-17 08:03:29 -07:00
SChernykh 3ce4f06b6c Update CMakeLists.txt 2022-07-14 20:57:22 +02:00
SChernykh 555414cd51 SideChain: improved precalculation coverage 2022-07-14 19:29:41 +02:00
SChernykh c33033b2f4 Code cleanup 2022-07-14 14:28:06 +02:00
SChernykh 3c697c2d7e Fixed potential deadlock
Can't lock `m_minerLock` inside SideChain update
2022-07-14 13:15:35 +02:00
SChernykh 0e7c1aa481 SideChain: stricter checks for number of threads 2022-07-14 11:39:19 +02:00
SChernykh 7d5ff13f75 Fixed cppcheck warnings 2022-07-14 11:23:56 +02:00
SChernykh 02a8a512dc SideChain: precalculate tx pubkeys for faster sync 2022-07-14 10:28:25 +02:00
SChernykh 4733f46a28 Added uv_cond_init_checked 2022-07-14 09:02:08 +02:00
SChernykh 945de348f3 Rewrote get_view_tag without std::find_if 2022-07-12 09:50:49 +02:00
SChernykh 20dd9d7adc More efficient view tags caching 2022-07-11 16:42:26 +02:00
SChernykh c5bd184bbc P2PServer: don't deserialize the same block twice 2022-07-10 10:24:03 +02:00
SChernykhandGitHub ae161fac49 Merge pull request #181 from bladedoyle/more_docker_compose_configure
add more configurations; add statistics webserver
2022-07-10 09:15:11 +02:00
Blade Doyle 8eb780a8b5 add more configurations; add statistics webserver 2022-07-09 16:54:43 -07:00
SChernykh 1c33cc9f3d p2pool v2.2.1 2022-07-08 17:47:28 +02:00
SChernykh 9d692d5194 ConsoleCommands: check if console is available 2022-07-07 08:32:37 +02:00
SChernykh a7da7f932c Update libuv to Version 1.44.1 (Stable) 2022-07-06 20:22:02 +02:00
SChernykh 2e4f7adee4 TCPServer: added more checks during disconnects 2022-07-06 14:17:09 +02:00
SChernykh dcb822f812 ConsoleCommands: read from stdin using libuv 2022-07-05 22:08:18 +02:00
SChernykh 5da286fed4 JSON RPC: added HTTP headers 2022-07-05 14:54:04 +02:00
SChernykh 4ec0fe8d96 Fixed JSON RPC handling during shutdown 2022-07-05 14:34:22 +02:00
SChernykh 340a3e85c8 Fixed autodiff when running for a long time
16-bit wrap around wasn't processed correctly
2022-07-05 10:02:18 +02:00
SChernykh 64baf660bb JSON RPC: fixed shutdown on connect error 2022-07-05 08:36:16 +02:00
SChernykh 4f24f18b27 Updated Monero network upgrade info 2022-07-01 09:23:28 +02:00
67 changed files with 2228 additions and 707 deletions
+1
View File
@@ -1 +1,2 @@
**/build **/build
**/docker-compose
+7 -7
View File
@@ -10,8 +10,8 @@ jobs:
strategy: strategy:
matrix: matrix:
config: config:
- {os: ubuntu-latest, c: gcc-11, cpp: g++-11} - {os: ubuntu-20.04, c: gcc-11, cpp: g++-11}
- {os: ubuntu-18.04, c: gcc, cpp: g++} - {os: ubuntu-22.04, c: gcc-12, cpp: g++-12}
steps: steps:
- name: Install dependencies - name: Install dependencies
@@ -51,15 +51,15 @@ jobs:
build-ubuntu-static-libs: build-ubuntu-static-libs:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
steps: steps:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt update sudo apt update
sudo apt install -y git build-essential cmake autoconf libgss-dev gcc-11 g++-11 sudo apt install -y git build-essential cmake autoconf libgss-dev gcc-12 g++-12
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100 --slave /usr/bin/g++ g++ /usr/bin/g++-11 --slave /usr/bin/gcov gcov /usr/bin/gcov-11 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100 --slave /usr/bin/g++ g++ /usr/bin/g++-12 --slave /usr/bin/gcov gcov /usr/bin/gcov-12
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -115,7 +115,7 @@ jobs:
build-ubuntu-aarch64: build-ubuntu-aarch64:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
steps: steps:
- name: Install dependencies - name: Install dependencies
@@ -285,7 +285,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [macos-10.15, macos-11] os: [macos-11, macos-12]
steps: steps:
- name: Checkout repository - name: Checkout repository
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
# run: cmake --build ${{ env.build }} # run: cmake --build ${{ env.build }}
- name: Initialize MSVC Code Analysis - name: Initialize MSVC Code Analysis
uses: microsoft/msvc-code-analysis-action@47ecec99bf7ce0399ed85cbb5012b6f24eab5959 uses: microsoft/msvc-code-analysis-action@1e7491d1428ffa2e8aa70916d0d0309c7ca32baf
# Provide a unique ID to access the sarif output path # Provide a unique ID to access the sarif output path
id: run-analysis id: run-analysis
with: with:
+103
View File
@@ -0,0 +1,103 @@
name: Sync test
on: [push, pull_request]
jobs:
sync-test-ubuntu:
runs-on: ubuntu-22.04
steps:
- name: Install dependencies
run: |
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install -y git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev libcurl4-openssl-dev libidn2-0-dev gcc-12 g++-12
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -DDEV_TEST_SYNC=ON -DCMAKE_C_COMPILER=gcc-12 -DCMAKE_CXX_COMPILER=g++-12
make -j$(nproc)
- name: Run p2pool
timeout-minutes: 15
run: |
cd build
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 4
- name: Archive p2pool.log
uses: actions/upload-artifact@v2
with:
name: p2pool_ubuntu.log
path: build/p2pool.log
sync-test-macos:
runs-on: macos-12
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: recursive
- name: Install dependencies
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake autoconf libtool automake libuv zmq libpgm curl
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -DDEV_TEST_SYNC=ON
make -j3
- name: Run p2pool
timeout-minutes: 15
run: |
cd build
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 4
- name: Archive p2pool.log
uses: actions/upload-artifact@v2
with:
name: p2pool_macos.log
path: build/p2pool.log
sync-test-windows:
runs-on: windows-2022
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: recursive
- name: Setup cmake
uses: lukka/get-cmake@latest
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -G "Visual Studio 17 2022" -DDEV_TEST_SYNC=ON
& "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\msbuild" /m /p:Configuration=Release p2pool.vcxproj
- name: Run p2pool
timeout-minutes: 15
run: |
cd build/Release
./p2pool.exe --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 4
- name: Archive p2pool.log
uses: actions/upload-artifact@v2
with:
name: p2pool_windows.log
path: build/Release/p2pool.log
+22 -9
View File
@@ -1,10 +1,12 @@
cmake_minimum_required(VERSION 3.12) cmake_minimum_required(VERSION 3.6)
project(p2pool) project(p2pool)
option(STATIC_BINARY "Build static binary" OFF) option(STATIC_BINARY "Build static binary" OFF)
option(STATIC_LIBS "Link libuv and libzmq statically" 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) 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)
option(DEV_TEST_SYNC "[Developer only] Sync test, stop p2pool after sync is complete" OFF)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
@@ -15,6 +17,10 @@ if (WITH_RANDOMX)
set(LIBS randomx) set(LIBS randomx)
endif() endif()
if (DEV_TEST_SYNC)
add_definitions(-DDEV_TEST_SYNC)
endif()
include(cmake/flags.cmake) include(cmake/flags.cmake)
set(HEADERS set(HEADERS
@@ -77,10 +83,19 @@ if (WITH_RANDOMX)
set(SOURCES ${SOURCES} src/miner.cpp) set(SOURCES ${SOURCES} src/miner.cpp)
endif() endif()
if (NOT STATIC_BINARY AND NOT STATIC_LIBS)
include(FindCURL)
endif()
if (CURL_INCLUDE_DIRS)
include_directories(CURL_INCLUDE_DIRS)
else()
include_directories(external/src/curl/include)
endif()
include_directories(src) include_directories(src)
include_directories(external/src) include_directories(external/src)
include_directories(external/src/cryptonote) include_directories(external/src/cryptonote)
include_directories(external/src/curl/include)
include_directories(external/src/libuv/include) include_directories(external/src/libuv/include)
include_directories(external/src/cppzmq) include_directories(external/src/cppzmq)
include_directories(external/src/libzmq/include) include_directories(external/src/libzmq/include)
@@ -114,10 +129,15 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang
if (WIN32) if (WIN32)
find_library(CURL_LIBRARY_DEBUG NAMES libcurl.a PATHS "external/src/curl/lib/.libs" NO_DEFAULT_PATH) find_library(CURL_LIBRARY_DEBUG NAMES libcurl.a PATHS "external/src/curl/lib/.libs" NO_DEFAULT_PATH)
find_library(CURL_LIBRARY NAMES libcurl.a PATHS "external/src/curl/lib/.libs" NO_DEFAULT_PATH) find_library(CURL_LIBRARY NAMES libcurl.a PATHS "external/src/curl/lib/.libs" NO_DEFAULT_PATH)
else()
if (CURL_LIBRARIES)
set(CURL_LIBRARY_DEBUG ${CURL_LIBRARIES})
set(CURL_LIBRARY ${CURL_LIBRARIES})
else() else()
find_library(CURL_LIBRARY_DEBUG NAMES curl) find_library(CURL_LIBRARY_DEBUG NAMES curl)
find_library(CURL_LIBRARY NAMES curl) find_library(CURL_LIBRARY NAMES curl)
endif() endif()
endif()
find_library(SODIUM_LIBRARY sodium) find_library(SODIUM_LIBRARY sodium)
endif() endif()
@@ -145,14 +165,7 @@ endif()
add_definitions(/DZMQ_STATIC) add_definitions(/DZMQ_STATIC)
include(CheckSymbolExists)
set(CMAKE_REQUIRED_FLAGS "${GENERAL_FLAGS}") 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) include(CheckCXXSourceCompiles)
+7 -7
View File
@@ -66,12 +66,12 @@ First you need to find a pool share. This share will stay in PPLNS window for 21
## Monero version support ## Monero version support
Monero will undergo a network upgrade on July 16th, 2022 (block 2,668,888). In order to continue mining after that date, you must update both Monero and P2Pool software to the latest available versions as soon as they are released. Monero will undergo a network upgrade on August 13th, 2022 (block 2,688,888). In order to continue mining after that date, you must update both Monero and P2Pool software to the latest available versions as soon as they are released.
|Monero protocol version|Required Monero software version|Required P2Pool version |Monero protocol version|Required Monero software version|Required P2Pool version
|-|-|-| |-|-|-|
|v14 (active until July 16th, 2022)|v0.17.3.0 or newer|v1.0 or newer |v14 (active until August 13th, 2022)|v0.17.3.0 or newer|v1.0 or newer
|v15, v16 (active after July 16th, 2022)|v0.18.0.0 or newer|v2.0 or newer |v15, v16 (active after August 13th, 2022)|v0.18.0.0 or newer|v2.2 or newer
## How to mine on P2Pool ## How to mine on P2Pool
@@ -226,12 +226,12 @@ nix shell github:nixos/nix/master
Run the binary: Run the binary:
``` ```
nix run git+https://github.com/SChernykh/p2pool?ref=master nix run git+https://github.com/SChernykh/p2pool?ref=master&submodules=1
``` ```
Run the binary with arguments: Run the binary with arguments:
``` ```
nix run git+https://github.com/SChernykh/p2pool?ref=master -- --help nix run git+https://github.com/SChernykh/p2pool?ref=master&submodules=1 -- --help
``` ```
### Windows ### Windows
@@ -253,7 +253,7 @@ Alternatively, you can select "Clone a repository" within the GUI, then select "
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on your Mac: Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on your Mac:
``` ```
brew update && brew install git cmake libuv zmq libpgm brew update && brew install git cmake libuv zmq libpgm curl
git clone --recursive https://github.com/SChernykh/p2pool git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool cd p2pool
mkdir build && cd build mkdir build && cd build
@@ -265,7 +265,7 @@ make -j$(sysctl -n hw.logicalcpu)
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on FreeBSD: Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on FreeBSD:
``` ```
pkg install git cmake libuv libzmq4 pkg install git cmake libuv libzmq4 curl
git clone --recursive https://github.com/SChernykh/p2pool git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool cd p2pool
mkdir build && cd build mkdir build && cd build
+7 -4
View File
@@ -24,7 +24,7 @@ Make sure to set your own monero **Wallet Address**. The default is to donate m
#### Build the docker containers #### Build the docker containers
``` ```
docker compose build docker compose build --no-cache
``` ```
#### Run the node, pool, and CPU miner (or updated configuration) #### Run the node, pool, and CPU miner (or updated configuration)
@@ -34,21 +34,24 @@ docker compose up
#### Optional #### Optional
* Open ports 18080 (Monero p2p port) and 37889 (P2Pool p2p port) or 37888 (P2Pool-mini p2p port) in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine * Open ports 18080 (Monero p2p port) and 37889 (P2Pool p2p port) or 37888 (P2Pool-mini p2p port) in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine
* An XMRig CPU miner is included by default, but you can connect additional miners to this same p2pool node using port 3333 * An XMRig CPU miner is included by default, but you can connect additional miners to this same p2pool node using port 3333 (or alternate if configured) when you set it as "exposed" in the configuration
* Configure your kernel for maximum mining performance: [XMRig RandomX Optimization Guide](https://xmrig.com/docs/miner/randomx-optimization-guide) * Configure your kernel for maximum mining performance: [XMRig RandomX Optimization Guide](https://xmrig.com/docs/miner/randomx-optimization-guide)
* Small miners can mine on p2pool-mini by editing the .env file * Many optional configurations and customizations are available by running './configure'
#### Other usefull commands #### Other usefull commands
* You can **run everything in the background** by adding the "-d" argument to the "docker compose up" command: ```docker compose up -d``` * You can **run everything in the background** by adding the "-d" argument to the "docker compose up" command: ```docker compose up -d```
* You can **stop everything** with CTRL-C or ```docker compose down``` * You can **stop everything** with CTRL-C or ```docker compose down```
* You can see logs when running in the background for with the "docker logs" command: ```docker logs -f p2pool-xmrig``` or ```docker logs -f p2pool-p2pool``` or ```docker logs -f p2pool-monero``` * You can **update** by building new images with the ```--no-cache``` option. Example: ```docker compose build --no-cache``` or just update Monero with: ```docker compose build --no-cache monero``` followed by ```docker compose up```
* You can see logs when running in the background for with the "docker compose logs" command: ```docker compose logs -f```
* You can pause mining with: ```docker compose pause xmrig``` and resume mining with: ```docker compose unpause xmrig``` * You can pause mining with: ```docker compose pause xmrig``` and resume mining with: ```docker compose unpause xmrig```
* You can disable mining with: ```docker compose stop xmrig``` and re-enable mining with: ```docker compose start xmrig``` * You can disable mining with: ```docker compose stop xmrig``` and re-enable mining with: ```docker compose start xmrig```
* You can view your Server Statistics using a web browser if you enabled that feature in the configuration at: http://localhost:3380 (or alternate port as configured)
#### Uninstall #### Uninstall
Change to p2pool/docker-compose directory <br /> Change to p2pool/docker-compose directory <br />
Stop and remove all containers: ```docker compose down``` <br /> Stop and remove all containers: ```docker compose down``` <br />
Remove the p2pool data: ```docker volume rm p2pool``` <br /> Remove the p2pool data: ```docker volume rm p2pool``` <br />
Remove the p2pool-mini data: ```docker volume rm p2pool-mini``` <br />
Remove the monero data: ```docker volume rm monero``` Remove the monero data: ```docker volume rm monero```
File diff suppressed because it is too large Load Diff
+13
View File
@@ -3,14 +3,26 @@
"configure_monero": {{ configure_monero | tojson(indent=2) }}, "configure_monero": {{ configure_monero | tojson(indent=2) }},
"configure_xmrig": {{ configure_xmrig | tojson(indent=2) }}, "configure_xmrig": {{ configure_xmrig | tojson(indent=2) }},
"sidechain": {{ sidechain | tojson(indent=2) }}, "sidechain": {{ sidechain | tojson(indent=2) }},
"enable_statistics": {{ enable_statistics | tojson(indent=2) }},
"statistics_port": {{ statistics_port | tojson(indent=2) }},
"expose_stratum_port": {{ expose_stratum_port | tojson(indent=2) }}, "expose_stratum_port": {{ expose_stratum_port | tojson(indent=2) }},
"stratum_port": {{ stratum_port | tojson(indent=2) }}, "stratum_port": {{ stratum_port | tojson(indent=2) }},
"p2pool_log_level": {{ p2pool_log_level | int | tojson(indent=2) }}, "p2pool_log_level": {{ p2pool_log_level | int | tojson(indent=2) }},
"enable_autodiff": {{ enable_autodiff | tojson(indent=2) }}, "enable_autodiff": {{ enable_autodiff | tojson(indent=2) }},
"light_mode": {{ light_mode | tojson(indent=2) }},
"no_cache": {{ no_cache | tojson(indent=2) }},
"p2pool_options": {{ p2pool_options | tojson(indent=2) }}, "p2pool_options": {{ p2pool_options | tojson(indent=2) }},
"monero_version": {{ monero_version | tojson(indent=2) }}, "monero_version": {{ monero_version | tojson(indent=2) }},
"prune_blockchain": {{ prune_blockchain | tojson(indent=2) }}, "prune_blockchain": {{ prune_blockchain | tojson(indent=2) }},
"monero_log_level": {{ monero_log_level | int | tojson(indent=2) }}, "monero_log_level": {{ monero_log_level | int | tojson(indent=2) }},
"expose_rpc_port": {{ expose_rpc_port | tojson(indent=2) }},
"rpc_port": {{ rpc_port | tojson(indent=2) }},
"rpc_login": {{ rpc_login | tojson(indent=2) }},
"limit_data_rates": {{ limit_data_rates | tojson(indent=2) }},
"rate_limit_up": {{ rate_limit_up | tojson(indent=2) }},
"rate_limit_down": {{ rate_limit_down | tojson(indent=2) }},
"sync_pruned_blocks": {{ sync_pruned_blocks | tojson(indent=2) }},
"fast_sync": {{ fast_sync | tojson(indent=2) }},
"monero_options": {{ monero_options | tojson(indent=2) }}, "monero_options": {{ monero_options | tojson(indent=2) }},
"public_monero_node": {{ public_monero_node | tojson(indent=2) }}, "public_monero_node": {{ public_monero_node | tojson(indent=2) }},
"monero_node_login": {{ monero_node_login | tojson(indent=2) }}, "monero_node_login": {{ monero_node_login | tojson(indent=2) }},
@@ -18,5 +30,6 @@
"use_fixed_difficulty": {{ use_fixed_difficulty | tojson(indent=2) }}, "use_fixed_difficulty": {{ use_fixed_difficulty | tojson(indent=2) }},
"fixed_difficulty": {{ fixed_difficulty | int | tojson(indent=2) }}, "fixed_difficulty": {{ fixed_difficulty | int | tojson(indent=2) }},
"cpu_percent": {{ cpu_percent | int | tojson(indent=2) }}, "cpu_percent": {{ cpu_percent | int | tojson(indent=2) }},
"cpu_priority": {{ cpu_priority | int | tojson(indent=2) }},
"xmrig_options": {{ xmrig_options | tojson(indent=2) }} "xmrig_options": {{ xmrig_options | tojson(indent=2) }}
} }
+13
View File
@@ -3,14 +3,26 @@
"configure_monero": true, "configure_monero": true,
"configure_xmrig": true, "configure_xmrig": true,
"sidechain": [0], "sidechain": [0],
"enable_statistics": true,
"statistics_port": "3380",
"expose_stratum_port": false, "expose_stratum_port": false,
"stratum_port": "3333", "stratum_port": "3333",
"p2pool_log_level": 3, "p2pool_log_level": 3,
"enable_autodiff": true, "enable_autodiff": true,
"light_mode": false,
"no_cache": false,
"p2pool_options": "", "p2pool_options": "",
"monero_version": "latest", "monero_version": "latest",
"prune_blockchain": true, "prune_blockchain": true,
"monero_log_level": 0, "monero_log_level": 0,
"expose_rpc_port": false,
"rpc_port": "18081",
"rpc_login": "",
"limit_data_rates": false,
"rate_limit_up": "2048",
"rate_limit_down": "8192",
"sync_pruned_blocks": false,
"fast_sync": false,
"monero_options": "", "monero_options": "",
"public_monero_node": "", "public_monero_node": "",
"monero_node_login": "", "monero_node_login": "",
@@ -18,5 +30,6 @@
"use_fixed_difficulty": true, "use_fixed_difficulty": true,
"fixed_difficulty": 500000, "fixed_difficulty": 500000,
"cpu_percent": 100, "cpu_percent": 100,
"cpu_priority": 2,
"xmrig_options": "" "xmrig_options": ""
} }
+58 -1
View File
@@ -7,7 +7,12 @@ networks:
volumes: volumes:
p2pool: p2pool:
{% if sidechain[0] == 0 %}
name: p2pool name: p2pool
{% else %}
name: p2pool-mini
{% endif %}
{% if configure_monero == True %} {% if configure_monero == True %}
monero: monero:
name: monero name: monero
@@ -20,6 +25,7 @@ services:
container_name: p2pool-p2pool container_name: p2pool-p2pool
networks: networks:
- p2pool - p2pool
privileged: true
ports: ports:
{% if sidechain[0] == 0 %} {% if sidechain[0] == 0 %}
- 37888:37888/tcp - 37888:37888/tcp
@@ -27,7 +33,7 @@ services:
- 37889:37889/tcp - 37889:37889/tcp
{% endif %} {% endif %}
{% if expose_stratum_port == True %} {% if expose_stratum_port == True %}
- {{ stratum_port | int }}:3333/tcp - {{ stratum_port }}:3333/tcp
{% endif %} {% endif %}
volumes: volumes:
- p2pool:/home/p2pool/.p2pool:rw - p2pool:/home/p2pool/.p2pool:rw
@@ -53,11 +59,42 @@ services:
{% if enable_autodiff == False %} {% if enable_autodiff == False %}
--no-autodiff --no-autodiff
{% endif %} {% endif %}
{% if enable_statistics == True %}
--local-api
--data-api /home/p2pool/.p2pool
{% endif %}
{% if light_mode == True %}
--light-mode
{% endif %}
{% if no_cache == True %}
--no-cache
{% endif %}
{% if rpc_login != "" %}
--rpc-login {{ rpc_login }}
{% endif %}
{% if p2pool_options != "" %} {% if p2pool_options != "" %}
{{ p2pool_options }} {{ p2pool_options }}
{% endif %} {% endif %}
{% if enable_statistics == True %}
statistics:
image: statistics:latest
build:
context: statistics
container_name: p2pool-statistics
networks:
- p2pool
ports:
- {{ statistics_port }}:80/tcp
volumes:
- p2pool:/data:ro
depends_on:
- p2pool
restart: unless-stopped
{% endif %}
{% if configure_monero == True %} {% if configure_monero == True %}
monero: monero:
image: monero:latest image: monero:latest
@@ -70,6 +107,9 @@ services:
- p2pool - p2pool
ports: ports:
- 18080:18080/tcp - 18080:18080/tcp
{% if expose_rpc_port == True %}
- {{ rpc_port }}:18081/tcp
{% endif %}
volumes: volumes:
- monero:/home/monero/.bitmonero:rw - monero:/home/monero/.bitmonero:rw
- /dev/null:/home/monero/.bitmonero/bitmonero.log:rw - /dev/null:/home/monero/.bitmonero/bitmonero.log:rw
@@ -84,10 +124,26 @@ services:
--p2p-bind-port=18080 --p2p-bind-port=18080
--rpc-bind-ip=0.0.0.0 --rpc-bind-ip=0.0.0.0
--rpc-bind-port=18081 --rpc-bind-port=18081
--restricted-rpc
--confirm-external-bind --confirm-external-bind
--log-level={{ monero_log_level | int }} --log-level={{ monero_log_level | int }}
{% if prune_blockchain == True %} {% if prune_blockchain == True %}
--prune-blockchain --prune-blockchain
{% if sync_pruned_blocks == True %}
--sync-pruned-blocks
{% endif %}
{% endif %}
{% if rpc_login != "" %}
--rpc-login {{ rpc_login }}
{% endif %}
{% if limit_data_rates == True %}
--limit-rate-up {{ rate_limit_up }}
--limit-rate-down {{ rate_limit_down }}
{% endif %}
{% if fast_sync == True %}
--fast-block-sync=1
{% else %}
--fast-block-sync=0
{% endif %} {% endif %}
{% if monero_options != "" %} {% if monero_options != "" %}
{{ monero_options }} {{ monero_options }}
@@ -119,6 +175,7 @@ services:
-u {{ xmrig_username }} -u {{ xmrig_username }}
{% endif %} {% endif %}
--cpu-max-threads-hint={{ cpu_percent | int }} --cpu-max-threads-hint={{ cpu_percent | int }}
--cpu-priority={{ cpu_priority | int }}
{% if xmrig_options != "" %} {% if xmrig_options != "" %}
{{ xmrig_options }} {{ xmrig_options }}
{% endif %} {% endif %}
+39
View File
@@ -1,6 +1,45 @@
#!/bin/bash #!/bin/bash
echo "" echo ""
echo "" echo ""
echo "Verifying Requirements:"
DOCKER_VER=$(docker version -f "{{.Server.Version}}" 2> /dev/null)
if [ -z "$DOCKER_VER" ]; then
echo "Docker not found; install it: https://docs.docker.com/engine/install/"
exit 1
fi
if [ "$(echo "$DOCKER_VER"| cut -d'.' -f 1)" -ge 19 ] && \
[ "$(echo "$DOCKER_VER"| cut -d'.' -f 2)" -ge 0 ] && \
[ "$(echo "$DOCKER_VER"| cut -d'.' -f 3)" -ge 3 ]; then
echo "Docker Found; OK"
else
echo "Docker version less than 19.0.3; upgrade it: https://docs.docker.com/engine/install/"
exit 1
fi
docker compose version 2>&1 > /dev/null
COMPOSE_PLUGIN_RC=$?
docker-compose --version 2>&1 > /dev/null
COMPOSE_CLI_RC=$?
if [ "$COMPOSE_PLUGIN_RC" -eq 0 ] || [ "$COMPOSE_CLI_RC" -eq 0 ]; then
echo "Docker Compose found; OK"
if [ "$COMPOSE_PLUGIN_RC" -eq 0 ]; then
COMPOSE_COMMAND="docker compose"
else
COMPOSE_COMMAND="docker-compose"
fi
else
echo "Docker Compose not found; install it: https://docs.docker.com/compose/install/compose-plugin/"
exit 1
fi
echo ""
echo ""
echo "Building and Running P2Pool docker-compose Configuration" echo "Building and Running P2Pool docker-compose Configuration"
docker build -t p2pool_config:latest cfg docker build -t p2pool_config:latest cfg
docker run -it --rm -v $PWD:/docker-compose --user $(id -u):$(id -g) p2pool_config:latest docker run -it --rm -v $PWD:/docker-compose --user $(id -u):$(id -g) p2pool_config:latest
CONFIGURE_RC=$?
echo ""
echo ""
if [ "$CONFIGURE_RC" -eq 0 ]; then
echo "P2Pool is configured. Start the project with: $COMPOSE_COMMAND up --build -d"
else
exit 1
fi
+13
View File
@@ -5,14 +5,26 @@
"sidechain": [ "sidechain": [
0 0
], ],
"enable_statistics": true,
"statistics_port": "3380",
"expose_stratum_port": false, "expose_stratum_port": false,
"stratum_port": "3333", "stratum_port": "3333",
"p2pool_log_level": 3, "p2pool_log_level": 3,
"enable_autodiff": true, "enable_autodiff": true,
"light_mode": false,
"no_cache": false,
"p2pool_options": "", "p2pool_options": "",
"monero_version": "latest", "monero_version": "latest",
"prune_blockchain": true, "prune_blockchain": true,
"monero_log_level": 0, "monero_log_level": 0,
"expose_rpc_port": false,
"rpc_port": "18081",
"rpc_login": "",
"limit_data_rates": false,
"rate_limit_up": "2048",
"rate_limit_down": "8192",
"sync_pruned_blocks": false,
"fast_sync": false,
"monero_options": "", "monero_options": "",
"public_monero_node": "", "public_monero_node": "",
"monero_node_login": "", "monero_node_login": "",
@@ -20,5 +32,6 @@
"use_fixed_difficulty": true, "use_fixed_difficulty": true,
"fixed_difficulty": 500000, "fixed_difficulty": 500000,
"cpu_percent": 100, "cpu_percent": 100,
"cpu_priority": 2,
"xmrig_options": "" "xmrig_options": ""
} }
+38
View File
@@ -7,8 +7,11 @@ networks:
volumes: volumes:
p2pool: p2pool:
name: p2pool name: p2pool
monero: monero:
name: monero name: monero
@@ -20,6 +23,7 @@ services:
container_name: p2pool-p2pool container_name: p2pool-p2pool
networks: networks:
- p2pool - p2pool
privileged: true
ports: ports:
- 37888:37888/tcp - 37888:37888/tcp
@@ -43,6 +47,31 @@ services:
--local-api
--data-api /home/p2pool/.p2pool
statistics:
image: statistics:latest
build:
context: statistics
container_name: p2pool-statistics
networks:
- p2pool
ports:
- 3380:80/tcp
volumes:
- p2pool:/data:ro
depends_on:
- p2pool
restart: unless-stopped
@@ -57,6 +86,7 @@ services:
- p2pool - p2pool
ports: ports:
- 18080:18080/tcp - 18080:18080/tcp
volumes: volumes:
- monero:/home/monero/.bitmonero:rw - monero:/home/monero/.bitmonero:rw
- /dev/null:/home/monero/.bitmonero/bitmonero.log:rw - /dev/null:/home/monero/.bitmonero/bitmonero.log:rw
@@ -71,6 +101,7 @@ services:
--p2p-bind-port=18080 --p2p-bind-port=18080
--rpc-bind-ip=0.0.0.0 --rpc-bind-ip=0.0.0.0
--rpc-bind-port=18081 --rpc-bind-port=18081
--restricted-rpc
--confirm-external-bind --confirm-external-bind
--log-level=0 --log-level=0
@@ -80,6 +111,12 @@ services:
--fast-block-sync=0
xmrig: xmrig:
image: xmrig:latest image: xmrig:latest
@@ -102,4 +139,5 @@ services:
-u p2pool -u p2pool
--cpu-max-threads-hint=100 --cpu-max-threads-hint=100
--cpu-priority=2
+27 -48
View File
@@ -1,80 +1,59 @@
FROM ubuntu:20.04 # Multistage docker build, requires docker 17.05
# builder stage
FROM ubuntu:20.04 as builder
ARG MONERO_GIT_TAG="latest" ARG MONERO_GIT_TAG="latest"
RUN set -e && \ RUN set -e && \
apt-get update -q -y --no-install-recommends && \ apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \ DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
git \ automake \
ca-certificates \ autotools-dev \
bsdmainutils \
build-essential \ build-essential \
pkg-config \ ca-certificates \
cmake \
libuv1-dev \
libzmq3-dev \
libsodium-dev \
libpgm-dev \
libnorm-dev \
libgss-dev \
libssl-dev \
libzmq3-dev \
libunbound-dev \
libsodium-dev \
libunwind8-dev \
liblzma-dev \
libreadline6-dev \
libldns-dev \
libexpat1-dev \
libpgm-dev \
qttools5-dev-tools \
libhidapi-dev \
libusb-1.0-0-dev \
libprotobuf-dev \
protobuf-compiler \
libudev-dev \
libboost-chrono-dev \
libboost-date-time-dev \
libboost-filesystem-dev \
libboost-locale-dev \
libboost-program-options-dev \
libboost-regex-dev \
libboost-serialization-dev \
libboost-system-dev \
libboost-thread-dev \
ccache \ ccache \
doxygen \ cmake \
graphviz curl \
git \
libtool \
pkg-config \
gperf
WORKDIR /usr/src
WORKDIR /src
RUN git clone --recursive https://github.com/monero-project/monero && \ RUN git clone --recursive https://github.com/monero-project/monero && \
cd monero && \ cd monero && \
if [ "x$MONERO_GIT_TAG" = "xlatest" ]; then MONERO_GIT_TAG=$(git describe --tags $(git rev-list --tags --max-count=1)); fi && \ if [ "x$MONERO_GIT_TAG" = "xlatest" ]; then MONERO_GIT_TAG=$(git describe --tags $(git rev-list --tags --max-count=1)); fi && \
git checkout $MONERO_GIT_TAG && \ git checkout $MONERO_GIT_TAG && \
git submodule sync && git submodule update --init --force --recursive && \ git submodule sync && git submodule update --init --force --recursive && \
make release-static -j$(nproc) make -j$(nproc) depends target=$(contrib/depends/config.guess)
# --- # ---
# runtime stage
FROM ubuntu:20.04 FROM ubuntu:20.04
COPY --from=0 /usr/src/monero/build/Linux/*/release/bin/* / COPY --from=0 /src/monero/build/*/release/bin /usr/local/bin/
RUN set -e && \ RUN set -e && \
apt-get update -q -y --no-install-recommends && \ apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \ DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
libgssapi-krb5-2 \ ca-certificates \
netcat \
&& \ && \
apt-get clean && \ apt-get clean && \
rm -rf /var/lib/apt rm -rf /var/lib/apt
RUN groupadd -r monero -g 1000 && \ RUN groupadd -r monero -g 1000 && \
useradd -u 1000 -r -g monero -s /sbin/nologin -c "monero node user" monero adduser --uid 1000 --gid 1000 --system --disabled-password monero && \
RUN mkdir -p /home/monero/.bitmonero && \ mkdir -p /home/monero/.bitmonero && \
chown monero.monero /home/monero /home/monero/.bitmonero chown -R monero:monero /home/monero/.bitmonero
USER monero USER monero
WORKDIR /home/monero
EXPOSE 18080 18081 18083 EXPOSE 18080 18081 18083
VOLUME /home/monero/.bitmonero VOLUME /home/monero/.bitmonero
WORKDIR /home/monero ENTRYPOINT ["/usr/local/bin/monerod"]
ENTRYPOINT ["/monerod"]
+8
View File
@@ -0,0 +1,8 @@
FROM python:slim
COPY app /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["/app/p2pool_statistics.py"]
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
import json
from datetime import datetime
from prefixed import Float
import humanfriendly
from flask import Flask, render_template
app = Flask(__name__)
##
# Add some custom jinja filters
def timeago(value):
"""Format a date time to human friendly time ago"""
if value is None:
return ""
if type(value) is int:
dt = datetime.fromtimestamp(value).replace(microsecond=0)
now = datetime.now().replace(microsecond=0)
return humanfriendly.format_timespan(now - dt)
app.jinja_env.filters["timeago"] = timeago
def human_numbers(value):
"""Format a number in human readable format"""
if value is None:
return ""
return "{:!.3h}".format(Float(value))
app.jinja_env.filters["humanize"] = human_numbers
##
# Get Pool Instance Birth Date
def birthdate():
try:
with open("/data/p2pool.blocks") as reader:
first_block = reader.readline().rstrip()
bday_ts = int(first_block.split(" ")[0])
bday = timeago(bday_ts)
return bday
except Exception as e:
return "unknown time"
##
# The App Routes
@app.route("/")
def render():
try:
my_bday = birthdate()
with open("/data/stats_mod", "r") as reader:
stats_mod = json.loads(reader.read())
with open("/data/pool/stats", "r") as reader:
pool_stats = json.loads(reader.read())
with open("/data/network/stats", "r") as reader:
network_stats = json.loads(reader.read())
with open("/data/local/stats", "r") as reader:
local_stats = json.loads(reader.read())
return render_template(
"index.html",
my_bday=my_bday,
stats_mod=stats_mod,
pool_stats=pool_stats,
network_stats=network_stats,
local_stats=local_stats,
)
except Exception as e:
return render_template("oops.html", error=str(e))
##
# main()
if __name__ == "__main__":
app.run(debug=False, host="0.0.0.0", port=80)
@@ -0,0 +1,3 @@
flask
prefixed
humanfriendly
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because one or more lines are too long
@@ -0,0 +1,158 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="{{url_for('static', filename='bootstrap.min.css')}}">
<title>Monero P2Pool Server Statistics</title>
</head>
<body style="font-size:85%;">
<script src="{{url_for('static', filename='jquery-3.2.1.slim.min.js')}}"></script>
<script src="{{url_for('static', filename='popper.min.js')}}"></script>
<script src="{{url_for('static', filename='bootstrap.min.js')}}"></script>
<div style="text-align:center;">
<img src="{{url_for('static', filename='monero-symbol-480.png')}}" width="75px" height="75px" alt="Monero"/>
<h1 style="color: #FF6600;">P2Pool Server Statistics</h1>
</div>
<div class="card-group">
<div class="col-sm-4 grid-margin stretch-card">
<div class="card">
<div class="card-header text-black mb-1 pb-1" style="text-align: center; background-color: #FF6600;"><h2>Local Pool</h2></div>
<div class="card-body mb-0 pb-0">
<h6 class="card-subtitle text-muted" style="font-size:80%;">(note: stats reset on restart)</h6>
<div class="table-responsive table-hover table-condensed table-striped">
<table class="table">
<tbody>
<tr>
<td>Hashrate 15 Minutes</td>
<td>{{ local_stats["hashrate_15m"]|humanize }}H/s</td>
</tr>
<tr>
<td>Hashrate 1 Hour</td>
<td>{{ local_stats["hashrate_1h"]|humanize }}H/s</td>
</tr>
<tr>
<td>Hashrate 24 Hours</td>
<td>{{ local_stats["hashrate_24h"]|humanize }}H/s</td>
</tr>
<tr>
<td>Shares Found</td>
<td>{{ local_stats["shares_found"] }}</td>
</tr>
<tr>
<td>Current Effort</td>
<td>{{ local_stats["current_effort"] }}%</td>
</tr>
<tr>
<td>Average Effort</td>
<td>{{ local_stats["average_effort"] }}%</td>
</tr>
<tr>
<td>Total Hashes</td>
<td>{{ local_stats["total_hashes"]|humanize }}</td>
</tr>
<tr>
<td>Miner Connections</td>
<td>{{ local_stats["incoming_connections"] }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-sm-4 grid-margin stretch-card">
<div class="card">
<div class="card-header text-black mb-1 pb-1" style="text-align: center; background-color: #FF6600;"><h2>Global Pool</h2></div>
<div class="card-body mb-0 pb-0">
<h6 class="card-subtitle text-muted" style="font-size:80%;">&nbsp;</h6>
<div class="table-responsive table-hover table-condensed table-striped">
<table class="table">
<tbody>
<tr>
<td>Hash Rate</td>
<td>{{ pool_stats["pool_statistics"]["hashRate"]|humanize }}H/s</td>
</tr>
<tr>
<td>Round Hashes</td>
<td>{{ stats_mod["pool"]["roundHashes"]|humanize }}</td>
</tr>
<tr>
<td>Last Block Found</td>
<td>{{ pool_stats["pool_statistics"]["lastBlockFound"] }} <br>
{{ pool_stats["pool_statistics"]["lastBlockFoundTime"]|timeago }} ago</td>
</tr>
<tr>
<td>Payout Method</td>
<td>{{ pool_stats["pool_list"]|join(',') }}<br>2160 block window (~6 hours)</td>
</tr>
</tbody>
</table>
</div>
<h6 class="card-subtitle text-muted" style="font-size:80%;">(since instance birth: {{ my_bday }} ago)</h6>
<div class="table-responsive table-hover table-condensed table-striped">
<table class="table">
<tbody>
<tr>
<td>Total Hashes</td>
<td>{{ pool_stats["pool_statistics"]["totalHashes"]|humanize }}</td>
</tr>
<tr>
<td>Blocks Found</td>
<td>{{ pool_stats["pool_statistics"]["totalBlocksFound"] }}</td>
</tr>
<tr>
<td>Known Miners</td>
<td>{{ pool_stats["pool_statistics"]["miners"] }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-sm-4 grid-margin stretch-card">
<div class="card">
<div class="card-header text-black mb-1 pb-1" style="text-align: center; background-color: #FF6600;"><h2>Monero Network</h2></div>
<div class="card-body mb-0 pb-0">
<h6 class="card-subtitle text-muted" style="font-size:80%;">&nbsp;</h6>
<div class="table-responsive table-hover table-condensed table-striped">
<table class="table">
<tbody>
<tr>
<td>Height</td>
<td>{{ network_stats["height"] }}<br>
{{ network_stats["timestamp"]|timeago }} ago</td>
</tr>
<tr>
<td>Difficulty</td>
<td>{{ network_stats["difficulty"]|humanize }}</td>
</tr>
<tr>
<td>Reward</td>
<td>0.{{ network_stats["reward"] }} Monero</td>
</tr>
<tr>
<td>Head&nbsp;Hash</td>
<td>{{ network_stats["hash"] }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
window.setInterval('refresh()', 30000); // Call refresh function every 30000 milliseconds (30 seconds).
function refresh() {
window .location.reload();
}
</script>
</body>
</html>
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Oops</title>
</head>
<body>
<h2>Ooops, something went wrong:</h2>
{{ error }}
(Maybe you need to wait a few minutes for p2pool to start and sync for the first time?)
</body>
</html>
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 -1
View File
@@ -23,7 +23,7 @@
}; };
buildInputs = builtins.attrValues { buildInputs = builtins.attrValues {
inherit (pkgs) libuv zeromq libsodium gss; inherit (pkgs) libuv zeromq libsodium gss curl;
}; };
installPhase = '' installPhase = ''
+1 -1
View File
@@ -198,7 +198,7 @@ void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
continue; continue;
} }
if (block.deserialize(data + sizeof(uint32_t), n, side_chain) == 0) { if (block.deserialize(data + sizeof(uint32_t), n, side_chain, uv_default_loop_checked()) == 0) {
server.add_cached_block(block); server.add_cached_block(block);
++blocks_loaded; ++blocks_loaded;
} }
+2 -2
View File
@@ -548,7 +548,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end()); buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end());
PoolBlock check; PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain()); const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain(), nullptr);
if (result != 0) { if (result != 0) {
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result); LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
} }
@@ -1077,7 +1077,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end()); buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end());
PoolBlock check; PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), side_chain); const int result = check.deserialize(buf.data(), buf.size(), side_chain, nullptr);
if (result != 0) { if (result != 0) {
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result); LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
} }
+103 -66
View File
@@ -27,35 +27,61 @@
#include "side_chain.h" #include "side_chain.h"
#include <iostream> #include <iostream>
#ifdef HAVE_PTHREAD_CANCEL
#include <pthread.h>
#endif
static constexpr char log_category_prefix[] = "ConsoleCommands "; static constexpr char log_category_prefix[] = "ConsoleCommands ";
namespace p2pool { namespace p2pool {
bool ConsoleCommands::stopped = false;
ConsoleCommands::ConsoleCommands(p2pool* pool) ConsoleCommands::ConsoleCommands(p2pool* pool)
: m_pool(pool) : m_pool(pool)
, m_loop{}
, m_shutdownAsync{}
, m_tty{}
, m_loopThread{}
, m_readBuf{}
, m_readBufInUse(false)
{ {
m_worker = new std::thread(&ConsoleCommands::run, this); if (uv_guess_handle(0) != UV_TTY) {
LOGERR(1, "tty is not available");
throw std::exception();
}
int err = uv_loop_init(&m_loop);
if (err) {
LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
throw std::exception();
}
err = uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
throw std::exception();
}
m_shutdownAsync.data = this;
err = uv_tty_init(&m_loop, &m_tty, 0, 1);
if (err) {
LOGERR(1, "uv_tty_init failed, error " << uv_err_name(err));
throw std::exception();
}
m_tty.data = this;
err = uv_read_start(reinterpret_cast<uv_stream_t*>(&m_tty), allocCallback, stdinReadCallback);
if (err) {
LOGERR(1, "uv_read_start failed, error " << uv_err_name(err));
throw std::exception();
}
err = uv_thread_create(&m_loopThread, loop, this);
if (err) {
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
throw std::exception();
}
} }
ConsoleCommands::~ConsoleCommands() ConsoleCommands::~ConsoleCommands()
{ {
stopped = true; uv_async_send(&m_shutdownAsync);
uv_thread_join(&m_loopThread);
#ifdef _WIN32
TerminateThread(reinterpret_cast<HANDLE>(m_worker->native_handle()), 0);
#elif defined HAVE_PTHREAD_CANCEL
pthread_cancel(m_worker->native_handle());
#endif
m_worker->join();
delete m_worker;
LOGINFO(1, "stopped"); LOGINFO(1, "stopped");
} }
@@ -67,7 +93,7 @@ typedef struct strconst {
#define STRCONST(x) {x, sizeof(x)-1} #define STRCONST(x) {x, sizeof(x)-1}
#define STRCNULL {NULL, 0} #define STRCNULL {NULL, 0}
typedef int (cmdfunc)(p2pool *pool, const char *args); typedef void (cmdfunc)(p2pool *pool, const char *args);
typedef struct cmd { typedef struct cmd {
strconst name; strconst name;
@@ -101,16 +127,15 @@ static cmd cmds[] = {
{ STRCNULL, NULL, NULL, NULL } { STRCNULL, NULL, NULL, NULL }
}; };
static int do_help(p2pool * /* m_pool */, const char * /* args */) static void do_help(p2pool * /* m_pool */, const char * /* args */)
{ {
LOGINFO(0, "List of commands"); LOGINFO(0, "List of commands");
for (int i = 0; cmds[i].name.len; ++i) { for (int i = 0; cmds[i].name.len; ++i) {
LOGINFO(0, cmds[i].name.str << " " << cmds[i].arg << "\t" << cmds[i].descr); LOGINFO(0, cmds[i].name.str << " " << cmds[i].arg << "\t" << cmds[i].descr);
} }
return 0;
} }
static int do_status(p2pool *m_pool, const char * /* args */) static void do_status(p2pool *m_pool, const char * /* args */)
{ {
m_pool->side_chain().print_status(); m_pool->side_chain().print_status();
if (m_pool->stratum_server()) { if (m_pool->stratum_server()) {
@@ -123,51 +148,45 @@ static int do_status(p2pool *m_pool, const char * /* args */)
m_pool->print_miner_status(); m_pool->print_miner_status();
#endif #endif
bkg_jobs_tracker.print_status(); bkg_jobs_tracker.print_status();
return 0;
} }
static int do_loglevel(p2pool * /* m_pool */, const char *args) static void do_loglevel(p2pool * /* m_pool */, const char *args)
{ {
int level = strtol(args, nullptr, 10); int level = strtol(args, nullptr, 10);
level = std::min(std::max(level, 0), log::MAX_GLOBAL_LOG_LEVEL); level = std::min(std::max(level, 0), log::MAX_GLOBAL_LOG_LEVEL);
log::GLOBAL_LOG_LEVEL = level; log::GLOBAL_LOG_LEVEL = level;
LOGINFO(0, "log level set to " << level); LOGINFO(0, "log level set to " << level);
return 0;
} }
static int do_addpeers(p2pool *m_pool, const char *args) static void do_addpeers(p2pool *m_pool, const char *args)
{ {
if (m_pool->p2p_server()) { if (m_pool->p2p_server()) {
m_pool->p2p_server()->connect_to_peers(args); m_pool->p2p_server()->connect_to_peers(args);
} }
return 0;
} }
static int do_droppeers(p2pool *m_pool, const char * /* args */) static void do_droppeers(p2pool *m_pool, const char * /* args */)
{ {
if (m_pool->p2p_server()) { if (m_pool->p2p_server()) {
m_pool->p2p_server()->drop_connections(); m_pool->p2p_server()->drop_connections();
} }
return 0;
} }
static int do_showpeers(p2pool* m_pool, const char* /* args */) static void do_showpeers(p2pool* m_pool, const char* /* args */)
{ {
if (m_pool->p2p_server()) { if (m_pool->p2p_server()) {
m_pool->p2p_server()->show_peers(); m_pool->p2p_server()->show_peers();
} }
return 0;
} }
static int do_showworkers(p2pool* m_pool, const char* /* args */) static void do_showworkers(p2pool* m_pool, const char* /* args */)
{ {
if (m_pool->stratum_server()) { if (m_pool->stratum_server()) {
m_pool->stratum_server()->show_workers(); m_pool->stratum_server()->show_workers();
} }
return 0;
} }
static int do_showbans(p2pool* m_pool, const char* /* args */) static void do_showbans(p2pool* m_pool, const char* /* args */)
{ {
if (m_pool->stratum_server()) { if (m_pool->stratum_server()) {
m_pool->stratum_server()->print_bans(); m_pool->stratum_server()->print_bans();
@@ -175,87 +194,105 @@ static int do_showbans(p2pool* m_pool, const char* /* args */)
if (m_pool->p2p_server()) { if (m_pool->p2p_server()) {
m_pool->p2p_server()->print_bans(); m_pool->p2p_server()->print_bans();
} }
return 0;
} }
static int do_outpeers(p2pool* m_pool, const char* args) static void do_outpeers(p2pool* m_pool, const char* args)
{ {
if (m_pool->p2p_server()) { if (m_pool->p2p_server()) {
m_pool->p2p_server()->set_max_outgoing_peers(strtoul(args, nullptr, 10)); m_pool->p2p_server()->set_max_outgoing_peers(strtoul(args, nullptr, 10));
LOGINFO(0, "max outgoing peers set to " << m_pool->p2p_server()->max_outgoing_peers()); LOGINFO(0, "max outgoing peers set to " << m_pool->p2p_server()->max_outgoing_peers());
} }
return 0;
} }
static int do_inpeers(p2pool* m_pool, const char* args) static void do_inpeers(p2pool* m_pool, const char* args)
{ {
if (m_pool->p2p_server()) { if (m_pool->p2p_server()) {
m_pool->p2p_server()->set_max_incoming_peers(strtoul(args, nullptr, 10)); m_pool->p2p_server()->set_max_incoming_peers(strtoul(args, nullptr, 10));
LOGINFO(0, "max incoming peers set to " << m_pool->p2p_server()->max_incoming_peers()); LOGINFO(0, "max incoming peers set to " << m_pool->p2p_server()->max_incoming_peers());
} }
return 0;
} }
#ifdef WITH_RANDOMX #ifdef WITH_RANDOMX
static int do_start_mining(p2pool* m_pool, const char* args) static void do_start_mining(p2pool* m_pool, const char* args)
{ {
uint32_t threads = strtoul(args, nullptr, 10); uint32_t threads = strtoul(args, nullptr, 10);
threads = std::min(std::max(threads, 1u), 64u); threads = std::min(std::max(threads, 1u), 64u);
m_pool->start_mining(threads); m_pool->start_mining(threads);
return 0;
} }
static int do_stop_mining(p2pool* m_pool, const char* /*args*/) static void do_stop_mining(p2pool* m_pool, const char* /*args*/)
{ {
m_pool->stop_mining(); m_pool->stop_mining();
return 0;
} }
#endif #endif
static int do_exit(p2pool *m_pool, const char * /* args */) static void do_exit(p2pool *m_pool, const char * /* args */)
{ {
bkg_jobs_tracker.wait(); bkg_jobs_tracker.wait();
m_pool->stop(); m_pool->stop();
return 1;
} }
void ConsoleCommands::run() void ConsoleCommands::allocCallback(uv_handle_t* handle, size_t /*suggested_size*/, uv_buf_t* buf)
{ {
LOGINFO(1, "started"); ConsoleCommands* pThis = static_cast<ConsoleCommands*>(handle->data);
std::string command; if (pThis->m_readBufInUse) {
command.reserve(1024); buf->len = 0;
buf->base = nullptr;
return;
}
buf->len = sizeof(pThis->m_readBuf);
buf->base = pThis->m_readBuf;
pThis->m_readBufInUse = true;
}
void ConsoleCommands::stdinReadCallback(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
{
ConsoleCommands* pThis = static_cast<ConsoleCommands*>(stream->data);
if (nread > 0) {
std::string& command = pThis->m_command;
command.append(buf->base, nread);
do { do {
std::getline(std::cin, command); size_t k = command.find_first_of("\r\n");
if (k == std::string::npos) {
if (std::cin.eof()) { break;
LOGINFO(1, "EOF, stopping");
return;
}
if (stopped) {
LOGINFO(1, "stopping");
return;
} }
command[k] = '\0';
cmd* c = cmds; cmd* c = cmds;
for (; c->name.len; ++c) { for (; c->name.len; ++c) {
if (!strncmp(command.c_str(), c->name.str, c->name.len)) { if (!strncmp(command.c_str(), c->name.str, c->name.len)) {
const char *args = command.c_str() + c->name.len + 1; const char* args = (c->name.len + 1 <= k) ? (command.c_str() + c->name.len + 1) : "";
if (c->func(m_pool, args)) { c->func(pThis->m_pool, args);
LOGINFO(1, "exit requested, stopping");
return;
}
break; break;
} }
} }
if (!c->name.len) { if (!c->name.len) {
LOGWARN(0, "Unknown command " << command); LOGWARN(0, "Unknown command " << command.c_str());
} }
k = command.find_first_not_of("\r\n", k + 1);
command.erase(0, k);
} while (true); } while (true);
}
else if (nread < 0) {
LOGWARN(4, "read error " << uv_err_name(static_cast<int>(nread)));
}
pThis->m_readBufInUse = false;
}
void ConsoleCommands::loop(void* data)
{
LOGINFO(1, "event loop started");
ConsoleCommands* pThis = static_cast<ConsoleCommands*>(data);
uv_run(&pThis->m_loop, UV_RUN_DEFAULT);
uv_loop_close(&pThis->m_loop);
LOGINFO(1, "event loop stopped");
} }
} // namespace p2pool } // namespace p2pool
+22 -4
View File
@@ -17,7 +17,7 @@
#pragma once #pragma once
#include <thread> #include "uv_util.h"
namespace p2pool { namespace p2pool {
@@ -31,10 +31,28 @@ public:
private: private:
p2pool* m_pool; p2pool* m_pool;
std::thread* m_worker;
static bool stopped; uv_loop_t m_loop;
void run(); uv_async_t m_shutdownAsync;
uv_tty_t m_tty;
uv_thread_t m_loopThread;
char m_readBuf[64];
bool m_readBufInUse;
std::string m_command;
static void loop(void* data);
static void on_shutdown(uv_async_t* async)
{
ConsoleCommands* pThis = reinterpret_cast<ConsoleCommands*>(async->data);
uv_close(reinterpret_cast<uv_handle_t*>(&pThis->m_shutdownAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pThis->m_tty), nullptr);
}
static void allocCallback(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
static void stdinReadCallback(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf);
}; };
} // namespace p2pool } // namespace p2pool
+48 -23
View File
@@ -159,31 +159,38 @@ class Cache
public: public:
Cache() Cache()
{ {
uv_mutex_init_checked(&m); uv_rwlock_init_checked(&derivations_lock);
uv_rwlock_init_checked(&public_keys_lock);
uv_rwlock_init_checked(&tx_keys_lock);
} }
~Cache() ~Cache()
{ {
uv_mutex_destroy(&m); uv_rwlock_destroy(&derivations_lock);
uv_rwlock_destroy(&public_keys_lock);
uv_rwlock_destroy(&tx_keys_lock);
} }
bool get_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag) bool get_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag)
{ {
std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)> index; std::array<uint8_t, HASH_SIZE * 2> index;
memcpy(index.data(), key1.h, HASH_SIZE); memcpy(index.data(), key1.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE, key2.h, HASH_SIZE); memcpy(index.data() + HASH_SIZE, key2.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t));
derivation = {};
{ {
MutexLock lock(m); ReadLock lock(derivations_lock);
auto it = derivations.find(index); auto it = derivations.find(index);
if (it != derivations.end()) { if (it != derivations.end()) {
derivation = it->second.derivation; const DerivationEntry& entry = it->second;
view_tag = it->second.view_tag; derivation = entry.m_derivation;
if (entry.find_view_tag(output_index, view_tag)) {
return true; return true;
} }
} }
}
if (derivation.empty()) {
ge_p3 point; ge_p3 point;
ge_p2 point2; ge_p2 point2;
ge_p1p1 point3; ge_p1p1 point3;
@@ -196,12 +203,19 @@ public:
ge_mul8(&point3, &point2); ge_mul8(&point3, &point2);
ge_p1p1_to_p2(&point2, &point3); ge_p1p1_to_p2(&point2, &point3);
ge_tobytes(reinterpret_cast<uint8_t*>(&derivation), &point2); ge_tobytes(reinterpret_cast<uint8_t*>(&derivation), &point2);
}
derive_view_tag(derivation, output_index, view_tag); derive_view_tag(derivation, output_index, view_tag);
{ {
MutexLock lock(m); WriteLock lock(derivations_lock);
derivations.emplace(index, DerivationEntry{ derivation, view_tag } );
DerivationEntry& entry = derivations.emplace(index, DerivationEntry{ derivation, {} }).first->second;
const uint32_t k = static_cast<uint32_t>(output_index << 8) | view_tag;
if (std::find(entry.m_viewTags.begin(), entry.m_viewTags.end(), k) == entry.m_viewTags.end()) {
entry.m_viewTags.emplace_back(k);
}
} }
return true; return true;
@@ -215,7 +229,7 @@ public:
memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t)); memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t));
{ {
MutexLock lock(m); ReadLock lock(public_keys_lock);
auto it = public_keys.find(index); auto it = public_keys.find(index);
if (it != public_keys.end()) { if (it != public_keys.end()) {
derived_key = it->second; derived_key = it->second;
@@ -242,7 +256,7 @@ public:
ge_tobytes(derived_key.h, &point5); ge_tobytes(derived_key.h, &point5);
{ {
MutexLock lock(m); WriteLock lock(public_keys_lock);
public_keys.emplace(index, derived_key); public_keys.emplace(index, derived_key);
} }
@@ -256,7 +270,7 @@ public:
memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE); memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE);
{ {
MutexLock lock(m); ReadLock lock(tx_keys_lock);
auto it = tx_keys.find(index); auto it = tx_keys.find(index);
if (it != tx_keys.end()) { if (it != tx_keys.end()) {
pub = it->second.first; pub = it->second.first;
@@ -276,31 +290,42 @@ public:
generate_keys_deterministic(pub, sec, entropy, sizeof(entropy)); generate_keys_deterministic(pub, sec, entropy, sizeof(entropy));
{ {
MutexLock lock(m); WriteLock lock(tx_keys_lock);
tx_keys.emplace(index, std::pair<hash, hash>(pub, sec)); tx_keys.emplace(index, std::pair<hash, hash>(pub, sec));
} }
} }
void clear() void clear()
{ {
MutexLock lock(m); { WriteLock lock(derivations_lock); derivations.clear(); }
{ WriteLock lock(public_keys_lock); public_keys.clear(); }
derivations.clear(); { WriteLock lock(tx_keys_lock); tx_keys.clear(); }
public_keys.clear();
tx_keys.clear();
} }
private: private:
struct DerivationEntry struct DerivationEntry
{ {
hash derivation; hash m_derivation;
// cppcheck-suppress unusedStructMember std::vector<uint32_t> m_viewTags;
uint8_t view_tag;
bool find_view_tag(size_t output_index, uint8_t& view_tag) const {
for (uint32_t k : m_viewTags) {
if ((k >> 8) == output_index) {
view_tag = static_cast<uint8_t>(k);
return true;
}
}
return false;
}
}; };
uv_mutex_t m; uv_rwlock_t derivations_lock;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, DerivationEntry> derivations; unordered_map<std::array<uint8_t, HASH_SIZE * 2>, DerivationEntry> derivations;
uv_rwlock_t public_keys_lock;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys; unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
uv_rwlock_t tx_keys_lock;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> tx_keys; unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> tx_keys;
}; };
+137 -30
View File
@@ -58,8 +58,9 @@ struct CurlContext
static void on_close(uv_handle_t* h); static void on_close(uv_handle_t* h);
uv_poll_t m_pollHandle; void shutdown();
curl_socket_t m_socket;
std::vector<std::pair<curl_socket_t, uv_poll_t*>> m_pollHandles;
CallbackBase* m_callback; CallbackBase* m_callback;
CallbackBase* m_closeCallback; CallbackBase* m_closeCallback;
@@ -76,12 +77,12 @@ struct CurlContext
std::vector<char> m_response; std::vector<char> m_response;
std::string m_error; std::string m_error;
curl_slist* m_headers;
}; };
CurlContext::CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop) CurlContext::CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
: m_pollHandle{} : m_callback(cb)
, m_socket{}
, m_callback(cb)
, m_closeCallback(close_cb) , m_closeCallback(close_cb)
, m_loop(loop) , m_loop(loop)
, m_timer{} , m_timer{}
@@ -90,7 +91,10 @@ CurlContext::CurlContext(const std::string& address, int port, const std::string
, m_handle(nullptr) , m_handle(nullptr)
, m_req(req) , m_req(req)
, m_auth(auth) , m_auth(auth)
, m_headers(nullptr)
{ {
m_pollHandles.reserve(2);
{ {
char buf[log::Stream::BUF_SIZE + 1]; char buf[log::Stream::BUF_SIZE + 1];
buf[0] = '\0'; buf[0] = '\0';
@@ -177,6 +181,11 @@ CurlContext::CurlContext(const std::string& address, int port, const std::string
curl_easy_setopt_checked(m_handle, CURLOPT_CONNECTTIMEOUT, 1); curl_easy_setopt_checked(m_handle, CURLOPT_CONNECTTIMEOUT, 1);
curl_easy_setopt_checked(m_handle, CURLOPT_TIMEOUT, 10); curl_easy_setopt_checked(m_handle, CURLOPT_TIMEOUT, 10);
m_headers = curl_slist_append(m_headers, "Content-Type: application/json");
if (m_headers) {
curl_easy_setopt_checked(m_handle, CURLOPT_HTTPHEADER, m_headers);
}
if (!m_auth.empty()) { if (!m_auth.empty()) {
curl_easy_setopt_checked(m_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_ONLY); curl_easy_setopt_checked(m_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_ONLY);
curl_easy_setopt_checked(m_handle, CURLOPT_USERPWD, m_auth.c_str()); curl_easy_setopt_checked(m_handle, CURLOPT_USERPWD, m_auth.c_str());
@@ -200,45 +209,89 @@ CurlContext::~CurlContext()
} }
delete m_callback; delete m_callback;
if (m_response.empty()) {
if (m_error.empty()) {
m_error = "empty response";
}
else {
m_error += " (empty response)";
}
}
(*m_closeCallback)(m_error.c_str(), m_error.length()); (*m_closeCallback)(m_error.c_str(), m_error.length());
delete m_closeCallback; delete m_closeCallback;
curl_slist_free_all(m_headers);
} }
int CurlContext::on_socket(CURL* /*easy*/, curl_socket_t s, int action) int CurlContext::on_socket(CURL* /*easy*/, curl_socket_t s, int action)
{ {
auto it = std::find_if(m_pollHandles.begin(), m_pollHandles.end(), [s](const auto& value) { return value.first == s; });
switch (action) { switch (action) {
case CURL_POLL_IN: case CURL_POLL_IN:
case CURL_POLL_OUT: case CURL_POLL_OUT:
case CURL_POLL_INOUT: case CURL_POLL_INOUT:
{ {
if (!m_socket) { uv_poll_t* h = nullptr;
m_socket = s;
curl_multi_assign(m_multiHandle, s, this); if (it != m_pollHandles.end()) {
h = it->second;
} }
else if (m_socket != s) { else {
LOGERR(1, "This code can't work with multiple parallel requests. Fix the code!"); h = new uv_poll_t{};
// cppcheck-suppress nullPointer
h->data = this;
const int result = uv_poll_init_socket(m_loop, h, s);
if (result < 0) {
LOGERR(1, "uv_poll_init_socket failed: " << uv_err_name(result));
delete h;
h = nullptr;
}
else {
m_pollHandles.emplace_back(s, h);
}
}
if (h) {
const CURLMcode err = curl_multi_assign(m_multiHandle, s, this);
if (err != CURLM_OK) {
LOGERR(1, "curl_multi_assign(action = " << action << ") failed: " << curl_multi_strerror(err));
} }
int events = 0; int events = 0;
if (action != CURL_POLL_IN) events |= UV_WRITABLE; if (action != CURL_POLL_IN) events |= UV_WRITABLE;
if (action != CURL_POLL_OUT) events |= UV_READABLE; if (action != CURL_POLL_OUT) events |= UV_READABLE;
if (!m_pollHandle.data) { const int result = uv_poll_start(h, events, curl_perform);
uv_poll_init_socket(m_loop, &m_pollHandle, s); if (result < 0) {
m_pollHandle.data = this; LOGERR(1, "uv_poll_start failed with error " << uv_err_name(result));
}
}
else {
LOGERR(1, "failed to start polling on socket " << static_cast<int>(s));
} }
uv_poll_start(&m_pollHandle, events, curl_perform);
} }
break; break;
case CURL_POLL_REMOVE: case CURL_POLL_REMOVE:
default: default:
curl_multi_assign(m_multiHandle, s, nullptr); {
uv_poll_stop(&m_pollHandle); if (it != m_pollHandles.end()) {
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), on_close); uv_poll_t* h = it->second;
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), on_close); m_pollHandles.erase(it);
uv_close(reinterpret_cast<uv_handle_t*>(&m_pollHandle), on_close);
uv_poll_stop(h);
uv_close(reinterpret_cast<uv_handle_t*>(h), [](uv_handle_t* h) { delete reinterpret_cast<uv_poll_t*>(h); });
}
const CURLMcode err = curl_multi_assign(m_multiHandle, s, nullptr);
if (err != CURLM_OK) {
LOGERR(1, "curl_multi_assign(action = " << action << ") failed: " << curl_multi_strerror(err));
}
}
break; break;
} }
@@ -254,11 +307,24 @@ int CurlContext::on_timer(CURLM* /*multi*/, long timeout_ms)
if (timeout_ms == 0) { if (timeout_ms == 0) {
// 0 ms timeout, but we can't just call on_timeout() here - we have to kick the UV loop // 0 ms timeout, but we can't just call on_timeout() here - we have to kick the UV loop
uv_async_send(&m_async); const int result = uv_async_send(&m_async);
if (result < 0) {
LOGERR(1, "uv_async_send failed with error " << uv_err_name(result));
// if async call didn't work, try to use the timer with 1 ms timeout
timeout_ms = 1;
}
else {
return 0; return 0;
} }
}
const int result = uv_timer_start(&m_timer, reinterpret_cast<uv_timer_cb>(on_timeout), timeout_ms, 0);
if (result < 0) {
LOGERR(1, "uv_timer_start failed with error " << uv_err_name(result));
return -1;
}
uv_timer_start(&m_timer, reinterpret_cast<uv_timer_cb>(on_timeout), timeout_ms, 0);
return 0; return 0;
} }
@@ -266,16 +332,25 @@ void CurlContext::on_timeout(uv_handle_t* req)
{ {
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data); CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
int running_handles; int running_handles = 0;
curl_multi_socket_action(ctx->m_multiHandle, CURL_SOCKET_TIMEOUT, 0, &running_handles); const CURLMcode err = curl_multi_socket_action(ctx->m_multiHandle, CURL_SOCKET_TIMEOUT, 0, &running_handles);
if (err != CURLM_OK) {
LOGERR(1, "curl_multi_socket_action failed, error " << curl_multi_strerror(err));
}
ctx->check_multi_info(); ctx->check_multi_info();
if (running_handles == 0) {
ctx->shutdown();
}
} }
size_t CurlContext::on_write(const void* buffer, size_t size, size_t count) size_t CurlContext::on_write(const void* buffer, size_t size, size_t count)
{ {
const size_t realsize = size * count;
const char* p = reinterpret_cast<const char*>(buffer); const char* p = reinterpret_cast<const char*>(buffer);
m_response.insert(m_response.end(), p, p + size * count); m_response.insert(m_response.end(), p, p + realsize);
return count; return realsize;
} }
void CurlContext::curl_perform(uv_poll_t* req, int status, int events) void CurlContext::curl_perform(uv_poll_t* req, int status, int events)
@@ -292,9 +367,20 @@ void CurlContext::curl_perform(uv_poll_t* req, int status, int events)
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data); CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
int running_handles; int running_handles = 0;
curl_multi_socket_action(ctx->m_multiHandle, ctx->m_socket, flags, &running_handles); auto it = std::find_if(ctx->m_pollHandles.begin(), ctx->m_pollHandles.end(), [req](const auto& value) { return value.second == req; });
if (it != ctx->m_pollHandles.end()) {
const CURLMcode err = curl_multi_socket_action(ctx->m_multiHandle, it->first, flags, &running_handles);
if (err != CURLM_OK) {
LOGERR(1, "curl_multi_socket_action failed, error " << curl_multi_strerror(err));
}
}
ctx->check_multi_info(); ctx->check_multi_info();
if (running_handles == 0) {
ctx->shutdown();
}
} }
void CurlContext::check_multi_info() void CurlContext::check_multi_info()
@@ -333,20 +419,37 @@ void CurlContext::on_close(uv_handle_t* h)
CurlContext* ctx = reinterpret_cast<CurlContext*>(h->data); CurlContext* ctx = reinterpret_cast<CurlContext*>(h->data);
h->data = nullptr; h->data = nullptr;
if (ctx->m_timer.data || ctx->m_async.data || ctx->m_pollHandle.data) { if (ctx->m_timer.data || ctx->m_async.data) {
return; return;
} }
delete ctx; delete ctx;
} }
void CurlContext::shutdown()
{
for (const auto& p : m_pollHandles) {
uv_poll_stop(p.second);
uv_close(reinterpret_cast<uv_handle_t*>(p.second), [](uv_handle_t* h) { delete reinterpret_cast<uv_poll_t*>(h); });
}
m_pollHandles.clear();
if (m_async.data && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_async))) {
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), on_close);
}
if (m_timer.data && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_timer))) {
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), on_close);
}
}
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop) void Call(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
{ {
if (!loop) { if (!loop) {
loop = uv_default_loop(); loop = uv_default_loop();
} }
CallOnLoop(loop, const bool result = CallOnLoop(loop,
[=]() [=]()
{ {
try { try {
@@ -357,6 +460,10 @@ void Call(const std::string& address, int port, const std::string& req, const st
(*close_cb)(msg, strlen(msg)); (*close_cb)(msg, strlen(msg));
} }
}); });
if (!result) {
LOGERR(1, "JSON RPC \"" << req << "\" failed");
}
} }
} // namespace JSONRPCRequest } // namespace JSONRPCRequest
+1 -1
View File
@@ -32,7 +32,7 @@ namespace p2pool {
static bool track_memory = false; static bool track_memory = false;
constexpr size_t N = 1048576; constexpr size_t N = 2097152;
constexpr size_t MAX_FRAMES = 30; constexpr size_t MAX_FRAMES = 30;
struct TrackedAllocation struct TrackedAllocation
+49 -29
View File
@@ -52,11 +52,15 @@ P2PServer::P2PServer(p2pool* pool)
, m_cachedBlocks(nullptr) , m_cachedBlocks(nullptr)
, m_rng(RandomDeviceSeed::instance) , m_rng(RandomDeviceSeed::instance)
, m_block(new PoolBlock()) , m_block(new PoolBlock())
, m_blockDeserializeResult(0)
, m_timer{} , m_timer{}
, m_timerCounter(0) , m_timerCounter(0)
, m_timerInterval(2) , m_timerInterval(2)
, m_peerListLastSaved(0) , m_peerListLastSaved(0)
, m_lookForMissingBlocks(true)
{ {
m_blockDeserializeBuf.reserve(131072);
// Diffuse the initial state in case it has low quality // Diffuse the initial state in case it has low quality
m_rng.discard(10000); m_rng.discard(10000);
@@ -211,16 +215,15 @@ void P2PServer::update_peer_connections()
unordered_set<raw_ip> connected_clients; unordered_set<raw_ip> connected_clients;
{ {
MutexLock lock(m_clientsListLock); MutexLock lock(m_clientsListLock);
connected_clients.reserve(m_numConnections); connected_clients.reserve(m_numConnections);
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) { for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
bool disconnected = false;
const int timeout = client->m_handshakeComplete ? 300 : 10; const int timeout = client->m_handshakeComplete ? 300 : 10;
if (cur_time >= client->m_lastAlive + timeout) { if (cur_time >= client->m_lastAlive + timeout) {
const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive); const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive);
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting"); LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting");
client->close(); client->close();
disconnected = true; continue;
} }
if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) { if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) {
@@ -234,18 +237,16 @@ void P2PServer::update_peer_connections()
client->ban(DEFAULT_BAN_TIME); client->ban(DEFAULT_BAN_TIME);
remove_peer_from_list(client); remove_peer_from_list(client);
client->close(); client->close();
disconnected = true; continue;
} }
} }
if (!disconnected) {
connected_clients.insert(client->m_addr); connected_clients.insert(client->m_addr);
if (client->m_handshakeComplete && !client->m_handshakeInvalid && (client->m_listenPort >= 0)) { if (client->is_good()) {
has_good_peers = true; has_good_peers = true;
} }
} }
} }
}
std::vector<Peer> peer_list; std::vector<Peer> peer_list;
{ {
@@ -305,15 +306,11 @@ void P2PServer::update_peer_connections()
void P2PServer::update_peer_list() void P2PServer::update_peer_list()
{ {
const uint64_t cur_time = seconds_since_epoch(); const uint64_t cur_time = seconds_since_epoch();
{
MutexLock lock(m_clientsListLock); MutexLock lock(m_clientsListLock);
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) { for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if (!client->m_handshakeComplete || !client->m_handshakeSolutionSent) { if (client->is_good() && (cur_time >= client->m_nextOutgoingPeerListRequest)) {
continue;
}
if (cur_time >= client->m_nextOutgoingPeerListRequest) {
// Send peer list requests at random intervals (60-120 seconds) // Send peer list requests at random intervals (60-120 seconds)
client->m_nextOutgoingPeerListRequest = cur_time + (60 + (get_random64() % 61)); client->m_nextOutgoingPeerListRequest = cur_time + (60 + (get_random64() % 61));
@@ -336,7 +333,6 @@ void P2PServer::update_peer_list()
} }
} }
} }
}
} }
void P2PServer::save_peer_list_async() void P2PServer::save_peer_list_async()
@@ -895,6 +891,24 @@ void P2PServer::show_peers()
LOGINFO(0, "Total: " << n << " peers"); LOGINFO(0, "Total: " << n << " peers");
} }
int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size)
{
int result;
if ((m_blockDeserializeBuf.size() == size) && (memcmp(m_blockDeserializeBuf.data(), buf, size) == 0)) {
m_block->reset_offchain_data();
result = m_blockDeserializeResult;
}
else {
result = m_block->deserialize(buf, size, m_pool->side_chain(), &m_loop);
m_blockDeserializeBuf.assign(buf, buf + size);
m_blockDeserializeResult = result;
m_lookForMissingBlocks = true;
}
return result;
}
void P2PServer::on_timer() void P2PServer::on_timer()
{ {
++m_timerCounter; ++m_timerCounter;
@@ -948,10 +962,16 @@ void P2PServer::flush_cache()
void P2PServer::download_missing_blocks() void P2PServer::download_missing_blocks()
{ {
if (!m_lookForMissingBlocks) {
return;
}
std::vector<hash> missing_blocks; std::vector<hash> missing_blocks;
m_pool->side_chain().get_missing_blocks(missing_blocks); m_pool->side_chain().get_missing_blocks(missing_blocks);
if (missing_blocks.empty()) { if (missing_blocks.empty()) {
m_lookForMissingBlocks = false;
MutexLock lock(m_missingBlockRequestsLock); MutexLock lock(m_missingBlockRequestsLock);
m_missingBlockRequests.clear(); m_missingBlockRequests.clear();
return; return;
@@ -967,12 +987,10 @@ void P2PServer::download_missing_blocks()
clients.reserve(m_numConnections); clients.reserve(m_numConnections);
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) { for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if (!client->m_handshakeComplete || !client->m_handshakeSolutionSent) { if (client->is_good()) {
continue;
}
clients.emplace_back(client); clients.emplace_back(client);
} }
}
if (clients.empty()) { if (clients.empty()) {
return; return;
@@ -1032,7 +1050,7 @@ void P2PServer::download_missing_blocks()
void P2PServer::check_zmq() void P2PServer::check_zmq()
{ {
if ((m_timerCounter % 30) != 0) { if ((m_timerCounter % 30) != 3) {
return; return;
} }
@@ -1764,7 +1782,7 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size)
MutexLock lock(server->m_blockLock); MutexLock lock(server->m_blockLock);
const int result = server->m_block->deserialize(buf, size, server->m_pool->side_chain()); const int result = server->deserialize_block(buf, size);
if (result != 0) { if (result != 0) {
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result); LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
return false; return false;
@@ -1773,7 +1791,7 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size)
if (m_chainTipBlockRequest) { if (m_chainTipBlockRequest) {
m_chainTipBlockRequest = false; m_chainTipBlockRequest = false;
const uint64_t peer_height = server->m_block->m_txinGenHeight; const uint64_t peer_height = server->get_block()->m_txinGenHeight;
const uint64_t our_height = server->m_pool->miner_data().height; const uint64_t our_height = server->m_pool->miner_data().height;
if (peer_height + 2 < our_height) { if (peer_height + 2 < our_height) {
@@ -1782,7 +1800,7 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size)
} }
} }
return handle_incoming_block_async(server->m_block); return handle_incoming_block_async(server->get_block());
} }
bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size) bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
@@ -1796,19 +1814,21 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
MutexLock lock(server->m_blockLock); MutexLock lock(server->m_blockLock);
const int result = server->m_block->deserialize(buf, size, server->m_pool->side_chain()); const int result = server->deserialize_block(buf, size);
if (result != 0) { if (result != 0) {
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result); LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
return false; return false;
} }
m_broadcastedHashes[m_broadcastedHashesIndex.fetch_add(1) % array_size(&P2PClient::m_broadcastedHashes)] = server->m_block->m_sidechainId; const PoolBlock* block = server->get_block();
m_broadcastedHashes[m_broadcastedHashesIndex.fetch_add(1) % array_size(&P2PClient::m_broadcastedHashes)] = block->m_sidechainId;
MinerData miner_data = server->m_pool->miner_data(); MinerData miner_data = server->m_pool->miner_data();
if (server->m_block->m_prevId != miner_data.prev_id) { if (block->m_prevId != miner_data.prev_id) {
// This peer is mining on top of a different Monero block, investigate it // This peer is mining on top of a different Monero block, investigate it
const uint64_t peer_height = server->m_block->m_txinGenHeight; const uint64_t peer_height = block->m_txinGenHeight;
const uint64_t our_height = miner_data.height; const uint64_t our_height = miner_data.height;
if (peer_height < our_height) { if (peer_height < our_height) {
@@ -1838,11 +1858,11 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
} }
} }
server->m_block->m_wantBroadcast = true; block->m_wantBroadcast = true;
m_lastBroadcastTimestamp = seconds_since_epoch(); m_lastBroadcastTimestamp = seconds_since_epoch();
return handle_incoming_block_async(server->m_block); return handle_incoming_block_async(block);
} }
bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*) bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
@@ -1967,7 +1987,7 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
return true; return true;
} }
bool P2PServer::P2PClient::handle_incoming_block_async(PoolBlock* block) bool P2PServer::P2PClient::handle_incoming_block_async(const PoolBlock* block)
{ {
P2PServer* server = static_cast<P2PServer*>(m_owner); P2PServer* server = static_cast<P2PServer*>(m_owner);
+10 -1
View File
@@ -97,10 +97,12 @@ public:
bool on_peer_list_request(const uint8_t* buf); bool on_peer_list_request(const uint8_t* buf);
bool on_peer_list_response(const uint8_t* buf) const; bool on_peer_list_response(const uint8_t* buf) const;
bool handle_incoming_block_async(PoolBlock* block); bool handle_incoming_block_async(const PoolBlock* block);
void handle_incoming_block(p2pool* pool, PoolBlock& block, const uint32_t reset_counter, const raw_ip& addr, std::vector<hash>& missing_blocks); void handle_incoming_block(p2pool* pool, PoolBlock& block, const uint32_t reset_counter, const raw_ip& addr, std::vector<hash>& missing_blocks);
void post_handle_incoming_block(const uint32_t reset_counter, std::vector<hash>& missing_blocks); void post_handle_incoming_block(const uint32_t reset_counter, std::vector<hash>& missing_blocks);
bool is_good() const { return m_handshakeComplete && !m_handshakeInvalid && (m_listenPort >= 0); }
uint64_t m_peerId; uint64_t m_peerId;
MessageId m_expectedMessage; MessageId m_expectedMessage;
uint64_t m_handshakeChallenge; uint64_t m_handshakeChallenge;
@@ -141,6 +143,9 @@ public:
void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 1000U); } void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 1000U); }
void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 1000U); } void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 1000U); }
int deserialize_block(const uint8_t* buf, uint32_t size);
const PoolBlock* get_block() const { return m_block; }
private: private:
p2pool* m_pool; p2pool* m_pool;
BlockCache* m_cache; BlockCache* m_cache;
@@ -174,6 +179,8 @@ private:
uv_mutex_t m_blockLock; uv_mutex_t m_blockLock;
PoolBlock* m_block; PoolBlock* m_block;
std::vector<uint8_t> m_blockDeserializeBuf;
int m_blockDeserializeResult;
uv_timer_t m_timer; uv_timer_t m_timer;
uint64_t m_timerCounter; uint64_t m_timerCounter;
@@ -207,6 +214,8 @@ private:
uv_async_t m_broadcastAsync; uv_async_t m_broadcastAsync;
std::vector<Broadcast*> m_broadcastQueue; std::vector<Broadcast*> m_broadcastQueue;
bool m_lookForMissingBlocks;
uv_mutex_t m_missingBlockRequestsLock; uv_mutex_t m_missingBlockRequestsLock;
unordered_set<std::pair<uint64_t, uint64_t>> m_missingBlockRequests; unordered_set<std::pair<uint64_t, uint64_t>> m_missingBlockRequests;
+38 -10
View File
@@ -156,7 +156,14 @@ p2pool::p2pool(int argc, char* argv[])
m_blockTemplate = new BlockTemplate(this); m_blockTemplate = new BlockTemplate(this);
m_mempool = new Mempool(); m_mempool = new Mempool();
try {
m_consoleCommands = new ConsoleCommands(this); m_consoleCommands = new ConsoleCommands(this);
}
catch (...) {
LOGERR(1, "Couldn't start console commands handler");
m_consoleCommands = nullptr;
}
} }
p2pool::~p2pool() p2pool::~p2pool()
@@ -589,13 +596,15 @@ void p2pool::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32
m_blockTemplate->submit_sidechain_block(template_id, nonce, extra_nonce); m_blockTemplate->submit_sidechain_block(template_id, nonce, extra_nonce);
} }
void p2pool::update_block_template_async() void p2pool::update_block_template_async(bool is_alternative_block)
{ {
// If p2pool is stopped, m_blockTemplateAsync is most likely already closed // If p2pool is stopped, m_blockTemplateAsync is most likely already closed
if (m_stopped) { if (m_stopped) {
return; return;
} }
m_isAlternativeBlock = is_alternative_block;
const int err = uv_async_send(&m_blockTemplateAsync); const int err = uv_async_send(&m_blockTemplateAsync);
if (err) { if (err) {
LOGERR(1, "uv_async_send failed, error " << uv_err_name(err)); LOGERR(1, "uv_async_send failed, error " << uv_err_name(err));
@@ -613,6 +622,16 @@ void p2pool::update_block_template()
m_blockTemplate->update(data, *m_mempool, &m_params->m_wallet); m_blockTemplate->update(data, *m_mempool, &m_params->m_wallet);
stratum_on_block(); stratum_on_block();
api_update_pool_stats(); api_update_pool_stats();
#ifdef WITH_RANDOMX
if (m_isAlternativeBlock.exchange(false)) {
MutexLock lock(m_minerLock);
if (m_miner) {
m_miner->reset_share_counters();
}
}
#endif
} }
void p2pool::download_block_headers(uint64_t current_height) void p2pool::download_block_headers(uint64_t current_height)
@@ -775,9 +794,11 @@ void p2pool::get_info()
{ {
if (size > 0) { if (size > 0) {
LOGWARN(1, "get_info RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second"); LOGWARN(1, "get_info RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second");
if (!m_stopped) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_info(); get_info();
} }
}
}); });
} }
@@ -820,6 +841,10 @@ void p2pool::load_found_blocks()
void p2pool::parse_get_info_rpc(const char* data, size_t size) void p2pool::parse_get_info_rpc(const char* data, size_t size)
{ {
if (m_stopped) {
return;
}
rapidjson::Document doc; rapidjson::Document doc;
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size); doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
@@ -881,14 +906,20 @@ void p2pool::get_version()
{ {
if (size > 0) { if (size > 0) {
LOGWARN(1, "get_version RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second"); LOGWARN(1, "get_version RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second");
if (!m_stopped) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_version(); get_version();
} }
}
}); });
} }
void p2pool::parse_get_version_rpc(const char* data, size_t size) void p2pool::parse_get_version_rpc(const char* data, size_t size)
{ {
if (m_stopped) {
return;
}
rapidjson::Document doc; rapidjson::Document doc;
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size); doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
@@ -945,9 +976,11 @@ void p2pool::get_miner_data()
{ {
if (size > 0) { if (size > 0) {
LOGWARN(1, "get_miner_data RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second"); LOGWARN(1, "get_miner_data RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second");
if (!m_stopped) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_miner_data(); get_miner_data();
} }
}
else { else {
m_getMinerDataPending = false; m_getMinerDataPending = false;
} }
@@ -956,6 +989,10 @@ void p2pool::get_miner_data()
void p2pool::parse_get_miner_data_rpc(const char* data, size_t size) void p2pool::parse_get_miner_data_rpc(const char* data, size_t size)
{ {
if (m_stopped) {
return;
}
hash h; hash h;
keccak(reinterpret_cast<const uint8_t*>(data), static_cast<int>(size), h.h, HASH_SIZE); keccak(reinterpret_cast<const uint8_t*>(data), static_cast<int>(size), h.h, HASH_SIZE);
if (h == m_getMinerDataHash) { if (h == m_getMinerDataHash) {
@@ -1348,15 +1385,6 @@ void p2pool::stop_mining()
m_miner = nullptr; m_miner = nullptr;
} }
} }
void p2pool::reset_miner()
{
MutexLock lock(m_minerLock);
if (m_miner) {
m_miner->reset_share_counters();
}
}
#endif #endif
static void on_signal(uv_signal_t* handle, int signum) static void on_signal(uv_signal_t* handle, int signum)
+3 -2
View File
@@ -77,7 +77,7 @@ public:
void submit_block_async(const std::vector<uint8_t>& blob); void submit_block_async(const std::vector<uint8_t>& blob);
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce); void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
void update_block_template_async(); void update_block_template_async(bool is_alternative_block = false);
void update_block_template(); void update_block_template();
void download_block_headers(uint64_t current_height); void download_block_headers(uint64_t current_height);
@@ -91,7 +91,6 @@ public:
#ifdef WITH_RANDOMX #ifdef WITH_RANDOMX
void start_mining(uint32_t threads); void start_mining(uint32_t threads);
void stop_mining(); void stop_mining();
void reset_miner();
#endif #endif
uint64_t zmq_last_active() const { return m_zmqLastActive; } uint64_t zmq_last_active() const { return m_zmqLastActive; }
@@ -198,6 +197,8 @@ private:
uv_async_t m_blockTemplateAsync; uv_async_t m_blockTemplateAsync;
uv_async_t m_stopAsync; uv_async_t m_stopAsync;
std::atomic<bool> m_isAlternativeBlock{ false };
std::atomic<uint64_t> m_zmqLastActive; std::atomic<uint64_t> m_zmqLastActive;
uint64_t m_startTime; uint64_t m_startTime;
uv_async_t m_restartZMQAsync; uv_async_t m_restartZMQAsync;
+38 -24
View File
@@ -28,7 +28,9 @@ static constexpr char log_category_prefix[] = "P2Pool API ";
namespace p2pool { namespace p2pool {
p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats): m_apiPath(api_path) p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats)
: m_apiPath(api_path)
, m_counter(0)
{ {
if (m_apiPath.empty()) { if (m_apiPath.empty()) {
LOGERR(1, "api path is empty"); LOGERR(1, "api path is empty");
@@ -131,14 +133,12 @@ void p2pool_api::dump_to_file()
{ {
MutexLock lock(m_dumpDataLock); MutexLock lock(m_dumpDataLock);
data = std::move(m_dumpData); data = std::move(m_dumpData);
m_dumpData.clear();
} }
for (auto& it : data) { for (auto& it : data) {
DumpFileWork* work = new DumpFileWork{ {}, {}, {}, it.first, std::move(it.second) }; DumpFileWork* work = new DumpFileWork{ {}, 0, it.first, it.first + std::to_string(m_counter), std::move(it.second) };
work->open_req.data = work; work->req.data = work;
work->write_req.data = work; ++m_counter;
work->close_req.data = work;
const int flags = O_WRONLY | O_CREAT | O_TRUNC const int flags = O_WRONLY | O_CREAT | O_TRUNC
#ifdef O_BINARY #ifdef O_BINARY
@@ -146,9 +146,9 @@ void p2pool_api::dump_to_file()
#endif #endif
; ;
const int result = uv_fs_open(uv_default_loop_checked(), &work->open_req, it.first.c_str(), flags, 0644, on_fs_open); const int result = uv_fs_open(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), flags, 0644, on_fs_open);
if (result < 0) { if (result < 0) {
LOGWARN(4, "failed to open " << it.first << ", error " << uv_err_name(result)); LOGWARN(4, "failed to open " << work->tmp_name << ", error " << uv_err_name(result));
delete work; delete work;
} }
} }
@@ -157,11 +157,11 @@ void p2pool_api::dump_to_file()
void p2pool_api::on_fs_open(uv_fs_t* req) void p2pool_api::on_fs_open(uv_fs_t* req)
{ {
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data); DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
const int fd = static_cast<int>(req->result); work->fd = static_cast<int>(req->result);
if (fd < 0) {
LOGWARN(4, "failed to open " << work->name << ", error " << uv_err_name(fd));
uv_fs_req_cleanup(req); uv_fs_req_cleanup(req);
if (work->fd < 0) {
LOGWARN(4, "failed to open " << work->tmp_name << ", error " << uv_err_name(work->fd));
delete work; delete work;
return; return;
} }
@@ -170,15 +170,12 @@ void p2pool_api::on_fs_open(uv_fs_t* req)
buf[0].base = work->buf.data(); buf[0].base = work->buf.data();
buf[0].len = static_cast<uint32_t>(work->buf.size()); buf[0].len = static_cast<uint32_t>(work->buf.size());
const int result = uv_fs_write(uv_default_loop_checked(), &work->write_req, static_cast<uv_file>(fd), buf, 1, 0, on_fs_write); const int result = uv_fs_write(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), buf, 1, 0, on_fs_write);
if (result < 0) { if (result < 0) {
LOGWARN(4, "failed to write to " << work->name << ", error " << uv_err_name(result)); LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
uv_fs_req_cleanup(req);
delete work; delete work;
return; return;
} }
uv_fs_req_cleanup(req);
} }
void p2pool_api::on_fs_write(uv_fs_t* req) void p2pool_api::on_fs_write(uv_fs_t* req)
@@ -186,18 +183,17 @@ void p2pool_api::on_fs_write(uv_fs_t* req)
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data); DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
if (req->result < 0) { if (req->result < 0) {
LOGWARN(4, "failed to write to " << work->name << ", error " << uv_err_name(static_cast<int>(req->result))); LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(static_cast<int>(req->result)));
} }
const int result = uv_fs_close(uv_default_loop_checked(), &work->close_req, static_cast<uv_file>(work->open_req.result), on_fs_close);
if (result < 0) {
LOGWARN(4, "failed to close " << work->name << ", error " << uv_err_name(result));
uv_fs_req_cleanup(req); uv_fs_req_cleanup(req);
const int result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_close);
if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
delete work; delete work;
return; return;
} }
uv_fs_req_cleanup(req);
} }
void p2pool_api::on_fs_close(uv_fs_t* req) void p2pool_api::on_fs_close(uv_fs_t* req)
@@ -205,7 +201,25 @@ void p2pool_api::on_fs_close(uv_fs_t* req)
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data); DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
if (req->result < 0) { if (req->result < 0) {
LOGWARN(4, "failed to close " << work->name << ", error " << uv_err_name(static_cast<int>(req->result))); LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(static_cast<int>(req->result)));
}
uv_fs_req_cleanup(req);
const int result = uv_fs_rename(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), work->name.c_str(), on_fs_rename);
if (result < 0) {
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(result));
delete work;
return;
}
}
void p2pool_api::on_fs_rename(uv_fs_t* req)
{
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
if (req->result < 0) {
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(static_cast<int>(req->result)));
} }
uv_fs_req_cleanup(req); uv_fs_req_cleanup(req);
+6 -3
View File
@@ -46,11 +46,11 @@ private:
static void on_dump_to_file(uv_async_t* async) { reinterpret_cast<p2pool_api*>(async->data)->dump_to_file(); } static void on_dump_to_file(uv_async_t* async) { reinterpret_cast<p2pool_api*>(async->data)->dump_to_file(); }
struct DumpFileWork { struct DumpFileWork {
uv_fs_t open_req; uv_fs_t req;
uv_fs_t write_req; int fd;
uv_fs_t close_req;
std::string name; std::string name;
std::string tmp_name;
std::vector<char> buf; std::vector<char> buf;
}; };
@@ -77,6 +77,7 @@ private:
static void on_fs_open(uv_fs_t* req); static void on_fs_open(uv_fs_t* req);
static void on_fs_write(uv_fs_t* req); static void on_fs_write(uv_fs_t* req);
static void on_fs_close(uv_fs_t* req); static void on_fs_close(uv_fs_t* req);
static void on_fs_rename(uv_fs_t* req);
std::string m_apiPath; std::string m_apiPath;
std::string m_networkPath; std::string m_networkPath;
@@ -87,6 +88,8 @@ private:
unordered_map<std::string, std::vector<char>> m_dumpData; unordered_map<std::string, std::vector<char>> m_dumpData;
uv_async_t m_dumpToFileAsync; uv_async_t m_dumpToFileAsync;
uint64_t m_counter;
}; };
} // namespace p2pool } // namespace p2pool
+20
View File
@@ -53,6 +53,7 @@ PoolBlock::PoolBlock()
, m_invalid(false) , m_invalid(false)
, m_broadcasted(false) , m_broadcasted(false)
, m_wantBroadcast(false) , m_wantBroadcast(false)
, m_precalculated(false)
, m_localTimestamp(seconds_since_epoch()) , m_localTimestamp(seconds_since_epoch())
{ {
uv_mutex_init_checked(&m_lock); uv_mutex_init_checked(&m_lock);
@@ -114,6 +115,7 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_invalid = b.m_invalid; m_invalid = b.m_invalid;
m_broadcasted = b.m_broadcasted; m_broadcasted = b.m_broadcasted;
m_wantBroadcast = b.m_wantBroadcast; m_wantBroadcast = b.m_wantBroadcast;
m_precalculated = b.m_precalculated;
m_localTimestamp = seconds_since_epoch(); m_localTimestamp = seconds_since_epoch();
@@ -229,6 +231,24 @@ void PoolBlock::serialize_sidechain_data()
writeVarint(m_cumulativeDifficulty.hi, m_sideChainData); writeVarint(m_cumulativeDifficulty.hi, m_sideChainData);
} }
void PoolBlock::reset_offchain_data()
{
// Defaults for off-chain variables
m_tmpTxExtra.clear();
m_depth = 0;
m_verified = false;
m_invalid = false;
m_broadcasted = false;
m_wantBroadcast = false;
m_precalculated = false;
m_localTimestamp = seconds_since_epoch();
}
bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash) bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash)
{ {
alignas(8) uint8_t hashes[HASH_SIZE * 3]; alignas(8) uint8_t hashes[HASH_SIZE * 3];
+7 -3
View File
@@ -129,15 +129,19 @@ struct PoolBlock
bool m_verified; bool m_verified;
bool m_invalid; bool m_invalid;
bool m_broadcasted; mutable bool m_broadcasted;
bool m_wantBroadcast; mutable bool m_wantBroadcast;
bool m_precalculated;
uint64_t m_localTimestamp; uint64_t m_localTimestamp;
void serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash); void serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash);
void serialize_sidechain_data(); void serialize_sidechain_data();
int deserialize(const uint8_t* data, size_t size, SideChain& sidechain); int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop);
void reset_offchain_data();
bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash); bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash);
uint64_t get_payout(const Wallet& w) const; uint64_t get_payout(const Wallet& w) const;
+3 -15
View File
@@ -23,7 +23,7 @@ namespace p2pool {
// Since data here can come from external and possibly malicious sources, check everything // Since data here can come from external and possibly malicious sources, check everything
// Only the syntax (i.e. the serialized block binary format) and the keccak hash are checked here // Only the syntax (i.e. the serialized block binary format) and the keccak hash are checked here
// Semantics must also be checked elsewhere before accepting the block (PoW, reward split between miners, difficulty calculation and so on) // Semantics must also be checked elsewhere before accepting the block (PoW, reward split between miners, difficulty calculation and so on)
int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechain) int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop)
{ {
try { try {
// Sanity check // Sanity check
@@ -272,7 +272,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
return __LINE__; return __LINE__;
} }
if ((num_outputs == 0) && !sidechain.get_outputs_blob(this, total_reward, outputs_blob)) { if ((num_outputs == 0) && !sidechain.get_outputs_blob(this, total_reward, outputs_blob, loop)) {
return __LINE__; return __LINE__;
} }
@@ -333,19 +333,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
return __LINE__; return __LINE__;
} }
// Defaults for off-chain variables reset_offchain_data();
m_tmpTxExtra.clear();
m_depth = 0;
m_verified = false;
m_invalid = false;
m_broadcasted = false;
m_wantBroadcast = false;
m_localTimestamp = seconds_since_epoch();
return 0; return 0;
} }
+1 -5
View File
@@ -389,11 +389,7 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
uv_mutex_init_checked(&m_requestMutex); uv_mutex_init_checked(&m_requestMutex);
uv_mutex_init_checked(&m_condMutex); uv_mutex_init_checked(&m_condMutex);
err = uv_cond_init(&m_cond); uv_cond_init_checked(&m_cond);
if (err) {
LOGERR(1, "failed to create cond, error " << uv_err_name(err));
panic();
}
err = uv_thread_create(&m_loopThread, loop, this); err = uv_thread_create(&m_loopThread, loop, this);
if (err) { if (err) {
+295 -49
View File
@@ -38,13 +38,6 @@
#include <iterator> #include <iterator>
#include <numeric> #include <numeric>
// Only uncomment it to debug issues with uncle/orphan blocks
//#define DEBUG_BROADCAST_DELAY_MS 100
#ifdef DEBUG_BROADCAST_DELAY_MS
#include <thread>
#endif
static constexpr char log_category_prefix[] = "SideChain "; static constexpr char log_category_prefix[] = "SideChain ";
static constexpr uint64_t MIN_DIFFICULTY = 100000; static constexpr uint64_t MIN_DIFFICULTY = 100000;
@@ -70,6 +63,7 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
, m_chainWindowSize(2160) , m_chainWindowSize(2160)
, m_unclePenalty(20) , m_unclePenalty(20)
, m_curDifficulty(m_minDifficulty) , m_curDifficulty(m_minDifficulty)
, m_precalcFinished(false)
{ {
LOGINFO(1, log::LightCyan() << "network type = " << m_networkType); LOGINFO(1, log::LightCyan() << "network type = " << m_networkType);
@@ -161,14 +155,41 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
memset(buf + 8, '*', HASH_SIZE * 2 - 16); memset(buf + 8, '*', HASH_SIZE * 2 - 16);
m_consensusIdDisplayStr.assign(buf); m_consensusIdDisplayStr.assign(buf);
LOGINFO(1, "consensus ID = " << log::LightCyan() << m_consensusIdDisplayStr.c_str()); LOGINFO(1, "consensus ID = " << log::LightCyan() << m_consensusIdDisplayStr.c_str());
uv_cond_init_checked(&m_precalcJobsCond);
uv_mutex_init_checked(&m_precalcJobsMutex);
m_precalcJobs.reserve(16);
uint32_t numThreads = std::thread::hardware_concurrency();
// Leave 1 CPU core free from worker threads
if (numThreads > 1) {
--numThreads;
}
// Use between 1 and 8 threads
if (numThreads < 1) numThreads = 1;
if (numThreads > 8) numThreads = 8;
LOGINFO(4, "running " << numThreads << " pre-calculation workers");
m_precalcWorkers.reserve(numThreads);
for (uint32_t i = 0; i < numThreads; ++i) {
m_precalcWorkers.emplace_back(&SideChain::precalc_worker, this);
}
m_uniquePrecalcInputs = new unordered_set<size_t>();
} }
SideChain::~SideChain() SideChain::~SideChain()
{ {
finish_precalc();
uv_rwlock_destroy(&m_sidechainLock); uv_rwlock_destroy(&m_sidechainLock);
uv_mutex_destroy(&m_seenWalletsLock); uv_mutex_destroy(&m_seenWalletsLock);
uv_mutex_destroy(&m_seenBlocksLock); uv_mutex_destroy(&m_seenBlocksLock);
uv_rwlock_destroy(&m_curDifficultyLock); uv_rwlock_destroy(&m_curDifficultyLock);
for (const auto& it : m_blocksById) { for (const auto& it : m_blocksById) {
delete it.second; delete it.second;
} }
@@ -289,7 +310,7 @@ P2PServer* SideChain::p2pServer() const
return m_pool ? m_pool->p2p_server() : nullptr; return m_pool ? m_pool->p2p_server() : nullptr;
} }
bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) const bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares) const
{ {
shares.clear(); shares.clear();
shares.reserve(m_chainWindowSize * 2); shares.reserve(m_chainWindowSize * 2);
@@ -297,7 +318,7 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
// Collect shares from each block in the PPLNS window, starting from the "tip" // Collect shares from each block in the PPLNS window, starting from the "tip"
uint64_t block_depth = 0; uint64_t block_depth = 0;
PoolBlock* cur = tip; const PoolBlock* cur = tip;
do { do {
MinerShare cur_share{ cur->m_difficulty.lo, &cur->m_minerWallet }; MinerShare cur_share{ cur->m_difficulty.lo, &cur->m_minerWallet };
@@ -371,6 +392,50 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
return true; return true;
} }
bool SideChain::get_wallets(const PoolBlock* tip, std::vector<const Wallet*>& wallets) const
{
// Collect wallets from each block in the PPLNS window, starting from the "tip"
wallets.clear();
wallets.reserve(m_chainWindowSize * 2);
uint64_t block_depth = 0;
const PoolBlock* cur = tip;
do {
wallets.push_back(&cur->m_minerWallet);
for (const hash& uncle_id : cur->m_uncles) {
auto it = m_blocksById.find(uncle_id);
if (it == m_blocksById.end()) {
return false;
}
// Skip uncles which are already out of PPLNS window
if (tip->m_sidechainHeight - it->second->m_sidechainHeight < m_chainWindowSize) {
wallets.push_back(&it->second->m_minerWallet);
}
}
++block_depth;
if ((block_depth >= m_chainWindowSize) || (cur->m_sidechainHeight == 0)) {
break;
}
auto it = m_blocksById.find(cur->m_parent);
if (it == m_blocksById.end()) {
return false;
}
cur = it->second;
} while (true);
// Remove duplicates
std::sort(wallets.begin(), wallets.end(), [](const Wallet* a, const Wallet* b) { return *a < *b; });
wallets.erase(std::unique(wallets.begin(), wallets.end(), [](const Wallet* a, const Wallet* b) { return *a == *b; }), wallets.end());
return true;
}
bool SideChain::block_seen(const PoolBlock& block) bool SideChain::block_seen(const PoolBlock& block)
{ {
// Check if it's some old block // Check if it's some old block
@@ -545,6 +610,9 @@ void SideChain::add_block(const PoolBlock& block)
m_blocksByHeight[new_block->m_sidechainHeight].push_back(new_block); m_blocksByHeight[new_block->m_sidechainHeight].push_back(new_block);
// Pre-calculate eph_public_keys during initial sync
launch_precalc(new_block);
update_depths(new_block); update_depths(new_block);
if (new_block->m_verified) { if (new_block->m_verified) {
@@ -614,7 +682,7 @@ bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob) const
return true; return true;
} }
bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob) const bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob, uv_loop_t* loop) const
{ {
blob.clear(); blob.clear();
@@ -651,6 +719,63 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
const size_t n = tmpShares.size(); const size_t n = tmpShares.size();
// Helper jobs call get_eph_public_key with indices in descending order
// Current thread will process indices in ascending order so when they meet, everything will be cached
std::atomic<int> counter{ 0 };
std::atomic<int> num_helper_jobs_finished{ 0 };
int num_helper_jobs_started = 0;
if (loop) {
constexpr size_t HELPER_JOBS_COUNT = 4;
struct Work
{
uv_work_t req;
const std::vector<MinerShare>& tmpShares;
const hash& txkeySec;
std::atomic<int>& counter;
std::atomic<int>& num_helper_jobs_finished;
// Fix MSVC warnings
Work() = delete;
Work& operator=(Work&&) = delete;
};
counter = static_cast<int>(n) - 1;
num_helper_jobs_started = HELPER_JOBS_COUNT;
for (size_t i = 0; i < HELPER_JOBS_COUNT; ++i) {
Work* w = new Work{ {}, tmpShares, block->m_txkeySec, counter, num_helper_jobs_finished };
w->req.data = w;
const int err = uv_queue_work(loop, &w->req,
[](uv_work_t* req)
{
Work* work = reinterpret_cast<Work*>(req->data);
hash eph_public_key;
int index;
while ((index = work->counter.fetch_sub(1)) >= 0) {
uint8_t view_tag;
work->tmpShares[index].m_wallet->get_eph_public_key(work->txkeySec, static_cast<size_t>(index), eph_public_key, view_tag);
}
++work->num_helper_jobs_finished;
},
[](uv_work_t* req, int /*status*/)
{
delete reinterpret_cast<Work*>(req->data);
});
if (err) {
LOGERR(1, "get_outputs_blob: uv_queue_work failed, error " << uv_err_name(err));
--num_helper_jobs_started;
delete w;
}
}
}
blob.reserve(n * 39 + 64); blob.reserve(n * 39 + 64);
writeVarint(n, blob); writeVarint(n, blob);
@@ -662,6 +787,13 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
hash eph_public_key; hash eph_public_key;
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
// stop helper jobs when they meet with current thread
const int c = counter.load();
if ((c >= 0) && (static_cast<int>(i) >= c)) {
// this will cause all helper jobs to finish immediately
counter = -1;
}
writeVarint(tmpRewards[i], blob); writeVarint(tmpRewards[i], blob);
blob.emplace_back(tx_type); blob.emplace_back(tx_type);
@@ -679,17 +811,27 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
block->m_outputs.emplace_back(tmpRewards[i], eph_public_key, tx_type, view_tag); block->m_outputs.emplace_back(tmpRewards[i], eph_public_key, tx_type, view_tag);
} }
if (loop) {
// this will cause all helper jobs to finish immediately
counter = -1;
while (num_helper_jobs_finished < num_helper_jobs_started) {
std::this_thread::yield();
}
}
return true; return true;
} }
void SideChain::print_status() const void SideChain::print_status(bool obtain_sidechain_lock) const
{ {
std::vector<hash> blocks_in_window; std::vector<hash> blocks_in_window;
blocks_in_window.reserve(m_chainWindowSize * 9 / 8); blocks_in_window.reserve(m_chainWindowSize * 9 / 8);
const difficulty_type diff = difficulty(); const difficulty_type diff = difficulty();
ReadLock lock(m_sidechainLock); if (obtain_sidechain_lock) uv_rwlock_rdlock(&m_sidechainLock);
ON_SCOPE_LEAVE([this, obtain_sidechain_lock]() { if (obtain_sidechain_lock) uv_rwlock_rdunlock(&m_sidechainLock); });
uint64_t rem; uint64_t rem;
uint64_t pool_hashrate = udiv128(diff.hi, diff.lo, m_targetBlockTime, &rem); uint64_t pool_hashrate = udiv128(diff.hi, diff.lo, m_targetBlockTime, &rem);
@@ -946,11 +1088,11 @@ bool SideChain::split_reward(uint64_t reward, const std::vector<MinerShare>& sha
return true; return true;
} }
bool SideChain::get_difficulty(PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const bool SideChain::get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const
{ {
difficultyData.clear(); difficultyData.clear();
PoolBlock* cur = tip; const PoolBlock* cur = tip;
uint64_t oldest_timestamp = std::numeric_limits<uint64_t>::max(); uint64_t oldest_timestamp = std::numeric_limits<uint64_t>::max();
uint64_t block_depth = 0; uint64_t block_depth = 0;
@@ -1442,7 +1584,7 @@ void SideChain::verify(PoolBlock* block)
block->m_invalid = false; block->m_invalid = false;
} }
void SideChain::update_chain_tip(PoolBlock* block) void SideChain::update_chain_tip(const PoolBlock* block)
{ {
if (!block->m_verified || block->m_invalid) { if (!block->m_verified || block->m_invalid) {
LOGERR(1, "trying to update chain tip to an unverified or invalid block, fix the code!"); LOGERR(1, "trying to update chain tip to an unverified or invalid block, fix the code!");
@@ -1460,7 +1602,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
if (is_longer_chain(tip, block, is_alternative)) { if (is_longer_chain(tip, block, is_alternative)) {
difficulty_type diff; difficulty_type diff;
if (get_difficulty(block, m_difficultyData, diff)) { if (get_difficulty(block, m_difficultyData, diff)) {
m_chainTip = block; m_chainTip = const_cast<PoolBlock*>(block);
{ {
WriteLock lock(m_curDifficultyLock); WriteLock lock(m_curDifficultyLock);
m_curDifficulty = diff; m_curDifficulty = diff;
@@ -1472,7 +1614,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
block->m_wantBroadcast = true; block->m_wantBroadcast = true;
if (m_pool) { if (m_pool) {
m_pool->update_block_template_async(); m_pool->update_block_template_async(is_alternative);
// Reset stratum share counters when switching to an alternative chain to avoid confusion // Reset stratum share counters when switching to an alternative chain to avoid confusion
if (is_alternative) { if (is_alternative) {
@@ -1480,9 +1622,6 @@ void SideChain::update_chain_tip(PoolBlock* block)
if (s) { if (s) {
s->reset_share_counters(); s->reset_share_counters();
} }
#ifdef WITH_RANDOMX
m_pool->reset_miner();
#endif
LOGINFO(0, log::LightCyan() << "SYNCHRONIZED"); LOGINFO(0, log::LightCyan() << "SYNCHRONIZED");
} }
} }
@@ -1503,36 +1642,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
if (p2pServer() && block->m_wantBroadcast && !block->m_broadcasted) { if (p2pServer() && block->m_wantBroadcast && !block->m_broadcasted) {
block->m_broadcasted = true; block->m_broadcasted = true;
#ifdef DEBUG_BROADCAST_DELAY_MS
struct Work
{
uv_work_t req;
P2PServer* server;
PoolBlock* block;
};
Work* work = new Work{};
work->req.data = work;
work->server = p2pServer();
work->block = block;
const int err = uv_queue_work(uv_default_loop(), &work->req,
[](uv_work_t*)
{
num_running_jobs.fetch_add(1);
std::this_thread::sleep_for(std::chrono::milliseconds(DEBUG_BROADCAST_DELAY_MS));
},
[](uv_work_t* req, int)
{
Work* work = reinterpret_cast<Work*>(req->data);
work->server->broadcast(*work->block);
delete reinterpret_cast<Work*>(req->data);
num_running_jobs.fetch_sub(1);
});
if (err) {
LOGERR(1, "update_chain_tip: uv_queue_work failed, error " << uv_err_name(err));
}
#else
p2pServer()->broadcast(*block); p2pServer()->broadcast(*block);
#endif
} }
} }
@@ -1774,6 +1884,9 @@ void SideChain::prune_old_blocks()
if (p2pServer()) { if (p2pServer()) {
p2pServer()->clear_cached_blocks(); p2pServer()->clear_cached_blocks();
} }
// Pre-calc workers are not needed anymore
finish_precalc();
} }
} }
@@ -1792,9 +1905,18 @@ void SideChain::get_missing_blocks(std::vector<hash>& missing_blocks) const
missing_blocks.push_back(b.second->m_parent); missing_blocks.push_back(b.second->m_parent);
} }
int num_missing_uncles = 0;
for (const hash& h : b.second->m_uncles) { for (const hash& h : b.second->m_uncles) {
if (!h.empty() && (m_blocksById.find(h) == m_blocksById.end())) { if (!h.empty() && (m_blocksById.find(h) == m_blocksById.end())) {
missing_blocks.push_back(h); missing_blocks.push_back(h);
// Get no more than 2 first missing uncles at a time from each block
// Blocks with more than 2 uncles are very rare and they will be processed in several steps
++num_missing_uncles;
if (num_missing_uncles >= 2) {
break;
}
} }
} }
} }
@@ -1891,4 +2013,128 @@ bool SideChain::check_config()
return true; return true;
} }
void SideChain::launch_precalc(const PoolBlock* block)
{
if (m_precalcFinished) {
return;
}
for (int h = UNCLE_BLOCK_DEPTH - 1; h >= 0; --h) {
auto it = m_blocksByHeight.find(block->m_sidechainHeight + m_chainWindowSize + h - 1);
if (it == m_blocksByHeight.end()) {
continue;
}
for (PoolBlock* b : it->second) {
if (b->m_precalculated) {
continue;
}
std::vector<const Wallet*> wallets;
if (get_wallets(b, wallets)) {
b->m_precalculated = true;
PrecalcJob* job = new PrecalcJob{ b, std::move(wallets) };
{
MutexLock lock2(m_precalcJobsMutex);
m_precalcJobs.push_back(job);
}
uv_cond_signal(&m_precalcJobsCond);
}
}
}
}
void SideChain::precalc_worker()
{
do {
PrecalcJob* job;
{
MutexLock lock(m_precalcJobsMutex);
if (m_precalcFinished) {
return;
}
while (m_precalcJobs.empty()) {
uv_cond_wait(&m_precalcJobsCond, &m_precalcJobsMutex);
if (m_precalcFinished) {
return;
}
}
job = m_precalcJobs.back();
m_precalcJobs.pop_back();
// Filter out duplicate inputs for get_eph_public_key()
uint8_t t[HASH_SIZE * 2 + sizeof(size_t)];
memcpy(t, job->b->m_txkeySec.h, HASH_SIZE);
for (size_t i = 0, n = job->wallets.size(); i < n; ++i) {
memcpy(t + HASH_SIZE, job->wallets[i]->view_public_key().h, HASH_SIZE);
memcpy(t + HASH_SIZE * 2, &i, sizeof(i));
if (!m_uniquePrecalcInputs->insert(robin_hood::hash_bytes(t, array_size(t))).second) {
job->wallets[i] = nullptr;
}
}
}
for (size_t i = 0, n = job->wallets.size(); i < n; ++i) {
if (job->wallets[i]) {
hash eph_public_key;
uint8_t view_tag;
job->wallets[i]->get_eph_public_key(job->b->m_txkeySec, i, eph_public_key, view_tag);
}
}
delete job;
} while (true);
}
void SideChain::finish_precalc()
{
if (m_precalcFinished.exchange(true)) {
return;
}
try
{
{
MutexLock lock(m_precalcJobsMutex);
for (PrecalcJob* job : m_precalcJobs) {
delete job;
}
m_precalcJobs.clear();
m_precalcJobs.shrink_to_fit();
uv_cond_broadcast(&m_precalcJobsCond);
}
for (std::thread& t : m_precalcWorkers) {
t.join();
}
m_precalcWorkers.clear();
m_precalcWorkers.shrink_to_fit();
delete m_uniquePrecalcInputs;
m_uniquePrecalcInputs = nullptr;
uv_mutex_destroy(&m_precalcJobsMutex);
uv_cond_destroy(&m_precalcJobsCond);
LOGINFO(4, "pre-calculation workers stopped");
}
catch (const std::exception& e)
{
LOGERR(1, "exception in finish_precalc(): " << e.what());
}
#ifdef DEV_TEST_SYNC
if (m_pool) {
LOGINFO(0, log::LightGreen() << "[DEV] Synchronization finished successfully, stopping P2Pool now");
print_status(false);
if (m_pool->p2p_server()) {
m_pool->p2p_server()->print_status();
}
m_pool->stop();
}
#endif
}
} // namespace p2pool } // namespace p2pool
+29 -8
View File
@@ -19,6 +19,7 @@
#include "uv_util.h" #include "uv_util.h"
#include <map> #include <map>
#include <thread>
namespace p2pool { namespace p2pool {
@@ -31,13 +32,13 @@ class Wallet;
struct MinerShare struct MinerShare
{ {
FORCEINLINE MinerShare() : m_weight(0), m_wallet(nullptr) {} FORCEINLINE MinerShare() : m_weight(0), m_wallet(nullptr) {}
FORCEINLINE MinerShare(uint64_t w, Wallet* x) : m_weight(w), m_wallet(x) {} FORCEINLINE MinerShare(uint64_t w, const Wallet* x) : m_weight(w), m_wallet(x) {}
uint64_t m_weight; uint64_t m_weight;
Wallet* m_wallet; const Wallet* m_wallet;
}; };
class SideChain class SideChain : public nocopy_nomove
{ {
public: public:
SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr); SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr);
@@ -55,9 +56,9 @@ public:
void watch_mainchain_block(const ChainMain& data, const hash& possible_id); void watch_mainchain_block(const ChainMain& data, const hash& possible_id);
bool get_block_blob(const hash& id, std::vector<uint8_t>& blob) const; 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; bool get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob, uv_loop_t* loop) const;
void print_status() const; void print_status(bool obtain_sidechain_lock = true) const;
double get_reward_share(const Wallet& w) const; double get_reward_share(const Wallet& w) const;
// Consensus ID can be used to spawn independent P2Pools with their own sidechains // Consensus ID can be used to spawn independent P2Pools with their own sidechains
@@ -84,11 +85,12 @@ private:
NetworkType m_networkType; NetworkType m_networkType;
private: private:
bool get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) const; bool get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares) const;
bool get_difficulty(PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const; bool get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const;
bool get_wallets(const PoolBlock* tip, std::vector<const Wallet*>& wallets) const;
void verify_loop(PoolBlock* block); void verify_loop(PoolBlock* block);
void verify(PoolBlock* block); void verify(PoolBlock* block);
void update_chain_tip(PoolBlock* block); void update_chain_tip(const PoolBlock* block);
PoolBlock* get_parent(const PoolBlock* block) const; PoolBlock* get_parent(const PoolBlock* block) const;
// Checks if "candidate" has longer (higher difficulty) chain than "block" // Checks if "candidate" has longer (higher difficulty) chain than "block"
@@ -128,6 +130,25 @@ private:
ChainMain m_watchBlock; ChainMain m_watchBlock;
hash m_watchBlockSidechainId; hash m_watchBlockSidechainId;
struct PrecalcJob
{
const PoolBlock* b;
std::vector<const Wallet*> wallets;
};
uv_cond_t m_precalcJobsCond;
uv_mutex_t m_precalcJobsMutex;
std::vector<PrecalcJob*> m_precalcJobs;
std::vector<std::thread> m_precalcWorkers;
unordered_set<size_t>* m_uniquePrecalcInputs;
std::atomic<bool> m_precalcFinished;
void launch_precalc(const PoolBlock* block);
void precalc_worker();
void finish_precalc();
}; };
} // namespace p2pool } // namespace p2pool
+14 -3
View File
@@ -33,6 +33,10 @@ static constexpr uint64_t AUTO_DIFF_TARGET_TIME = 30;
// Use short target format (4 bytes) for diff <= 4 million // Use short target format (4 bytes) for diff <= 4 million
static constexpr uint64_t TARGET_4_BYTES_LIMIT = std::numeric_limits<uint64_t>::max() / 4000001; static constexpr uint64_t TARGET_4_BYTES_LIMIT = std::numeric_limits<uint64_t>::max() / 4000001;
static constexpr int32_t BAD_SHARE_POINTS = -5;
static constexpr int32_t GOOD_SHARE_POINTS = 1;
static constexpr int32_t BAN_THRESHOLD_POINTS = -15;
#include "tcp_server.inl" #include "tcp_server.inl"
namespace p2pool { namespace p2pool {
@@ -614,14 +618,14 @@ void StratumServer::update_auto_diff(StratumClient* client, const uint64_t times
if (k >= N) { if (k >= N) {
// Full window // Full window
const uint64_t dt = t2 - t1; const uint16_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.lo = std::max<uint64_t>((client->m_autoDiffWindowHashes * AUTO_DIFF_TARGET_TIME) / (dt ? dt : 1), MIN_DIFF);
client->m_autoDiff.hi = 0; client->m_autoDiff.hi = 0;
} }
else if (k >= 10) { else if (k >= 10) {
// Partial window // Partial window
const uint64_t h0 = hash_uncompress(client->m_autoDiffData[0].m_hashes); 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; const uint16_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.lo = std::max<uint64_t>(((client->m_autoDiffWindowHashes - h0) * AUTO_DIFF_TARGET_TIME) / (dt ? dt : 1), MIN_DIFF);
client->m_autoDiff.hi = 0; client->m_autoDiff.hi = 0;
@@ -840,9 +844,12 @@ void StratumServer::on_share_found(uv_work_t* req)
if (pow_hash != share->m_resultHash) { if (pow_hash != share->m_resultHash) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " submitted a share with invalid PoW"); LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " submitted a share with invalid PoW");
share->m_result = SubmittedShare::Result::INVALID_POW; share->m_result = SubmittedShare::Result::INVALID_POW;
client->m_score += BAD_SHARE_POINTS;
return; return;
} }
client->m_score += GOOD_SHARE_POINTS;
const uint64_t n = server->m_cumulativeHashes + hashes; const uint64_t n = server->m_cumulativeHashes + hashes;
const double diff = sidechain_difficulty.to_double(); const double diff = sidechain_difficulty.to_double();
const double effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff; const double effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
@@ -868,6 +875,7 @@ void StratumServer::on_share_found(uv_work_t* req)
else { else {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a low diff share"); LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a low diff share");
share->m_result = SubmittedShare::Result::LOW_DIFF; share->m_result = SubmittedShare::Result::LOW_DIFF;
client->m_score += BAD_SHARE_POINTS;
} }
} }
@@ -910,7 +918,7 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
return s.m_pos; return s.m_pos;
}); });
if (bad_share) { if (bad_share && (client->m_score <= BAN_THRESHOLD_POINTS)) {
client->ban(DEFAULT_BAN_TIME); client->ban(DEFAULT_BAN_TIME);
client->close(); client->close();
} }
@@ -934,6 +942,7 @@ StratumServer::StratumClient::StratumClient()
, m_customDiff{} , m_customDiff{}
, m_autoDiff{} , m_autoDiff{}
, m_customUser{} , m_customUser{}
, m_score(0)
{ {
} }
@@ -953,6 +962,8 @@ void StratumServer::StratumClient::reset()
m_customDiff = {}; m_customDiff = {};
m_autoDiff = {}; m_autoDiff = {};
m_customUser[0] = '\0'; m_customUser[0] = '\0';
m_score = 0;
} }
bool StratumServer::StratumClient::on_connect() bool StratumServer::StratumClient::on_connect()
+2
View File
@@ -78,6 +78,8 @@ public:
difficulty_type m_customDiff; difficulty_type m_customDiff;
difficulty_type m_autoDiff; difficulty_type m_autoDiff;
char m_customUser[32]; char m_customUser[32];
int32_t m_score;
}; };
bool on_login(StratumClient* client, uint32_t id, const char* login); bool on_login(StratumClient* client, uint32_t id, const char* login);
+1
View File
@@ -83,6 +83,7 @@ public:
bool m_isV6; bool m_isV6;
bool m_isIncoming; bool m_isIncoming;
bool m_readBufInUse; bool m_readBufInUse;
bool m_isClosing;
uint32_t m_numRead; uint32_t m_numRead;
raw_ip m_addr; raw_ip m_addr;
+20 -6
View File
@@ -185,7 +185,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
err = uv_tcp_bind(socket, reinterpret_cast<sockaddr*>(&addr6), UV_TCP_IPV6ONLY); err = uv_tcp_bind(socket, reinterpret_cast<sockaddr*>(&addr6), UV_TCP_IPV6ONLY);
if (err) { if (err) {
LOGERR(1, "failed to bind tcp server IPv6 socket, error " << uv_err_name(err)); LOGERR(1, "failed to bind tcp server IPv6 socket " << address << ", error " << uv_err_name(err));
panic(); panic();
} }
} }
@@ -199,14 +199,14 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
err = uv_tcp_bind(socket, reinterpret_cast<sockaddr*>(&addr), 0); err = uv_tcp_bind(socket, reinterpret_cast<sockaddr*>(&addr), 0);
if (err) { if (err) {
LOGERR(1, "failed to bind tcp server IPv4 socket, error " << uv_err_name(err)); LOGERR(1, "failed to bind tcp server IPv4 socket " << address << ", error " << uv_err_name(err));
panic(); panic();
} }
} }
err = uv_listen(reinterpret_cast<uv_stream_t*>(socket), DEFAULT_BACKLOG, on_new_connection); err = uv_listen(reinterpret_cast<uv_stream_t*>(socket), DEFAULT_BACKLOG, on_new_connection);
if (err) { if (err) {
LOGERR(1, "failed to listen on tcp server socket, error " << uv_err_name(err)); LOGERR(1, "failed to listen on tcp server socket " << address << ", error " << uv_err_name(err));
panic(); panic();
} }
@@ -395,13 +395,13 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer_nolock(Client* cl
connect_request->data = client; connect_request->data = client;
err = uv_tcp_connect(connect_request, &client->m_socket, addr, on_connect); err = uv_tcp_connect(connect_request, &client->m_socket, addr, on_connect);
if (err) { if (err) {
LOGERR(1, "failed to initiate tcp connection, error " << uv_err_name(err)); LOGERR(1, "failed to initiate tcp connection to " << static_cast<const char*>(client->m_addrString) << ", error " << uv_err_name(err));
m_pendingConnections.erase(client->m_addr); m_pendingConnections.erase(client->m_addr);
uv_close(reinterpret_cast<uv_handle_t*>(&client->m_socket), on_connection_error); uv_close(reinterpret_cast<uv_handle_t*>(&client->m_socket), on_connection_error);
return false; return false;
} }
else { else {
LOGINFO(5, "connecting to " << log::Gray() << static_cast<char*>(client->m_addrString)); LOGINFO(5, "connecting to " << log::Gray() << static_cast<const char*>(client->m_addrString));
} }
return true; return true;
@@ -556,6 +556,11 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
LOGERR(1, "sending data from another thread, this is not thread safe"); LOGERR(1, "sending data from another thread, this is not thread safe");
} }
if (client->m_isClosing) {
LOGWARN(5, "client " << static_cast<const char*>(client->m_addrString) << " is being disconnected, can't send any more data");
return true;
}
WriteBuf* buf = nullptr; WriteBuf* buf = nullptr;
{ {
@@ -845,6 +850,7 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::Client()
, m_isV6(false) , m_isV6(false)
, m_isIncoming(false) , m_isIncoming(false)
, m_readBufInUse(false) , m_readBufInUse(false)
, m_isClosing(false)
, m_numRead(0) , m_numRead(0)
, m_addr{} , m_addr{}
, m_port(0) , m_port(0)
@@ -867,6 +873,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::reset()
m_isV6 = false; m_isV6 = false;
m_isIncoming = false; m_isIncoming = false;
m_readBufInUse = false; m_readBufInUse = false;
m_isClosing = false;
m_numRead = 0; m_numRead = 0;
m_addr = {}; m_addr = {};
m_port = -1; m_port = -1;
@@ -903,6 +910,11 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::on_read(uv_stream_t* stre
Client* pThis = static_cast<Client*>(stream->data); Client* pThis = static_cast<Client*>(stream->data);
pThis->m_readBufInUse = false; pThis->m_readBufInUse = false;
if (pThis->m_isClosing) {
LOGWARN(5, "client " << static_cast<const char*>(pThis->m_addrString) << " is being disconnected but data received from it, nread = " << nread << ". Ignoring it.");
return;
}
if (nread > 0) { if (nread > 0) {
if (pThis->m_owner && !pThis->m_owner->m_finished.load()) { if (pThis->m_owner && !pThis->m_owner->m_finished.load()) {
if (!pThis->on_read(buf->base, static_cast<uint32_t>(nread))) { if (!pThis->on_read(buf->base, static_cast<uint32_t>(nread))) {
@@ -944,11 +956,13 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::on_write(uv_write_t* req,
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE> template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::close() void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::close()
{ {
if (!m_owner) { if (m_isClosing || !m_owner) {
// Already closed // Already closed
return; return;
} }
m_isClosing = true;
uv_read_stop(reinterpret_cast<uv_stream_t*>(&m_socket)); uv_read_stop(reinterpret_cast<uv_stream_t*>(&m_socket));
uv_tcp_t* s = &m_socket; uv_tcp_t* s = &m_socket;
+10 -1
View File
@@ -32,7 +32,7 @@ namespace p2pool {
#define STR2(X) STR(X) #define STR2(X) STR(X)
#define STR(X) #X #define STR(X) #X
const char* VERSION = "v2.2 (built" const char* VERSION = "v2.3 (built"
#if defined(__clang__) #if defined(__clang__)
" with clang/" __clang_version__ " with clang/" __clang_version__
#elif defined(__GNUC__) #elif defined(__GNUC__)
@@ -205,6 +205,15 @@ std::istream& operator>>(std::istream& s, hash& h)
return s; return s;
} }
void uv_cond_init_checked(uv_cond_t* cond)
{
const int result = uv_cond_init(cond);
if (result) {
LOGERR(1, "failed to create conditional variable, error " << uv_err_name(result));
panic();
}
}
void uv_mutex_init_checked(uv_mutex_t* mutex) void uv_mutex_init_checked(uv_mutex_t* mutex)
{ {
const int result = uv_mutex_init(mutex); const int result = uv_mutex_init(mutex);
+1 -1
View File
@@ -68,7 +68,7 @@ private:
template<typename T> FORCEINLINE ScopeGuard<T> on_scope_leave(T&& handler) { return ScopeGuard<T>(std::move(handler)); } template<typename T> FORCEINLINE ScopeGuard<T> on_scope_leave(T&& handler) { return ScopeGuard<T>(std::move(handler)); }
#define ON_SCOPE_LEAVE(x) auto CONCAT(scope_guard_, __LINE__) = on_scope_leave(x); #define ON_SCOPE_LEAVE(...) auto CONCAT(scope_guard_, __LINE__) = on_scope_leave(__VA_ARGS__);
struct MinerCallbackHandler struct MinerCallbackHandler
{ {
+26 -2
View File
@@ -57,6 +57,7 @@ private:
typedef RWLock<true> WriteLock; typedef RWLock<true> WriteLock;
void uv_cond_init_checked(uv_cond_t* cond);
void uv_mutex_init_checked(uv_mutex_t* mutex); void uv_mutex_init_checked(uv_mutex_t* mutex);
void uv_rwlock_init_checked(uv_rwlock_t* lock); void uv_rwlock_init_checked(uv_rwlock_t* lock);
uv_loop_t* uv_default_loop_checked(); uv_loop_t* uv_default_loop_checked();
@@ -137,9 +138,12 @@ struct UV_LoopUserData
UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create = true); UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create = true);
template<typename T> template<typename T>
void CallOnLoop(uv_loop_t* loop, T&& callback) bool CallOnLoop(uv_loop_t* loop, T&& callback)
{ {
UV_LoopUserData* data = GetLoopUserData(loop, false); UV_LoopUserData* data = GetLoopUserData(loop, false);
if (!data) {
return false;
}
UV_LoopCallbackBase* cb = new UV_LoopCallback<T>(std::move(callback)); UV_LoopCallbackBase* cb = new UV_LoopCallback<T>(std::move(callback));
{ {
@@ -147,7 +151,27 @@ void CallOnLoop(uv_loop_t* loop, T&& callback)
data->m_callbacks.push_back(cb); data->m_callbacks.push_back(cb);
} }
uv_async_send(data->m_async); if (uv_async_send(data->m_async) == 0) {
return true;
}
// Clean up after uv_async_send error
bool found = false;
{
MutexLock lock(data->m_callbacksLock);
auto it = std::find(data->m_callbacks.begin(), data->m_callbacks.end(), cb);
if (it != data->m_callbacks.end()) {
found = true;
data->m_callbacks.erase(it);
}
}
if (found) {
delete cb;
}
return false;
} }
} // namespace p2pool } // namespace p2pool
+1
View File
@@ -148,3 +148,4 @@ target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${U
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}>) 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}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/mainnet_test2_block.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>) add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/mainnet_test2_block.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/sidechain_dump.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>) add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/sidechain_dump.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/sidechain_dump_mini.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
+21 -7
View File
@@ -49,7 +49,7 @@ TEST(pool_block, deserialize)
f.read(reinterpret_cast<char*>(buf.data()), buf.size()); f.read(reinterpret_cast<char*>(buf.data()), buf.size());
ASSERT_EQ(f.good(), true); ASSERT_EQ(f.good(), true);
ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain), 0); ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain, nullptr), 0);
ASSERT_EQ(b.m_mainChainData.size(), 5607); ASSERT_EQ(b.m_mainChainData.size(), 5607);
ASSERT_EQ(b.m_mainChainHeaderSize, 43); ASSERT_EQ(b.m_mainChainHeaderSize, 43);
@@ -104,10 +104,23 @@ TEST(pool_block, verify)
{ {
init_crypto_cache(); init_crypto_cache();
PoolBlock b; struct STest
SideChain sidechain(nullptr, NetworkType::Mainnet); {
const char* m_poolName;
const char* m_fileName;
uint64_t m_txinGenHeight;
uint64_t m_sidechainHeight;
} tests[2] = {
{ "default", "sidechain_dump.dat", 2483901, 522805 },
{ "mini", "sidechain_dump_mini.dat", 2696040, 2424349 },
};
std::ifstream f("sidechain_dump.dat", std::ios::binary | std::ios::ate); for (const STest& t : tests)
{
PoolBlock b;
SideChain sidechain(nullptr, NetworkType::Mainnet, t.m_poolName);
std::ifstream f(t.m_fileName, std::ios::binary | std::ios::ate);
ASSERT_EQ(f.good() && f.is_open(), true); ASSERT_EQ(f.good() && f.is_open(), true);
std::vector<uint8_t> buf(f.tellg()); std::vector<uint8_t> buf(f.tellg());
@@ -121,7 +134,7 @@ TEST(pool_block, verify)
p += sizeof(uint32_t); p += sizeof(uint32_t);
ASSERT_TRUE(p + n <= e); ASSERT_TRUE(p + n <= e);
ASSERT_EQ(b.deserialize(p, n, sidechain), 0); ASSERT_EQ(b.deserialize(p, n, sidechain, nullptr), 0);
p += n; p += n;
sidechain.add_block(b); sidechain.add_block(b);
@@ -133,8 +146,9 @@ TEST(pool_block, verify)
ASSERT_TRUE(tip->m_verified); ASSERT_TRUE(tip->m_verified);
ASSERT_FALSE(tip->m_invalid); ASSERT_FALSE(tip->m_invalid);
ASSERT_EQ(tip->m_txinGenHeight, 2483901); ASSERT_EQ(tip->m_txinGenHeight, t.m_txinGenHeight);
ASSERT_EQ(tip->m_sidechainHeight, 522805); ASSERT_EQ(tip->m_sidechainHeight, t.m_sidechainHeight);
}
destroy_crypto_cache(); destroy_crypto_cache();
} }
Binary file not shown.