Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8aebcdc580 | |||
| e23a2e77a8 | |||
| 0f9362e14e | |||
| ab974b9684 | |||
| 54acfee036 | |||
| a78ddb50d5 | |||
| b2f86a5f6b | |||
| 2a77695832 | |||
| 21d8cb917b | |||
| 2c70bf8616 | |||
| 7522111bb7 | |||
| 7b5b910f3f | |||
| afb52e4235 | |||
| 93cd9a659a | |||
| 9e287ab4f0 | |||
| 094209f311 | |||
| dee66c3f32 | |||
| 32533e3c96 | |||
| a073923a82 | |||
| 3484b24caa | |||
| 1b3c1eb850 | |||
| 7fe032915f | |||
| 5b9b73abcc | |||
| a2afa29052 | |||
| a2d3dbad5e | |||
| e50cf060cb | |||
| 915988d694 | |||
| 345c231a9a | |||
| a7aed2f221 | |||
| 292e2580e5 | |||
| 028a5d0c88 | |||
| 8b27faad6d | |||
| cdc3206ee8 | |||
| 0c2b7d6010 | |||
| ef15c3b54f | |||
| f4bcdc7fe9 | |||
| aada1bb5cc | |||
| 796850d8c5 | |||
| ab0bc0488e | |||
| f3af02111d | |||
| c21d052d7a | |||
| 62b1690780 | |||
| 52050bbcfb | |||
| 0db57e5047 | |||
| e7919514d4 | |||
| 7ab21c6afd | |||
| 2209e0cc70 | |||
| a73b4616a4 | |||
| 9593311332 | |||
| 9a09d15bea | |||
| f082488e4b | |||
| c56773d4d6 | |||
| 752e2381d6 | |||
| 03f829e18c | |||
| 06b66c99db | |||
| 0b0ad9ff82 | |||
| de9f13d393 | |||
| 272d206741 | |||
| c9493ec7fa | |||
| 5ef4134955 | |||
| 8045c40026 | |||
| d4a1b14820 | |||
| cc6ce4a366 | |||
| 25e5f80a81 | |||
| d4b339b1a3 | |||
| 4bdd524d84 | |||
| b78fe02405 | |||
| dfa0c7ffb1 | |||
| ec97a14da7 | |||
| 3a2e9e8b94 | |||
| 069c96aa1d | |||
| 42370bf11e | |||
| dc506bb1b9 | |||
| ec2f4467fb | |||
| 9f449320b5 | |||
| 8767ef9e19 | |||
| cf184295b4 | |||
| 0cd9876a46 | |||
| 1e4e4bd08e | |||
| a640e41d74 | |||
| f90e00edea | |||
| 38ffd73b54 | |||
| c10a5ce323 | |||
| e2931f94b5 | |||
| 95c268be4c | |||
| 4ac4f8f860 | |||
| 248fcee174 | |||
| 89bcceb986 | |||
| 2b01350add | |||
| f1b6212c82 | |||
| 7d961ab3b9 | |||
| 862e7d965b | |||
| da2d9f7e2e | |||
| d8bba4dc82 | |||
| 3a8a24a5ee | |||
| eaf29872cb | |||
| 74f553274f | |||
| d0f1e6eb2e | |||
| f9def6cb73 | |||
| 2f659c8ecf | |||
| 2d559ca764 | |||
| 1328ea6aef | |||
| 07cfb54bdc | |||
| 5dd8d11b72 | |||
| dbab191bfc | |||
| e44baad73c | |||
| bb7a5b4d6d | |||
| 64116643c2 | |||
| 1e4c6ef5f4 | |||
| 61edc6ed70 | |||
| d283f91eb0 | |||
| dff2413cae | |||
| 998c2ba72f | |||
| b56d027dd5 | |||
| eaed654012 | |||
| 981b161cc9 | |||
| f59e9171e8 | |||
| b45540ca08 | |||
| 806e1ca0a3 | |||
| 113e39817d | |||
| 0869326f39 | |||
| 75d62c0303 | |||
| da4686b88f | |||
| a008eac8c6 | |||
| 3d60ae8c32 | |||
| 88f346414a |
+36
-14
@@ -9,15 +9,20 @@ on:
|
||||
jobs:
|
||||
build-ubuntu:
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, ubuntu-18.04]
|
||||
config:
|
||||
- {os: ubuntu-latest, c: gcc-11, cpp: g++-11}
|
||||
- {os: ubuntu-18.04, c: gcc, cpp: g++}
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev
|
||||
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 ${{ matrix.config.c }} ${{ matrix.config.cpp }}
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
@@ -28,7 +33,7 @@ jobs:
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
|
||||
make -j2
|
||||
|
||||
- name: Build tests
|
||||
@@ -36,7 +41,7 @@ jobs:
|
||||
cd tests
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
|
||||
make -j2
|
||||
|
||||
- name: Run tests
|
||||
@@ -45,7 +50,7 @@ jobs:
|
||||
- name: Archive binary
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: p2pool-${{ matrix.os }}
|
||||
name: p2pool-${{ matrix.config.os }}
|
||||
path: build/p2pool
|
||||
|
||||
build-windows-msys2:
|
||||
@@ -101,8 +106,9 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\"}
|
||||
- {vs: Visual Studio 17 2022, os: 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Preview\\Msbuild\\Current\\Bin\\amd64\\"}
|
||||
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\", rx: "ON"}
|
||||
- {vs: Visual Studio 17 2022, os: 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\", rx: "ON"}
|
||||
- {vs: Visual Studio 17 2022, os: 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\", rx: "OFF"}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -117,7 +123,7 @@ jobs:
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "${{ matrix.config.vs }}"
|
||||
cmake .. -G "${{ matrix.config.vs }}" -DWITH_RANDOMX=${{ matrix.config.rx }}
|
||||
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool.vcxproj
|
||||
|
||||
- name: Build tests
|
||||
@@ -136,7 +142,7 @@ jobs:
|
||||
- name: Archive binary
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: p2pool-msbuild-${{ matrix.config.os }}.exe
|
||||
name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}.exe
|
||||
path: build/Release/p2pool.exe
|
||||
|
||||
build-macos:
|
||||
@@ -145,7 +151,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, macos-11]
|
||||
os: [macos-10.15, macos-11]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -154,13 +160,29 @@ jobs:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake libuv zmq libpgm
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
|
||||
|
||||
- name: Build libuv
|
||||
run: |
|
||||
cd external/src/libuv
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j3
|
||||
|
||||
- name: Build libzmq
|
||||
run: |
|
||||
cd external/src/libzmq
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF
|
||||
make -j3
|
||||
|
||||
- name: Build p2pool
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake .. -DSTATIC_BINARY=ON
|
||||
make -j3
|
||||
|
||||
- name: Build tests
|
||||
@@ -168,7 +190,7 @@ jobs:
|
||||
cd tests
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake .. -DSTATIC_LIBS=ON
|
||||
make -j3
|
||||
|
||||
- name: Run tests
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Visual Studio 16 2019"
|
||||
cmake .. -G "Visual Studio 17 2022"
|
||||
|
||||
- name: Run cppcheck
|
||||
run: |
|
||||
|
||||
@@ -19,6 +19,7 @@ on:
|
||||
env:
|
||||
# Path to the CMake build directory.
|
||||
build: '${{ github.workspace }}/build'
|
||||
config: 'Release'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@@ -32,18 +33,19 @@ jobs:
|
||||
submodules: true
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{ env.build }}
|
||||
run: cmake -B ${{ env.build }} -DCMAKE_BUILD_TYPE=${{ env.config }}
|
||||
|
||||
# Build is not required unless generated source files are used
|
||||
# - name: Build CMake
|
||||
# run: cmake --build ${{ env.build }}
|
||||
|
||||
- name: Initialize MSVC Code Analysis
|
||||
uses: microsoft/msvc-code-analysis-action@04825f6d9e00f87422d6bf04e1a38b1f3ed60d99
|
||||
uses: microsoft/msvc-code-analysis-action@47ecec99bf7ce0399ed85cbb5012b6f24eab5959
|
||||
# Provide a unique ID to access the sarif output path
|
||||
id: run-analysis
|
||||
with:
|
||||
cmakeBuildDirectory: ${{ env.build }}
|
||||
buildConfiguration: ${{ env.config }}
|
||||
# Ruleset file that will determine what checks will be run
|
||||
ruleset: NativeRecommendedRules.ruleset
|
||||
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
[submodule "external/src/cppzmq"]
|
||||
path = external/src/cppzmq
|
||||
url = https://github.com/SChernykh/cppzmq
|
||||
[submodule "external/src/libsodium"]
|
||||
path = external/src/libsodium
|
||||
url = https://github.com/SChernykh/libsodium
|
||||
[submodule "external/src/libuv"]
|
||||
path = external/src/libuv
|
||||
url = https://github.com/SChernykh/libuv
|
||||
|
||||
+29
-8
@@ -1,7 +1,8 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
project(p2pool)
|
||||
|
||||
option(STATIC_LINUX_BINARY "Build static Linux binary" OFF)
|
||||
option(STATIC_BINARY "Build static binary" OFF)
|
||||
option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned off, p2pool will rely on monerod for verifying RandomX hashes" ON)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
@@ -9,8 +10,11 @@ if (${CMAKE_VERSION} VERSION_GREATER "3.5.2")
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
|
||||
endif()
|
||||
|
||||
add_subdirectory(external/src/RandomX)
|
||||
set(LIBS randomx)
|
||||
if (WITH_RANDOMX)
|
||||
add_definitions(-DWITH_RANDOMX)
|
||||
add_subdirectory(external/src/RandomX)
|
||||
set(LIBS randomx)
|
||||
endif()
|
||||
|
||||
include(cmake/flags.cmake)
|
||||
|
||||
@@ -73,6 +77,11 @@ set(SOURCES
|
||||
src/zmq_reader.cpp
|
||||
)
|
||||
|
||||
if (WITH_RANDOMX)
|
||||
set(HEADERS ${HEADERS} src/miner.h)
|
||||
set(SOURCES ${SOURCES} src/miner.cpp)
|
||||
endif()
|
||||
|
||||
include_directories(src)
|
||||
include_directories(external/src)
|
||||
include_directories(external/src/cryptonote)
|
||||
@@ -80,12 +89,16 @@ include_directories(external/src/libuv/include)
|
||||
include_directories(external/src/cppzmq)
|
||||
include_directories(external/src/libzmq/include)
|
||||
include_directories(external/src/llhttp)
|
||||
include_directories(external/src/RandomX/src)
|
||||
if (WITH_RANDOMX)
|
||||
include_directories(external/src/RandomX/src)
|
||||
endif()
|
||||
include_directories(external/src/rapidjson/include)
|
||||
include_directories(external/src/robin-hood-hashing/src/include)
|
||||
|
||||
if (WIN32)
|
||||
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
|
||||
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
|
||||
set(LIBS ${LIBS} pthread)
|
||||
elseif (NOT APPLE)
|
||||
set(LIBS ${LIBS} pthread gss dl)
|
||||
endif()
|
||||
@@ -122,15 +135,23 @@ add_definitions(/DZMQ_STATIC)
|
||||
|
||||
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
|
||||
|
||||
if (STATIC_LINUX_BINARY)
|
||||
if (STATIC_BINARY)
|
||||
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME})
|
||||
|
||||
if (WITH_RANDOMX)
|
||||
set(STATIC_LIBS randomx)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
|
||||
set(STATIC_LIBS ${STATIC_LIBS} pthread)
|
||||
elseif (NOT APPLE)
|
||||
set(STATIC_LIBS ${STATIC_LIBS} pthread dl)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||
"${CMAKE_SOURCE_DIR}/external/src/libsodium/src/libsodium/.libs/libsodium.a"
|
||||
"${CMAKE_SOURCE_DIR}/external/src/libzmq/build/lib/libzmq.a"
|
||||
"${CMAKE_SOURCE_DIR}/external/src/libuv/build/libuv_a.a"
|
||||
randomx
|
||||
pthread
|
||||
${STATIC_LIBS}
|
||||
)
|
||||
else()
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} ${LIBS})
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
FROM ubuntu:latest
|
||||
|
||||
RUN set -e && \
|
||||
apt-get update -q -y --no-install-recommends && \
|
||||
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
|
||||
git \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
cmake \
|
||||
libuv1-dev \
|
||||
libzmq3-dev \
|
||||
libsodium-dev \
|
||||
libpgm-dev \
|
||||
libnorm-dev \
|
||||
libgss-dev
|
||||
|
||||
ADD . /usr/src/p2pool
|
||||
WORKDIR /usr/src/p2pool
|
||||
RUN git submodule update --init --recursive && \
|
||||
mkdir build && \
|
||||
cd build && \
|
||||
cmake .. && \
|
||||
make -j$(nproc)
|
||||
|
||||
# ---
|
||||
|
||||
FROM ubuntu:latest
|
||||
COPY --from=0 /usr/src/p2pool/build/p2pool /
|
||||
|
||||
RUN set -e && \
|
||||
apt-get update -q -y --no-install-recommends && \
|
||||
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
|
||||
libzmq5 \
|
||||
libuv1 \
|
||||
&& \
|
||||
apt-get clean
|
||||
|
||||
RUN groupadd -r p2pool -g 1000 && \
|
||||
useradd -u 1000 -r -g p2pool -s /sbin/nologin -c "p2pool user" p2pool
|
||||
RUN mkdir -p /home/p2pool/.p2pool && \
|
||||
chown p2pool.p2pool /home/p2pool /home/p2pool/.p2pool
|
||||
USER p2pool
|
||||
|
||||
EXPOSE 3333
|
||||
EXPOSE 37889
|
||||
|
||||
VOLUME /home/p2pool/.p2pool
|
||||
|
||||
WORKDIR /home/p2pool/.p2pool
|
||||
ENTRYPOINT ["/p2pool"]
|
||||
@@ -2,17 +2,30 @@
|
||||
|
||||
Decentralized pool for Monero mining.
|
||||
|
||||
Mining on the mainnet has started! You can check the pool status at https://p2pool.io/
|
||||
Pool status and monitoring pages can be found at https://p2pool.io/, https://p2pool.io/mini/ and https://p2pool.observer/
|
||||
|
||||
### Build status
|
||||
### Build Status
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
<a href="https://scan.coverity.com/projects/schernykh-p2pool">
|
||||
<img alt="Coverity Scan Build Status"
|
||||
src="https://scan.coverity.com/projects/23659/badge.svg"/>
|
||||
</a>
|
||||
# Contents
|
||||
- [Pool mining vs Solo mining vs P2Pool mining](#pool-mining-vs-solo-mining-vs-p2pool-mining)
|
||||
- [Features](#features)
|
||||
- [How PPLNS works in P2Pool](#how-pplns-works-in-p2pool)
|
||||
- [Default P2Pool parameters](#default-p2pool-parameters)
|
||||
- [Monero version support](#monero-version-support)
|
||||
- [How to mine on P2Pool](#how-to-mine-on-p2pool)
|
||||
- [General Considerations](#general-considerations)
|
||||
- [GNU/Linux](#gnulinux)
|
||||
- [Windows](#windows)
|
||||
- [Build instructions](#build-instructions)
|
||||
- [Donations](#donations)
|
||||
|
||||
## Pool mining vs Solo mining vs P2Pool mining
|
||||
|
||||
@@ -22,7 +35,7 @@ Here's the comparison table of the different ways of mining. While pool mining i
|
||||
|-|-|-|-|-|-|-|-|
|
||||
|Centralized pool|Regular|0-3%|0.001-0.01 XMR|Yes|Less stable due to pool server outages|Pool admin controls your mined funds, what you mine and can execute network attacks|Only miner software is required
|
||||
|Solo|Rare|0%|0.6 XMR or more|No|As stable as your Monero node|100% under your control|Monero node + optional miner
|
||||
|**P2Pool**|Regular|0%|less than 0.0004 XMR|No|As stable as your Monero node|100% under your control|Monero node + P2Pool node + miner
|
||||
|**P2Pool**|Regular|0%|~0.0003 XMR|No|As stable as your Monero node|100% under your control|Monero node + P2Pool node + miner
|
||||
|
||||
## Features
|
||||
|
||||
@@ -32,7 +45,7 @@ Here's the comparison table of the different ways of mining. While pool mining i
|
||||
* PPLNS payout scheme
|
||||
* **0% fee**
|
||||
* **0 XMR payout fee**
|
||||
* **Less than 0.0004 XMR minimal payout**
|
||||
* **~0.0003 XMR minimal payout**
|
||||
* Fast block times, down to 1 second
|
||||
* Uncle blocks are supported to avoid orphans - all your shares will be accounted for!
|
||||
* Configurable PPLNS window size and block time
|
||||
@@ -49,13 +62,143 @@ First you need to find a pool share. This share will stay in PPLNS window for 21
|
||||
|
||||
* Block time: 10 seconds
|
||||
* PPLNS window: 2160 blocks (6 hours)
|
||||
* Minimum payout = Monero block reward/2160, currently ~0.0004 XMR
|
||||
* Minimum payout = Monero block reward/2160, ~0.0003 XMR
|
||||
|
||||
## 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 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
|
||||
|v15, v16 (active after July 16th, 2022)|v0.18.0.0 or newer|v2.0 or newer
|
||||
|
||||
## How to mine on P2Pool
|
||||
|
||||
### General Considerations
|
||||
|
||||
- In order to mine on P2Pool, a synced Monero node using monerod v0.17.3.0 or newer is required. If you do not currently have one configured, you can find instructions to do so [here](https://sethforprivacy.com/guides/run-a-monero-node-advanced/).
|
||||
- It is highly recommended that you create a separate restricted user account for mining. While P2Pool has been battle-tested for a long time now, any software may have unknown bugs/vulnerabilities.
|
||||
- You have to use a primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
|
||||
- Starting from P2Pool v1.7, you can add the `--mini` parameter to your P2Pool command to connect to the **p2pool-mini** sidechain. Note that it will also change the default p2p port from 37889 to 37888.
|
||||
- Check that ports 18080 (Monero p2p port) and 37889/37888 (P2Pool/P2Pool mini p2p port) are open 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.
|
||||
- You can connect multiple miners to the same P2Pool node. The more the better!
|
||||
- The below steps assume that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup.
|
||||
- It is highly recommended to create a new mainnet wallet for P2Pool mining because **wallet addresses are public on P2Pool**.
|
||||
|
||||
**Wallet software compatible with P2Pool payouts**
|
||||
- [Official Monero CLI and GUI v0.17.2.3 and newer](https://www.getmonero.org/downloads/)
|
||||
- [Monerujo v2.1.0 "Vertant" and newer](https://www.monerujo.io/)
|
||||
- [Cake Wallet v4.2.7 and newer](https://cakewallet.com/)
|
||||
- [Monero.com by Cake Wallet](https://monero.com/)
|
||||
- [Feather Wallet v1.0.0 and newer](https://featherwallet.org/)
|
||||
- [MyMonero](https://mymonero.com/)
|
||||
|
||||
### GNU/Linux
|
||||
|
||||
1. Download the latest P2Pool binaries [here](https://github.com/SChernykh/p2pool/releases/latest).
|
||||
- Alternatively, grab the latest source code for P2Pool and [build it](#build-instructions).
|
||||
2. Download the latest XMRig (linux-static-x64) binary [here](https://github.com/xmrig/xmrig/releases/latest).
|
||||
3. Prepare enough huge pages (required for each instance of monerod/P2Pool/XMRig):
|
||||
```
|
||||
sudo sysctl vm.nr_hugepages=3072
|
||||
```
|
||||
4. Check that ports 18080 (Monero p2p port) and 37889/37888 (P2Pool/P2Pool mini p2p port) are open in your local firewall to ensure better connectivity.
|
||||
5. Start `monerod` with the following command/options:
|
||||
```
|
||||
./monerod --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
|
||||
```
|
||||
**Note:** The `--zmq-pub` option is required for P2Pool to work properly.
|
||||
|
||||
6. Start P2Pool with the following command/options:
|
||||
```
|
||||
./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS
|
||||
```
|
||||
7. Wait until the initial P2Pool sync is finished (shouldn't take more than 5-10 minutes).
|
||||
8. Start XMRig with the following command/options:
|
||||
```
|
||||
./xmrig -o 127.0.0.1:3333
|
||||
```
|
||||
- Note that you don't need to specify your wallet address for XMRig. **Wallet addresses set in XMRig config will be ignored!**
|
||||
- To set a custom fixed difficulty for your miner (for example, 10000), instead start XMRig with the following options:
|
||||
```
|
||||
./xmrig -u x+10000 -o 127.0.0.1:3333
|
||||
```
|
||||
9. XMRig should connect and start mining!
|
||||
|
||||
**Additional Information:**
|
||||
- For a more in-depth beginner friendly walk-through with the option of using Docker, please see SethForPrivacy's guide at: https://sethforprivacy.com/guides/run-a-p2pool-node/
|
||||
- You can check the p2pool.log for any warnings or errors using the following command:
|
||||
```
|
||||
grep -E 'WARNING|ERROR' p2pool.log
|
||||
```
|
||||
- P2Pool has verbose logging by default, you can reduce it by using "loglevel N" command where N is between 0 and 6. Default loglevel is 3.
|
||||
- You can use `logrotate` with a config like this to control logfile growth:
|
||||
```
|
||||
<path-to-logfile>
|
||||
{
|
||||
rotate 7
|
||||
daily
|
||||
missingok
|
||||
delaycompress
|
||||
nocreate
|
||||
}
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
**Note:** *Windows SmartScreen may block incoming connections by files that are "Downloaded from the Internet". You can allow 'p2pool.exe' and 'monerod.exe' by double-clicking them, clicking "More Info", then click "Run Anyway" and then closing them immediately so you can run them from the command line. Advanced users can use the PowerShell cmdlet `Unblock-File` to remove this flag.*
|
||||
|
||||
1. Download the latest P2Pool binaries [here](https://github.com/SChernykh/p2pool/releases/latest).
|
||||
- Alternatively, grab the latest source code for P2Pool and [build it](#build-instructions).
|
||||
2. Download the latest XMRig binary [here](https://github.com/xmrig/xmrig/releases/latest).
|
||||
3. Expand the P2Pool binaries into an appropriate location (`%USERPROFILE%/bin` or `C:/bin/` are good options)
|
||||
4. Expand XMRig binary into an appropriate location (the same folder as P2Pool is fine).
|
||||
5. Prepare huge pages to work properly (each instance of monerod/P2Pool/XMRig needs them):
|
||||
- On Windows 10 or above, run XMRig at least once as Administrator (right-click Run As Administrator)
|
||||
- On earlier versions of Windows, you'll need to run XMRig as Administrator at least once per login.
|
||||
6. Open a command prompt and navigate to the folder where you extracted P2Pool.
|
||||
|
||||
**Note:** *When running the below commands, Windows Firewall may prompt to allow connections, click "Allow" if prompted.*
|
||||
|
||||
7. Start `monerod` with the following command/options:
|
||||
```
|
||||
.\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
|
||||
```
|
||||
**Note:** The `--zmq-pub` option is required for P2Pool to work properly.
|
||||
8. Start P2Pool with the following command/options:
|
||||
```
|
||||
.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS
|
||||
```
|
||||
9. Wait until the initial P2Pool sync is finished (shouldn't take more than 5-10 minutes).
|
||||
10. Start XMRig with the following command/options:
|
||||
```
|
||||
.\xmrig.exe -o 127.0.0.1:3333
|
||||
```
|
||||
- Note that you don't need to specify your wallet address for XMRig. **Wallet addresses set in XMRig config will be ignored!**
|
||||
- To set a custom fixed difficulty for your miner (for example, 10000), instead start XMRig with the following options:
|
||||
```
|
||||
xmrig.exe -u x+10000 -o 127.0.0.1:3333
|
||||
```
|
||||
11. XMRig should connect and start mining!
|
||||
12. *(Optional but highly recommended)* You can create a Quickstart by creating a batch (.bat) file with the following contents and placing it in your P2Pool directory along with `xmrig.exe`.
|
||||
```
|
||||
@ECHO OFF
|
||||
start cmd /k %~dp0\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
|
||||
ECHO Wait until the Monero daemon shows fully synced before continuing. This can take some time. Type 'status' in other window to check progress.
|
||||
PAUSE
|
||||
start cmd /k %~dp0\p2pool.exe --wallet YOUR_WALLET_ADDRESS
|
||||
ECHO Wait until the daemon shows fully synced before continuing. This can take some time.
|
||||
PAUSE
|
||||
%~dp0\xmrig.exe -u x+30000 -o 127.0.0.1
|
||||
```
|
||||
|
||||
## Build instructions
|
||||
Please see the relevant instructions for your platform:
|
||||
|
||||
### Ubuntu 20.04
|
||||
|
||||
p2pool binary:
|
||||
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on Ubuntu 20.04:
|
||||
```
|
||||
sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev
|
||||
git clone --recursive https://github.com/SChernykh/p2pool
|
||||
@@ -65,23 +208,13 @@ cmake ..
|
||||
make -j$(nproc)
|
||||
```
|
||||
|
||||
monerod binary compatible with p2pool:
|
||||
```
|
||||
sudo apt update && sudo apt install git build-essential cmake pkg-config 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 doxygen graphviz
|
||||
git clone --recursive https://github.com/SChernykh/monero
|
||||
cd monero
|
||||
git checkout p2pool-api-v0.17
|
||||
git submodule sync && git submodule update --init --force --recursive
|
||||
make release-static -j$(nproc)
|
||||
```
|
||||
|
||||
### Arch Linux [AUR](https://wiki.archlinux.org/title/Arch_User_Repository)
|
||||
|
||||
Make the package: [p2pool-git](https://aur.archlinux.org/packages/p2pool-git/)
|
||||
|
||||
### [Nix/NixOS](https://nixos.org)
|
||||
|
||||
This is a flake only project. So you have to use [nixUnstable with nix flakes](https://nixos.wiki/wiki/Flakes) to build or install p2pool.
|
||||
This is a flake only project. So you have to use [nixUnstable with nix flakes](https://nixos.wiki/wiki/Flakes) to build or install P2Pool.
|
||||
The commands below use the new flake specific reference-format, so be sure to also set `ca-references` in `--experimental-features`.
|
||||
|
||||
Because this project has submodules which are not fixed in _nixUnstable_ yet you have to use the `nix/master` branch:
|
||||
@@ -99,31 +232,9 @@ Run the binary with arguments:
|
||||
nix run git+https://github.com/SChernykh/p2pool?ref=master -- --help
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
p2pool binary:
|
||||
```
|
||||
brew update && brew install git cmake libuv zmq libpgm
|
||||
git clone --recursive https://github.com/SChernykh/p2pool
|
||||
cd p2pool
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make -j$(sysctl -n hw.logicalcpu)
|
||||
```
|
||||
|
||||
monerod binary compatible with p2pool:
|
||||
```
|
||||
git clone --recursive https://github.com/SChernykh/monero
|
||||
cd monero
|
||||
git checkout p2pool-api-v0.17
|
||||
git submodule sync && git submodule update --init --force --recursive
|
||||
brew update && brew bundle --file=contrib/brew/Brewfile
|
||||
make -j$(sysctl -n hw.logicalcpu)
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
p2pool binary (Visual Studio Community 2019 build):
|
||||
P2Pool binary (Visual Studio Community 2019 build):
|
||||
*NOTE: You need to have the "Desktop Development with C++" module installed.*
|
||||
```
|
||||
git clone --recursive https://github.com/SChernykh/p2pool
|
||||
@@ -136,91 +247,28 @@ then open generated build\p2pool.sln in Visual Studio and build it there
|
||||
|
||||
Alternatively, you can select "Clone a repository" within the GUI, then select "Build" from the menu.
|
||||
|
||||
monerod binary compatible with p2pool:
|
||||
### macOS
|
||||
|
||||
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on your Mac:
|
||||
```
|
||||
git clone --recursive https://github.com/SChernykh/monero
|
||||
cd monero
|
||||
git checkout p2pool-api-v0.17
|
||||
git submodule sync && git submodule update --init --force --recursive
|
||||
brew update && brew install git cmake libuv zmq libpgm
|
||||
git clone --recursive https://github.com/SChernykh/p2pool
|
||||
cd p2pool
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make -j$(sysctl -n hw.logicalcpu)
|
||||
```
|
||||
then follow the instructions from https://github.com/monero-project/monero/#on-windows
|
||||
|
||||
## How to mine on P2Pool
|
||||
### FreeBSD
|
||||
|
||||
This guide assumes that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup. It's highly recommended to create a new mainnet wallet for mining because **wallet addresses are public on p2pool**.
|
||||
|
||||
**Wallet software compatible with p2pool payouts**
|
||||
- Official Monero CLI and GUI v0.17.2.3 and newer
|
||||
- Monerujo v2.1.0 "Vertant" and newer
|
||||
- Cake Wallet v4.2.7 and newer
|
||||
- MyMonero
|
||||
|
||||
**General Considerations**
|
||||
|
||||
- Create a separate restricted user account for mining. p2pool is relatively new and may still have serious bugs/vulnerabilities!
|
||||
- You have to use the primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
|
||||
- Check that ports 18080 (Monero p2p port) and 37889 (p2pool p2p port) are open 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.
|
||||
- You can connect multiple miners to the same p2pool node. The more the better!
|
||||
|
||||
Step-by-step guide:
|
||||
|
||||
### GNU/Linux
|
||||
|
||||
- Download binaries from https://github.com/SChernykh/p2pool/releases/latest
|
||||
- Alternatively, grab the latest source code for both p2pool and monerod and build them (see above, also notice that the branch name for monerod changed, you'll need to checkout p2pool-api-v0.17)
|
||||
- Prepare enough huge pages (each of monerod/p2pool/xmrig needs them): `sudo sysctl vm.nr_hugepages=3072`
|
||||
- Get xmrig (linux-static-x64) binary from https://github.com/xmrig/xmrig/releases/latest
|
||||
- Check that ports 18080 (Monero p2p port) and 37889 (p2pool p2p port) are open in your firewall to ensure better connectivity
|
||||
- Run `./monerod --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist` **don't forget --zmq-pub parameter in the command line**
|
||||
- Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-7dbb0d1fc)** on startup. Wait until it's synchronized.
|
||||
- Run `./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
|
||||
- p2pool has _very_ verbose logging by default, it will spam a lot, no I mean A LOT in both console and in p2pool.log. Logs help testing immensely!
|
||||
- You can use `logrotate` with a config like this to control logfile growth:
|
||||
```
|
||||
<path-to-logfile>
|
||||
{
|
||||
rotate 7
|
||||
daily
|
||||
missingok
|
||||
delaycompress
|
||||
nocreate
|
||||
}
|
||||
```
|
||||
- Wait until initial p2pool sync is finished, it shouldn't take more than 5-10 minutes, once completed xmrig should be able to connect to the stratum server on port 3333.
|
||||
- Run `./xmrig -o 127.0.0.1:3333`. Note that you don't need to specify wallet address for xmrig. **Wallet address set in xmrig config will be ignored!**
|
||||
- To set custom fixed difficulty for your miner (for example, 10000), run `./xmrig -u x+10000 -o 127.0.0.1:3333`
|
||||
- xmrig should connect and start mining
|
||||
- Also check p2pool.log for any warnings and errors: `grep -E 'WARNING|ERROR' p2pool.log`
|
||||
|
||||
### Windows
|
||||
|
||||
*NOTE: Windows SmartScreen may block incoming connections by files that are "Downloaded from the Internet". You can allow 'p2pool.exe' and 'monerod.exe' by double-clicking them, clicking "More Info", then click "Run Anyway" and then closing them immediately so you can run them from the command line. Advanced users can use the PowerShell cmdlet `Unblock-File` to remove this flag.*
|
||||
|
||||
- Download p2pool binaries from https://github.com/SChernykh/p2pool/releases/latest
|
||||
- Download xmrig binary from https://github.com/xmrig/xmrig/releases/latest *(xmrig-6.15.0-gcc-win64.zip is the current version)*
|
||||
- Expand the p2pool binaries into an appropriate location (`%USERPROFILE%/bin` or `C:/bin/` are good options)
|
||||
- Expand xmrig binary into appropriate location (same folder as p2pool is fine)
|
||||
- Prepare huge pages (each of monerod/p2pool/xmrig needs them):
|
||||
- On Windows 10 or above, run xmrig at least once as Administrator (right-click Run As Administrator)
|
||||
- On earlier versions of Windows, you'll need to run it as admin at least once per login.
|
||||
- Open a command prompt and navigate to the folder where you extracted p2pool.
|
||||
- *When running these commands, Windows Firewall may prompt to allow connections, click "Allow"*
|
||||
- Run `.\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist` *NOTE: don't forget --zmq-pub parameter in the command line*
|
||||
- Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-7dbb0d1fc)** on startup. Wait until it's synchronized.
|
||||
- Run `.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
|
||||
- Wait until initial p2pool sync is finished, it shouldn't take more than 5-10 minutes, once completed xmrig should be able to connect to the stratum server on port 3333.
|
||||
- Run `.\xmrig.exe -o 127.0.0.1:3333`. Note that you don't need to specify wallet address for xmrig. **Wallet address set in xmrig config will be ignored!**
|
||||
- To set custom fixed difficulty for your miner (for example, 10000), run `xmrig.exe -u x+10000 -o 127.0.0.1:3333`
|
||||
- Windows Quickstart: Create a batch (.bat) file with the following contents and place it in your p2pool directory along with xmrig.exe.
|
||||
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on FreeBSD:
|
||||
```
|
||||
@ECHO OFF
|
||||
start cmd /k %~dp0\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
|
||||
ECHO Wait until the Monero daemon shows fully synced before continuing. This can take some time. Type 'status' in other window to check progress.
|
||||
PAUSE
|
||||
start cmd /k %~dp0\p2pool.exe --wallet YOUR_WALLET_ADDRESS
|
||||
ECHO Wait until the daemon shows fully synced before continuing. This can take some time.
|
||||
PAUSE
|
||||
%~dp0\xmrig.exe -u x+30000 -o 127.0.0.1
|
||||
pkg install git cmake libuv libzmq4
|
||||
git clone --recursive https://github.com/SChernykh/p2pool
|
||||
cd p2pool
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
|
||||
## Donations
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||
if (WIN32)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
|
||||
else()
|
||||
if (STATIC_LINUX_BINARY)
|
||||
if (STATIC_BINARY)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
|
||||
else()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
// Fixed difficulty for miners that connect to your node must be set in their config.json
|
||||
//
|
||||
{
|
||||
"name": "mainnet test 2",
|
||||
"name": "default",
|
||||
"password": "",
|
||||
"block_time": 10,
|
||||
"min_diff": 100000,
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
##
|
||||
# Monero Wallet Address for mining rewards (replace this with your own address)
|
||||
WALLET_ADDRESS="44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg"
|
||||
|
||||
##
|
||||
# p2pool settings
|
||||
#
|
||||
# Use p2pool-mini (uncomment to enable)
|
||||
#P2POOL_MINI="--mini"
|
||||
#
|
||||
# Which port to listen for miner connections
|
||||
P2POOL_STRATUM_PORT=3333
|
||||
#
|
||||
# How much logging - (Less) 0 - 6 (More)
|
||||
P2POOL_LOGLEVEL=2
|
||||
|
||||
|
||||
##
|
||||
# Monero Node Settings
|
||||
#
|
||||
# Version of Monero to build. Must be v0.17.3.0 or later for p2pool support. "latest" pulls the most recent release tag
|
||||
MONERO_GIT_TAG=latest
|
||||
#
|
||||
# Limit the size of the blockchain on disk (comment to disable)
|
||||
PRUNE_NODE="--prune-blockchain"
|
||||
#
|
||||
# Other monerod commandline options
|
||||
MONERO_EXTRA_OPTIONS=""
|
||||
|
||||
|
||||
##
|
||||
# Xmrig Miner Settings
|
||||
#
|
||||
# Submit shares at a lower fixed difficulty to show mining progress (comment to disable, edit to customize)
|
||||
FIXED_MINING_DIFFICULTY="-u x+500000"
|
||||
#
|
||||
# set process priority (0 idle, 2 normal to 5 highest)
|
||||
MINING_CPU_PRIORITY="--cpu-priority=2"
|
||||
#
|
||||
# Other XMRIG commandline options
|
||||
# See: https://xmrig.com/docs/miner/command-line-options
|
||||
XMRIG_EXTRA_OPTIONS=""
|
||||
@@ -0,0 +1,52 @@
|
||||
# Monero P2Pool Docker Compose
|
||||
|
||||
Run your own <b>Monero Node + P2Pool + XMRig</b> in Docker
|
||||
|
||||
## Instructions
|
||||
|
||||
#### Install docker and docker-compose
|
||||
[Install Docker](https://docs.docker.com/engine/install/)
|
||||
[Install Docker Compose](https://docs.docker.com/compose/install/)
|
||||
|
||||
#### Clone the P2Pool project
|
||||
```
|
||||
git clone --recursive https://github.com/SChernykh/p2pool
|
||||
```
|
||||
|
||||
#### Configure your Monero address for mining rewards
|
||||
```
|
||||
cd p2pool/docker-compose
|
||||
vi .env
|
||||
```
|
||||
**WALLET_ADDRESS** is the only setting that needs to be updated in that file
|
||||
|
||||
#### Build the docker containers
|
||||
```
|
||||
docker-compose build
|
||||
```
|
||||
|
||||
#### Run the node, pool, and CPU miner
|
||||
```
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
#### 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
|
||||
* An XMRig CPU miner is included by default, but you can connect additional miners to this same p2pool node using port 3333
|
||||
* 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
|
||||
|
||||
|
||||
#### Other usefull commands
|
||||
* You can **run everythng 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 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 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```
|
||||
|
||||
|
||||
#### Uninstall
|
||||
Change to p2pool/docker-compose directory
|
||||
Stop and remove all containers: ```docker-compose down```
|
||||
Remove the p2pool data: ```docker volume rm p2pool```
|
||||
Remove the monero data: ```docker volume rm monero```
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
version: '3.4'
|
||||
|
||||
networks:
|
||||
p2pool:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
p2pool:
|
||||
name: p2pool
|
||||
monero:
|
||||
name: monero
|
||||
|
||||
services:
|
||||
monero:
|
||||
image: monero:latest
|
||||
build:
|
||||
context: monero
|
||||
args:
|
||||
- MONERO_GIT_TAG=${MONERO_GIT_TAG}
|
||||
container_name: p2pool-monero
|
||||
networks:
|
||||
- p2pool
|
||||
ports:
|
||||
- 18080:18080/tcp
|
||||
volumes:
|
||||
- monero:/home/monero/.bitmonero:rw
|
||||
- /dev/null:/home/monero/.bitmonero/bitmonero.log:rw
|
||||
- /dev/hugepages:/dev/hugepages:rw
|
||||
restart: unless-stopped
|
||||
command: >-
|
||||
--zmq-pub tcp://0.0.0.0:18083
|
||||
--disable-dns-checkpoints
|
||||
--enable-dns-blocklist
|
||||
--non-interactive
|
||||
--p2p-bind-ip=0.0.0.0
|
||||
--p2p-bind-port=18080
|
||||
--rpc-bind-ip=0.0.0.0
|
||||
--rpc-bind-port=18081
|
||||
--confirm-external-bind
|
||||
${PRUNE_NODE}
|
||||
${MONERO_EXTRA_OPTIONS}
|
||||
|
||||
p2pool:
|
||||
image: p2pool:latest
|
||||
build: ../
|
||||
container_name: p2pool-p2pool
|
||||
networks:
|
||||
- p2pool
|
||||
ports:
|
||||
- ${P2POOL_STRATUM_PORT}:3333/tcp
|
||||
- 37888:37888/tcp
|
||||
- 37889:37889/tcp
|
||||
volumes:
|
||||
- p2pool:/home/p2pool/.p2pool:rw
|
||||
- /dev/null:/home/p2pool/.p2pool/p2pool.log:rw
|
||||
- /dev/hugepages:/dev/hugepages:rw
|
||||
depends_on:
|
||||
- monero
|
||||
restart: unless-stopped
|
||||
command: >-
|
||||
--host monero
|
||||
--wallet ${WALLET_ADDRESS}
|
||||
--loglevel ${P2POOL_LOGLEVEL}
|
||||
${P2POOL_MINI}
|
||||
|
||||
xmrig:
|
||||
image: xmrig:latest
|
||||
build: xmrig
|
||||
container_name: p2pool-xmrig
|
||||
networks:
|
||||
- p2pool
|
||||
privileged: true
|
||||
volumes:
|
||||
- /dev:/dev:ro
|
||||
- /lib/modules:/lib/modules:ro
|
||||
- /dev/hugepages:/dev/hugepages:rw
|
||||
depends_on:
|
||||
- p2pool
|
||||
restart: unless-stopped
|
||||
command: >-
|
||||
${FIXED_MINING_DIFFICULTY}
|
||||
${MINING_CPU_PRIORITY}
|
||||
${XMRIG_EXTRA_OPTIONS}
|
||||
--randomx-1gb-pages
|
||||
-o p2pool:3333
|
||||
@@ -0,0 +1,79 @@
|
||||
FROM ubuntu:latest
|
||||
ARG MONERO_GIT_TAG="latest"
|
||||
|
||||
RUN set -e && \
|
||||
apt-get update -q -y --no-install-recommends && \
|
||||
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
|
||||
git \
|
||||
ca-certificates \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
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 \
|
||||
doxygen \
|
||||
graphviz
|
||||
|
||||
WORKDIR /usr/src
|
||||
RUN git clone --recursive https://github.com/monero-project/monero && \
|
||||
cd monero && \
|
||||
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 submodule sync && git submodule update --init --force --recursive && \
|
||||
make release-static -j$(nproc)
|
||||
|
||||
# ---
|
||||
|
||||
FROM ubuntu:latest
|
||||
|
||||
COPY --from=0 /usr/src/monero/build/Linux/*/release/bin/* /
|
||||
|
||||
RUN set -e && \
|
||||
apt-get update -q -y --no-install-recommends && \
|
||||
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
|
||||
libgssapi-krb5-2 \
|
||||
&& \
|
||||
apt-get clean
|
||||
|
||||
RUN groupadd -r monero -g 1000 && \
|
||||
useradd -u 1000 -r -g monero -s /sbin/nologin -c "monero node user" monero
|
||||
RUN mkdir -p /home/monero/.bitmonero && \
|
||||
chown monero.monero /home/monero /home/monero/.bitmonero
|
||||
USER monero
|
||||
|
||||
EXPOSE 18080 18081 18083
|
||||
|
||||
VOLUME /home/monero/.bitmonero
|
||||
|
||||
WORKDIR /home/monero
|
||||
ENTRYPOINT ["/monerod"]
|
||||
@@ -0,0 +1,37 @@
|
||||
FROM ubuntu:latest
|
||||
|
||||
RUN set -e && \
|
||||
apt-get update -q -y --no-install-recommends && \
|
||||
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
|
||||
git \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
cmake \
|
||||
libuv1-dev \
|
||||
libssl-dev \
|
||||
libhwloc-dev
|
||||
|
||||
WORKDIR /usr/src
|
||||
RUN git clone https://github.com/xmrig/xmrig.git && \
|
||||
mkdir xmrig/build && \
|
||||
cd xmrig/build && \
|
||||
cmake .. && \
|
||||
make -j$(nproc)
|
||||
|
||||
# ---
|
||||
|
||||
FROM ubuntu:latest
|
||||
|
||||
COPY --from=0 /usr/src/xmrig/build/xmrig /
|
||||
|
||||
RUN set -e && \
|
||||
apt-get update -q -y --no-install-recommends && \
|
||||
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
|
||||
libssl1.1 \
|
||||
libcrypto++6 \
|
||||
libhwloc15 \
|
||||
&& \
|
||||
apt-get clean
|
||||
|
||||
WORKDIR /
|
||||
ENTRYPOINT ["/xmrig"]
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Vendored
+1
-1
Submodule external/src/cppzmq updated: 286d8be432...a98fa4a91d
Vendored
+44
-8
@@ -3831,15 +3831,51 @@ int sc_isnonzero(const unsigned char *s) {
|
||||
s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1;
|
||||
}
|
||||
|
||||
int ge_p3_is_point_at_infinity(const ge_p3 *p) {
|
||||
// X = 0 and Y == Z
|
||||
int n;
|
||||
for (n = 0; n < 10; ++n)
|
||||
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) {
|
||||
// https://eprint.iacr.org/2008/522
|
||||
// X == T == 0 and Y/Z == 1
|
||||
// note: convert all pieces to canonical bytes in case rounding is required (i.e. an element is > q)
|
||||
// note2: even though T = XY/Z is true for valid point representations (implying it isn't necessary to
|
||||
// test T == 0), the input to this function might NOT be valid, so we must test T == 0
|
||||
char result_X_bytes[32];
|
||||
fe_tobytes((unsigned char*)&result_X_bytes, p->X);
|
||||
|
||||
// X != 0
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
if (p->X[n] | p->T[n])
|
||||
return 0;
|
||||
if (p->Y[n] != p->Z[n])
|
||||
if (result_X_bytes[i])
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
char result_T_bytes[32];
|
||||
fe_tobytes((unsigned char*)&result_T_bytes, p->T);
|
||||
|
||||
// T != 0
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
if (result_T_bytes[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
char result_Y_bytes[32];
|
||||
char result_Z_bytes[32];
|
||||
fe_tobytes((unsigned char*)&result_Y_bytes, p->Y);
|
||||
fe_tobytes((unsigned char*)&result_Z_bytes, p->Z);
|
||||
|
||||
// Y != Z
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
if (result_Y_bytes[i] != result_Z_bytes[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
// is Y nonzero? then Y/Z == 1
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
if (result_Y_bytes[i] != 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Y/Z = 0/0
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -162,4 +162,4 @@ void fe_add(fe h, const fe f, const fe g);
|
||||
void fe_tobytes(unsigned char *, const fe);
|
||||
void fe_invert(fe out, const fe z);
|
||||
|
||||
int ge_p3_is_point_at_infinity(const ge_p3 *p);
|
||||
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p);
|
||||
|
||||
Vendored
-1
Submodule external/src/libsodium deleted from 64129657a5
Vendored
+1
-1
Submodule external/src/libuv updated: a39009a5a9...03a698b9c6
Vendored
+1
-1
Submodule external/src/libzmq updated: 056f37f352...572eb008f1
Vendored
+1
-1
Submodule external/src/rapidjson updated: 5bbae3e50b...3c8d1dc5a5
Vendored
+1
-1
Submodule external/src/robin-hood-hashing updated: 668936f62e...7f87d77122
+3
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -154,6 +154,7 @@ struct BlockCache::Impl : public nocopy_nomove
|
||||
BlockCache::BlockCache()
|
||||
: m_impl(new Impl())
|
||||
, m_flushRunning(0)
|
||||
, m_storeIndex(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -171,7 +172,7 @@ void BlockCache::store(const PoolBlock& block)
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* data = m_impl->m_data + (static_cast<size_t>(block.m_sidechainHeight % NUM_BLOCKS) * BLOCK_SIZE);
|
||||
uint8_t* data = m_impl->m_data + (static_cast<size_t>((m_storeIndex++) % NUM_BLOCKS) * BLOCK_SIZE);
|
||||
|
||||
*reinterpret_cast<uint32_t*>(data) = static_cast<uint32_t>(n1 + n2);
|
||||
memcpy(data + sizeof(uint32_t), block.m_mainChainData.data(), n1);
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -37,6 +37,7 @@ private:
|
||||
struct Impl;
|
||||
Impl* m_impl;
|
||||
std::atomic<uint32_t> m_flushRunning;
|
||||
std::atomic<uint32_t> m_storeIndex;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+94
-23
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
* Portions Copyright (c) 2012-2013 The Cryptonote developers
|
||||
* Portions Copyright (c) 2014-2021 The Monero Project
|
||||
* Portions Copyright (c) 2021 XMRig <https://github.com/xmrig>
|
||||
@@ -190,12 +190,17 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
|
||||
++m_templateId;
|
||||
|
||||
// When block template generation fails for any reason
|
||||
auto use_old_template = [this]() {
|
||||
const uint32_t id = m_templateId - 1;
|
||||
LOGWARN(4, "using old block template with ID = " << id);
|
||||
*this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)];
|
||||
};
|
||||
|
||||
m_height = data.height;
|
||||
m_difficulty = data.difficulty;
|
||||
m_seedHash = data.seed_hash;
|
||||
|
||||
const time_t cur_time = time(nullptr);
|
||||
|
||||
// Only choose transactions that were received 10 or more seconds ago
|
||||
size_t total_mempool_transactions;
|
||||
{
|
||||
@@ -205,6 +210,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
|
||||
total_mempool_transactions = mempool.m_transactions.size();
|
||||
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
|
||||
for (auto& it : mempool.m_transactions) {
|
||||
if (cur_time >= it.second.time_received + 10) {
|
||||
m_mempoolTxs.emplace_back(it.second);
|
||||
@@ -252,7 +259,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
m_poolBlockTemplate->m_minorVersion = HARDFORK_SUPPORTED_VERSION;
|
||||
|
||||
// Timestamp
|
||||
m_timestamp = cur_time;
|
||||
m_timestamp = time(nullptr);
|
||||
if (m_timestamp <= data.median_timestamp) {
|
||||
LOGWARN(2, "timestamp adjusted from " << m_timestamp << " to " << data.median_timestamp + 1 << ". Fix your system time!");
|
||||
m_timestamp = data.median_timestamp + 1;
|
||||
@@ -275,21 +282,26 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
|
||||
m_pool->side_chain().fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares);
|
||||
if (!SideChain::split_reward(max_reward, m_shares, m_rewards)) {
|
||||
use_old_template();
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t max_reward_amounts_weight = std::accumulate(m_rewards.begin(), m_rewards.end(), 0ULL,
|
||||
[](uint64_t a, uint64_t b)
|
||||
{
|
||||
writeVarint(b, [&a](uint8_t) { ++a; });
|
||||
return a;
|
||||
});
|
||||
auto get_reward_amounts_weight = [this]() {
|
||||
return std::accumulate(m_rewards.begin(), m_rewards.end(), 0ULL,
|
||||
[](uint64_t a, uint64_t b)
|
||||
{
|
||||
writeVarint(b, [&a](uint8_t) { ++a; });
|
||||
return a;
|
||||
});
|
||||
};
|
||||
uint64_t max_reward_amounts_weight = get_reward_amounts_weight();
|
||||
|
||||
if (!create_miner_tx(data, m_shares, max_reward_amounts_weight, true)) {
|
||||
if (create_miner_tx(data, m_shares, max_reward_amounts_weight, true) < 0) {
|
||||
use_old_template();
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t miner_tx_weight = m_minerTx.size();
|
||||
uint64_t miner_tx_weight = m_minerTx.size();
|
||||
|
||||
// Select transactions from the mempool
|
||||
uint64_t final_reward, final_fees, final_weight;
|
||||
@@ -427,17 +439,64 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
}
|
||||
|
||||
if (!SideChain::split_reward(final_reward, m_shares, m_rewards)) {
|
||||
use_old_template();
|
||||
return;
|
||||
}
|
||||
|
||||
m_finalReward = final_reward;
|
||||
|
||||
if (!create_miner_tx(data, m_shares, max_reward_amounts_weight, false)) {
|
||||
return;
|
||||
const int create_miner_tx_result = create_miner_tx(data, m_shares, max_reward_amounts_weight, false);
|
||||
if (create_miner_tx_result < 0) {
|
||||
if (create_miner_tx_result == -3) {
|
||||
// Too many extra bytes were added, refine max_reward_amounts_weight and miner_tx_weight
|
||||
LOGINFO(4, "Readjusting miner_tx to reduce extra nonce size");
|
||||
|
||||
// The difference between max possible reward and the actual reward can't reduce the size of output amount varints by more than 1 byte each
|
||||
// So block weight will be >= current weight - number of outputs
|
||||
const uint64_t w = (final_weight > m_rewards.size()) ? (final_weight - m_rewards.size()) : 0;
|
||||
|
||||
// Block reward will be <= r due to how block size penalty works
|
||||
const uint64_t r = get_block_reward(base_reward, data.median_weight, final_fees, w);
|
||||
|
||||
if (!SideChain::split_reward(r, m_shares, m_rewards)) {
|
||||
use_old_template();
|
||||
return;
|
||||
}
|
||||
|
||||
max_reward_amounts_weight = get_reward_amounts_weight();
|
||||
|
||||
if (create_miner_tx(data, m_shares, max_reward_amounts_weight, true) < 0) {
|
||||
use_old_template();
|
||||
return;
|
||||
}
|
||||
|
||||
final_weight -= miner_tx_weight;
|
||||
final_weight += m_minerTx.size();
|
||||
miner_tx_weight = m_minerTx.size();
|
||||
|
||||
final_reward = get_block_reward(base_reward, data.median_weight, final_fees, final_weight);
|
||||
|
||||
if (!SideChain::split_reward(final_reward, m_shares, m_rewards)) {
|
||||
use_old_template();
|
||||
return;
|
||||
}
|
||||
|
||||
if (create_miner_tx(data, m_shares, max_reward_amounts_weight, false) < 0) {
|
||||
use_old_template();
|
||||
return;
|
||||
}
|
||||
|
||||
LOGINFO(4, "New extra nonce size = " << m_poolBlockTemplate->m_extraNonceSize);
|
||||
}
|
||||
else {
|
||||
use_old_template();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_minerTx.size() != miner_tx_weight) {
|
||||
LOGERR(1, "miner tx size changed after adjusting reward");
|
||||
use_old_template();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -600,7 +659,7 @@ void BlockTemplate::fill_optimal_knapsack(const MinerData& data, uint64_t base_r
|
||||
}
|
||||
#endif
|
||||
|
||||
bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run)
|
||||
int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run)
|
||||
{
|
||||
// Miner transaction (coinbase)
|
||||
m_minerTx.clear();
|
||||
@@ -630,6 +689,8 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
|
||||
m_poolBlockTemplate->m_outputs.clear();
|
||||
m_poolBlockTemplate->m_outputs.reserve(num_outputs);
|
||||
|
||||
const uint8_t tx_type = m_poolBlockTemplate->get_tx_type();
|
||||
|
||||
uint64_t reward_amounts_weight = 0;
|
||||
for (size_t i = 0; i < num_outputs; ++i) {
|
||||
writeVarint(m_rewards[i], [this, &reward_amounts_weight](uint8_t b)
|
||||
@@ -637,30 +698,36 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
|
||||
m_minerTx.push_back(b);
|
||||
++reward_amounts_weight;
|
||||
});
|
||||
m_minerTx.push_back(TXOUT_TO_KEY);
|
||||
m_minerTx.push_back(tx_type);
|
||||
|
||||
uint8_t view_tag = 0;
|
||||
|
||||
if (dry_run) {
|
||||
m_minerTx.insert(m_minerTx.end(), HASH_SIZE, 0);
|
||||
}
|
||||
else {
|
||||
hash eph_public_key;
|
||||
if (!shares[i].m_wallet->get_eph_public_key(m_txkeySec, i, eph_public_key)) {
|
||||
if (!shares[i].m_wallet->get_eph_public_key(m_txkeySec, i, eph_public_key, view_tag)) {
|
||||
LOGERR(1, "get_eph_public_key failed at index " << i);
|
||||
}
|
||||
m_minerTx.insert(m_minerTx.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
|
||||
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key);
|
||||
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key, tx_type, view_tag);
|
||||
}
|
||||
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
m_minerTx.emplace_back(view_tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (dry_run) {
|
||||
if (reward_amounts_weight != max_reward_amounts_weight) {
|
||||
LOGERR(1, "create_miner_tx: incorrect miner rewards during the dry run (" << reward_amounts_weight << " != " << max_reward_amounts_weight << ")");
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (reward_amounts_weight > max_reward_amounts_weight) {
|
||||
LOGERR(1, "create_miner_tx: incorrect miner rewards during the real run (" << reward_amounts_weight << " > " << max_reward_amounts_weight << ")");
|
||||
return false;
|
||||
return -2;
|
||||
}
|
||||
|
||||
m_poolBlockTemplate->m_txkeyPub = m_txkeyPub;
|
||||
@@ -676,6 +743,10 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
|
||||
|
||||
const uint64_t corrected_extra_nonce_size = EXTRA_NONCE_SIZE + max_reward_amounts_weight - reward_amounts_weight;
|
||||
if (corrected_extra_nonce_size > EXTRA_NONCE_SIZE) {
|
||||
if (corrected_extra_nonce_size > EXTRA_NONCE_MAX_SIZE) {
|
||||
LOGWARN(4, "create_miner_tx: corrected_extra_nonce_size (" << corrected_extra_nonce_size << ") is too large");
|
||||
return -3;
|
||||
}
|
||||
LOGINFO(4, "increased EXTRA_NONCE from " << EXTRA_NONCE_SIZE << " to " << corrected_extra_nonce_size << " bytes to maintain miner tx weight");
|
||||
}
|
||||
writeVarint(corrected_extra_nonce_size, m_minerTxExtra);
|
||||
@@ -701,7 +772,7 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
|
||||
// Not a part of transaction hash data
|
||||
m_minerTx.push_back(0);
|
||||
|
||||
return true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
hash BlockTemplate::calc_sidechain_hash() const
|
||||
@@ -1002,7 +1073,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
|
||||
m_poolBlockTemplate->m_nonce = nonce;
|
||||
m_poolBlockTemplate->m_extraNonce = extra_nonce;
|
||||
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_nonceOffset, &nonce, NONCE_SIZE);
|
||||
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_extraNonceOffsetInTemplate, &extra_nonce, NONCE_SIZE);
|
||||
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_extraNonceOffsetInTemplate, &extra_nonce, EXTRA_NONCE_SIZE);
|
||||
|
||||
SideChain& side_chain = m_pool->side_chain();
|
||||
|
||||
@@ -1018,7 +1089,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
|
||||
}
|
||||
|
||||
hash pow_hash;
|
||||
if (!check.get_pow_hash(m_pool->hasher(), m_seedHash, pow_hash)) {
|
||||
if (!check.get_pow_hash(m_pool->hasher(), check.m_txinGenHeight, m_seedHash, pow_hash)) {
|
||||
LOGERR(1, "PoW check failed for the sidechain block. Fix it! ");
|
||||
}
|
||||
else if (!check.m_difficulty.check_pow(pow_hash)) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
void update_tx_keys();
|
||||
|
||||
FORCEINLINE uint64_t height() const { return m_height; }
|
||||
FORCEINLINE time_t timestamp() const { return m_timestamp; }
|
||||
FORCEINLINE uint64_t timestamp() const { return m_timestamp; }
|
||||
FORCEINLINE difficulty_type difficulty() const { return m_difficulty; }
|
||||
|
||||
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
|
||||
@@ -61,7 +61,7 @@ private:
|
||||
p2pool* m_pool;
|
||||
|
||||
private:
|
||||
bool create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run);
|
||||
int create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run);
|
||||
hash calc_sidechain_hash() const;
|
||||
hash calc_miner_tx_hash(uint32_t extra_nonce) const;
|
||||
void calc_merkle_tree_main_branch();
|
||||
|
||||
+38
-4
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -54,6 +54,7 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
@@ -80,13 +81,16 @@
|
||||
namespace p2pool {
|
||||
|
||||
constexpr size_t HASH_SIZE = 32;
|
||||
constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 14;
|
||||
constexpr uint8_t HARDFORK_VIEW_TAGS_VERSION = 15;
|
||||
constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 16;
|
||||
constexpr uint8_t MINER_REWARD_UNLOCK_TIME = 60;
|
||||
constexpr uint8_t NONCE_SIZE = 4;
|
||||
constexpr uint8_t EXTRA_NONCE_SIZE = 4;
|
||||
constexpr uint8_t EXTRA_NONCE_MAX_SIZE = EXTRA_NONCE_SIZE + 10;
|
||||
constexpr uint8_t TX_VERSION = 2;
|
||||
constexpr uint8_t TXIN_GEN = 0xFF;
|
||||
constexpr uint8_t TXOUT_TO_KEY = 2;
|
||||
constexpr uint8_t TXOUT_TO_TAGGED_KEY = 3;
|
||||
constexpr uint8_t TX_EXTRA_TAG_PUBKEY = 1;
|
||||
constexpr uint8_t TX_EXTRA_NONCE = 2;
|
||||
constexpr uint8_t TX_EXTRA_MERGE_MINING_TAG = 3;
|
||||
@@ -113,6 +117,8 @@ FORCEINLINE uint64_t udiv128(uint64_t hi, uint64_t lo, uint64_t divisor, uint64_
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T> FORCEINLINE T round_up(T a, size_t granularity) { return static_cast<T>(((a + (granularity - static_cast<size_t>(1))) / granularity) * granularity); }
|
||||
|
||||
struct hash
|
||||
{
|
||||
alignas(8) uint8_t h[HASH_SIZE];
|
||||
@@ -232,7 +238,7 @@ struct TxMempoolData
|
||||
uint64_t blob_size;
|
||||
uint64_t weight;
|
||||
uint64_t fee;
|
||||
time_t time_received;
|
||||
uint64_t time_received;
|
||||
};
|
||||
|
||||
struct MinerData
|
||||
@@ -258,7 +264,7 @@ struct MinerData
|
||||
uint64_t median_timestamp;
|
||||
std::vector<TxMempoolData> tx_backlog;
|
||||
|
||||
std::chrono::system_clock::time_point time_received;
|
||||
std::chrono::high_resolution_clock::time_point time_received;
|
||||
};
|
||||
|
||||
struct ChainMain
|
||||
@@ -279,6 +285,34 @@ enum class NetworkType {
|
||||
Stagenet,
|
||||
};
|
||||
|
||||
struct raw_ip
|
||||
{
|
||||
alignas(8) uint8_t data[16];
|
||||
|
||||
FORCEINLINE bool operator<(const raw_ip& other) const
|
||||
{
|
||||
const uint64_t* a = reinterpret_cast<const uint64_t*>(data);
|
||||
const uint64_t* b = reinterpret_cast<const uint64_t*>(other.data);
|
||||
|
||||
if (a[1] < b[1]) return true;
|
||||
if (a[1] > b[1]) return false;
|
||||
|
||||
return a[0] < b[0];
|
||||
}
|
||||
|
||||
FORCEINLINE bool operator==(const raw_ip& other) const
|
||||
{
|
||||
const uint64_t* a = reinterpret_cast<const uint64_t*>(data);
|
||||
const uint64_t* b = reinterpret_cast<const uint64_t*>(other.data);
|
||||
|
||||
return (a[0] == b[0]) && (a[1] == b[1]);
|
||||
}
|
||||
|
||||
FORCEINLINE bool operator!=(const raw_ip& other) const { return !operator==(other); }
|
||||
};
|
||||
|
||||
static_assert(sizeof(raw_ip) == 16, "struct raw_ip has invalid size");
|
||||
|
||||
void* malloc_hook(size_t n) noexcept;
|
||||
void* realloc_hook(void* ptr, size_t size) noexcept;
|
||||
void* calloc_hook(size_t count, size_t size) noexcept;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021 hyc <https://github.com/hyc>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@@ -21,6 +21,9 @@
|
||||
#include "p2pool.h"
|
||||
#include "stratum_server.h"
|
||||
#include "p2p_server.h"
|
||||
#ifdef WITH_RANDOMX
|
||||
#include "miner.h"
|
||||
#endif
|
||||
#include "side_chain.h"
|
||||
#include <iostream>
|
||||
|
||||
@@ -69,7 +72,11 @@ typedef struct cmd {
|
||||
cmdfunc *func;
|
||||
} cmd;
|
||||
|
||||
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_showpeers, do_exit;
|
||||
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_showpeers, do_showbans, do_outpeers, do_inpeers, do_exit;
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
static cmdfunc do_start_mining, do_stop_mining;
|
||||
#endif
|
||||
|
||||
static cmd cmds[] = {
|
||||
{ STRCONST("help"), "", "display list of commands", do_help },
|
||||
@@ -78,17 +85,23 @@ static cmd cmds[] = {
|
||||
{ STRCONST("addpeers"), "<peeraddr>", "add peer", do_addpeers },
|
||||
{ STRCONST("droppeers"), "", "disconnect all peers", do_droppeers },
|
||||
{ STRCONST("peers"), "", "show all peers", do_showpeers },
|
||||
{ STRCONST("bans"), "", "show all banned IPs", do_showbans },
|
||||
{ STRCONST("outpeers"), "", "set maximum number of outgoing connections", do_outpeers },
|
||||
{ STRCONST("inpeers"), "", "set maximum number of incoming connections", do_inpeers },
|
||||
#ifdef WITH_RANDOMX
|
||||
{ STRCONST("start_mining"), "<threads>", "start mining", do_start_mining },
|
||||
{ STRCONST("stop_mining"), "", "stop mining", do_stop_mining },
|
||||
#endif
|
||||
{ STRCONST("exit"), "", "terminate p2pool", do_exit },
|
||||
{ STRCNULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static int do_help(p2pool * /* m_pool */, const char * /* args */)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOGINFO(0, "List of commands");
|
||||
for (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);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -101,13 +114,16 @@ static int do_status(p2pool *m_pool, const char * /* args */)
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->print_status();
|
||||
}
|
||||
#ifdef WITH_RANDOMX
|
||||
m_pool->print_miner_status();
|
||||
#endif
|
||||
bkg_jobs_tracker.print_status();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_loglevel(p2pool * /* m_pool */, const char *args)
|
||||
{
|
||||
int level = atoi(args);
|
||||
int level = strtol(args, nullptr, 10);
|
||||
level = std::min(std::max(level, 0), log::MAX_GLOBAL_LOG_LEVEL);
|
||||
log::GLOBAL_LOG_LEVEL = level;
|
||||
LOGINFO(0, "log level set to " << level);
|
||||
@@ -138,6 +154,51 @@ static int do_showpeers(p2pool* m_pool, const char* /* args */)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_showbans(p2pool* m_pool, const char* /* args */)
|
||||
{
|
||||
if (m_pool->stratum_server()) {
|
||||
m_pool->stratum_server()->print_bans();
|
||||
}
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->print_bans();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_outpeers(p2pool* m_pool, const char* args)
|
||||
{
|
||||
if (m_pool->p2p_server()) {
|
||||
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());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_inpeers(p2pool* m_pool, const char* args)
|
||||
{
|
||||
if (m_pool->p2p_server()) {
|
||||
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());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
static int do_start_mining(p2pool* m_pool, const char* args)
|
||||
{
|
||||
uint32_t threads = strtoul(args, nullptr, 10);
|
||||
threads = std::min(std::max(threads, 1u), 64u);
|
||||
m_pool->start_mining(threads);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_stop_mining(p2pool* m_pool, const char* /*args*/)
|
||||
{
|
||||
m_pool->stop_mining();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int do_exit(p2pool *m_pool, const char * /* args */)
|
||||
{
|
||||
bkg_jobs_tracker.wait();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+44
-22
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "crypto.h"
|
||||
#include "keccak.h"
|
||||
#include "uv_util.h"
|
||||
#include <random>
|
||||
|
||||
extern "C" {
|
||||
#include "crypto-ops.h"
|
||||
@@ -32,9 +31,12 @@ namespace {
|
||||
class RandomBytes
|
||||
{
|
||||
public:
|
||||
RandomBytes() : rd(), rng(rd()), dist(0, 255)
|
||||
RandomBytes() : rng(RandomDeviceSeed::instance), dist(0, 255)
|
||||
{
|
||||
uv_mutex_init_checked(&m);
|
||||
|
||||
// Diffuse the initial state in case it has low quality
|
||||
rng.discard(10000);
|
||||
}
|
||||
|
||||
~RandomBytes()
|
||||
@@ -54,7 +56,6 @@ public:
|
||||
private:
|
||||
uv_mutex_t m;
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937_64 rng;
|
||||
std::uniform_int_distribution<> dist;
|
||||
};
|
||||
@@ -108,9 +109,9 @@ bool check_keys(const hash& pub, const hash& sec)
|
||||
return pub == pub_check;
|
||||
}
|
||||
|
||||
static FORCEINLINE void hash_to_scalar(const uint8_t* data, size_t length, uint8_t(&res)[HASH_SIZE])
|
||||
static FORCEINLINE void hash_to_scalar(const uint8_t* data, int length, uint8_t (&res)[HASH_SIZE])
|
||||
{
|
||||
keccak(data, static_cast<int>(length), res, HASH_SIZE);
|
||||
keccak(data, length, res, HASH_SIZE);
|
||||
sc_reduce32(res);
|
||||
}
|
||||
|
||||
@@ -121,18 +122,13 @@ static FORCEINLINE void derivation_to_scalar(const hash& derivation, size_t outp
|
||||
uint8_t output_index[(sizeof(size_t) * 8 + 6) / 7];
|
||||
} buf;
|
||||
|
||||
uint8_t* begin = buf.derivation;
|
||||
uint8_t* end = buf.output_index;
|
||||
memcpy(buf.derivation, derivation.h, sizeof(buf.derivation));
|
||||
|
||||
size_t k = output_index;
|
||||
while (k >= 0x80) {
|
||||
*(end++) = (static_cast<uint8_t>(k) & 0x7F) | 0x80;
|
||||
k >>= 7;
|
||||
}
|
||||
*(end++) = static_cast<uint8_t>(k);
|
||||
uint8_t* p = buf.output_index;
|
||||
writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; });
|
||||
|
||||
hash_to_scalar(begin, end - begin, res);
|
||||
const uint8_t* data = buf.derivation;
|
||||
hash_to_scalar(data, static_cast<int>(p - data), res);
|
||||
}
|
||||
|
||||
class Cache
|
||||
@@ -148,17 +144,19 @@ public:
|
||||
uv_mutex_destroy(&m);
|
||||
}
|
||||
|
||||
bool get_derivation(const hash& key1, const hash& key2, hash& derivation)
|
||||
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> index;
|
||||
std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)> index;
|
||||
memcpy(index.data(), key1.h, HASH_SIZE);
|
||||
memcpy(index.data() + HASH_SIZE, key2.h, HASH_SIZE);
|
||||
memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t));
|
||||
|
||||
{
|
||||
MutexLock lock(m);
|
||||
auto it = derivations.find(index);
|
||||
if (it != derivations.end()) {
|
||||
derivation = it->second;
|
||||
derivation = it->second.derivation;
|
||||
view_tag = it->second.view_tag;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -176,9 +174,11 @@ public:
|
||||
ge_p1p1_to_p2(&point2, &point3);
|
||||
ge_tobytes(reinterpret_cast<uint8_t*>(&derivation), &point2);
|
||||
|
||||
derive_view_tag(derivation, output_index, view_tag);
|
||||
|
||||
{
|
||||
MutexLock lock(m);
|
||||
derivations.emplace(index, derivation);
|
||||
derivations.emplace(index, DerivationEntry{ derivation, view_tag } );
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -235,16 +235,22 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
struct DerivationEntry
|
||||
{
|
||||
hash derivation;
|
||||
uint8_t view_tag;
|
||||
};
|
||||
|
||||
uv_mutex_t m;
|
||||
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, hash> derivations;
|
||||
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, DerivationEntry> derivations;
|
||||
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
|
||||
};
|
||||
|
||||
static Cache* cache = nullptr;
|
||||
|
||||
bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation)
|
||||
bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag)
|
||||
{
|
||||
return cache->get_derivation(key1, key2, derivation);
|
||||
return cache->get_derivation(key1, key2, output_index, derivation, view_tag);
|
||||
}
|
||||
|
||||
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key)
|
||||
@@ -252,6 +258,22 @@ bool derive_public_key(const hash& derivation, size_t output_index, const hash&
|
||||
return cache->get_public_key(derivation, output_index, base, derived_key);
|
||||
}
|
||||
|
||||
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag)
|
||||
{
|
||||
constexpr uint8_t salt[] = "view_tag";
|
||||
constexpr size_t SALT_SIZE = sizeof(salt) - 1;
|
||||
|
||||
uint8_t buf[64];
|
||||
memcpy(buf, salt, SALT_SIZE);
|
||||
memcpy(buf + SALT_SIZE, derivation.h, HASH_SIZE);
|
||||
uint8_t* p = buf + SALT_SIZE + HASH_SIZE;
|
||||
writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; });
|
||||
|
||||
hash view_tag_full;
|
||||
keccak(buf, static_cast<int>(p - buf), view_tag_full.h, HASH_SIZE);
|
||||
view_tag = view_tag_full.h[0];
|
||||
}
|
||||
|
||||
void init_crypto_cache()
|
||||
{
|
||||
if (!cache) {
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,8 +21,9 @@ namespace p2pool {
|
||||
|
||||
void generate_keys(hash& pub, hash& sec);
|
||||
bool check_keys(const hash& pub, const hash& sec);
|
||||
bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation);
|
||||
bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag);
|
||||
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key);
|
||||
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag);
|
||||
|
||||
void init_crypto_cache();
|
||||
void destroy_crypto_cache();
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -25,7 +25,7 @@ static constexpr char log_category_prefix[] = "JSONRPCRequest ";
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb)
|
||||
JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
|
||||
: m_socket{}
|
||||
, m_connect{}
|
||||
, m_write{}
|
||||
@@ -38,7 +38,7 @@ JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, C
|
||||
{
|
||||
m_readBuf[0] = '\0';
|
||||
|
||||
uv_tcp_init(uv_default_loop_checked(), &m_socket);
|
||||
uv_tcp_init(loop ? loop : uv_default_loop_checked(), &m_socket);
|
||||
uv_tcp_nodelay(&m_socket, 1);
|
||||
|
||||
sockaddr_storage addr;
|
||||
@@ -55,13 +55,25 @@ JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, C
|
||||
m_connect.data = this;
|
||||
m_write.data = this;
|
||||
|
||||
const size_t len = strlen(req);
|
||||
const char* uri = "/json_rpc";
|
||||
|
||||
size_t len = req ? strlen(req) : 0;
|
||||
if (!len) {
|
||||
LOGERR(1, "Empty JSONRPCRequest, fix the code!");
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (req[0] == '/') {
|
||||
uri = req;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
m_request.reserve(std::max<size_t>(len + 128, log::Stream::BUF_SIZE + 1));
|
||||
m_request.resize(log::Stream::BUF_SIZE + 1);
|
||||
|
||||
log::Stream s(m_request.data());
|
||||
s << "POST /json_rpc HTTP/1.1\nContent-Type: application/json\nContent-Length: " << len << "\n\n";
|
||||
s << "POST " << uri << " HTTP/1.1\nContent-Type: application/json\nContent-Length: " << len << "\n\n";
|
||||
|
||||
m_request.resize(s.m_pos);
|
||||
m_request.insert(m_request.end(), req, req + len);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -26,20 +26,21 @@ public:
|
||||
static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb)
|
||||
{
|
||||
// It will be deleted in one of the tcp callbacks eventually
|
||||
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), nullptr);
|
||||
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), nullptr, nullptr);
|
||||
if (!r->m_valid) {
|
||||
delete r;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb, U&& close_cb)
|
||||
static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr)
|
||||
{
|
||||
// It will be deleted in one of the tcp callbacks eventually
|
||||
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), new Callback<U>(std::move(close_cb)));
|
||||
CallbackBase* close_callback = new Callback<U>(std::move(close_cb));
|
||||
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), close_callback, loop);
|
||||
if (!r->m_valid) {
|
||||
constexpr char err[] = "internal error";
|
||||
close_cb(err, sizeof(err) - 1);
|
||||
(*close_callback)(err, sizeof(err) - 1);
|
||||
delete r;
|
||||
}
|
||||
}
|
||||
@@ -62,7 +63,7 @@ private:
|
||||
T m_cb;
|
||||
};
|
||||
|
||||
JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb);
|
||||
JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
|
||||
~JSONRPCRequest();
|
||||
|
||||
static void on_connect(uv_connect_t* req, int status);
|
||||
|
||||
+14
-10
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -48,17 +48,21 @@ NOINLINE void keccakf(uint64_t* st)
|
||||
bc[3] = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
|
||||
bc[4] = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
uint64_t t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1);
|
||||
st[i + 0 ] ^= t;
|
||||
st[i + 5] ^= t;
|
||||
st[i + 10] ^= t;
|
||||
st[i + 15] ^= t;
|
||||
st[i + 20] ^= t;
|
||||
#define X(i) { \
|
||||
const uint64_t t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); \
|
||||
st[i + 0 ] ^= t; \
|
||||
st[i + 5] ^= t; \
|
||||
st[i + 10] ^= t; \
|
||||
st[i + 15] ^= t; \
|
||||
st[i + 20] ^= t; \
|
||||
}
|
||||
|
||||
X(0); X(1); X(2); X(3); X(4);
|
||||
|
||||
#undef X
|
||||
|
||||
// Rho Pi
|
||||
uint64_t t = st[1];
|
||||
const uint64_t t = st[1];
|
||||
st[ 1] = ROTL64(st[ 6], 44);
|
||||
st[ 6] = ROTL64(st[ 9], 20);
|
||||
st[ 9] = ROTL64(st[22], 61);
|
||||
@@ -155,7 +159,7 @@ NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen)
|
||||
|
||||
for (; inlen >= rsiz; inlen -= rsiz, in += rsiz) {
|
||||
for (int i = 0; i < rsizw; i++) {
|
||||
st[i] ^= ((uint64_t*)in)[i];
|
||||
st[i] ^= read_unaligned(reinterpret_cast<const uint64_t*>(in) + i);
|
||||
}
|
||||
keccakf(st);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+67
-17
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "uv_util.h"
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
|
||||
static constexpr char log_category_prefix[] = "Log ";
|
||||
static constexpr char log_file_name[] = "p2pool.log";
|
||||
@@ -32,7 +33,7 @@ bool CONSOLE_COLORS = true;
|
||||
|
||||
#ifndef P2POOL_LOG_DISABLE
|
||||
|
||||
static volatile bool stopped = false;
|
||||
static std::atomic<bool> stopped{ false };
|
||||
static volatile bool worker_started = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -47,14 +48,14 @@ public:
|
||||
enum params : int
|
||||
{
|
||||
SLOT_SIZE = 1024,
|
||||
BUF_SIZE = SLOT_SIZE * 16384,
|
||||
BUF_SIZE = SLOT_SIZE * 8192,
|
||||
};
|
||||
|
||||
FORCEINLINE Worker()
|
||||
: m_writePos(0)
|
||||
, m_readPos(0)
|
||||
{
|
||||
is_main_thread = true;
|
||||
set_main_thread();
|
||||
|
||||
m_logFile.open(log_file_name, std::ios::app | std::ios::binary);
|
||||
|
||||
@@ -65,9 +66,16 @@ public:
|
||||
|
||||
uv_cond_init(&m_cond);
|
||||
uv_mutex_init(&m_mutex);
|
||||
uv_thread_create(&m_worker, run_wrapper, this);
|
||||
|
||||
do {} while (!worker_started);
|
||||
const int err = uv_thread_create(&m_worker, run_wrapper, this);
|
||||
if (err) {
|
||||
fprintf(stderr, "failed to start logger thread (%s), aborting\n", uv_err_name(err));
|
||||
abort();
|
||||
}
|
||||
|
||||
while (!worker_started) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD dwConsoleMode;
|
||||
@@ -86,18 +94,21 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
FORCEINLINE ~Worker()
|
||||
~Worker()
|
||||
{
|
||||
stop();
|
||||
try {
|
||||
stop();
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
FORCEINLINE void stop()
|
||||
{
|
||||
if (stopped) {
|
||||
if (stopped.exchange(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
stopped = true;
|
||||
LOGINFO(0, "stopped");
|
||||
uv_thread_join(&m_worker);
|
||||
uv_cond_destroy(&m_cond);
|
||||
@@ -122,6 +133,7 @@ public:
|
||||
|
||||
memcpy(p + 1, buf + 1, size - 1);
|
||||
|
||||
// Ensure memory order in the writer thread
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
// Mark that everything is written into this log slot
|
||||
@@ -140,18 +152,26 @@ private:
|
||||
|
||||
do {
|
||||
uv_mutex_lock(&m_mutex);
|
||||
uv_cond_wait(&m_cond, &m_mutex);
|
||||
if (m_readPos == m_writePos.load()) {
|
||||
// Nothing to do, wait for the signal
|
||||
uv_cond_wait(&m_cond, &m_mutex);
|
||||
}
|
||||
uv_mutex_unlock(&m_mutex);
|
||||
|
||||
for (uint32_t writePos = m_writePos.load(); m_readPos < writePos; writePos = m_writePos.load()) {
|
||||
for (uint32_t writePos = m_writePos.load(); m_readPos != writePos; writePos = m_writePos.load()) {
|
||||
// We have at least one log slot pending, possibly more than one
|
||||
// Process everything in a loop before reading m_writePos again
|
||||
do {
|
||||
char* p = m_buf.data() + (m_readPos % BUF_SIZE);
|
||||
|
||||
// Wait until everything is written into this log slot
|
||||
volatile char& severity = *p;
|
||||
while (!severity) {}
|
||||
volatile char& severity = p[0];
|
||||
while (!severity) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
// Ensure memory order in the reader thread
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
uint32_t size = static_cast<uint8_t>(p[2]);
|
||||
size = (size << 8) + static_cast<uint8_t>(p[1]);
|
||||
@@ -160,7 +180,10 @@ private:
|
||||
p += 3;
|
||||
size -= 3;
|
||||
|
||||
if (!CONSOLE_COLORS) {
|
||||
// Read CONSOLE_COLORS only once because its value can be changed in another thread
|
||||
const bool c = CONSOLE_COLORS;
|
||||
|
||||
if (!c) {
|
||||
strip_colors(p, size);
|
||||
}
|
||||
|
||||
@@ -181,7 +204,7 @@ private:
|
||||
}
|
||||
|
||||
if (m_logFile.is_open()) {
|
||||
if (CONSOLE_COLORS) {
|
||||
if (c) {
|
||||
strip_colors(p, size);
|
||||
}
|
||||
|
||||
@@ -196,7 +219,6 @@ private:
|
||||
}
|
||||
|
||||
m_logFile.write(p, size);
|
||||
m_logFile.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +228,11 @@ private:
|
||||
m_readPos += SLOT_SIZE;
|
||||
} while (m_readPos < writePos);
|
||||
}
|
||||
|
||||
// Flush the log file only after all pending log lines have been written
|
||||
if (m_logFile.is_open()) {
|
||||
m_logFile.flush();
|
||||
}
|
||||
} while (!stopped);
|
||||
}
|
||||
|
||||
@@ -312,6 +339,29 @@ NOINLINE void Stream::writeCurrentTime()
|
||||
m_numberWidth = 1;
|
||||
}
|
||||
|
||||
NOINLINE void put_rawip(const raw_ip& value, Stream* wrapper)
|
||||
{
|
||||
const char* addr_str;
|
||||
char addr_str_buf[64];
|
||||
|
||||
static constexpr uint8_t ipv4_prefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255 };
|
||||
const bool is_v6 = (memcmp(value.data, ipv4_prefix, 12) != 0);
|
||||
|
||||
if (is_v6) {
|
||||
addr_str = inet_ntop(AF_INET6, value.data, addr_str_buf, sizeof(addr_str_buf));
|
||||
}
|
||||
else {
|
||||
addr_str = inet_ntop(AF_INET, value.data + 12, addr_str_buf, sizeof(addr_str_buf));
|
||||
}
|
||||
|
||||
if (addr_str) {
|
||||
*wrapper << addr_str;
|
||||
}
|
||||
else {
|
||||
*wrapper << "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace log
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -384,6 +384,13 @@ template<> struct log::Stream::Entry<NetworkType>
|
||||
}
|
||||
};
|
||||
|
||||
void put_rawip(const raw_ip& value, Stream* wrapper);
|
||||
|
||||
template<> struct log::Stream::Entry<raw_ip>
|
||||
{
|
||||
static FORCEINLINE void put(const raw_ip& value, Stream* wrapper) { put_rawip(value, wrapper); }
|
||||
};
|
||||
|
||||
namespace {
|
||||
template<log::Severity severity> void apply_severity(log::Stream&);
|
||||
|
||||
|
||||
+13
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "stratum_server.h"
|
||||
#include "p2p_server.h"
|
||||
|
||||
static void usage()
|
||||
void p2pool_usage()
|
||||
{
|
||||
printf("P2Pool %s\n"
|
||||
"\nUsage:\n\n" \
|
||||
@@ -36,14 +36,22 @@ static void usage()
|
||||
"--loglevel Verbosity of the log, integer number between 0 and %d\n"
|
||||
"--config Name of the p2pool config file\n"
|
||||
"--data-api Path to the p2pool JSON data (use it in tandem with an external web-server)\n"
|
||||
"--stratum-api Enable /local/ path in api path for Stratum Server statistics\n"
|
||||
"--local-api Enable /local/ path in api path for Stratum Server and built-in miner statistics\n"
|
||||
"--stratum-api An alias for --local-api\n"
|
||||
"--no-cache Disable p2pool.cache\n"
|
||||
"--no-color Disable colors in console output\n"
|
||||
"--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes\n"
|
||||
"--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 1000)\n"
|
||||
"--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 1000)\n"
|
||||
"--start-mining N Start built-in miner using N threads (any value between 1 and 64)\n"
|
||||
"--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d.\n"
|
||||
"--help Show this help message\n\n"
|
||||
"Example command line:\n\n"
|
||||
"%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",
|
||||
p2pool::VERSION,
|
||||
p2pool::log::MAX_GLOBAL_LOG_LEVEL,
|
||||
p2pool::DEFAULT_P2P_PORT,
|
||||
p2pool::DEFAULT_P2P_PORT_MINI,
|
||||
#ifdef _WIN32
|
||||
"p2pool.exe"
|
||||
#else
|
||||
@@ -60,13 +68,13 @@ void memory_tracking_stop();
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc == 1) {
|
||||
usage();
|
||||
p2pool_usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "/help") || !strcmp(argv[i], "-h") || !strcmp(argv[i], "/h")) {
|
||||
usage();
|
||||
p2pool_usage();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -44,7 +44,7 @@ void Mempool::add(const TxMempoolData& tx)
|
||||
|
||||
void Mempool::swap(std::vector<TxMempoolData>& transactions)
|
||||
{
|
||||
const time_t cur_time = time(nullptr);
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
|
||||
WriteLock lock(m_lock);
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "miner.h"
|
||||
#include "p2pool.h"
|
||||
#include "stratum_server.h"
|
||||
#include "block_template.h"
|
||||
#include "pow_hash.h"
|
||||
#include "randomx.h"
|
||||
#include "params.h"
|
||||
#include "p2pool_api.h"
|
||||
#include <thread>
|
||||
|
||||
static constexpr char log_category_prefix[] = "Miner ";
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
Miner::Miner(p2pool* pool, uint32_t threads)
|
||||
: m_pool(pool)
|
||||
, m_threads(threads)
|
||||
, m_stopped{ false }
|
||||
, m_startTimestamp(high_resolution_clock::now())
|
||||
, m_nonce(0)
|
||||
, m_nonceTimestamp(m_startTimestamp)
|
||||
, m_extraNonce(0xF19E3779U)
|
||||
, m_totalHashes(0)
|
||||
, m_sharesFound(0)
|
||||
, m_job{}
|
||||
, m_jobIndex{ 0 }
|
||||
{
|
||||
on_block(m_pool->block_template());
|
||||
|
||||
m_minerThreads.reserve(threads);
|
||||
|
||||
for (uint32_t i = 0; i < threads; ++i) {
|
||||
WorkerData* data = new WorkerData{ this, i + 1, threads, {} };
|
||||
const int err = uv_thread_create(&data->m_worker, run, data);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to start worker thread " << data->m_index << '/' << threads << ", error " << uv_err_name(err));
|
||||
delete data;
|
||||
continue;
|
||||
}
|
||||
m_minerThreads.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
Miner::~Miner()
|
||||
{
|
||||
m_stopped = true;
|
||||
|
||||
for (WorkerData* data : m_minerThreads) {
|
||||
uv_thread_join(&data->m_worker);
|
||||
delete data;
|
||||
}
|
||||
}
|
||||
|
||||
void Miner::print_status()
|
||||
{
|
||||
const uint32_t hash_count = 0 - m_nonce.load();
|
||||
|
||||
const double dt = static_cast<double>(duration_cast<nanoseconds>(high_resolution_clock::now() - m_nonceTimestamp).count()) / 1e9;
|
||||
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
|
||||
|
||||
LOGINFO(0, "status" <<
|
||||
"\nThreads = " << m_threads <<
|
||||
"\nHashrate = " << log::Hashrate(hr)
|
||||
);
|
||||
}
|
||||
|
||||
void Miner::on_block(const BlockTemplate& block)
|
||||
{
|
||||
const uint32_t next_index = m_jobIndex ^ 1;
|
||||
Job& j = m_job[next_index];
|
||||
hash seed;
|
||||
j.m_blobSize = block.get_hashing_blob(m_extraNonce, j.m_blob, j.m_height, j.m_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
|
||||
|
||||
const uint32_t hash_count = 0 - m_nonce.exchange(0);
|
||||
m_jobIndex = next_index;
|
||||
|
||||
const auto cur_ts = high_resolution_clock::now();
|
||||
const double dt = static_cast<double>(duration_cast<nanoseconds>(cur_ts - m_nonceTimestamp).count()) / 1e9;
|
||||
|
||||
m_nonceTimestamp = cur_ts;
|
||||
m_totalHashes += hash_count;
|
||||
|
||||
if (m_pool->api() && m_pool->params().m_localStats) {
|
||||
m_pool->api()->set(p2pool_api::Category::LOCAL, "miner",
|
||||
[cur_ts, hash_count, dt, this](log::Stream& s)
|
||||
{
|
||||
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
|
||||
const double time_running = static_cast<double>(duration_cast<milliseconds>(cur_ts - m_startTimestamp).count()) / 1e3;
|
||||
|
||||
s << "{\"current_hashrate\":" << hr
|
||||
<< ",\"total_hashes\":" << m_totalHashes
|
||||
<< ",\"time_running\":" << time_running
|
||||
<< ",\"shares_found\":" << m_sharesFound.load()
|
||||
<< ",\"threads\":" << m_threads
|
||||
<< "}";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Miner::run(void* data)
|
||||
{
|
||||
WorkerData* d = static_cast<WorkerData*>(data);
|
||||
LOGINFO(1, "worker thread " << d->m_index << '/' << d->m_count << " started");
|
||||
make_thread_background();
|
||||
d->m_miner->run(d);
|
||||
LOGINFO(1, "worker thread " << d->m_index << '/' << d->m_count << " stopped");
|
||||
}
|
||||
|
||||
void Miner::run(WorkerData* data)
|
||||
{
|
||||
RandomX_Hasher_Base* hasher = m_pool->hasher();
|
||||
randomx_cache* cache = hasher->cache();
|
||||
randomx_dataset* dataset = hasher->dataset();
|
||||
|
||||
if (!cache && !dataset) {
|
||||
LOGERR(1, "worker thread " << data->m_index << '/' << data->m_count << ": RandomX cache and dataset are not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
randomx_flags flags = randomx_get_flags();
|
||||
if (dataset) {
|
||||
flags |= RANDOMX_FLAG_FULL_MEM;
|
||||
}
|
||||
|
||||
randomx_vm* vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, dataset ? nullptr : cache, dataset);
|
||||
if (!vm) {
|
||||
LOGWARN(1, "couldn't allocate RandomX VM using large pages");
|
||||
vm = randomx_create_vm(flags, dataset ? nullptr : cache, dataset);
|
||||
if (!vm) {
|
||||
LOGERR(1, "couldn't allocate RandomX VM");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t index = 0;
|
||||
Job job[2];
|
||||
|
||||
uint32_t seed_counter = 0;
|
||||
bool first = true;
|
||||
|
||||
Miner* miner = data->m_miner;
|
||||
|
||||
while (!m_stopped) {
|
||||
if (hasher->seed_counter() != seed_counter) {
|
||||
LOGINFO(5, "worker thread " << data->m_index << '/' << data->m_count << " paused (waiting for RandomX cache/dataset update)");
|
||||
hasher->sync_wait();
|
||||
seed_counter = hasher->seed_counter();
|
||||
if (flags & RANDOMX_FLAG_FULL_MEM) {
|
||||
dataset = hasher->dataset();
|
||||
randomx_vm_set_dataset(vm, dataset);
|
||||
}
|
||||
else {
|
||||
cache = hasher->cache();
|
||||
randomx_vm_set_cache(vm, cache);
|
||||
}
|
||||
LOGINFO(5, "worker thread " << data->m_index << '/' << data->m_count << " resumed");
|
||||
}
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job));
|
||||
job[index].set_nonce(miner->m_nonce.fetch_sub(1), miner->m_extraNonce);
|
||||
randomx_calculate_hash_first(vm, job[index].m_blob, job[index].m_blobSize);
|
||||
}
|
||||
|
||||
const Job& j = job[index];
|
||||
index ^= 1;
|
||||
memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job));
|
||||
job[index].set_nonce(miner->m_nonce.fetch_sub(1), miner->m_extraNonce);
|
||||
|
||||
hash h;
|
||||
randomx_calculate_hash_next(vm, job[index].m_blob, job[index].m_blobSize, &h);
|
||||
|
||||
if (j.m_diff.check_pow(h)) {
|
||||
LOGINFO(0, log::Green() << "worker thread " << data->m_index << '/' << data->m_count << " found a mainchain block, submitting it");
|
||||
m_pool->submit_block_async(j.m_templateId, j.m_nonce, j.m_extraNonce);
|
||||
m_pool->block_template().update_tx_keys();
|
||||
}
|
||||
|
||||
if (j.m_sidechainDiff.check_pow(h)) {
|
||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << j.m_height << ", diff " << j.m_sidechainDiff << ", worker thread " << data->m_index << '/' << data->m_count);
|
||||
m_pool->submit_sidechain_block(j.m_templateId, j.m_nonce, j.m_extraNonce);
|
||||
++m_sharesFound;
|
||||
}
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
randomx_destroy_vm(vm);
|
||||
}
|
||||
|
||||
void Miner::Job::set_nonce(uint32_t nonce, uint32_t extra_nonce)
|
||||
{
|
||||
m_nonce = nonce;
|
||||
m_extraNonce = extra_nonce;
|
||||
|
||||
uint8_t* p = m_blob + m_nonceOffset;
|
||||
p[0] = static_cast<uint8_t>(nonce);
|
||||
p[1] = static_cast<uint8_t>(nonce >> 8);
|
||||
p[2] = static_cast<uint8_t>(nonce >> 16);
|
||||
p[3] = static_cast<uint8_t>(nonce >> 24);
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "uv_util.h"
|
||||
#include <chrono>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
class p2pool;
|
||||
class BlockTemplate;
|
||||
|
||||
class Miner
|
||||
{
|
||||
public:
|
||||
Miner(p2pool* pool, uint32_t threads);
|
||||
~Miner();
|
||||
|
||||
void print_status();
|
||||
void on_block(const BlockTemplate& block);
|
||||
|
||||
private:
|
||||
static void run(void* data);
|
||||
|
||||
p2pool* m_pool;
|
||||
uint32_t m_threads;
|
||||
|
||||
struct WorkerData
|
||||
{
|
||||
Miner* m_miner;
|
||||
uint32_t m_index;
|
||||
uint32_t m_count;
|
||||
uv_thread_t m_worker;
|
||||
};
|
||||
|
||||
std::vector<WorkerData*> m_minerThreads;
|
||||
std::atomic<bool> m_stopped;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point m_startTimestamp;
|
||||
|
||||
std::atomic<uint32_t> m_nonce;
|
||||
std::chrono::high_resolution_clock::time_point m_nonceTimestamp;
|
||||
const uint32_t m_extraNonce;
|
||||
|
||||
uint64_t m_totalHashes;
|
||||
std::atomic<uint32_t> m_sharesFound;
|
||||
|
||||
struct Job
|
||||
{
|
||||
uint8_t m_blob[128] = {};
|
||||
uint32_t m_blobSize = 0;
|
||||
uint32_t m_templateId = 0;
|
||||
difficulty_type m_diff = {};
|
||||
difficulty_type m_sidechainDiff = {};
|
||||
uint64_t m_height = 0;
|
||||
size_t m_nonceOffset = 0;
|
||||
uint32_t m_nonce = 0;
|
||||
uint32_t m_extraNonce = 0;
|
||||
|
||||
void set_nonce(uint32_t nonce, uint32_t extra_nonce);
|
||||
};
|
||||
Job m_job[2];
|
||||
std::atomic<uint32_t> m_jobIndex;
|
||||
|
||||
void run(WorkerData* data);
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
+330
-90
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -23,14 +23,16 @@
|
||||
#include "side_chain.h"
|
||||
#include "pool_block.h"
|
||||
#include "block_cache.h"
|
||||
#include "json_rpc_request.h"
|
||||
#include "json_parsers.h"
|
||||
#include <rapidjson/document.h>
|
||||
#include <fstream>
|
||||
#include <numeric>
|
||||
|
||||
static constexpr char log_category_prefix[] = "P2PServer ";
|
||||
static constexpr char saved_peer_list_file_name[] = "p2pool_peers.txt";
|
||||
static const char* seed_nodes[] = {
|
||||
"seeds.p2pool.io"
|
||||
};
|
||||
static const char* seed_nodes[] = { "seeds.p2pool.io", ""};
|
||||
static const char* seed_nodes_mini[] = { "seeds-mini.p2pool.io", "" };
|
||||
|
||||
static constexpr int DEFAULT_BACKLOG = 16;
|
||||
static constexpr uint64_t DEFAULT_BAN_TIME = 600;
|
||||
@@ -45,14 +47,24 @@ P2PServer::P2PServer(p2pool* pool)
|
||||
, m_cache(pool->params().m_blockCache ? new BlockCache() : nullptr)
|
||||
, m_cacheLoaded(false)
|
||||
, m_initialPeerList(pool->params().m_p2pPeerList)
|
||||
, m_rd{}
|
||||
, m_rng(m_rd())
|
||||
, m_cachedBlocks(nullptr)
|
||||
, m_rng(RandomDeviceSeed::instance)
|
||||
, m_block(new PoolBlock())
|
||||
, m_timer{}
|
||||
, m_timerCounter(0)
|
||||
, m_peerId(m_rng())
|
||||
, m_timerInterval(2)
|
||||
, m_peerListLastSaved(0)
|
||||
{
|
||||
// Diffuse the initial state in case it has low quality
|
||||
m_rng.discard(10000);
|
||||
|
||||
m_peerId = m_rng();
|
||||
|
||||
const Params& params = pool->params();
|
||||
|
||||
set_max_outgoing_peers(params.m_maxOutgoingPeers);
|
||||
set_max_incoming_peers(params.m_maxIncomingPeers);
|
||||
|
||||
uv_mutex_init_checked(&m_rngLock);
|
||||
uv_mutex_init_checked(&m_blockLock);
|
||||
uv_mutex_init_checked(&m_peerListLock);
|
||||
@@ -81,14 +93,14 @@ P2PServer::P2PServer(p2pool* pool)
|
||||
}
|
||||
|
||||
m_timer.data = this;
|
||||
err = uv_timer_start(&m_timer, on_timer, 1000, 2000);
|
||||
err = uv_timer_start(&m_timer, on_timer, 1000, m_timerInterval * 1000);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to start timer, error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
|
||||
load_peer_list();
|
||||
start_listening(pool->params().m_p2pAddresses);
|
||||
start_listening(params.m_p2pAddresses);
|
||||
}
|
||||
|
||||
P2PServer::~P2PServer()
|
||||
@@ -119,18 +131,34 @@ void P2PServer::add_cached_block(const PoolBlock& block)
|
||||
return;
|
||||
}
|
||||
|
||||
PoolBlock* new_block = new PoolBlock(block);
|
||||
m_cachedBlocks.insert({ new_block->m_sidechainId, new_block });
|
||||
if (!m_cachedBlocks) {
|
||||
m_cachedBlocks = new unordered_map<hash, PoolBlock*>();
|
||||
}
|
||||
|
||||
if (m_cachedBlocks->find(block.m_sidechainId) == m_cachedBlocks->end()) {
|
||||
PoolBlock* new_block = new PoolBlock(block);
|
||||
m_cachedBlocks->insert({ new_block->m_sidechainId, new_block });
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::clear_cached_blocks()
|
||||
{
|
||||
if (!m_cachedBlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
WriteLock lock(m_cachedBlocksLock);
|
||||
|
||||
for (auto it : m_cachedBlocks) {
|
||||
if (!m_cachedBlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto it : *m_cachedBlocks) {
|
||||
delete it.second;
|
||||
}
|
||||
m_cachedBlocks.clear();
|
||||
|
||||
delete m_cachedBlocks;
|
||||
m_cachedBlocks = nullptr;
|
||||
}
|
||||
|
||||
void P2PServer::store_in_cache(const PoolBlock& block)
|
||||
@@ -168,10 +196,12 @@ void P2PServer::on_connect_failed(bool is_v6, const raw_ip& ip, int port)
|
||||
|
||||
void P2PServer::update_peer_connections()
|
||||
{
|
||||
const time_t cur_time = time(nullptr);
|
||||
const time_t last_updated = m_pool->side_chain().last_updated();
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
const uint64_t last_updated = m_pool->side_chain().last_updated();
|
||||
|
||||
std::vector<raw_ip> connected_clients;
|
||||
bool has_good_peers = false;
|
||||
|
||||
unordered_set<raw_ip> connected_clients;
|
||||
{
|
||||
MutexLock lock(m_clientsListLock);
|
||||
connected_clients.reserve(m_numConnections);
|
||||
@@ -187,12 +217,12 @@ void P2PServer::update_peer_connections()
|
||||
}
|
||||
|
||||
if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) {
|
||||
// - Side chain is at least 5 minutes newer (last_updated >= client->m_lastBroadcastTimestamp + 300)
|
||||
// - Side chain is at least 15 minutes newer (last_updated >= client->m_lastBroadcastTimestamp + 900)
|
||||
// - It's been at least 10 seconds since side chain updated (cur_time >= last_updated + 10)
|
||||
// - It's been at least 10 seconds since the last block request (peer is not syncing)
|
||||
// - Peer should have sent a broadcast by now
|
||||
if (last_updated && (cur_time >= std::max(last_updated, client->m_lastBlockrequestTimestamp) + 10) && (last_updated >= client->m_lastBroadcastTimestamp + 300)) {
|
||||
const time_t dt = last_updated - client->m_lastBroadcastTimestamp;
|
||||
if (last_updated && (cur_time >= std::max(last_updated, client->m_lastBlockrequestTimestamp) + 10) && (last_updated >= client->m_lastBroadcastTimestamp + 900)) {
|
||||
const uint64_t dt = last_updated - client->m_lastBroadcastTimestamp;
|
||||
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " is not broadcasting blocks (last update " << dt << " seconds ago)");
|
||||
client->ban(DEFAULT_BAN_TIME);
|
||||
remove_peer_from_list(client);
|
||||
@@ -202,7 +232,10 @@ void P2PServer::update_peer_connections()
|
||||
}
|
||||
|
||||
if (!disconnected) {
|
||||
connected_clients.emplace_back(client->m_addr);
|
||||
connected_clients.insert(client->m_addr);
|
||||
if (client->m_handshakeComplete && !client->m_handshakeInvalid && (client->m_listenPort >= 0)) {
|
||||
has_good_peers = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,7 +247,7 @@ void P2PServer::update_peer_connections()
|
||||
if ((m_timerCounter % 30) == 1) {
|
||||
// Update last seen time for currently connected peers
|
||||
for (Peer& p : m_peerList) {
|
||||
if (std::find_if(connected_clients.begin(), connected_clients.end(), [&p](const raw_ip& addr) { return p.m_addr == addr; }) != connected_clients.end()) {
|
||||
if (connected_clients.find(p.m_addr) != connected_clients.end()) {
|
||||
p.m_lastSeen = cur_time;
|
||||
}
|
||||
}
|
||||
@@ -226,20 +259,24 @@ void P2PServer::update_peer_connections()
|
||||
peer_list = m_peerList;
|
||||
}
|
||||
|
||||
// Try to have at least 10 outgoing connections
|
||||
for (uint32_t i = m_numConnections - m_numIncomingConnections; (i < 10) && !peer_list.empty();) {
|
||||
uint32_t N = m_maxOutgoingPeers;
|
||||
|
||||
// Special case: when we can't find p2pool peers, scan through monerod peers (try 25 peers at a time)
|
||||
if (!has_good_peers && !m_peerListMonero.empty()) {
|
||||
LOGINFO(3, "Scanning monerod peers, " << m_peerListMonero.size() << " left");
|
||||
for (uint32_t i = 0; (i < 25) && !m_peerListMonero.empty(); ++i) {
|
||||
peer_list.push_back(m_peerListMonero.back());
|
||||
m_peerListMonero.pop_back();
|
||||
}
|
||||
N = static_cast<uint32_t>(peer_list.size());
|
||||
}
|
||||
|
||||
// Try to have at least N outgoing connections (N defaults to 10, can be set via --out-peers command line parameter)
|
||||
for (uint32_t i = m_numConnections - m_numIncomingConnections; (i < N) && !peer_list.empty();) {
|
||||
const uint64_t k = get_random64() % peer_list.size();
|
||||
const Peer& peer = peer_list[k];
|
||||
|
||||
bool already_connected = false;
|
||||
for (const raw_ip& ip : connected_clients) {
|
||||
if (ip == peer.m_addr) {
|
||||
already_connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!already_connected && connect_to_peer(peer.m_isV6, peer.m_addr, peer.m_port)) {
|
||||
if ((connected_clients.find(peer.m_addr) == connected_clients.end()) && connect_to_peer(peer.m_isV6, peer.m_addr, peer.m_port)) {
|
||||
++i;
|
||||
}
|
||||
|
||||
@@ -249,14 +286,17 @@ void P2PServer::update_peer_connections()
|
||||
peer_list.pop_back();
|
||||
}
|
||||
|
||||
if ((m_numConnections == 0) && ((m_timerCounter % 30) == 0)) {
|
||||
if (!has_good_peers && ((m_timerCounter % 30) == 0)) {
|
||||
LOGERR(1, "no connections to other p2pool nodes, check your monerod/p2pool/network/firewall setup!!!");
|
||||
if (m_peerListMonero.empty()) {
|
||||
load_monerod_peer_list();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::update_peer_list()
|
||||
{
|
||||
const time_t cur_time = time(nullptr);
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
{
|
||||
MutexLock lock(m_clientsListLock);
|
||||
|
||||
@@ -265,9 +305,9 @@ void P2PServer::update_peer_list()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur_time >= client->m_nextPeerListRequest) {
|
||||
if (cur_time >= client->m_nextOutgoingPeerListRequest) {
|
||||
// Send peer list requests at random intervals (60-120 seconds)
|
||||
client->m_nextPeerListRequest = cur_time + 60 + (get_random64() % 61);
|
||||
client->m_nextOutgoingPeerListRequest = cur_time + (60 + (get_random64() % 61));
|
||||
|
||||
const bool result = send(client,
|
||||
[](void* buf)
|
||||
@@ -278,7 +318,7 @@ void P2PServer::update_peer_list()
|
||||
});
|
||||
|
||||
if (result) {
|
||||
client->m_lastPeerListRequestTime = std::chrono::system_clock::now();
|
||||
client->m_lastPeerListRequestTime = std::chrono::high_resolution_clock::now();
|
||||
++client->m_peerListPendingRequests;
|
||||
}
|
||||
}
|
||||
@@ -288,7 +328,7 @@ void P2PServer::update_peer_list()
|
||||
|
||||
void P2PServer::save_peer_list_async()
|
||||
{
|
||||
const time_t cur_time = time(nullptr);
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
if (cur_time < m_peerListLastSaved + 300) {
|
||||
return;
|
||||
}
|
||||
@@ -361,19 +401,18 @@ void P2PServer::save_peer_list()
|
||||
f.close();
|
||||
|
||||
LOGINFO(5, "peer list saved (" << peer_list.size() << " peers)");
|
||||
m_peerListLastSaved = time(nullptr);
|
||||
m_peerListLastSaved = seconds_since_epoch();
|
||||
}
|
||||
|
||||
void P2PServer::load_peer_list()
|
||||
{
|
||||
std::string saved_list;
|
||||
const size_t old_size = m_peerList.size();
|
||||
|
||||
// Load peers from seed nodes if we're on the default sidechain
|
||||
if (m_pool->side_chain().is_default()) {
|
||||
const int p2p_port = DEFAULT_P2P_PORT;
|
||||
|
||||
for (size_t i = 0; i < array_size(seed_nodes); ++i) {
|
||||
LOGINFO(4, "loading peers from " << seed_nodes[i]);
|
||||
// Load peers from seed nodes if we're on the default or mini sidechain
|
||||
auto load_from_seed_nodes = [&saved_list](const char** nodes, int p2p_port) {
|
||||
for (size_t i = 0; nodes[i][0]; ++i) {
|
||||
LOGINFO(4, "loading peers from " << nodes[i]);
|
||||
|
||||
addrinfo hints{};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
@@ -381,7 +420,7 @@ void P2PServer::load_peer_list()
|
||||
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
|
||||
|
||||
addrinfo* result;
|
||||
const int err = getaddrinfo(seed_nodes[i], nullptr, &hints, &result);
|
||||
const int err = getaddrinfo(nodes[i], nullptr, &hints, &result);
|
||||
if (err == 0) {
|
||||
for (addrinfo* r = result; r != NULL; r = r->ai_next) {
|
||||
const char* addr_str;
|
||||
@@ -404,7 +443,7 @@ void P2PServer::load_peer_list()
|
||||
}
|
||||
|
||||
if (s.m_pos) {
|
||||
LOGINFO(4, "added " << static_cast<char*>(buf) << " from " << seed_nodes[i]);
|
||||
LOGINFO(4, "added " << static_cast<char*>(buf) << " from " << nodes[i]);
|
||||
if (!saved_list.empty()) {
|
||||
saved_list += ',';
|
||||
}
|
||||
@@ -414,9 +453,16 @@ void P2PServer::load_peer_list()
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
else {
|
||||
LOGWARN(3, "getaddrinfo failed for " << seed_nodes[i] << ": " << gai_strerror(err));
|
||||
LOGWARN(3, "getaddrinfo failed for " << nodes[i] << ": " << gai_strerror(err));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (m_pool->side_chain().is_default()) {
|
||||
load_from_seed_nodes(seed_nodes, DEFAULT_P2P_PORT);
|
||||
}
|
||||
else if (m_pool->side_chain().is_mini()) {
|
||||
load_from_seed_nodes(seed_nodes_mini, DEFAULT_P2P_PORT_MINI);
|
||||
}
|
||||
|
||||
// Finally load peers from p2pool_peers.txt
|
||||
@@ -479,19 +525,114 @@ void P2PServer::load_peer_list()
|
||||
|
||||
p.m_port = port;
|
||||
p.m_numFailedConnections = 0;
|
||||
p.m_lastSeen = time(nullptr);
|
||||
p.m_lastSeen = seconds_since_epoch();
|
||||
|
||||
if (!already_added && !is_banned(p.m_addr)) {
|
||||
m_peerList.push_back(p);
|
||||
}
|
||||
});
|
||||
|
||||
LOGINFO(4, "peer list loaded (" << m_peerList.size() << " peers)");
|
||||
LOGINFO(4, "peer list loaded (" << (m_peerList.size() - old_size) << " peers)");
|
||||
}
|
||||
|
||||
void P2PServer::load_monerod_peer_list()
|
||||
{
|
||||
const Params& params = m_pool->params();
|
||||
|
||||
JSONRPCRequest::call(params.m_host.c_str(), params.m_rpcPort, "/get_peer_list",
|
||||
[this](const char* data, size_t size)
|
||||
{
|
||||
constexpr char err_str[] = "/get_peer_list RPC request returned invalid JSON ";
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
Document doc;
|
||||
if (doc.Parse(data, size).HasParseError()) {
|
||||
LOGWARN(4, err_str << "(parse error)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!doc.IsObject()) {
|
||||
LOGWARN(4, err_str << "(not an object)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!doc.HasMember("white_list")) {
|
||||
LOGWARN(4, err_str << "('white_list' not found)");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& white_list = doc["white_list"];
|
||||
|
||||
if (!white_list.IsArray()) {
|
||||
LOGWARN(4, err_str << "('white_list' is not an array)");
|
||||
return;
|
||||
}
|
||||
|
||||
const int port = m_pool->side_chain().is_mini() ? DEFAULT_P2P_PORT_MINI : DEFAULT_P2P_PORT;
|
||||
|
||||
const SizeType n = white_list.Size();
|
||||
|
||||
m_peerListMonero.clear();
|
||||
m_peerListMonero.reserve(n);
|
||||
|
||||
for (SizeType i = 0; i < n; ++i) {
|
||||
auto& v = white_list[i];
|
||||
const char* ip;
|
||||
uint64_t last_seen;
|
||||
if (!parseValue(v, "host", ip) || !parseValue(v, "last_seen", last_seen)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Peer p;
|
||||
p.m_lastSeen = last_seen;
|
||||
|
||||
if (strchr(ip, ':')) {
|
||||
sockaddr_in6 addr6;
|
||||
const int err = uv_ip6_addr(ip, port, &addr6);
|
||||
if (err) {
|
||||
continue;
|
||||
}
|
||||
p.m_isV6 = true;
|
||||
memcpy(p.m_addr.data, &addr6.sin6_addr, sizeof(in6_addr));
|
||||
}
|
||||
else {
|
||||
sockaddr_in addr4;
|
||||
const int err = uv_ip4_addr(ip, port, &addr4);
|
||||
if (err) {
|
||||
continue;
|
||||
}
|
||||
p.m_isV6 = false;
|
||||
p.m_addr = {};
|
||||
p.m_addr.data[10] = 0xFF;
|
||||
p.m_addr.data[11] = 0xFF;
|
||||
memcpy(p.m_addr.data + 12, &addr4.sin_addr, sizeof(in_addr));
|
||||
}
|
||||
|
||||
p.m_port = port;
|
||||
p.m_numFailedConnections = 0;
|
||||
|
||||
if (!is_banned(p.m_addr)) {
|
||||
m_peerListMonero.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
// Put recently active peers first in the list
|
||||
std::sort(m_peerListMonero.begin(), m_peerListMonero.end(), [](const Peer& a, const Peer& b) { return a.m_lastSeen > b.m_lastSeen; });
|
||||
|
||||
LOGINFO(4, "monerod peer list loaded (" << m_peerListMonero.size() << " peers)");
|
||||
},
|
||||
[](const char* data, size_t size)
|
||||
{
|
||||
if (size > 0) {
|
||||
LOGWARN(4, "/get_peer_list RPC request failed: error " << log::const_buf(data, size));
|
||||
}
|
||||
}, &m_loop);
|
||||
}
|
||||
|
||||
void P2PServer::update_peer_in_list(bool is_v6, const raw_ip& ip, int port)
|
||||
{
|
||||
const time_t cur_time = time(nullptr);
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
|
||||
MutexLock lock(m_peerListLock);
|
||||
|
||||
@@ -537,13 +678,15 @@ void P2PServer::remove_peer_from_list(const raw_ip& ip)
|
||||
|
||||
void P2PServer::broadcast(const PoolBlock& block)
|
||||
{
|
||||
if (block.m_txinGenHeight + 2 < m_pool->miner_data().height) {
|
||||
LOGWARN(3, "Trying to broadcast a stale block " << block.m_sidechainId << " (mainchain height " << block.m_txinGenHeight << ", current height is " << m_pool->miner_data().height << ')');
|
||||
MinerData miner_data = m_pool->miner_data();
|
||||
|
||||
if (block.m_txinGenHeight + 2 < miner_data.height) {
|
||||
LOGWARN(3, "Trying to broadcast a stale block " << block.m_sidechainId << " (mainchain height " << block.m_txinGenHeight << ", current height is " << miner_data.height << ')');
|
||||
return;
|
||||
}
|
||||
|
||||
if (block.m_txinGenHeight > m_pool->miner_data().height + 2) {
|
||||
LOGWARN(3, "Trying to broadcast a block " << block.m_sidechainId << " ahead on mainchain (mainchain height " << block.m_txinGenHeight << ", current height is " << m_pool->miner_data().height << ')');
|
||||
if (block.m_txinGenHeight > miner_data.height + 2) {
|
||||
LOGWARN(3, "Trying to broadcast a block " << block.m_sidechainId << " ahead on mainchain (mainchain height " << block.m_txinGenHeight << ", current height is " << miner_data.height << ')');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -657,25 +800,31 @@ void P2PServer::on_broadcast()
|
||||
LOGINFO(6, "sending BLOCK_BROADCAST (pruned) to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
|
||||
|
||||
*reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(data->pruned_blob.size());
|
||||
const uint32_t len = static_cast<uint32_t>(data->pruned_blob.size());
|
||||
memcpy(p, &len, sizeof(uint32_t));
|
||||
p += sizeof(uint32_t);
|
||||
|
||||
memcpy(p, data->pruned_blob.data(), data->pruned_blob.size());
|
||||
p += data->pruned_blob.size();
|
||||
if (len) {
|
||||
memcpy(p, data->pruned_blob.data(), len);
|
||||
p += len;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGINFO(5, "sending BLOCK_BROADCAST (full) to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
|
||||
|
||||
*reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(data->blob.size());
|
||||
const uint32_t len = static_cast<uint32_t>(data->blob.size());
|
||||
memcpy(p, &len, sizeof(uint32_t));
|
||||
p += sizeof(uint32_t);
|
||||
|
||||
memcpy(p, data->blob.data(), data->blob.size());
|
||||
p += data->blob.size();
|
||||
if (len) {
|
||||
memcpy(p, data->blob.data(), len);
|
||||
p += len;
|
||||
}
|
||||
}
|
||||
|
||||
return p - p0;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -688,7 +837,7 @@ uint64_t P2PServer::get_random64()
|
||||
|
||||
void P2PServer::print_status()
|
||||
{
|
||||
const int64_t uptime = time(nullptr) - m_pool->start_time();
|
||||
const int64_t uptime = seconds_since_epoch() - m_pool->start_time();
|
||||
|
||||
const int64_t s = uptime % 60;
|
||||
const int64_t m = (uptime / 60) % 60;
|
||||
@@ -740,7 +889,7 @@ void P2PServer::on_timer()
|
||||
|
||||
void P2PServer::flush_cache()
|
||||
{
|
||||
if (!m_cache) {
|
||||
if (!m_cache || ((m_timerCounter % 30) != 2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -804,12 +953,14 @@ void P2PServer::download_missing_blocks()
|
||||
return;
|
||||
}
|
||||
|
||||
ReadLock lock2(m_cachedBlocksLock);
|
||||
|
||||
// Try to download each block from a random client
|
||||
for (const hash& id : missing_blocks) {
|
||||
P2PClient* client = clients[get_random64() % clients.size()];
|
||||
|
||||
{
|
||||
MutexLock lock2(m_missingBlockRequestsLock);
|
||||
MutexLock lock3(m_missingBlockRequestsLock);
|
||||
|
||||
const uint64_t truncated_block_id = *reinterpret_cast<const uint64_t*>(id.h);
|
||||
if (!m_missingBlockRequests.insert({ client->m_peerId, truncated_block_id }).second) {
|
||||
@@ -819,7 +970,16 @@ void P2PServer::download_missing_blocks()
|
||||
}
|
||||
}
|
||||
|
||||
send(client,
|
||||
if (m_cachedBlocks) {
|
||||
auto it = m_cachedBlocks->find(id);
|
||||
if (it != m_cachedBlocks->end()) {
|
||||
LOGINFO(5, "using cached block for id = " << id);
|
||||
client->handle_incoming_block_async(it->second);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const bool result = send(client,
|
||||
[&id](void* buf)
|
||||
{
|
||||
uint8_t* p0 = reinterpret_cast<uint8_t*>(buf);
|
||||
@@ -833,6 +993,10 @@ void P2PServer::download_missing_blocks()
|
||||
|
||||
return p - p0;
|
||||
});
|
||||
|
||||
if (result) {
|
||||
++client->m_blockPendingRequests;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,8 +1006,8 @@ void P2PServer::check_zmq()
|
||||
return;
|
||||
}
|
||||
|
||||
const time_t cur_time = time(nullptr);
|
||||
const time_t last_active = m_pool->zmq_last_active();
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
const uint64_t last_active = m_pool->zmq_last_active();
|
||||
|
||||
if (cur_time >= last_active + 300) {
|
||||
const uint64_t dt = static_cast<uint64_t>(cur_time - last_active);
|
||||
@@ -859,10 +1023,14 @@ P2PServer::P2PClient::P2PClient()
|
||||
, m_handshakeComplete(false)
|
||||
, m_handshakeInvalid(false)
|
||||
, m_listenPort(-1)
|
||||
, m_nextPeerListRequest(0)
|
||||
, m_fastPeerListRequestCount(0)
|
||||
, m_prevIncomingPeerListRequest(0)
|
||||
, m_nextOutgoingPeerListRequest(0)
|
||||
, m_lastPeerListRequestTime{}
|
||||
, m_peerListPendingRequests(0)
|
||||
, m_pingTime(0)
|
||||
, m_blockPendingRequests(0)
|
||||
, m_chainTipBlockRequest(false)
|
||||
, m_lastAlive(0)
|
||||
, m_lastBroadcastTimestamp(0)
|
||||
, m_lastBlockrequestTimestamp(0)
|
||||
@@ -885,10 +1053,14 @@ void P2PServer::P2PClient::reset()
|
||||
m_handshakeComplete = false;
|
||||
m_handshakeInvalid = false;
|
||||
m_listenPort = -1;
|
||||
m_nextPeerListRequest = 0;
|
||||
m_fastPeerListRequestCount = 0;
|
||||
m_prevIncomingPeerListRequest = 0;
|
||||
m_nextOutgoingPeerListRequest = 0;
|
||||
m_lastPeerListRequestTime = {};
|
||||
m_peerListPendingRequests = 0;
|
||||
m_pingTime = 0;
|
||||
m_blockPendingRequests = 0;
|
||||
m_chainTipBlockRequest = false;
|
||||
m_lastAlive = 0;
|
||||
m_lastBroadcastTimestamp = 0;
|
||||
m_lastBlockrequestTimestamp = 0;
|
||||
@@ -903,6 +1075,15 @@ bool P2PServer::P2PClient::on_connect()
|
||||
{
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
|
||||
if (!server) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_isIncoming && (server->m_numIncomingConnections > server->m_maxIncomingPeers)) {
|
||||
LOGINFO(5, "Connection from " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " rejected (incoming connections limit was reached)");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't allow multiple connections to/from the same IP
|
||||
// server->m_clientsListLock is already locked here
|
||||
for (P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
@@ -912,7 +1093,7 @@ bool P2PServer::P2PClient::on_connect()
|
||||
}
|
||||
}
|
||||
|
||||
m_lastAlive = time(nullptr);
|
||||
m_lastAlive = seconds_since_epoch();
|
||||
return send_handshake_challenge();
|
||||
}
|
||||
|
||||
@@ -936,6 +1117,8 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
uint8_t* buf = buf_begin;
|
||||
uint32_t bytes_left = m_numRead;
|
||||
|
||||
uint32_t num_block_requests = 0;
|
||||
|
||||
uint32_t bytes_read;
|
||||
do {
|
||||
MessageId id = static_cast<MessageId>(buf[0]);
|
||||
@@ -993,6 +1176,13 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
break;
|
||||
|
||||
case MessageId::LISTEN_PORT:
|
||||
if (m_listenPort >= 0) {
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " sent an unexpected LISTEN_PORT");
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent LISTEN_PORT");
|
||||
|
||||
if (bytes_left >= 1 + sizeof(int32_t)) {
|
||||
@@ -1006,6 +1196,14 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
break;
|
||||
|
||||
case MessageId::BLOCK_REQUEST:
|
||||
++num_block_requests;
|
||||
if (num_block_requests > 100) {
|
||||
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent too many BLOCK_REQUEST messages at once");
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_REQUEST");
|
||||
|
||||
if (bytes_left >= 1 + HASH_SIZE) {
|
||||
@@ -1019,12 +1217,21 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
break;
|
||||
|
||||
case MessageId::BLOCK_RESPONSE:
|
||||
if (m_blockPendingRequests <= 0) {
|
||||
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected BLOCK_RESPONSE");
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_RESPONSE");
|
||||
|
||||
if (bytes_left >= 1 + sizeof(uint32_t)) {
|
||||
const uint32_t block_size = *reinterpret_cast<uint32_t*>(buf + 1);
|
||||
const uint32_t block_size = read_unaligned(reinterpret_cast<uint32_t*>(buf + 1));
|
||||
if (bytes_left >= 1 + sizeof(uint32_t) + block_size) {
|
||||
bytes_read = 1 + sizeof(uint32_t) + block_size;
|
||||
|
||||
--m_blockPendingRequests;
|
||||
if (!on_block_response(buf + 1 + sizeof(uint32_t), block_size)) {
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
@@ -1038,7 +1245,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
LOGINFO(6, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_BROADCAST");
|
||||
|
||||
if (bytes_left >= 1 + sizeof(uint32_t)) {
|
||||
const uint32_t block_size = *reinterpret_cast<uint32_t*>(buf + 1);
|
||||
const uint32_t block_size = read_unaligned(reinterpret_cast<uint32_t*>(buf + 1));
|
||||
if (bytes_left >= 1 + sizeof(uint32_t) + block_size) {
|
||||
bytes_read = 1 + sizeof(uint32_t) + block_size;
|
||||
if (!on_block_broadcast(buf + 1 + sizeof(uint32_t), block_size)) {
|
||||
@@ -1086,7 +1293,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
bytes_read = 2u + num_peers * 19u;
|
||||
|
||||
using namespace std::chrono;
|
||||
m_pingTime = duration_cast<milliseconds>(system_clock::now() - m_lastPeerListRequestTime).count();
|
||||
m_pingTime = duration_cast<milliseconds>(high_resolution_clock::now() - m_lastPeerListRequestTime).count();
|
||||
|
||||
--m_peerListPendingRequests;
|
||||
if (!on_peer_list_response(buf + 1)) {
|
||||
@@ -1102,7 +1309,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
if (bytes_read) {
|
||||
buf += bytes_read;
|
||||
bytes_left -= bytes_read;
|
||||
m_lastAlive = time(nullptr);
|
||||
m_lastAlive = seconds_since_epoch();
|
||||
}
|
||||
} while (bytes_read && bytes_left);
|
||||
|
||||
@@ -1436,7 +1643,9 @@ void P2PServer::P2PClient::on_after_handshake(uint8_t* &p)
|
||||
memcpy(p, empty.h, HASH_SIZE);
|
||||
p += HASH_SIZE;
|
||||
|
||||
m_lastBroadcastTimestamp = time(nullptr);
|
||||
++m_blockPendingRequests;
|
||||
m_chainTipBlockRequest = true;
|
||||
m_lastBroadcastTimestamp = seconds_since_epoch();
|
||||
}
|
||||
|
||||
bool P2PServer::P2PClient::on_listen_port(const uint8_t* buf)
|
||||
@@ -1457,7 +1666,7 @@ bool P2PServer::P2PClient::on_listen_port(const uint8_t* buf)
|
||||
|
||||
bool P2PServer::P2PClient::on_block_request(const uint8_t* buf)
|
||||
{
|
||||
m_lastBlockrequestTimestamp = time(nullptr);
|
||||
m_lastBlockrequestTimestamp = seconds_since_epoch();
|
||||
|
||||
hash id;
|
||||
memcpy(id.h, buf, HASH_SIZE);
|
||||
@@ -1478,11 +1687,14 @@ bool P2PServer::P2PClient::on_block_request(const uint8_t* buf)
|
||||
LOGINFO(5, "sending BLOCK_RESPONSE");
|
||||
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_RESPONSE);
|
||||
|
||||
*reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(blob.size());
|
||||
const uint32_t len = static_cast<uint32_t>(blob.size());
|
||||
memcpy(p, &len, sizeof(uint32_t));
|
||||
p += sizeof(uint32_t);
|
||||
|
||||
memcpy(p, blob.data(), blob.size());
|
||||
p += blob.size();
|
||||
if (len) {
|
||||
memcpy(p, blob.data(), len);
|
||||
p += len;
|
||||
}
|
||||
|
||||
return p - p0;
|
||||
});
|
||||
@@ -1505,6 +1717,18 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_chainTipBlockRequest) {
|
||||
m_chainTipBlockRequest = false;
|
||||
|
||||
const uint64_t peer_height = server->m_block->m_txinGenHeight;
|
||||
const uint64_t our_height = server->m_pool->miner_data().height;
|
||||
|
||||
if (peer_height + 2 < our_height) {
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " is mining on top of a stale block (mainchain height " << peer_height << ", expected >= " << our_height << ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return handle_incoming_block_async(server->m_block);
|
||||
}
|
||||
|
||||
@@ -1527,7 +1751,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
|
||||
m_broadcastedHashes[m_broadcastedHashesIndex.fetch_add(1) % array_size(&P2PClient::m_broadcastedHashes)] = server->m_block->m_sidechainId;
|
||||
|
||||
const 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) {
|
||||
// This peer is mining on top of a different Monero block, investigate it
|
||||
@@ -1537,7 +1761,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
if (peer_height < our_height) {
|
||||
if (our_height - peer_height < 5) {
|
||||
using namespace std::chrono;
|
||||
const int64_t elapsed_ms = duration_cast<milliseconds>(system_clock::now() - miner_data.time_received).count();
|
||||
const int64_t elapsed_ms = duration_cast<milliseconds>(high_resolution_clock::now() - miner_data.time_received).count();
|
||||
if (our_height - peer_height > 1) {
|
||||
LOGWARN(5, "peer " << static_cast<char*>(m_addrString) << " broadcasted a stale block (" << elapsed_ms << " ms late, mainchain height " << peer_height << ", expected >= " << our_height << "), ignoring it");
|
||||
return true;
|
||||
@@ -1547,7 +1771,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGWARN(5, "peer " << static_cast<char*>(m_addrString) << " broadcasted an unreasonably stale block (mainchain height " << peer_height << ", expected >= " << our_height << ')');
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " broadcasted an unreasonably stale block (mainchain height " << peer_height << ", expected >= " << our_height << ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1563,7 +1787,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
|
||||
server->m_block->m_wantBroadcast = true;
|
||||
|
||||
m_lastBroadcastTimestamp = time(nullptr);
|
||||
m_lastBroadcastTimestamp = seconds_since_epoch();
|
||||
|
||||
return handle_incoming_block_async(server->m_block);
|
||||
}
|
||||
@@ -1571,6 +1795,18 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||
{
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
|
||||
// Allow peer list requests no more than once every 30 seconds
|
||||
if (cur_time - m_prevIncomingPeerListRequest < 30) {
|
||||
++m_fastPeerListRequestCount;
|
||||
if (m_fastPeerListRequestCount >= 3) {
|
||||
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " is sending PEER_LIST_REQUEST too often");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_prevIncomingPeerListRequest = cur_time;
|
||||
|
||||
Peer peers[PEER_LIST_RESPONSE_MAX_PEERS];
|
||||
uint32_t num_selected_peers = 0;
|
||||
@@ -1633,7 +1869,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||
bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
||||
{
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
const time_t cur_time = time(nullptr);
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
|
||||
MutexLock lock(server->m_peerListLock);
|
||||
|
||||
@@ -1756,11 +1992,13 @@ void P2PServer::P2PClient::post_handle_incoming_block(const uint32_t reset_count
|
||||
ReadLock lock(server->m_cachedBlocksLock);
|
||||
|
||||
for (const hash& id : missing_blocks) {
|
||||
auto it = server->m_cachedBlocks.find(id);
|
||||
if (it != server->m_cachedBlocks.end()) {
|
||||
LOGINFO(5, "using cached block for id = " << id);
|
||||
handle_incoming_block_async(it->second);
|
||||
continue;
|
||||
if (server->m_cachedBlocks) {
|
||||
auto it = server->m_cachedBlocks->find(id);
|
||||
if (it != server->m_cachedBlocks->end()) {
|
||||
LOGINFO(5, "using cached block for id = " << id);
|
||||
handle_incoming_block_async(it->second);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const bool result = m_owner->send(this,
|
||||
@@ -1781,6 +2019,8 @@ void P2PServer::P2PClient::post_handle_incoming_block(const uint32_t reset_count
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
++m_blockPendingRequests;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+29
-13
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,7 +18,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "tcp_server.h"
|
||||
#include <random>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
@@ -29,6 +28,7 @@ class BlockCache;
|
||||
static constexpr size_t P2P_BUF_SIZE = 128 * 1024;
|
||||
static constexpr size_t PEER_LIST_RESPONSE_MAX_PEERS = 16;
|
||||
static constexpr int DEFAULT_P2P_PORT = 37889;
|
||||
static constexpr int DEFAULT_P2P_PORT_MINI = 37888;
|
||||
|
||||
class P2PServer : public TCPServer<P2P_BUF_SIZE, P2P_BUF_SIZE>
|
||||
{
|
||||
@@ -109,14 +109,19 @@ public:
|
||||
bool m_handshakeInvalid;
|
||||
int m_listenPort;
|
||||
|
||||
time_t m_nextPeerListRequest;
|
||||
std::chrono::system_clock::time_point m_lastPeerListRequestTime;
|
||||
uint32_t m_fastPeerListRequestCount;
|
||||
uint64_t m_prevIncomingPeerListRequest;
|
||||
uint64_t m_nextOutgoingPeerListRequest;
|
||||
std::chrono::high_resolution_clock::time_point m_lastPeerListRequestTime;
|
||||
int m_peerListPendingRequests;
|
||||
int64_t m_pingTime;
|
||||
|
||||
time_t m_lastAlive;
|
||||
time_t m_lastBroadcastTimestamp;
|
||||
time_t m_lastBlockrequestTimestamp;
|
||||
int m_blockPendingRequests;
|
||||
bool m_chainTipBlockRequest;
|
||||
|
||||
uint64_t m_lastAlive;
|
||||
uint64_t m_lastBroadcastTimestamp;
|
||||
uint64_t m_lastBlockrequestTimestamp;
|
||||
|
||||
hash m_broadcastedHashes[8];
|
||||
std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 };
|
||||
@@ -128,15 +133,24 @@ public:
|
||||
|
||||
void print_status() override;
|
||||
void show_peers();
|
||||
size_t peer_list_size() const { return m_peerList.size(); }
|
||||
|
||||
uint32_t max_outgoing_peers() const { return m_maxOutgoingPeers; }
|
||||
uint32_t max_incoming_peers() const { return m_maxIncomingPeers; }
|
||||
|
||||
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); }
|
||||
|
||||
private:
|
||||
p2pool* m_pool;
|
||||
BlockCache* m_cache;
|
||||
bool m_cacheLoaded;
|
||||
std::string m_initialPeerList;
|
||||
uint32_t m_maxOutgoingPeers;
|
||||
uint32_t m_maxIncomingPeers;
|
||||
|
||||
uv_rwlock_t m_cachedBlocksLock;
|
||||
unordered_map<hash, PoolBlock*> m_cachedBlocks;
|
||||
unordered_map<hash, PoolBlock*>* m_cachedBlocks;
|
||||
|
||||
private:
|
||||
static void on_timer(uv_timer_t* timer) { reinterpret_cast<P2PServer*>(timer->data)->on_timer(); }
|
||||
@@ -150,19 +164,20 @@ private:
|
||||
void save_peer_list_async();
|
||||
void save_peer_list();
|
||||
void load_peer_list();
|
||||
void load_monerod_peer_list();
|
||||
void update_peer_in_list(bool is_v6, const raw_ip& ip, int port);
|
||||
void remove_peer_from_list(P2PClient* client);
|
||||
void remove_peer_from_list(const raw_ip& ip);
|
||||
|
||||
uv_mutex_t m_rngLock;
|
||||
std::random_device m_rd;
|
||||
std::mt19937_64 m_rng;
|
||||
|
||||
uv_mutex_t m_blockLock;
|
||||
PoolBlock* m_block;
|
||||
|
||||
uv_timer_t m_timer;
|
||||
uint32_t m_timerCounter;
|
||||
uint64_t m_timerCounter;
|
||||
uint64_t m_timerInterval;
|
||||
|
||||
uint64_t m_peerId;
|
||||
|
||||
@@ -174,11 +189,12 @@ private:
|
||||
raw_ip m_addr;
|
||||
int m_port;
|
||||
uint32_t m_numFailedConnections;
|
||||
time_t m_lastSeen;
|
||||
uint64_t m_lastSeen;
|
||||
};
|
||||
|
||||
std::vector<Peer> m_peerList;
|
||||
time_t m_peerListLastSaved;
|
||||
std::vector<Peer> m_peerListMonero;
|
||||
uint64_t m_peerListLastSaved;
|
||||
|
||||
struct Broadcast
|
||||
{
|
||||
@@ -192,7 +208,7 @@ private:
|
||||
std::vector<Broadcast*> m_broadcastQueue;
|
||||
|
||||
uv_mutex_t m_missingBlockRequestsLock;
|
||||
std::set<std::pair<uint64_t, uint64_t>> m_missingBlockRequests;
|
||||
unordered_set<std::pair<uint64_t, uint64_t>> m_missingBlockRequests;
|
||||
|
||||
static void on_broadcast(uv_async_t* handle) { reinterpret_cast<P2PServer*>(handle->data)->on_broadcast(); }
|
||||
void on_broadcast();
|
||||
|
||||
+232
-53
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -27,10 +27,14 @@
|
||||
#include "side_chain.h"
|
||||
#include "stratum_server.h"
|
||||
#include "p2p_server.h"
|
||||
#ifdef WITH_RANDOMX
|
||||
#include "miner.h"
|
||||
#endif
|
||||
#include "params.h"
|
||||
#include "console_commands.h"
|
||||
#include "crypto.h"
|
||||
#include "p2pool_api.h"
|
||||
#include "pool_block.h"
|
||||
#include <thread>
|
||||
#include <fstream>
|
||||
|
||||
@@ -50,7 +54,7 @@ p2pool::p2pool(int argc, char* argv[])
|
||||
, m_updateSeed(true)
|
||||
, m_submitBlockData{}
|
||||
, m_zmqLastActive(0)
|
||||
, m_startTime(time(nullptr))
|
||||
, m_startTime(seconds_since_epoch())
|
||||
{
|
||||
LOGINFO(1, log::LightCyan() << VERSION);
|
||||
|
||||
@@ -68,7 +72,8 @@ p2pool::p2pool(int argc, char* argv[])
|
||||
hash pub, sec, eph_public_key;
|
||||
generate_keys(pub, sec);
|
||||
|
||||
if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key)) {
|
||||
uint8_t view_tag;
|
||||
if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key, view_tag)) {
|
||||
LOGERR(1, "Invalid wallet address: get_eph_public_key failed");
|
||||
panic();
|
||||
}
|
||||
@@ -104,13 +109,43 @@ p2pool::p2pool(int argc, char* argv[])
|
||||
m_stopAsync.data = this;
|
||||
|
||||
uv_rwlock_init_checked(&m_mainchainLock);
|
||||
uv_rwlock_init_checked(&m_minerDataLock);
|
||||
uv_mutex_init_checked(&m_foundBlocksLock);
|
||||
#ifdef WITH_RANDOMX
|
||||
uv_mutex_init_checked(&m_minerLock);
|
||||
#endif
|
||||
uv_mutex_init_checked(&m_submitBlockDataLock);
|
||||
|
||||
m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath, m_params->m_localStats);
|
||||
|
||||
m_sideChain = new SideChain(this, type);
|
||||
m_hasher = new RandomX_Hasher(this);
|
||||
if (m_params->m_localStats && !m_api) {
|
||||
LOGERR(1, "--local-api and --stratum-api command line parameters can't be used without --data-api");
|
||||
panic();
|
||||
}
|
||||
|
||||
m_sideChain = new SideChain(this, type, m_params->m_mini ? "mini" : nullptr);
|
||||
|
||||
if (m_params->m_p2pAddresses.empty()) {
|
||||
const int p2p_port = m_sideChain->is_mini() ? DEFAULT_P2P_PORT_MINI : DEFAULT_P2P_PORT;
|
||||
|
||||
char buf[log::Stream::BUF_SIZE + 1];
|
||||
log::Stream s(buf);
|
||||
s << "[::]:" << p2p_port << ",0.0.0.0:" << p2p_port << '\0';
|
||||
|
||||
m_params->m_p2pAddresses = buf;
|
||||
}
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
if (m_params->m_disableRandomX) {
|
||||
m_hasher = new RandomX_Hasher_RPC(this);
|
||||
}
|
||||
else {
|
||||
m_hasher = new RandomX_Hasher(this);
|
||||
}
|
||||
#else
|
||||
m_hasher = new RandomX_Hasher_RPC(this);
|
||||
#endif
|
||||
|
||||
m_blockTemplate = new BlockTemplate(this);
|
||||
m_mempool = new Mempool();
|
||||
m_consoleCommands = new ConsoleCommands(this);
|
||||
@@ -119,7 +154,11 @@ p2pool::p2pool(int argc, char* argv[])
|
||||
p2pool::~p2pool()
|
||||
{
|
||||
uv_rwlock_destroy(&m_mainchainLock);
|
||||
uv_rwlock_destroy(&m_minerDataLock);
|
||||
uv_mutex_destroy(&m_foundBlocksLock);
|
||||
#ifdef WITH_RANDOMX
|
||||
uv_mutex_destroy(&m_minerLock);
|
||||
#endif
|
||||
uv_mutex_destroy(&m_submitBlockDataLock);
|
||||
|
||||
delete m_api;
|
||||
@@ -131,9 +170,9 @@ p2pool::~p2pool()
|
||||
delete m_consoleCommands;
|
||||
}
|
||||
|
||||
bool p2pool::calculate_hash(const void* data, size_t size, const hash& seed, hash& result)
|
||||
bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result)
|
||||
{
|
||||
return m_hasher->calculate(data, size, seed, result);
|
||||
return m_hasher->calculate(data, size, height, seed, result);
|
||||
}
|
||||
|
||||
uint64_t p2pool::get_seed_height(uint64_t height)
|
||||
@@ -157,6 +196,17 @@ bool p2pool::get_seed(uint64_t height, hash& seed) const
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
void p2pool::print_miner_status()
|
||||
{
|
||||
MutexLock lock(m_minerLock);
|
||||
|
||||
if (m_miner) {
|
||||
m_miner->print_status();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void p2pool::handle_tx(TxMempoolData& tx)
|
||||
{
|
||||
if (!tx.weight || !tx.fee) {
|
||||
@@ -173,10 +223,10 @@ void p2pool::handle_tx(TxMempoolData& tx)
|
||||
", fee = " << log::Gray() << static_cast<double>(tx.fee) / 1e6 << " um");
|
||||
|
||||
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
||||
m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet);
|
||||
m_blockTemplate->update(miner_data(), *m_mempool, &m_params->m_wallet);
|
||||
#endif
|
||||
|
||||
m_zmqLastActive = time(nullptr);
|
||||
m_zmqLastActive = seconds_since_epoch();
|
||||
}
|
||||
|
||||
void p2pool::handle_miner_data(MinerData& data)
|
||||
@@ -203,11 +253,16 @@ void p2pool::handle_miner_data(MinerData& data)
|
||||
c.reward = 0;
|
||||
|
||||
m_mainchainByHash[c.id] = c;
|
||||
|
||||
cleanup_mainchain_data(data.height);
|
||||
}
|
||||
|
||||
data.tx_backlog.clear();
|
||||
data.time_received = std::chrono::system_clock::now();
|
||||
m_minerData = data;
|
||||
data.time_received = std::chrono::high_resolution_clock::now();
|
||||
{
|
||||
WriteLock lock(m_minerDataLock);
|
||||
m_minerData = data;
|
||||
}
|
||||
m_updateSeed = true;
|
||||
update_median_timestamp();
|
||||
|
||||
@@ -224,14 +279,49 @@ void p2pool::handle_miner_data(MinerData& data)
|
||||
"\n---------------------------------------------------------------------------------------------------------------"
|
||||
);
|
||||
|
||||
if (!is_main_thread) {
|
||||
if (!is_main_thread()) {
|
||||
update_block_template_async();
|
||||
}
|
||||
else {
|
||||
update_block_template();
|
||||
}
|
||||
|
||||
m_zmqLastActive = time(nullptr);
|
||||
m_zmqLastActive = seconds_since_epoch();
|
||||
|
||||
if (m_serversStarted.load()) {
|
||||
std::vector<uint64_t> missing_heights;
|
||||
{
|
||||
WriteLock lock(m_mainchainLock);
|
||||
|
||||
for (uint64_t h = data.height; h && (h + BLOCK_HEADERS_REQUIRED > data.height); --h) {
|
||||
if (m_mainchainByHeight.find(h) == m_mainchainByHeight.end()) {
|
||||
LOGWARN(3, "Mainchain data for height " << h << " is missing, requesting it from monerod again");
|
||||
missing_heights.push_back(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint64_t h : missing_heights) {
|
||||
char buf[log::Stream::BUF_SIZE + 1];
|
||||
log::Stream s(buf);
|
||||
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << h << "}}\0";
|
||||
|
||||
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
|
||||
[this, h](const char* data, size_t size)
|
||||
{
|
||||
ChainMain block;
|
||||
if (!parse_block_header(data, size, block)) {
|
||||
LOGERR(1, "couldn't download block header for height " << h);
|
||||
}
|
||||
},
|
||||
[h](const char* data, size_t size)
|
||||
{
|
||||
if (size > 0) {
|
||||
LOGERR(1, "couldn't download block header for height " << h << ", error " << log::const_buf(data, size));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* BLOCK_FOUND = "\n\
|
||||
@@ -284,8 +374,13 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
|
||||
", reward = " << log::Gray() << log::XMRAmount(data.reward));
|
||||
|
||||
if (!sidechain_id.empty()) {
|
||||
if (side_chain().has_block(sidechain_id)) {
|
||||
PoolBlock* block = side_chain().find_block(sidechain_id);
|
||||
if (block) {
|
||||
LOGINFO(0, log::LightGreen() << "BLOCK FOUND: main chain block at height " << data.height << " was mined by this p2pool" << BLOCK_FOUND);
|
||||
const uint64_t payout = block->get_payout(params().m_wallet);
|
||||
if (payout) {
|
||||
LOGINFO(0, log::LightCyan() << "You received a payout of " << log::LightGreen() << log::XMRAmount(payout) << log::LightCyan() << " in block " << log::LightGreen() << data.height);
|
||||
}
|
||||
api_update_block_found(&data);
|
||||
}
|
||||
else {
|
||||
@@ -295,7 +390,7 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
|
||||
|
||||
api_update_network_stats();
|
||||
|
||||
m_zmqLastActive = time(nullptr);
|
||||
m_zmqLastActive = seconds_since_epoch();
|
||||
}
|
||||
|
||||
void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
|
||||
@@ -477,11 +572,13 @@ void p2pool::update_block_template_async()
|
||||
|
||||
void p2pool::update_block_template()
|
||||
{
|
||||
MinerData data = miner_data();
|
||||
|
||||
if (m_updateSeed) {
|
||||
m_hasher->set_seed_async(m_minerData.seed_hash);
|
||||
m_hasher->set_seed_async(data.seed_hash);
|
||||
m_updateSeed = false;
|
||||
}
|
||||
m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet);
|
||||
m_blockTemplate->update(data, *m_mempool, &m_params->m_wallet);
|
||||
stratum_on_block();
|
||||
api_update_pool_stats();
|
||||
}
|
||||
@@ -524,29 +621,37 @@ void p2pool::download_block_headers(uint64_t current_height)
|
||||
});
|
||||
}
|
||||
|
||||
const uint64_t start_height = (current_height > BLOCK_HEADERS_REQUIRED) ? (current_height - BLOCK_HEADERS_REQUIRED) : 0;
|
||||
|
||||
s.m_pos = 0;
|
||||
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_headers_range\",\"params\":{\"start_height\":" << current_height - BLOCK_HEADERS_REQUIRED << ",\"end_height\":" << current_height - 1 << "}}\0";
|
||||
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_headers_range\",\"params\":{\"start_height\":" << start_height << ",\"end_height\":" << current_height - 1 << "}}\0";
|
||||
|
||||
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
|
||||
[this, current_height](const char* data, size_t size)
|
||||
[this, start_height, current_height](const char* data, size_t size)
|
||||
{
|
||||
if (parse_block_headers_range(data, size) == BLOCK_HEADERS_REQUIRED) {
|
||||
if (parse_block_headers_range(data, size) == current_height - start_height) {
|
||||
update_median_timestamp();
|
||||
if (m_serversStarted.exchange(1) == 0) {
|
||||
m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this);
|
||||
m_stratumServer = new StratumServer(this);
|
||||
m_p2pServer = new P2PServer(this);
|
||||
#ifdef WITH_RANDOMX
|
||||
if (m_params->m_minerThreads) {
|
||||
start_mining(m_params->m_minerThreads);
|
||||
}
|
||||
#endif
|
||||
api_update_network_stats();
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGERR(1, "fatal error: couldn't download block headers for heights " << current_height - BLOCK_HEADERS_REQUIRED << " - " << current_height - 1);
|
||||
LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1);
|
||||
panic();
|
||||
}
|
||||
},
|
||||
[current_height](const char* data, size_t size)
|
||||
[start_height, current_height](const char* data, size_t size)
|
||||
{
|
||||
if (size > 0) {
|
||||
LOGERR(1, "fatal error: couldn't download block headers for heights " << current_height - BLOCK_HEADERS_REQUIRED << " - " << current_height - 1 << ", error " << log::const_buf(data, size));
|
||||
LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1 << ", error " << log::const_buf(data, size));
|
||||
panic();
|
||||
}
|
||||
});
|
||||
@@ -588,6 +693,7 @@ void p2pool::update_median_timestamp()
|
||||
uint64_t timestamps[TIMESTAMP_WINDOW];
|
||||
if (!get_timestamps(timestamps))
|
||||
{
|
||||
WriteLock lock(m_minerDataLock);
|
||||
m_minerData.median_timestamp = 0;
|
||||
return;
|
||||
}
|
||||
@@ -595,28 +701,28 @@ void p2pool::update_median_timestamp()
|
||||
std::sort(timestamps, timestamps + TIMESTAMP_WINDOW);
|
||||
|
||||
// Shift it +1 block compared to Monero's code because we don't have the latest block yet when we receive new miner data
|
||||
m_minerData.median_timestamp = (timestamps[TIMESTAMP_WINDOW / 2] + timestamps[TIMESTAMP_WINDOW / 2 + 1]) / 2;
|
||||
LOGINFO(4, "median timestamp updated to " << log::Gray() << m_minerData.median_timestamp);
|
||||
const uint64_t ts = (timestamps[TIMESTAMP_WINDOW / 2] + timestamps[TIMESTAMP_WINDOW / 2 + 1]) / 2;
|
||||
LOGINFO(4, "median timestamp updated to " << log::Gray() << ts);
|
||||
|
||||
WriteLock lock(m_minerDataLock);
|
||||
m_minerData.median_timestamp = ts;
|
||||
}
|
||||
|
||||
void p2pool::stratum_on_block()
|
||||
{
|
||||
#if 0
|
||||
uint8_t hashing_blob[128];
|
||||
uint64_t height;
|
||||
difficulty_type difficulty;
|
||||
difficulty_type sidechain_difficulty;
|
||||
hash seed_hash;
|
||||
size_t nonce_offset;
|
||||
uint32_t template_id;
|
||||
#ifdef WITH_RANDOMX
|
||||
{
|
||||
MutexLock lock(m_minerLock);
|
||||
|
||||
if (m_miner) {
|
||||
m_miner->on_block(*m_blockTemplate);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
m_blockTemplate->get_hashing_blob(0, hashing_blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset, template_id);
|
||||
submit_block(template_id, 0, 0);
|
||||
#else
|
||||
if (m_stratumServer) {
|
||||
m_stratumServer->on_block(*m_blockTemplate);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void p2pool::get_info()
|
||||
@@ -688,18 +794,22 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
|
||||
const auto& result = doc["result"];
|
||||
|
||||
struct {
|
||||
bool busy_syncing, mainnet, testnet, stagenet;
|
||||
bool busy_syncing, synchronized, mainnet, testnet, stagenet;
|
||||
} info;
|
||||
|
||||
if (!PARSE(result, info, busy_syncing) || !PARSE(result, info, mainnet) || !PARSE(result, info, testnet) || !PARSE(result, info, stagenet)) {
|
||||
if (!PARSE(result, info, busy_syncing) ||
|
||||
!PARSE(result, info, synchronized) ||
|
||||
!PARSE(result, info, mainnet) ||
|
||||
!PARSE(result, info, testnet) ||
|
||||
!PARSE(result, info, stagenet)) {
|
||||
LOGWARN(1, "get_info RPC response is invalid, trying again in 1 second");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
get_info();
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.busy_syncing) {
|
||||
LOGINFO(1, "monerod is busy syncing, trying again in 1 second");
|
||||
if (info.busy_syncing || !info.synchronized) {
|
||||
LOGINFO(1, "monerod is " << (info.busy_syncing ? "busy syncing" : "not synchronized") << ", trying again in 1 second");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
get_info();
|
||||
return;
|
||||
@@ -769,10 +879,14 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
|
||||
return;
|
||||
}
|
||||
|
||||
if (version < 0x30008) {
|
||||
const uint64_t required = m_params->m_disableRandomX ? 0x30009 : 0x30008;
|
||||
|
||||
if (version < required) {
|
||||
const uint64_t version_hi = version >> 16;
|
||||
const uint64_t version_lo = version & 65535;
|
||||
LOGERR(1, "monerod RPC v" << version_hi << '.' << version_lo << " is incompatible, update to RPC >= v3.8");
|
||||
const uint64_t required_version_hi = required >> 16;
|
||||
const uint64_t required_version_lo = required & 65535;
|
||||
LOGERR(1, "monerod RPC v" << version_hi << '.' << version_lo << " is incompatible, update to RPC >= v" << required_version_hi << '.' << required_version_lo << " (Monero v0.17.3.0 or newer)");
|
||||
panic();
|
||||
}
|
||||
|
||||
@@ -943,10 +1057,16 @@ void p2pool::api_update_network_stats()
|
||||
return;
|
||||
}
|
||||
|
||||
hash prev_id;
|
||||
{
|
||||
ReadLock lock(m_minerDataLock);
|
||||
prev_id = m_minerData.prev_id;
|
||||
}
|
||||
|
||||
ChainMain mainnet_tip;
|
||||
{
|
||||
ReadLock lock(m_mainchainLock);
|
||||
mainnet_tip = m_mainchainByHash[m_minerData.prev_id];
|
||||
mainnet_tip = m_mainchainByHash[prev_id];
|
||||
}
|
||||
|
||||
m_api->set(p2pool_api::Category::NETWORK, "stats",
|
||||
@@ -971,7 +1091,7 @@ void p2pool::api_update_pool_stats()
|
||||
uint64_t t;
|
||||
const difficulty_type& diff = m_sideChain->difficulty();
|
||||
const uint64_t hashrate = udiv128(diff.hi, diff.lo, m_sideChain->block_time(), &t);
|
||||
const uint64_t miners = m_sideChain->miner_count();
|
||||
const uint64_t miners = std::max<uint64_t>(m_sideChain->miner_count(), m_p2pServer ? m_p2pServer->peer_list_size() : 0U);
|
||||
const difficulty_type total_hashes = m_sideChain->total_hashes();
|
||||
|
||||
time_t last_block_found_time = 0;
|
||||
@@ -1008,10 +1128,16 @@ void p2pool::api_update_stats_mod()
|
||||
return;
|
||||
}
|
||||
|
||||
hash prev_id;
|
||||
{
|
||||
ReadLock lock(m_minerDataLock);
|
||||
prev_id = m_minerData.prev_id;
|
||||
}
|
||||
|
||||
ChainMain mainnet_tip;
|
||||
{
|
||||
ReadLock lock(m_mainchainLock);
|
||||
mainnet_tip = m_mainchainByHash[m_minerData.prev_id];
|
||||
mainnet_tip = m_mainchainByHash[prev_id];
|
||||
}
|
||||
|
||||
time_t last_block_found_time = 0;
|
||||
@@ -1022,10 +1148,11 @@ void p2pool::api_update_stats_mod()
|
||||
{
|
||||
MutexLock lock(m_foundBlocksLock);
|
||||
if (!m_foundBlocks.empty()) {
|
||||
last_block_found_time = m_foundBlocks.back().timestamp;
|
||||
last_block_found_height = m_foundBlocks.back().height;
|
||||
last_block_found_hash = m_foundBlocks.back().id;
|
||||
last_block_total_hashes = m_foundBlocks.back().total_hashes;
|
||||
const FoundBlock& b = m_foundBlocks.back();
|
||||
last_block_found_time = b.timestamp;
|
||||
last_block_found_height = b.height;
|
||||
last_block_found_hash = b.id;
|
||||
last_block_total_hashes = b.total_hashes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1034,7 +1161,7 @@ void p2pool::api_update_stats_mod()
|
||||
s << last_block_found_hash << '\0';
|
||||
memcpy(last_block_found_buf + 4, "...", 4);
|
||||
|
||||
const uint64_t miners = m_sideChain->miner_count();
|
||||
const uint64_t miners = std::max<uint64_t>(m_sideChain->miner_count(), m_p2pServer ? m_p2pServer->peer_list_size() : 0U);
|
||||
|
||||
uint64_t t;
|
||||
const difficulty_type& diff = m_sideChain->difficulty();
|
||||
@@ -1051,7 +1178,7 @@ void p2pool::api_update_stats_mod()
|
||||
m_api->set(p2pool_api::Category::GLOBAL, "stats_mod",
|
||||
[&mainnet_tip, last_block_found_time, &last_block_found_buf, last_block_found_height, miners, hashrate, round_hashes, stratum_port](log::Stream& s)
|
||||
{
|
||||
s << "{\"config\":{\"ports\":[{\"port\":" << stratum_port << ",\"tls\":false}],\"fee\":0,\"minPaymentThreshold\":400000000},\"network\":{\"height\":"
|
||||
s << "{\"config\":{\"ports\":[{\"port\":" << stratum_port << ",\"tls\":false}],\"fee\":0,\"minPaymentThreshold\":300000000},\"network\":{\"height\":"
|
||||
<< mainnet_tip.height << "},\"pool\":{\"stats\":{\"lastBlockFound\":\""
|
||||
<< last_block_found_time << "000\"},\"blocks\":[\""
|
||||
<< static_cast<char*>(last_block_found_buf) << static_cast<char*>(last_block_found_buf) + HASH_SIZE * 2 - 4 << ':'
|
||||
@@ -1063,6 +1190,31 @@ void p2pool::api_update_stats_mod()
|
||||
});
|
||||
}
|
||||
|
||||
void p2pool::cleanup_mainchain_data(uint64_t height)
|
||||
{
|
||||
// Expects m_mainchainLock to be already locked here
|
||||
// Deletes everything older than 720 blocks, except for the 3 latest RandomX seed heights
|
||||
|
||||
constexpr uint64_t PRUNE_DISTANCE = BLOCK_HEADERS_REQUIRED;
|
||||
const uint64_t seed_height = get_seed_height(height);
|
||||
const std::array<uint64_t, 3> seed_heights{ seed_height, seed_height - SEEDHASH_EPOCH_BLOCKS, seed_height - SEEDHASH_EPOCH_BLOCKS * 2 };
|
||||
|
||||
for (auto it = m_mainchainByHeight.begin(); it != m_mainchainByHeight.end();) {
|
||||
const uint64_t h = it->first;
|
||||
if (h + PRUNE_DISTANCE >= height) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (std::find(seed_heights.begin(), seed_heights.end(), h) == seed_heights.end()) {
|
||||
m_mainchainByHash.erase(it->second.id);
|
||||
it = m_mainchainByHeight.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void p2pool::api_update_block_found(const ChainMain* data)
|
||||
{
|
||||
clear_crypto_cache();
|
||||
@@ -1126,6 +1278,26 @@ bool p2pool::get_difficulty_at_height(uint64_t height, difficulty_type& diff)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
void p2pool::start_mining(uint32_t threads)
|
||||
{
|
||||
stop_mining();
|
||||
|
||||
MutexLock lock(m_minerLock);
|
||||
m_miner = new Miner(this, threads);
|
||||
}
|
||||
|
||||
void p2pool::stop_mining()
|
||||
{
|
||||
MutexLock lock(m_minerLock);
|
||||
|
||||
if (m_miner) {
|
||||
delete m_miner;
|
||||
m_miner = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void on_signal(uv_signal_t* handle, int signum)
|
||||
{
|
||||
p2pool* pool = reinterpret_cast<p2pool*>(handle->data);
|
||||
@@ -1168,6 +1340,10 @@ static bool init_uv_threadpool()
|
||||
|
||||
static bool init_signals(p2pool* pool)
|
||||
{
|
||||
#ifdef SIGPIPE
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
constexpr int signal_names[] = {
|
||||
SIGHUP,
|
||||
SIGINT,
|
||||
@@ -1218,11 +1394,11 @@ int p2pool::run()
|
||||
}
|
||||
|
||||
try {
|
||||
ZMQReader z(m_params->m_host.c_str(), m_params->m_zmqPort, this);
|
||||
get_info();
|
||||
load_found_blocks();
|
||||
const int rc = uv_run(uv_default_loop_checked(), UV_RUN_DEFAULT);
|
||||
LOGINFO(1, "uv_run exited, result = " << rc);
|
||||
delete m_ZMQReader;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
const char* s = e.what();
|
||||
@@ -1234,6 +1410,9 @@ int p2pool::run()
|
||||
|
||||
bkg_jobs_tracker.wait();
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
delete m_miner;
|
||||
#endif
|
||||
delete m_stratumServer;
|
||||
delete m_p2pServer;
|
||||
|
||||
|
||||
+38
-11
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -23,14 +23,16 @@
|
||||
namespace p2pool {
|
||||
|
||||
struct Params;
|
||||
class RandomX_Hasher;
|
||||
class RandomX_Hasher_Base;
|
||||
class BlockTemplate;
|
||||
class Mempool;
|
||||
class SideChain;
|
||||
class StratumServer;
|
||||
class P2PServer;
|
||||
class Miner;
|
||||
class ConsoleCommands;
|
||||
class p2pool_api;
|
||||
class ZMQReader;
|
||||
|
||||
class p2pool : public MinerCallbackHandler
|
||||
{
|
||||
@@ -46,18 +48,27 @@ public:
|
||||
const Params& params() const { return *m_params; }
|
||||
BlockTemplate& block_template() { return *m_blockTemplate; }
|
||||
SideChain& side_chain() { return *m_sideChain; }
|
||||
const MinerData& miner_data() const { return m_minerData; }
|
||||
|
||||
FORCEINLINE MinerData miner_data() const
|
||||
{
|
||||
ReadLock lock(m_minerDataLock);
|
||||
return m_minerData;
|
||||
}
|
||||
|
||||
p2pool_api* api() const { return m_api; }
|
||||
|
||||
RandomX_Hasher* hasher() const { return m_hasher; }
|
||||
bool calculate_hash(const void* data, size_t size, const hash& seed, hash& result);
|
||||
RandomX_Hasher_Base* hasher() const { return m_hasher; }
|
||||
bool calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result);
|
||||
static uint64_t get_seed_height(uint64_t height);
|
||||
bool get_seed(uint64_t height, hash& seed) const;
|
||||
|
||||
StratumServer* stratum_server() const { return m_stratumServer; }
|
||||
P2PServer* p2p_server() const { return m_p2pServer; }
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
void print_miner_status();
|
||||
#endif
|
||||
|
||||
virtual void handle_tx(TxMempoolData& tx) override;
|
||||
virtual void handle_miner_data(MinerData& data) override;
|
||||
virtual void handle_chain_main(ChainMain& data, const char* extra) override;
|
||||
@@ -77,8 +88,13 @@ public:
|
||||
|
||||
bool get_difficulty_at_height(uint64_t height, difficulty_type& diff);
|
||||
|
||||
time_t zmq_last_active() const { return m_zmqLastActive; }
|
||||
time_t start_time() const { return m_startTime; }
|
||||
#ifdef WITH_RANDOMX
|
||||
void start_mining(uint32_t threads);
|
||||
void stop_mining();
|
||||
#endif
|
||||
|
||||
uint64_t zmq_last_active() const { return m_zmqLastActive; }
|
||||
uint64_t start_time() const { return m_startTime; }
|
||||
|
||||
private:
|
||||
p2pool(const p2pool&) = delete;
|
||||
@@ -96,9 +112,8 @@ private:
|
||||
|
||||
p2pool_api* m_api;
|
||||
SideChain* m_sideChain;
|
||||
RandomX_Hasher* m_hasher;
|
||||
RandomX_Hasher_Base* m_hasher;
|
||||
BlockTemplate* m_blockTemplate;
|
||||
MinerData m_minerData;
|
||||
bool m_updateSeed;
|
||||
Mempool* m_mempool;
|
||||
|
||||
@@ -106,6 +121,9 @@ private:
|
||||
std::map<uint64_t, ChainMain> m_mainchainByHeight;
|
||||
unordered_map<hash, ChainMain> m_mainchainByHash;
|
||||
|
||||
mutable uv_rwlock_t m_minerDataLock;
|
||||
MinerData m_minerData;
|
||||
|
||||
enum { TIMESTAMP_WINDOW = 60 };
|
||||
bool get_timestamps(uint64_t (×tamps)[TIMESTAMP_WINDOW]) const;
|
||||
void update_median_timestamp();
|
||||
@@ -129,6 +147,8 @@ private:
|
||||
void api_update_pool_stats();
|
||||
void api_update_stats_mod();
|
||||
|
||||
void cleanup_mainchain_data(uint64_t height);
|
||||
|
||||
struct FoundBlock
|
||||
{
|
||||
FORCEINLINE FoundBlock(time_t _t, uint64_t _h, const hash& _id, const difficulty_type& _block_diff, const difficulty_type& _total_hashes)
|
||||
@@ -153,6 +173,11 @@ private:
|
||||
StratumServer* m_stratumServer = nullptr;
|
||||
P2PServer* m_p2pServer = nullptr;
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
uv_mutex_t m_minerLock;
|
||||
Miner* m_miner = nullptr;
|
||||
#endif
|
||||
|
||||
ConsoleCommands* m_consoleCommands;
|
||||
|
||||
struct SubmitBlockData
|
||||
@@ -170,8 +195,10 @@ private:
|
||||
uv_async_t m_blockTemplateAsync;
|
||||
uv_async_t m_stopAsync;
|
||||
|
||||
time_t m_zmqLastActive;
|
||||
time_t m_startTime;
|
||||
std::atomic<uint64_t> m_zmqLastActive;
|
||||
uint64_t m_startTime;
|
||||
|
||||
ZMQReader* m_ZMQReader = nullptr;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+54
-15
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,66 +20,115 @@
|
||||
#include "stratum_server.h"
|
||||
#include "p2p_server.h"
|
||||
|
||||
void p2pool_usage();
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
Params::Params(int argc, char* argv[])
|
||||
{
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
bool ok = false;
|
||||
|
||||
if ((strcmp(argv[i], "--host") == 0) && (i + 1 < argc)) {
|
||||
m_host = argv[++i];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--rpc-port") == 0) && (i + 1 < argc)) {
|
||||
m_rpcPort = static_cast<uint32_t>(atoi(argv[++i]));
|
||||
m_rpcPort = strtoul(argv[++i], nullptr, 10);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--zmq-port") == 0) && (i + 1 < argc)) {
|
||||
m_zmqPort = static_cast<uint32_t>(atoi(argv[++i]));
|
||||
m_zmqPort = strtoul(argv[++i], nullptr, 10);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--light-mode") == 0) {
|
||||
m_lightMode = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--wallet") == 0) && (i + 1 < argc)) {
|
||||
m_wallet.decode(argv[++i]);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--stratum") == 0) && (i + 1 < argc)) {
|
||||
m_stratumAddresses = argv[++i];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--p2p") == 0) && (i + 1 < argc)) {
|
||||
m_p2pAddresses = argv[++i];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--addpeers") == 0) && (i + 1 < argc)) {
|
||||
m_p2pPeerList = argv[++i];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--loglevel") == 0) && (i + 1 < argc)) {
|
||||
const int level = std::min(std::max(atoi(argv[++i]), 0), log::MAX_GLOBAL_LOG_LEVEL);
|
||||
const int level = std::min(std::max<int>(strtol(argv[++i], nullptr, 10), 0), log::MAX_GLOBAL_LOG_LEVEL);
|
||||
log::GLOBAL_LOG_LEVEL = level;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--config") == 0) && (i + 1 < argc)) {
|
||||
m_config = argv[++i];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--data-api") == 0) && (i + 1 < argc)) {
|
||||
m_apiPath = argv[++i];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--stratum-api") == 0) {
|
||||
if ((strcmp(argv[i], "--local-api") == 0) || (strcmp(argv[i], "--stratum-api") == 0)) {
|
||||
m_localStats = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--no-cache") == 0) {
|
||||
m_blockCache = false;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--no-color") == 0) {
|
||||
log::CONSOLE_COLORS = false;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--no-randomx") == 0) {
|
||||
m_disableRandomX = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--out-peers") == 0) && (i + 1 < argc)) {
|
||||
m_maxOutgoingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 1000UL);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--in-peers") == 0) && (i + 1 < argc)) {
|
||||
m_maxIncomingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 1000UL);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--start-mining") == 0) && (i + 1 < argc)) {
|
||||
m_minerThreads = std::min(std::max(strtoul(argv[++i], nullptr, 10), 1UL), 64UL);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--mini") == 0) {
|
||||
m_mini = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
|
||||
p2pool_usage();
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,16 +141,6 @@ Params::Params(int argc, char* argv[])
|
||||
|
||||
m_stratumAddresses = buf;
|
||||
}
|
||||
|
||||
if (m_p2pAddresses.empty()) {
|
||||
const int p2p_port = DEFAULT_P2P_PORT;
|
||||
|
||||
char buf[log::Stream::BUF_SIZE + 1];
|
||||
log::Stream s(buf);
|
||||
s << "[::]:" << p2p_port << ",0.0.0.0:" << p2p_port << '\0';
|
||||
|
||||
m_p2pAddresses = buf;
|
||||
}
|
||||
}
|
||||
|
||||
bool Params::ok() const
|
||||
|
||||
+10
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -39,6 +39,15 @@ struct Params
|
||||
std::string m_apiPath;
|
||||
bool m_localStats = false;
|
||||
bool m_blockCache = true;
|
||||
#ifdef WITH_RANDOMX
|
||||
bool m_disableRandomX = false;
|
||||
#else
|
||||
bool m_disableRandomX = true;
|
||||
#endif
|
||||
uint32_t m_maxOutgoingPeers = 10;
|
||||
uint32_t m_maxIncomingPeers = 1000;
|
||||
uint32_t m_minerThreads = 0;
|
||||
bool m_mini = false;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+37
-13
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -53,14 +53,13 @@ PoolBlock::PoolBlock()
|
||||
, m_invalid(false)
|
||||
, m_broadcasted(false)
|
||||
, m_wantBroadcast(false)
|
||||
, m_localTimestamp(time(nullptr))
|
||||
, m_localTimestamp(seconds_since_epoch())
|
||||
{
|
||||
uv_mutex_init_checked(&m_lock);
|
||||
|
||||
m_mainChainData.reserve(48 * 1024);
|
||||
m_outputs.reserve(2048);
|
||||
m_transactions.reserve(256);
|
||||
m_tmpInts.reserve(m_transactions.capacity() * HASH_SIZE);
|
||||
m_sideChainData.reserve(512);
|
||||
m_uncles.reserve(8);
|
||||
m_tmpTxExtra.reserve(80);
|
||||
@@ -110,14 +109,13 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
|
||||
m_cumulativeDifficulty = b.m_cumulativeDifficulty;
|
||||
m_sidechainId = b.m_sidechainId;
|
||||
m_tmpTxExtra = b.m_tmpTxExtra;
|
||||
m_tmpInts = b.m_tmpInts;
|
||||
m_depth = b.m_depth;
|
||||
m_verified = b.m_verified;
|
||||
m_invalid = b.m_invalid;
|
||||
m_broadcasted = b.m_broadcasted;
|
||||
m_wantBroadcast = b.m_wantBroadcast;
|
||||
|
||||
m_localTimestamp = time(nullptr);
|
||||
m_localTimestamp = seconds_since_epoch();
|
||||
|
||||
if (lock_result == 0) {
|
||||
uv_mutex_unlock(&b.m_lock);
|
||||
@@ -159,8 +157,12 @@ void PoolBlock::serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, c
|
||||
|
||||
for (TxOutput& output : m_outputs) {
|
||||
writeVarint(output.m_reward, m_mainChainData);
|
||||
m_mainChainData.push_back(TXOUT_TO_KEY);
|
||||
m_mainChainData.push_back(output.m_txType);
|
||||
m_mainChainData.insert(m_mainChainData.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
|
||||
|
||||
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
|
||||
m_mainChainData.push_back(output.m_viewTag);
|
||||
}
|
||||
}
|
||||
|
||||
m_mainChainOutputsBlobSize = static_cast<int>(m_mainChainData.size()) - m_mainChainOutputsOffset;
|
||||
@@ -227,7 +229,7 @@ void PoolBlock::serialize_sidechain_data()
|
||||
writeVarint(m_cumulativeDifficulty.hi, m_sideChainData);
|
||||
}
|
||||
|
||||
bool PoolBlock::get_pow_hash(RandomX_Hasher* hasher, 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];
|
||||
|
||||
@@ -276,28 +278,50 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher* hasher, const hash& seed_hash, hash
|
||||
|
||||
cnt >>= 1;
|
||||
|
||||
m_tmpInts.resize(cnt * HASH_SIZE);
|
||||
memcpy(m_tmpInts.data(), h, (cnt * 2 - count) * HASH_SIZE);
|
||||
std::vector<uint8_t> tmp_ints(cnt * HASH_SIZE);
|
||||
memcpy(tmp_ints.data(), h, (cnt * 2 - count) * HASH_SIZE);
|
||||
|
||||
for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
|
||||
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, m_tmpInts.data() + j * HASH_SIZE, HASH_SIZE);
|
||||
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, tmp_ints.data() + j * HASH_SIZE, HASH_SIZE);
|
||||
}
|
||||
|
||||
while (cnt > 2) {
|
||||
cnt >>= 1;
|
||||
for (i = 0, j = 0; j < cnt; i += 2, ++j) {
|
||||
keccak(m_tmpInts.data() + i * HASH_SIZE, HASH_SIZE * 2, m_tmpInts.data() + j * HASH_SIZE, HASH_SIZE);
|
||||
keccak(tmp_ints.data() + i * HASH_SIZE, HASH_SIZE * 2, tmp_ints.data() + j * HASH_SIZE, HASH_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
keccak(m_tmpInts.data(), HASH_SIZE * 2, blob + blob_size, HASH_SIZE);
|
||||
keccak(tmp_ints.data(), HASH_SIZE * 2, blob + blob_size, HASH_SIZE);
|
||||
}
|
||||
}
|
||||
blob_size += HASH_SIZE;
|
||||
|
||||
writeVarint(count, [&blob, &blob_size](uint8_t b) { blob[blob_size++] = b; });
|
||||
|
||||
return hasher->calculate(blob, blob_size, seed_hash, pow_hash);
|
||||
return hasher->calculate(blob, blob_size, height, seed_hash, pow_hash);
|
||||
}
|
||||
|
||||
uint64_t PoolBlock::get_payout(const Wallet& w) const
|
||||
{
|
||||
for (size_t i = 0, n = m_outputs.size(); i < n; ++i) {
|
||||
const TxOutput& out = m_outputs[i];
|
||||
hash eph_public_key;
|
||||
|
||||
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
|
||||
if (w.get_eph_public_key_with_view_tag(m_txkeySec, i, eph_public_key, out.m_viewTag) && (eph_public_key == out.m_ephPublicKey)) {
|
||||
return out.m_reward;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint8_t view_tag;
|
||||
if (w.get_eph_public_key(m_txkeySec, i, eph_public_key, view_tag) && (eph_public_key == out.m_ephPublicKey)) {
|
||||
return out.m_reward;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+14
-7
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
class RandomX_Hasher;
|
||||
class RandomX_Hasher_Base;
|
||||
class SideChain;
|
||||
|
||||
/*
|
||||
@@ -81,11 +81,13 @@ struct PoolBlock
|
||||
|
||||
struct TxOutput
|
||||
{
|
||||
FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey() {}
|
||||
FORCEINLINE TxOutput(uint64_t r, const hash& k) : m_reward(r), m_ephPublicKey(k) {}
|
||||
FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey(), m_txType(0), m_viewTag(0) {}
|
||||
FORCEINLINE TxOutput(uint64_t r, const hash& k, uint8_t tx_type, uint8_t view_tag) : m_reward(r), m_ephPublicKey(k), m_txType(tx_type), m_viewTag(view_tag) {}
|
||||
|
||||
uint64_t m_reward;
|
||||
hash m_ephPublicKey;
|
||||
uint8_t m_txType;
|
||||
uint8_t m_viewTag;
|
||||
};
|
||||
|
||||
std::vector<TxOutput> m_outputs;
|
||||
@@ -121,7 +123,6 @@ struct PoolBlock
|
||||
|
||||
// Just temporary stuff, not a part of the block
|
||||
std::vector<uint8_t> m_tmpTxExtra;
|
||||
std::vector<uint8_t> m_tmpInts;
|
||||
|
||||
uint64_t m_depth;
|
||||
|
||||
@@ -131,13 +132,19 @@ struct PoolBlock
|
||||
bool m_broadcasted;
|
||||
bool m_wantBroadcast;
|
||||
|
||||
time_t m_localTimestamp;
|
||||
uint64_t m_localTimestamp;
|
||||
|
||||
void serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash);
|
||||
void serialize_sidechain_data();
|
||||
|
||||
int deserialize(const uint8_t* data, size_t size, SideChain& sidechain);
|
||||
bool get_pow_hash(RandomX_Hasher* hasher, 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;
|
||||
|
||||
// Both tx types are allowed by Monero consensus during v15 because it needs to process pre-fork mempool transactions,
|
||||
// but P2Pool can switch to using only TXOUT_TO_TAGGED_KEY for miner payouts starting from v15
|
||||
FORCEINLINE uint8_t get_tx_type() const { return (m_majorVersion < HARDFORK_VIEW_TAGS_VERSION) ? TXOUT_TO_KEY : TXOUT_TO_TAGGED_KEY; }
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+14
-29
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -46,29 +46,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
#define READ_BYTE(x) do { if (!read_byte(x)) return __LINE__; } while (0)
|
||||
#define EXPECT_BYTE(value) do { uint8_t tmp; READ_BYTE(tmp); if (tmp != (value)) return __LINE__; } while (0)
|
||||
|
||||
auto read_varint = [&data, data_end](auto& b) -> bool
|
||||
{
|
||||
uint64_t result = 0;
|
||||
int k = 0;
|
||||
|
||||
while (data < data_end) {
|
||||
if (k >= static_cast<int>(sizeof(b)) * 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t cur_byte = *(data++);
|
||||
result |= (cur_byte & 0x7F) << k;
|
||||
k += 7;
|
||||
|
||||
if ((cur_byte & 0x80) == 0) {
|
||||
b = result;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
#define READ_VARINT(x) do { if (!read_varint(x)) return __LINE__; } while(0)
|
||||
#define READ_VARINT(x) do { data = readVarint(data, data_end, x); if (!data) return __LINE__; } while(0)
|
||||
|
||||
auto read_buf = [&data, data_end](void* buf, size_t size) -> bool
|
||||
{
|
||||
@@ -121,7 +99,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
if (num_outputs > 0) {
|
||||
// Outputs are in the buffer, just read them
|
||||
// Each output is at least 34 bytes, exit early if there's not enough data left
|
||||
// 1 byte for reward, 1 byte for TXOUT_TO_KEY, 32 bytes for eph_pub_key
|
||||
// 1 byte for reward, 1 byte for tx_type, 32 bytes for eph_pub_key
|
||||
constexpr uint64_t MIN_OUTPUT_SIZE = 34;
|
||||
|
||||
if (num_outputs > std::numeric_limits<uint64_t>::max() / MIN_OUTPUT_SIZE) return __LINE__;
|
||||
@@ -130,15 +108,23 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
m_outputs.clear();
|
||||
m_outputs.reserve(num_outputs);
|
||||
|
||||
const uint8_t expected_tx_type = get_tx_type();
|
||||
|
||||
for (uint64_t i = 0; i < num_outputs; ++i) {
|
||||
TxOutput t;
|
||||
|
||||
READ_VARINT(t.m_reward);
|
||||
total_reward += t.m_reward;
|
||||
|
||||
EXPECT_BYTE(TXOUT_TO_KEY);
|
||||
EXPECT_BYTE(expected_tx_type);
|
||||
t.m_txType = expected_tx_type;
|
||||
|
||||
READ_BUF(t.m_ephPublicKey.h, HASH_SIZE);
|
||||
|
||||
if (expected_tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
READ_BYTE(t.m_viewTag);
|
||||
}
|
||||
|
||||
m_outputs.emplace_back(std::move(t));
|
||||
}
|
||||
|
||||
@@ -187,7 +173,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
READ_VARINT(m_extraNonceSize);
|
||||
|
||||
// Sanity check
|
||||
if ((m_extraNonceSize < EXTRA_NONCE_SIZE) || (m_extraNonceSize > EXTRA_NONCE_SIZE + 10)) return __LINE__;
|
||||
if ((m_extraNonceSize < EXTRA_NONCE_SIZE) || (m_extraNonceSize > EXTRA_NONCE_MAX_SIZE)) return __LINE__;
|
||||
|
||||
const int extra_nonce_offset = static_cast<int>((data - data_begin) + outputs_blob_size_diff);
|
||||
READ_BUF(&m_extraNonce, EXTRA_NONCE_SIZE);
|
||||
@@ -341,7 +327,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
|
||||
// Defaults for off-chain variables
|
||||
m_tmpTxExtra.clear();
|
||||
m_tmpInts.clear();
|
||||
|
||||
m_depth = 0;
|
||||
|
||||
@@ -351,7 +336,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
m_broadcasted = false;
|
||||
m_wantBroadcast = false;
|
||||
|
||||
m_localTimestamp = time(nullptr);
|
||||
m_localTimestamp = seconds_since_epoch();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+135
-10
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,22 +19,29 @@
|
||||
#include "pow_hash.h"
|
||||
#include "p2pool.h"
|
||||
#include "params.h"
|
||||
#ifdef WITH_RANDOMX
|
||||
#include "randomx.h"
|
||||
#include "configuration.h"
|
||||
#include "virtual_machine.hpp"
|
||||
#endif
|
||||
#include "json_rpc_request.h"
|
||||
#include "json_parsers.h"
|
||||
#include <rapidjson/document.h>
|
||||
#include <thread>
|
||||
|
||||
static constexpr char log_category_prefix[] = "RandomX_Hasher ";
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
RandomX_Hasher::RandomX_Hasher(p2pool* pool)
|
||||
: m_pool(pool)
|
||||
, m_cache{}
|
||||
, m_dataset(nullptr)
|
||||
, m_seed{}
|
||||
, m_index(0)
|
||||
, m_setSeedCounter(0)
|
||||
, m_seedCounter(0)
|
||||
, m_oldSeedCounter(0)
|
||||
{
|
||||
uint64_t memory_allocated = 0;
|
||||
|
||||
@@ -116,6 +123,10 @@ RandomX_Hasher::~RandomX_Hasher()
|
||||
|
||||
void RandomX_Hasher::set_seed_async(const hash& seed)
|
||||
{
|
||||
if (m_seed[m_index] == seed) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct Work
|
||||
{
|
||||
p2pool* pool;
|
||||
@@ -124,10 +135,7 @@ void RandomX_Hasher::set_seed_async(const hash& seed)
|
||||
uv_work_t req;
|
||||
};
|
||||
|
||||
Work* work = new Work{};
|
||||
work->pool = m_pool;
|
||||
work->hasher = this;
|
||||
work->seed = seed;
|
||||
Work* work = new Work{ m_pool, this, seed, {} };
|
||||
work->req.data = work;
|
||||
|
||||
const int err = uv_queue_work(uv_default_loop_checked(), &work->req,
|
||||
@@ -164,7 +172,7 @@ void RandomX_Hasher::set_seed(const hash& seed)
|
||||
WriteLock lock(m_datasetLock);
|
||||
uv_rwlock_wrlock(&m_cacheLock);
|
||||
|
||||
m_setSeedCounter.fetch_add(1);
|
||||
m_seedCounter.fetch_add(1);
|
||||
|
||||
if (m_seed[m_index] == seed) {
|
||||
uv_rwlock_wrunlock(&m_cacheLock);
|
||||
@@ -216,6 +224,11 @@ void RandomX_Hasher::set_seed(const hash& seed)
|
||||
numThreads /= 2;
|
||||
}
|
||||
|
||||
// wait for set_old_seed() before initializing dataset
|
||||
while (m_oldSeedCounter.load() == 0) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
LOGINFO(1, log::LightCyan() << "running " << numThreads << " threads to update dataset");
|
||||
|
||||
ReadLock lock2(m_cacheLock);
|
||||
@@ -266,8 +279,8 @@ void RandomX_Hasher::set_seed(const hash& seed)
|
||||
void RandomX_Hasher::set_old_seed(const hash& seed)
|
||||
{
|
||||
// set_seed() must go first, wait for it
|
||||
while (m_setSeedCounter.load() == 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
while (m_seedCounter.load() == 0) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
LOGINFO(1, "old seed " << log::LightBlue() << seed);
|
||||
@@ -275,6 +288,8 @@ void RandomX_Hasher::set_old_seed(const hash& seed)
|
||||
{
|
||||
WriteLock lock(m_cacheLock);
|
||||
|
||||
m_oldSeedCounter.fetch_add(1);
|
||||
|
||||
const uint32_t old_index = m_index ^ 1;
|
||||
m_seed[old_index] = seed;
|
||||
|
||||
@@ -302,7 +317,13 @@ void RandomX_Hasher::set_old_seed(const hash& seed)
|
||||
LOGINFO(1, log::LightCyan() << "old cache updated");
|
||||
}
|
||||
|
||||
bool RandomX_Hasher::calculate(const void* data, size_t size, const hash& seed, hash& result)
|
||||
void RandomX_Hasher::sync_wait()
|
||||
{
|
||||
ReadLock lock(m_datasetLock);
|
||||
ReadLock lock2(m_cacheLock);
|
||||
}
|
||||
|
||||
bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*/, const hash& seed, hash& result)
|
||||
{
|
||||
// First try to use the dataset if it's ready
|
||||
if (uv_rwlock_tryrdlock(&m_datasetLock) == 0) {
|
||||
@@ -347,5 +368,109 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, const hash& seed,
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
|
||||
: m_pool(pool)
|
||||
, m_loopThread{}
|
||||
{
|
||||
int err = uv_loop_init(&m_loop);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
|
||||
uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
|
||||
uv_async_init(&m_loop, &m_kickTheLoopAsync, nullptr);
|
||||
m_shutdownAsync.data = this;
|
||||
|
||||
uv_mutex_init_checked(&m_requestMutex);
|
||||
uv_mutex_init_checked(&m_condMutex);
|
||||
|
||||
err = uv_cond_init(&m_cond);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to create cond, error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
|
||||
err = uv_thread_create(&m_loopThread, loop, this);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
RandomX_Hasher_RPC::~RandomX_Hasher_RPC()
|
||||
{
|
||||
uv_async_send(&m_shutdownAsync);
|
||||
uv_thread_join(&m_loopThread);
|
||||
|
||||
uv_mutex_destroy(&m_requestMutex);
|
||||
uv_mutex_destroy(&m_condMutex);
|
||||
uv_cond_destroy(&m_cond);
|
||||
|
||||
LOGINFO(1, "stopped");
|
||||
}
|
||||
|
||||
void RandomX_Hasher_RPC::loop(void* data)
|
||||
{
|
||||
LOGINFO(1, "event loop started");
|
||||
RandomX_Hasher_RPC* hasher = static_cast<RandomX_Hasher_RPC*>(data);
|
||||
uv_run(&hasher->m_loop, UV_RUN_DEFAULT);
|
||||
uv_loop_close(&hasher->m_loop);
|
||||
LOGINFO(1, "event loop stopped");
|
||||
}
|
||||
|
||||
bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t height, const hash& /*seed*/, hash& h)
|
||||
{
|
||||
MutexLock lock(m_requestMutex);
|
||||
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(data_ptr);
|
||||
const uint8_t major_version = data[0];
|
||||
|
||||
char buf[log::Stream::BUF_SIZE + 1];
|
||||
log::Stream s(buf);
|
||||
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"calc_pow\",\"params\":{\"major_version\":" << major_version <<
|
||||
",\"height\":" << height <<
|
||||
",\"block_blob\":\"" << log::hex_buf(data, size) << '"' <<
|
||||
",\"seed_hash\":\"\"}}";
|
||||
|
||||
volatile int result = 0;
|
||||
volatile bool done = false;
|
||||
|
||||
JSONRPCRequest::call(m_pool->params().m_host.c_str(), m_pool->params().m_rpcPort, buf,
|
||||
[&result, &h](const char* data, size_t size)
|
||||
{
|
||||
rapidjson::Document doc;
|
||||
if (doc.Parse(data, size).HasParseError() || !parseValue(doc, "result", h)) {
|
||||
LOGWARN(3, "RPC calc_pow: invalid JSON response (parse error)");
|
||||
result = -1;
|
||||
return;
|
||||
}
|
||||
result = 1;
|
||||
},
|
||||
[this, &result, &done](const char* data, size_t size)
|
||||
{
|
||||
if (size > 0) {
|
||||
LOGWARN(3, "RPC calc_pow: server returned error " << log::const_buf(data, size));
|
||||
result = -1;
|
||||
}
|
||||
|
||||
MutexLock lock2(m_condMutex);
|
||||
done = true;
|
||||
uv_cond_signal(&m_cond);
|
||||
}, &m_loop);
|
||||
|
||||
uv_async_send(&m_kickTheLoopAsync);
|
||||
|
||||
{
|
||||
MutexLock lock2(m_condMutex);
|
||||
while (!done) {
|
||||
uv_cond_wait(&m_cond, &m_condMutex);
|
||||
}
|
||||
}
|
||||
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+61
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -27,18 +27,40 @@ namespace p2pool {
|
||||
|
||||
class p2pool;
|
||||
|
||||
class RandomX_Hasher
|
||||
class RandomX_Hasher_Base
|
||||
{
|
||||
public:
|
||||
virtual ~RandomX_Hasher_Base() {}
|
||||
|
||||
virtual void set_seed_async(const hash&) {}
|
||||
virtual void set_old_seed(const hash&) {}
|
||||
|
||||
virtual randomx_cache* cache() const { return nullptr; }
|
||||
virtual randomx_dataset* dataset() const { return nullptr; }
|
||||
virtual uint32_t seed_counter() const { return 0; }
|
||||
virtual void sync_wait() {}
|
||||
|
||||
virtual bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) = 0;
|
||||
};
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
class RandomX_Hasher : public RandomX_Hasher_Base
|
||||
{
|
||||
public:
|
||||
explicit RandomX_Hasher(p2pool* pool);
|
||||
~RandomX_Hasher();
|
||||
|
||||
void set_seed_async(const hash& seed);
|
||||
void set_seed_async(const hash& seed) override;
|
||||
void set_seed(const hash& seed);
|
||||
|
||||
void set_old_seed(const hash& seed);
|
||||
void set_old_seed(const hash& seed) override;
|
||||
|
||||
bool calculate(const void* data, size_t size, const hash& seed, hash& result);
|
||||
randomx_cache* cache() const override { return m_cache[m_index]; }
|
||||
randomx_dataset* dataset() const override { return m_dataset; }
|
||||
uint32_t seed_counter() const override { return m_seedCounter.load(); }
|
||||
void sync_wait() override;
|
||||
|
||||
bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -67,7 +89,40 @@ private:
|
||||
hash m_seed[2];
|
||||
uint32_t m_index;
|
||||
|
||||
std::atomic<uint32_t> m_setSeedCounter;
|
||||
std::atomic<uint32_t> m_seedCounter;
|
||||
std::atomic<uint32_t> m_oldSeedCounter;
|
||||
};
|
||||
#endif
|
||||
|
||||
class RandomX_Hasher_RPC : public RandomX_Hasher_Base
|
||||
{
|
||||
public:
|
||||
explicit RandomX_Hasher_RPC(p2pool* pool);
|
||||
~RandomX_Hasher_RPC();
|
||||
|
||||
bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) override;
|
||||
|
||||
private:
|
||||
static void loop(void* data);
|
||||
|
||||
p2pool* m_pool;
|
||||
|
||||
uv_mutex_t m_requestMutex;
|
||||
uv_loop_t m_loop;
|
||||
|
||||
uv_thread_t m_loopThread;
|
||||
uv_mutex_t m_condMutex;
|
||||
uv_cond_t m_cond;
|
||||
|
||||
uv_async_t m_shutdownAsync;
|
||||
uv_async_t m_kickTheLoopAsync;
|
||||
|
||||
static void on_shutdown(uv_async_t* async)
|
||||
{
|
||||
RandomX_Hasher_RPC* server = reinterpret_cast<RandomX_Hasher_RPC*>(async->data);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_shutdownAsync), nullptr);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_kickTheLoopAsync), nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+244
-110
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,12 +21,15 @@
|
||||
#include "pool_block.h"
|
||||
#include "wallet.h"
|
||||
#include "block_template.h"
|
||||
#ifdef WITH_RANDOMX
|
||||
#include "randomx.h"
|
||||
#include "dataset.hpp"
|
||||
#include "configuration.h"
|
||||
#include "intrin_portable.h"
|
||||
#endif
|
||||
#include "keccak.h"
|
||||
#include "p2p_server.h"
|
||||
#include "stratum_server.h"
|
||||
#include "params.h"
|
||||
#include "json_parsers.h"
|
||||
#include <rapidjson/document.h>
|
||||
@@ -51,14 +54,13 @@ static_assert(1 <= UNCLE_BLOCK_DEPTH && UNCLE_BLOCK_DEPTH <= 10, "Invalid UNCLE_
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
static constexpr uint8_t default_consensus_id[HASH_SIZE] = {
|
||||
34,175,126,231,181,11,104,146,227,153,218,107,44,108,68,39,178,81,4,212,169,4,142,0,177,110,157,240,68,7,249,24
|
||||
};
|
||||
static constexpr uint8_t default_consensus_id[HASH_SIZE] = { 34,175,126,231,181,11,104,146,227,153,218,107,44,108,68,39,178,81,4,212,169,4,142,0,177,110,157,240,68,7,249,24 };
|
||||
static constexpr uint8_t mini_consensus_id[HASH_SIZE] = { 57,130,201,26,149,174,199,250,66,80,189,18,108,216,194,220,136,23,63,24,64,113,221,44,219,86,39,163,53,24,126,196 };
|
||||
|
||||
SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
|
||||
: m_pool(pool)
|
||||
, m_networkType(type)
|
||||
, m_chainTip(nullptr)
|
||||
, m_chainTip{ nullptr }
|
||||
, m_poolName(pool_name ? pool_name : "default")
|
||||
, m_targetBlockTime(10)
|
||||
, m_minDifficulty(MIN_DIFFICULTY, 0)
|
||||
@@ -77,8 +79,11 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
|
||||
}
|
||||
|
||||
uv_mutex_init_checked(&m_sidechainLock);
|
||||
uv_mutex_init_checked(&m_seenBlocksLock);
|
||||
|
||||
m_difficultyData.reserve(m_chainWindowSize);
|
||||
m_tmpShares.reserve(m_chainWindowSize * 2);
|
||||
m_tmpRewards.reserve(m_chainWindowSize * 2);
|
||||
|
||||
LOGINFO(1, "generating consensus ID");
|
||||
|
||||
@@ -94,12 +99,18 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
|
||||
<< m_unclePenalty << '\0';
|
||||
|
||||
constexpr char default_config[] = "mainnet\0" "default\0" "\0" "10\0" "100000\0" "2160\0" "20\0";
|
||||
constexpr char mini_config[] = "mainnet\0" "mini\0" "\0" "10\0" "100000\0" "2160\0" "20\0";
|
||||
|
||||
// Hardcoded default consensus ID
|
||||
if (memcmp(buf, default_config, sizeof(default_config) - 1) == 0) {
|
||||
m_consensusId.assign(default_consensus_id, default_consensus_id + HASH_SIZE);
|
||||
}
|
||||
// Hardcoded mini consensus ID
|
||||
else if (memcmp(buf, mini_config, sizeof(mini_config) - 1) == 0) {
|
||||
m_consensusId.assign(mini_consensus_id, mini_consensus_id + HASH_SIZE);
|
||||
}
|
||||
else {
|
||||
#ifdef WITH_RANDOMX
|
||||
const randomx_flags flags = randomx_get_flags();
|
||||
randomx_cache* cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
|
||||
if (!cache) {
|
||||
@@ -134,6 +145,10 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
|
||||
keccak(reinterpret_cast<uint8_t*>(scratchpad), static_cast<int>(scratchpad_size * sizeof(rx_vec_i128)), id.h, HASH_SIZE);
|
||||
randomx_release_cache(cache);
|
||||
m_consensusId.assign(id.h, id.h + HASH_SIZE);
|
||||
#else
|
||||
LOGERR(1, "Can't calculate consensus ID without RandomX library");
|
||||
panic();
|
||||
#endif
|
||||
}
|
||||
|
||||
s.m_pos = 0;
|
||||
@@ -141,12 +156,14 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
|
||||
|
||||
// Hide most consensus ID bytes, we only want it on screen to show that we're on the right sidechain
|
||||
memset(buf + 8, '*', HASH_SIZE * 2 - 16);
|
||||
LOGINFO(1, "consensus ID = " << log::LightCyan() << static_cast<char*>(buf));
|
||||
m_consensusIdDisplayStr.assign(buf);
|
||||
LOGINFO(1, "consensus ID = " << log::LightCyan() << m_consensusIdDisplayStr.c_str());
|
||||
}
|
||||
|
||||
SideChain::~SideChain()
|
||||
{
|
||||
uv_mutex_destroy(&m_sidechainLock);
|
||||
uv_mutex_destroy(&m_seenBlocksLock);
|
||||
for (auto& it : m_blocksById) {
|
||||
delete it.second;
|
||||
}
|
||||
@@ -160,7 +177,9 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
|
||||
block.m_txkeySec = txkeySec;
|
||||
block.m_uncles.clear();
|
||||
|
||||
if (!m_chainTip) {
|
||||
const PoolBlock* tip = m_chainTip;
|
||||
|
||||
if (!tip) {
|
||||
block.m_parent = {};
|
||||
block.m_sidechainHeight = 0;
|
||||
block.m_difficulty = m_minDifficulty;
|
||||
@@ -170,8 +189,8 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
|
||||
return;
|
||||
}
|
||||
|
||||
block.m_parent = m_chainTip->m_sidechainId;
|
||||
block.m_sidechainHeight = m_chainTip->m_sidechainHeight + 1;
|
||||
block.m_parent = tip->m_sidechainId;
|
||||
block.m_sidechainHeight = tip->m_sidechainHeight + 1;
|
||||
|
||||
// Collect uncles from 3 previous block heights
|
||||
|
||||
@@ -179,15 +198,15 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
|
||||
std::vector<hash> mined_blocks;
|
||||
mined_blocks.reserve(UNCLE_BLOCK_DEPTH * 2 + 1);
|
||||
|
||||
PoolBlock* tmp = m_chainTip;
|
||||
for (uint64_t i = 0, n = std::min<uint64_t>(UNCLE_BLOCK_DEPTH, m_chainTip->m_sidechainHeight + 1); tmp && (i < n); ++i) {
|
||||
const PoolBlock* tmp = tip;
|
||||
for (uint64_t i = 0, n = std::min<uint64_t>(UNCLE_BLOCK_DEPTH, tip->m_sidechainHeight + 1); tmp && (i < n); ++i) {
|
||||
mined_blocks.push_back(tmp->m_sidechainId);
|
||||
mined_blocks.insert(mined_blocks.end(), tmp->m_uncles.begin(), tmp->m_uncles.end());
|
||||
tmp = get_parent(tmp);
|
||||
}
|
||||
|
||||
for (uint64_t i = 0, n = std::min<uint64_t>(UNCLE_BLOCK_DEPTH, m_chainTip->m_sidechainHeight + 1); i < n; ++i) {
|
||||
for (PoolBlock* uncle : m_blocksByHeight[m_chainTip->m_sidechainHeight - i]) {
|
||||
for (uint64_t i = 0, n = std::min<uint64_t>(UNCLE_BLOCK_DEPTH, tip->m_sidechainHeight + 1); i < n; ++i) {
|
||||
for (PoolBlock* uncle : m_blocksByHeight[tip->m_sidechainHeight - i]) {
|
||||
// Only add verified and valid blocks
|
||||
if (!uncle || !uncle->m_verified || uncle->m_invalid) {
|
||||
continue;
|
||||
@@ -201,7 +220,7 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
|
||||
// Only add it if it's on the same chain
|
||||
bool same_chain = false;
|
||||
do {
|
||||
tmp = m_chainTip;
|
||||
tmp = tip;
|
||||
while (tmp->m_sidechainHeight > uncle->m_sidechainHeight) {
|
||||
tmp = get_parent(tmp);
|
||||
if (!tmp) {
|
||||
@@ -245,7 +264,7 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
|
||||
}
|
||||
|
||||
block.m_difficulty = m_curDifficulty;
|
||||
block.m_cumulativeDifficulty = m_chainTip->m_cumulativeDifficulty + block.m_difficulty;
|
||||
block.m_cumulativeDifficulty = tip->m_cumulativeDifficulty + block.m_difficulty;
|
||||
|
||||
for (const hash& uncle_id : block.m_uncles) {
|
||||
auto it = m_blocksById.find(uncle_id);
|
||||
@@ -259,6 +278,11 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
|
||||
get_shares(&block, shares);
|
||||
}
|
||||
|
||||
P2PServer* SideChain::p2pServer() const
|
||||
{
|
||||
return m_pool ? m_pool->p2p_server() : nullptr;
|
||||
}
|
||||
|
||||
bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) const
|
||||
{
|
||||
shares.clear();
|
||||
@@ -343,21 +367,22 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
|
||||
|
||||
bool SideChain::block_seen(const PoolBlock& block)
|
||||
{
|
||||
MutexLock lock(m_sidechainLock);
|
||||
|
||||
// Check if it's some old block
|
||||
if (m_chainTip && m_chainTip->m_sidechainHeight > block.m_sidechainHeight + m_chainWindowSize * 2 &&
|
||||
block.m_cumulativeDifficulty < m_chainTip->m_cumulativeDifficulty) {
|
||||
const PoolBlock* tip = m_chainTip;
|
||||
|
||||
if (tip && tip->m_sidechainHeight > block.m_sidechainHeight + m_chainWindowSize * 2 &&
|
||||
block.m_cumulativeDifficulty < tip->m_cumulativeDifficulty) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if it was received before
|
||||
MutexLock lock(m_seenBlocksLock);
|
||||
return !m_seenBlocks.insert(block.m_sidechainId).second;
|
||||
}
|
||||
|
||||
void SideChain::unsee_block(const PoolBlock& block)
|
||||
{
|
||||
MutexLock lock(m_sidechainLock);
|
||||
MutexLock lock(m_seenBlocksLock);
|
||||
m_seenBlocks.erase(block.m_sidechainId);
|
||||
}
|
||||
|
||||
@@ -370,7 +395,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
return false;
|
||||
}
|
||||
|
||||
difficulty_type min_accepted_diff;
|
||||
bool too_low_diff = (block.m_difficulty < m_curDifficulty);
|
||||
{
|
||||
MutexLock lock(m_sidechainLock);
|
||||
if (m_blocksById.find(block.m_sidechainId) != m_blocksById.end()) {
|
||||
@@ -378,23 +403,27 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find the minimum difficulty in the current PPLNS window
|
||||
min_accepted_diff = m_curDifficulty;
|
||||
for (PoolBlock* tmp = m_chainTip; tmp && (tmp->m_sidechainHeight + m_chainWindowSize > m_chainTip->m_sidechainHeight); tmp = get_parent(tmp)) {
|
||||
if (tmp->m_difficulty < min_accepted_diff) {
|
||||
min_accepted_diff = tmp->m_difficulty;
|
||||
// This is mainly an anti-spam measure, not an actual verification step
|
||||
if (too_low_diff) {
|
||||
// Reduce required diff by 50% (by doubling this block's diff) to account for alternative chains
|
||||
difficulty_type diff2 = block.m_difficulty;
|
||||
diff2 += block.m_difficulty;
|
||||
|
||||
const PoolBlock* tip = m_chainTip;
|
||||
|
||||
for (const PoolBlock* tmp = tip; tmp && (tmp->m_sidechainHeight + m_chainWindowSize > tip->m_sidechainHeight); tmp = get_parent(tmp)) {
|
||||
if (diff2 >= tmp->m_difficulty) {
|
||||
too_low_diff = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOGINFO(4, "add_external_block: height = " << block.m_sidechainHeight << ", id = " << block.m_sidechainId << ", mainchain height = " << block.m_txinGenHeight);
|
||||
|
||||
// Reduce it by 50% to account for alternative chains. This is mainly an anti-spam measure, not an actual verification step
|
||||
min_accepted_diff.lo = (min_accepted_diff.lo >> 1) | (min_accepted_diff.hi << 63);
|
||||
min_accepted_diff.hi >>= 1;
|
||||
|
||||
if (block.m_difficulty < min_accepted_diff) {
|
||||
LOGWARN(4, "add_external_block: block has too low difficulty " << block.m_difficulty << ", expected >= " << min_accepted_diff << ". Ignoring it.");
|
||||
if (too_low_diff) {
|
||||
LOGWARN(4, "add_external_block: block has too low difficulty " << block.m_difficulty << ", expected >= ~" << m_curDifficulty << ". Ignoring it.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -418,14 +447,14 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
}
|
||||
|
||||
hash pow_hash;
|
||||
if (!block.get_pow_hash(m_pool->hasher(), seed, pow_hash)) {
|
||||
if (!block.get_pow_hash(m_pool->hasher(), block.m_txinGenHeight, seed, pow_hash)) {
|
||||
LOGWARN(3, "add_external_block: couldn't get PoW hash for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight << ". Ignoring it.");
|
||||
unsee_block(block);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if it has the correct parent and difficulty to go right to monerod for checking
|
||||
const MinerData& miner_data = m_pool->miner_data();
|
||||
MinerData miner_data = m_pool->miner_data();
|
||||
if ((block.m_prevId == miner_data.prev_id) && miner_data.difficulty.check_pow(pow_hash)) {
|
||||
LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero network, submitting it");
|
||||
m_pool->submit_block_async(block.m_mainChainData);
|
||||
@@ -466,6 +495,11 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
m_watchBlockSidechainId = {};
|
||||
data = m_watchBlock;
|
||||
block_found = true;
|
||||
|
||||
const uint64_t payout = block.get_payout(m_pool->params().m_wallet);
|
||||
if (payout) {
|
||||
LOGINFO(0, log::LightCyan() << "You received a payout of " << log::LightGreen() << log::XMRAmount(payout) << log::LightCyan() << " in block " << log::LightGreen() << data.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,11 +519,6 @@ void SideChain::add_block(const PoolBlock& block)
|
||||
", verified = " << (block.m_verified ? 1 : 0)
|
||||
);
|
||||
|
||||
// Save it for faster syncing on the next p2pool start
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->store_in_cache(block);
|
||||
}
|
||||
|
||||
PoolBlock* new_block = new PoolBlock(block);
|
||||
|
||||
MutexLock lock(m_sidechainLock);
|
||||
@@ -512,6 +541,11 @@ void SideChain::add_block(const PoolBlock& block)
|
||||
if (new_block->m_verified) {
|
||||
if (!new_block->m_invalid) {
|
||||
update_chain_tip(new_block);
|
||||
|
||||
// Save it for faster syncing on the next p2pool start
|
||||
if (P2PServer* server = p2pServer()) {
|
||||
server->store_in_cache(*new_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -521,10 +555,16 @@ void SideChain::add_block(const PoolBlock& block)
|
||||
m_seenWallets[new_block->m_minerWallet.spend_public_key()] = new_block->m_localTimestamp;
|
||||
}
|
||||
|
||||
bool SideChain::has_block(const hash& id)
|
||||
PoolBlock* SideChain::find_block(const hash& id)
|
||||
{
|
||||
MutexLock lock(m_sidechainLock);
|
||||
return m_blocksById.find(id) != m_blocksById.end();
|
||||
|
||||
auto it = m_blocksById.find(id);
|
||||
if (it != m_blocksById.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SideChain::watch_mainchain_block(const ChainMain& data, const hash& possible_id)
|
||||
@@ -538,7 +578,7 @@ bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob)
|
||||
{
|
||||
MutexLock lock(m_sidechainLock);
|
||||
|
||||
PoolBlock* block = nullptr;
|
||||
const PoolBlock* block = nullptr;
|
||||
|
||||
// Empty hash means we return current sidechain tip
|
||||
if (id == hash()) {
|
||||
@@ -564,40 +604,64 @@ bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob)
|
||||
|
||||
bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob)
|
||||
{
|
||||
std::vector<MinerShare> shares;
|
||||
std::vector<uint64_t> rewards;
|
||||
|
||||
shares.reserve(m_chainWindowSize * 2);
|
||||
rewards.reserve(m_chainWindowSize * 2);
|
||||
blob.clear();
|
||||
|
||||
MutexLock lock(m_sidechainLock);
|
||||
|
||||
if (!get_shares(block, shares) || !split_reward(total_reward, shares, rewards) || (rewards.size() != shares.size())) {
|
||||
auto it = m_blocksById.find(block->m_sidechainId);
|
||||
if (it != m_blocksById.end()) {
|
||||
PoolBlock* b = it->second;
|
||||
const size_t n = b->m_outputs.size();
|
||||
|
||||
blob.reserve(n * 39 + 64);
|
||||
writeVarint(n, blob);
|
||||
|
||||
for (const PoolBlock::TxOutput& output : b->m_outputs) {
|
||||
writeVarint(output.m_reward, blob);
|
||||
blob.emplace_back(output.m_txType);
|
||||
blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
|
||||
|
||||
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
|
||||
blob.emplace_back(output.m_viewTag);
|
||||
}
|
||||
}
|
||||
|
||||
block->m_outputs = b->m_outputs;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!get_shares(block, m_tmpShares) || !split_reward(total_reward, m_tmpShares, m_tmpRewards) || (m_tmpRewards.size() != m_tmpShares.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t n = shares.size();
|
||||
const size_t n = m_tmpShares.size();
|
||||
|
||||
blob.clear();
|
||||
blob.reserve(n * 38 + 64);
|
||||
blob.reserve(n * 39 + 64);
|
||||
|
||||
writeVarint(n, blob);
|
||||
|
||||
block->m_outputs.clear();
|
||||
block->m_outputs.reserve(n);
|
||||
|
||||
const uint8_t tx_type = block->get_tx_type();
|
||||
|
||||
hash eph_public_key;
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
writeVarint(rewards[i], blob);
|
||||
writeVarint(m_tmpRewards[i], blob);
|
||||
|
||||
blob.emplace_back(TXOUT_TO_KEY);
|
||||
blob.emplace_back(tx_type);
|
||||
|
||||
if (!shares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key)) {
|
||||
uint8_t view_tag;
|
||||
if (!m_tmpShares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key, view_tag)) {
|
||||
LOGWARN(6, "get_eph_public_key failed at index " << i);
|
||||
}
|
||||
blob.insert(blob.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
|
||||
|
||||
block->m_outputs.emplace_back(rewards[i], eph_public_key);
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
blob.emplace_back(view_tag);
|
||||
}
|
||||
|
||||
block->m_outputs.emplace_back(m_tmpRewards[i], eph_public_key, tx_type, view_tag);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -613,12 +677,14 @@ void SideChain::print_status()
|
||||
uint64_t rem;
|
||||
uint64_t pool_hashrate = udiv128(m_curDifficulty.hi, m_curDifficulty.lo, m_targetBlockTime, &rem);
|
||||
|
||||
const difficulty_type& network_diff = m_pool->miner_data().difficulty;
|
||||
difficulty_type network_diff = m_pool->miner_data().difficulty;
|
||||
uint64_t network_hashrate = udiv128(network_diff.hi, network_diff.lo, 120, &rem);
|
||||
|
||||
const PoolBlock* tip = m_chainTip;
|
||||
|
||||
uint64_t block_depth = 0;
|
||||
PoolBlock* cur = m_chainTip;
|
||||
const uint64_t tip_height = m_chainTip ? m_chainTip->m_sidechainHeight : 0;
|
||||
const PoolBlock* cur = tip;
|
||||
const uint64_t tip_height = tip ? tip->m_sidechainHeight : 0;
|
||||
|
||||
uint32_t total_blocks_in_window = 0;
|
||||
uint32_t total_uncles_in_window = 0;
|
||||
@@ -667,7 +733,7 @@ void SideChain::print_status()
|
||||
uint64_t your_reward = 0;
|
||||
uint64_t total_reward = 0;
|
||||
|
||||
if (m_chainTip) {
|
||||
if (tip) {
|
||||
std::sort(blocks_in_window.begin(), blocks_in_window.end());
|
||||
for (uint64_t i = 0; (i < m_chainWindowSize) && (i <= tip_height); ++i) {
|
||||
for (PoolBlock* block : m_blocksByHeight[tip_height - i]) {
|
||||
@@ -682,14 +748,24 @@ void SideChain::print_status()
|
||||
}
|
||||
|
||||
Wallet w = m_pool->params().m_wallet;
|
||||
const std::vector<PoolBlock::TxOutput>& outs = m_chainTip->m_outputs;
|
||||
|
||||
hash eph_public_key;
|
||||
for (size_t i = 0, n = outs.size(); i < n; ++i) {
|
||||
if (w.get_eph_public_key(m_chainTip->m_txkeySec, i, eph_public_key) && (outs[i].m_ephPublicKey == eph_public_key)) {
|
||||
your_reward = outs[i].m_reward;
|
||||
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
|
||||
const PoolBlock::TxOutput& out = tip->m_outputs[i];
|
||||
if (!your_reward) {
|
||||
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
|
||||
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
|
||||
your_reward = out.m_reward;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint8_t view_tag;
|
||||
if (w.get_eph_public_key(tip->m_txkeySec, i, eph_public_key, view_tag) && (out.m_ephPublicKey == eph_public_key)) {
|
||||
your_reward = out.m_reward;
|
||||
}
|
||||
}
|
||||
}
|
||||
total_reward += outs[i].m_reward;
|
||||
total_reward += out.m_reward;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,49 +774,58 @@ void SideChain::print_status()
|
||||
const uint64_t hashrate_est = total_reward ? udiv128(product[1], product[0], total_reward, &rem) : 0;
|
||||
const double block_share = total_reward ? ((static_cast<double>(your_reward) * 100.0) / static_cast<double>(total_reward)) : 0.0;
|
||||
|
||||
uint32_t our_blocks_in_window_total = std::accumulate(our_blocks_in_window.begin(), our_blocks_in_window.end(), decltype(our_blocks_in_window)::value_type(0));
|
||||
uint32_t our_uncles_in_window_total = std::accumulate(our_uncles_in_window.begin(), our_uncles_in_window.end(), decltype(our_uncles_in_window)::value_type(0));
|
||||
const uint32_t our_blocks_in_window_total = std::accumulate(our_blocks_in_window.begin(), our_blocks_in_window.end(), 0U);
|
||||
const uint32_t our_uncles_in_window_total = std::accumulate(our_uncles_in_window.begin(), our_uncles_in_window.end(), 0U);
|
||||
|
||||
std::string our_blocks_in_window_chart;
|
||||
our_blocks_in_window_chart.reserve(our_blocks_in_window.size());
|
||||
for(const auto& p : our_blocks_in_window){
|
||||
our_blocks_in_window_chart += (p > 0 ? (p > 9 ? "+" : std::to_string(p)) : ".");
|
||||
if (our_blocks_in_window_total) {
|
||||
our_blocks_in_window_chart.reserve(our_blocks_in_window.size() + 32);
|
||||
our_blocks_in_window_chart = "\nYour shares position = [";
|
||||
for (uint32_t p : our_blocks_in_window) {
|
||||
our_blocks_in_window_chart += (p ? ((p > 9) ? '+' : static_cast<char>('0' + p)) : '.');
|
||||
}
|
||||
our_blocks_in_window_chart += ']';
|
||||
}
|
||||
|
||||
std::string our_uncles_in_window_chart;
|
||||
our_uncles_in_window_chart.reserve(our_uncles_in_window.size());
|
||||
for(const auto& p : our_uncles_in_window){
|
||||
our_uncles_in_window_chart += (p > 0 ? (p > 9 ? "+" : std::to_string(p)) : ".");
|
||||
if (our_uncles_in_window_total) {
|
||||
our_uncles_in_window_chart.reserve(our_uncles_in_window.size() + 32);
|
||||
our_uncles_in_window_chart = "\nYour uncles position = [";
|
||||
for (uint32_t p : our_uncles_in_window) {
|
||||
our_uncles_in_window_chart += (p ? ((p > 9) ? '+' : static_cast<char>('0' + p)) : '.');
|
||||
}
|
||||
our_uncles_in_window_chart += ']';
|
||||
}
|
||||
|
||||
LOGINFO(0, "status" <<
|
||||
"\nMain chain height = " << m_pool->block_template().height() <<
|
||||
"\nMain chain hashrate = " << log::Hashrate(network_hashrate) <<
|
||||
"\nSide chain ID = " << (is_default() ? "default" : (is_mini() ? "mini" : m_consensusIdDisplayStr.c_str())) <<
|
||||
"\nSide chain height = " << tip_height + 1 <<
|
||||
"\nSide chain hashrate = " << log::Hashrate(pool_hashrate) <<
|
||||
(hashrate_est ? "\nYour hashrate (pool-side) = " : "") << (hashrate_est ? log::Hashrate(hashrate_est) : log::Hashrate()) <<
|
||||
"\nPPLNS window = " << total_blocks_in_window << " blocks (+" << total_uncles_in_window << " uncles, " << total_orphans << " orphans)" <<
|
||||
"\nYour shares = " << our_blocks_in_window_total << " blocks (+" << our_uncles_in_window_total << " uncles, " << our_orphans << " orphans)" <<
|
||||
(our_blocks_in_window_total > 0 ? "\nYour shares position = " : "") << (our_blocks_in_window_total > 0 ? "[" + our_blocks_in_window_chart + "]" : "") <<
|
||||
(our_uncles_in_window_total > 0 ? "\nYour uncles position = " : "") << (our_uncles_in_window_total > 0 ? "[" + our_uncles_in_window_chart + "]" : "") <<
|
||||
"\nYour shares = " << our_blocks_in_window_total << " blocks (+" << our_uncles_in_window_total << " uncles, " << our_orphans << " orphans)"
|
||||
<< our_blocks_in_window_chart << our_uncles_in_window_chart <<
|
||||
"\nBlock reward share = " << block_share << "% (" << log::XMRAmount(your_reward) << ')'
|
||||
);
|
||||
}
|
||||
|
||||
difficulty_type SideChain::total_hashes() const
|
||||
{
|
||||
return m_chainTip ? m_chainTip->m_cumulativeDifficulty : difficulty_type();
|
||||
const PoolBlock* tip = m_chainTip;
|
||||
return tip ? tip->m_cumulativeDifficulty : difficulty_type();
|
||||
}
|
||||
|
||||
uint64_t SideChain::miner_count()
|
||||
{
|
||||
const time_t cur_time = time(nullptr);
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
|
||||
MutexLock lock(m_sidechainLock);
|
||||
|
||||
// Delete wallets that weren't seen for more than 24 hours and return how many remain
|
||||
// Delete wallets that weren't seen for more than 72 hours and return how many remain
|
||||
for (auto it = m_seenWallets.begin(); it != m_seenWallets.end();) {
|
||||
if (it->second + 24 * 60 * 60 <= cur_time) {
|
||||
if (it->second + 72 * 60 * 60 <= cur_time) {
|
||||
it = m_seenWallets.erase(it);
|
||||
}
|
||||
else {
|
||||
@@ -751,9 +836,10 @@ uint64_t SideChain::miner_count()
|
||||
return m_seenWallets.size();
|
||||
}
|
||||
|
||||
time_t SideChain::last_updated() const
|
||||
uint64_t SideChain::last_updated() const
|
||||
{
|
||||
return m_chainTip ? m_chainTip->m_localTimestamp : 0;
|
||||
const PoolBlock* tip = m_chainTip;
|
||||
return tip ? tip->m_localTimestamp : 0;
|
||||
}
|
||||
|
||||
bool SideChain::is_default() const
|
||||
@@ -761,6 +847,11 @@ bool SideChain::is_default() const
|
||||
return (memcmp(m_consensusId.data(), default_consensus_id, HASH_SIZE) == 0);
|
||||
}
|
||||
|
||||
bool SideChain::is_mini() const
|
||||
{
|
||||
return (memcmp(m_consensusId.data(), mini_consensus_id, HASH_SIZE) == 0);
|
||||
}
|
||||
|
||||
bool SideChain::split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards)
|
||||
{
|
||||
const size_t num_shares = shares.size();
|
||||
@@ -941,7 +1032,8 @@ void SideChain::verify_loop(PoolBlock* block)
|
||||
|
||||
// This block is now verified
|
||||
|
||||
if (is_longer_chain(highest_block, block)) {
|
||||
bool is_alternative;
|
||||
if (is_longer_chain(highest_block, block, is_alternative)) {
|
||||
highest_block = block;
|
||||
}
|
||||
else if (highest_block && (highest_block->m_sidechainHeight > block->m_sidechainHeight)) {
|
||||
@@ -951,17 +1043,19 @@ void SideChain::verify_loop(PoolBlock* block)
|
||||
", height " << block->m_sidechainHeight);
|
||||
}
|
||||
|
||||
P2PServer* server = p2pServer();
|
||||
|
||||
// If it came through a broadcast, send it to our peers
|
||||
if (block->m_wantBroadcast && !block->m_broadcasted) {
|
||||
block->m_broadcasted = true;
|
||||
if (m_pool->p2p_server() && (block->m_depth < UNCLE_BLOCK_DEPTH)) {
|
||||
m_pool->p2p_server()->broadcast(*block);
|
||||
if (server && (block->m_depth < UNCLE_BLOCK_DEPTH)) {
|
||||
server->broadcast(*block);
|
||||
}
|
||||
}
|
||||
|
||||
// Save it for faster syncing on the next p2pool start
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->store_in_cache(*block);
|
||||
if (server) {
|
||||
server->store_in_cache(*block);
|
||||
}
|
||||
|
||||
// Try to verify blocks on top of this one
|
||||
@@ -1229,7 +1323,13 @@ void SideChain::verify(PoolBlock* block)
|
||||
});
|
||||
|
||||
std::vector<uint64_t> rewards;
|
||||
split_reward(total_reward, shares, rewards);
|
||||
if (!split_reward(total_reward, shares, rewards)) {
|
||||
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
|
||||
", id = " << block->m_sidechainId <<
|
||||
", mainchain height = " << block->m_txinGenHeight << ": split_reward failed");
|
||||
block->m_invalid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rewards.size() != block->m_outputs.size()) {
|
||||
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
|
||||
@@ -1241,17 +1341,20 @@ void SideChain::verify(PoolBlock* block)
|
||||
}
|
||||
|
||||
for (size_t i = 0, n = rewards.size(); i < n; ++i) {
|
||||
if (rewards[i] != block->m_outputs[i].m_reward) {
|
||||
const PoolBlock::TxOutput& out = block->m_outputs[i];
|
||||
|
||||
if (rewards[i] != out.m_reward) {
|
||||
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
|
||||
", id = " << block->m_sidechainId <<
|
||||
", mainchain height = " << block->m_txinGenHeight <<
|
||||
" has invalid reward at index " << i << ": got " << block->m_outputs[i].m_reward << ", expected " << rewards[i]);
|
||||
" has invalid reward at index " << i << ": got " << out.m_reward << ", expected " << rewards[i]);
|
||||
block->m_invalid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
hash eph_public_key;
|
||||
if (!shares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key)) {
|
||||
uint8_t view_tag;
|
||||
if (!shares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key, view_tag)) {
|
||||
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
|
||||
", id = " << block->m_sidechainId <<
|
||||
", mainchain height = " << block->m_txinGenHeight <<
|
||||
@@ -1260,7 +1363,16 @@ void SideChain::verify(PoolBlock* block)
|
||||
return;
|
||||
}
|
||||
|
||||
if (eph_public_key != block->m_outputs[i].m_ephPublicKey) {
|
||||
if ((out.m_txType == TXOUT_TO_TAGGED_KEY) && (out.m_viewTag != view_tag)) {
|
||||
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
|
||||
", id = " << block->m_sidechainId <<
|
||||
", mainchain height = " << block->m_txinGenHeight <<
|
||||
" has an incorrect view tag at index " << i);
|
||||
block->m_invalid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (eph_public_key != out.m_ephPublicKey) {
|
||||
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
|
||||
", id = " << block->m_sidechainId <<
|
||||
", mainchain height = " << block->m_txinGenHeight <<
|
||||
@@ -1286,7 +1398,10 @@ void SideChain::update_chain_tip(PoolBlock* block)
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_longer_chain(m_chainTip, block)) {
|
||||
const PoolBlock* tip = m_chainTip;
|
||||
|
||||
bool is_alternative;
|
||||
if (is_longer_chain(tip, block, is_alternative)) {
|
||||
difficulty_type diff;
|
||||
if (get_difficulty(block, m_difficultyData, diff)) {
|
||||
m_chainTip = block;
|
||||
@@ -1294,26 +1409,34 @@ void SideChain::update_chain_tip(PoolBlock* block)
|
||||
|
||||
LOGINFO(2, "new chain tip: next height = " << log::Gray() << block->m_sidechainHeight + 1 << log::NoColor() <<
|
||||
", next difficulty = " << log::Gray() << m_curDifficulty << log::NoColor() <<
|
||||
", main chain height = " << log::Gray() << m_chainTip->m_txinGenHeight);
|
||||
", main chain height = " << log::Gray() << block->m_txinGenHeight);
|
||||
|
||||
block->m_wantBroadcast = true;
|
||||
m_pool->update_block_template_async();
|
||||
if (m_pool) {
|
||||
m_pool->update_block_template_async();
|
||||
|
||||
// Reset stratum share counters when switching to an alternative chain to avoid confusion
|
||||
StratumServer* s = m_pool->stratum_server();
|
||||
if (s && is_alternative) {
|
||||
s->reset_share_counters();
|
||||
}
|
||||
}
|
||||
prune_old_blocks();
|
||||
}
|
||||
}
|
||||
else if (block->m_sidechainHeight > m_chainTip->m_sidechainHeight) {
|
||||
else if (block->m_sidechainHeight > tip->m_sidechainHeight) {
|
||||
LOGINFO(4, "block " << block->m_sidechainId <<
|
||||
", height = " << block->m_sidechainHeight <<
|
||||
" is not a longer chain than " << m_chainTip->m_sidechainId <<
|
||||
", height " << m_chainTip->m_sidechainHeight);
|
||||
" is not a longer chain than " << tip->m_sidechainId <<
|
||||
", height " << tip->m_sidechainHeight);
|
||||
}
|
||||
else if (block->m_sidechainHeight + UNCLE_BLOCK_DEPTH > m_chainTip->m_sidechainHeight) {
|
||||
else if (block->m_sidechainHeight + UNCLE_BLOCK_DEPTH > tip->m_sidechainHeight) {
|
||||
LOGINFO(4, "possible uncle block: id = " << log::Gray() << block->m_sidechainId << log::NoColor() <<
|
||||
", height = " << log::Gray() << block->m_sidechainHeight);
|
||||
m_pool->update_block_template_async();
|
||||
}
|
||||
|
||||
if (m_pool->p2p_server() && block->m_wantBroadcast && !block->m_broadcasted) {
|
||||
if (p2pServer() && block->m_wantBroadcast && !block->m_broadcasted) {
|
||||
block->m_broadcasted = true;
|
||||
#ifdef DEBUG_BROADCAST_DELAY_MS
|
||||
struct Work
|
||||
@@ -1324,7 +1447,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
|
||||
};
|
||||
Work* work = new Work{};
|
||||
work->req.data = work;
|
||||
work->server = m_pool->p2p_server();
|
||||
work->server = p2pServer();
|
||||
work->block = block;
|
||||
const int err = uv_queue_work(uv_default_loop(), &work->req,
|
||||
[](uv_work_t*)
|
||||
@@ -1343,7 +1466,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
|
||||
LOGERR(1, "update_chain_tip: uv_queue_work failed, error " << uv_err_name(err));
|
||||
}
|
||||
#else
|
||||
m_pool->p2p_server()->broadcast(*block);
|
||||
p2pServer()->broadcast(*block);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1359,8 +1482,10 @@ PoolBlock* SideChain::get_parent(const PoolBlock* block)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candidate)
|
||||
bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative)
|
||||
{
|
||||
is_alternative = false;
|
||||
|
||||
if (!candidate || !candidate->m_verified || candidate->m_invalid) {
|
||||
return false;
|
||||
}
|
||||
@@ -1403,6 +1528,8 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
|
||||
}
|
||||
|
||||
// They're on totally different chains. Compare total difficulties over the last m_chainWindowSize blocks
|
||||
is_alternative = true;
|
||||
|
||||
difficulty_type block_total_diff;
|
||||
difficulty_type candidate_total_diff;
|
||||
|
||||
@@ -1436,8 +1563,9 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
|
||||
}
|
||||
|
||||
// Final check: candidate chain must be built on top of recent mainchain blocks
|
||||
if (candidate_mainchain_height + 10 < m_pool->miner_data().height) {
|
||||
LOGWARN(3, "received a longer alternative chain but it's stale: height " << candidate_mainchain_height << ", current height " << m_pool->miner_data().height);
|
||||
MinerData data = m_pool->miner_data();
|
||||
if (candidate_mainchain_height + 10 < data.height) {
|
||||
LOGWARN(3, "received a longer alternative chain but it's stale: height " << candidate_mainchain_height << ", current height " << data.height);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1518,13 +1646,16 @@ void SideChain::prune_old_blocks()
|
||||
const uint64_t prune_distance = m_chainWindowSize * 2 + 120 / m_targetBlockTime;
|
||||
|
||||
// Remove old blocks from alternative unconnected chains after long enough time
|
||||
const time_t prune_time = time(nullptr) - m_chainWindowSize * 4 * m_targetBlockTime;
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
const uint64_t prune_delay = m_chainWindowSize * 4 * m_targetBlockTime;
|
||||
|
||||
if (m_chainTip->m_sidechainHeight < prune_distance) {
|
||||
const PoolBlock* tip = m_chainTip;
|
||||
|
||||
if (tip->m_sidechainHeight < prune_distance) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t h = m_chainTip->m_sidechainHeight - prune_distance;
|
||||
const uint64_t h = tip->m_sidechainHeight - prune_distance;
|
||||
|
||||
uint64_t num_blocks_pruned = 0;
|
||||
|
||||
@@ -1533,12 +1664,13 @@ void SideChain::prune_old_blocks()
|
||||
std::vector<PoolBlock*>& v = it->second;
|
||||
|
||||
v.erase(std::remove_if(v.begin(), v.end(),
|
||||
[this, prune_distance, prune_time, &num_blocks_pruned, height](PoolBlock* block)
|
||||
[this, prune_distance, cur_time, prune_delay, &num_blocks_pruned, height](PoolBlock* block)
|
||||
{
|
||||
if ((block->m_depth >= prune_distance) || (block->m_localTimestamp <= prune_time)) {
|
||||
if ((block->m_depth >= prune_distance) || (cur_time >= block->m_localTimestamp + prune_delay)) {
|
||||
auto it2 = m_blocksById.find(block->m_sidechainId);
|
||||
if (it2 != m_blocksById.end()) {
|
||||
m_blocksById.erase(it2);
|
||||
unsee_block(*block);
|
||||
delete block;
|
||||
++num_blocks_pruned;
|
||||
}
|
||||
@@ -1563,7 +1695,9 @@ void SideChain::prune_old_blocks()
|
||||
|
||||
// If side-chain started pruning blocks it means the initial sync is complete
|
||||
// It's now safe to delete cached blocks
|
||||
m_pool->p2p_server()->clear_cached_blocks();
|
||||
if (p2pServer()) {
|
||||
p2pServer()->clear_cached_blocks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -23,6 +23,7 @@
|
||||
namespace p2pool {
|
||||
|
||||
class p2pool;
|
||||
class P2PServer;
|
||||
struct DifficultyData;
|
||||
struct PoolBlock;
|
||||
class Wallet;
|
||||
@@ -50,7 +51,7 @@ public:
|
||||
void add_block(const PoolBlock& block);
|
||||
void get_missing_blocks(std::vector<hash>& missing_blocks);
|
||||
|
||||
bool has_block(const hash& id);
|
||||
PoolBlock* find_block(const hash& id);
|
||||
void watch_mainchain_block(const ChainMain& data, const hash& possible_id);
|
||||
|
||||
bool get_block_blob(const hash& id, std::vector<uint8_t>& blob);
|
||||
@@ -68,13 +69,17 @@ public:
|
||||
difficulty_type total_hashes() const;
|
||||
uint64_t block_time() const { return m_targetBlockTime; }
|
||||
uint64_t miner_count();
|
||||
time_t last_updated() const;
|
||||
uint64_t last_updated() const;
|
||||
bool is_default() const;
|
||||
bool is_mini() const;
|
||||
|
||||
const PoolBlock* chainTip() const { return m_chainTip; }
|
||||
|
||||
static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards);
|
||||
|
||||
private:
|
||||
p2pool* m_pool;
|
||||
P2PServer* p2pServer() const;
|
||||
NetworkType m_networkType;
|
||||
|
||||
private:
|
||||
@@ -86,7 +91,7 @@ private:
|
||||
PoolBlock* get_parent(const PoolBlock* block);
|
||||
|
||||
// Checks if "candidate" has longer (higher difficulty) chain than "block"
|
||||
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate);
|
||||
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative);
|
||||
void update_depths(PoolBlock* block);
|
||||
void prune_old_blocks();
|
||||
|
||||
@@ -94,11 +99,15 @@ private:
|
||||
bool check_config();
|
||||
|
||||
mutable uv_mutex_t m_sidechainLock;
|
||||
PoolBlock* m_chainTip;
|
||||
std::atomic<PoolBlock*> m_chainTip;
|
||||
std::map<uint64_t, std::vector<PoolBlock*>> m_blocksByHeight;
|
||||
unordered_map<hash, PoolBlock*> m_blocksById;
|
||||
unordered_map<hash, uint64_t> m_seenWallets;
|
||||
std::vector<MinerShare> m_tmpShares;
|
||||
std::vector<uint64_t> m_tmpRewards;
|
||||
|
||||
uv_mutex_t m_seenBlocksLock;
|
||||
unordered_set<hash> m_seenBlocks;
|
||||
unordered_map<hash, time_t> m_seenWallets;
|
||||
|
||||
std::vector<DifficultyData> m_difficultyData;
|
||||
|
||||
@@ -110,6 +119,7 @@ private:
|
||||
uint64_t m_unclePenalty;
|
||||
|
||||
std::vector<uint8_t> m_consensusId;
|
||||
std::string m_consensusIdDisplayStr;
|
||||
|
||||
difficulty_type m_curDifficulty;
|
||||
|
||||
|
||||
+40
-14
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -39,8 +39,7 @@ StratumServer::StratumServer(p2pool* pool)
|
||||
: TCPServer(StratumClient::allocate)
|
||||
, m_pool(pool)
|
||||
, m_extraNonce(0)
|
||||
, m_rd{}
|
||||
, m_rng(m_rd())
|
||||
, m_rng(RandomDeviceSeed::instance)
|
||||
, m_cumulativeHashes(0)
|
||||
, m_cumulativeHashesAtLastShare(0)
|
||||
, m_hashrateDataHead(0)
|
||||
@@ -51,7 +50,10 @@ StratumServer::StratumServer(p2pool* pool)
|
||||
, m_totalFoundShares(0)
|
||||
, m_apiLastUpdateTime(0)
|
||||
{
|
||||
m_hashrateData[0] = { time(nullptr), 0 };
|
||||
// Diffuse the initial state in case it has low quality
|
||||
m_rng.discard(10000);
|
||||
|
||||
m_hashrateData[0] = { seconds_since_epoch(), 0 };
|
||||
|
||||
uv_mutex_init_checked(&m_blobsQueueLock);
|
||||
uv_mutex_init_checked(&m_rngLock);
|
||||
@@ -435,10 +437,17 @@ uint64_t StratumServer::get_random64()
|
||||
|
||||
void StratumServer::print_status()
|
||||
{
|
||||
update_hashrate_data(0, time(nullptr));
|
||||
update_hashrate_data(0, seconds_since_epoch());
|
||||
print_stratum_status();
|
||||
}
|
||||
|
||||
void StratumServer::reset_share_counters()
|
||||
{
|
||||
m_cumulativeHashesAtLastShare = 0;
|
||||
m_cumulativeFoundSharesDiff = 0.0;
|
||||
m_totalFoundShares = 0;
|
||||
}
|
||||
|
||||
void StratumServer::print_stratum_status() const
|
||||
{
|
||||
uint64_t hashes_15m, hashes_1h, hashes_24h, total_hashes;
|
||||
@@ -473,8 +482,9 @@ void StratumServer::print_stratum_status() const
|
||||
const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0;
|
||||
|
||||
double average_effort = 0.0;
|
||||
if (m_cumulativeFoundSharesDiff > 0.0) {
|
||||
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / m_cumulativeFoundSharesDiff;
|
||||
const double diff = m_cumulativeFoundSharesDiff;
|
||||
if (diff > 0.0) {
|
||||
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / diff;
|
||||
}
|
||||
|
||||
LOGINFO(0, "status" <<
|
||||
@@ -516,6 +526,8 @@ void StratumServer::on_blobs_ready()
|
||||
|
||||
size_t numClientsProcessed = 0;
|
||||
uint32_t extra_nonce = 0;
|
||||
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
{
|
||||
MutexLock lock2(m_clientsListLock);
|
||||
|
||||
@@ -523,7 +535,12 @@ void StratumServer::on_blobs_ready()
|
||||
++numClientsProcessed;
|
||||
|
||||
if (!client->m_rpcId) {
|
||||
// Not logged in yet, on_login() will send the job to this client
|
||||
// Not logged in yet, on_login() will send the job to this client. Also close inactive connections.
|
||||
if (cur_time >= client->m_connectedTime + 10) {
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " didn't send login data");
|
||||
client->ban(DEFAULT_BAN_TIME);
|
||||
client->close();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -588,7 +605,7 @@ void StratumServer::on_blobs_ready()
|
||||
LOGINFO(3, "sent new job to " << extra_nonce << '/' << numClientsProcessed << " clients");
|
||||
}
|
||||
|
||||
void StratumServer::update_hashrate_data(uint64_t hashes, time_t timestamp)
|
||||
void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp)
|
||||
{
|
||||
constexpr size_t N = array_size(&StratumServer::m_hashrateData);
|
||||
|
||||
@@ -661,7 +678,7 @@ void StratumServer::on_share_found(uv_work_t* req)
|
||||
}
|
||||
|
||||
hash pow_hash;
|
||||
if (!pool->calculate_hash(blob, blob_size, seed_hash, pow_hash)) {
|
||||
if (!pool->calculate_hash(blob, blob_size, height, seed_hash, pow_hash)) {
|
||||
LOGWARN(3, "client " << static_cast<char*>(client->m_addrString) << " couldn't check share PoW");
|
||||
share->m_result = SubmittedShare::Result::COULDNT_CHECK_POW;
|
||||
return;
|
||||
@@ -690,7 +707,7 @@ void StratumServer::on_share_found(uv_work_t* req)
|
||||
const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t));
|
||||
|
||||
if (LIKELY(value < target)) {
|
||||
const time_t timestamp = time(nullptr);
|
||||
const uint64_t timestamp = seconds_since_epoch();
|
||||
server->update_hashrate_data(hashes, timestamp);
|
||||
server->api_update_local_stats(timestamp);
|
||||
share->m_result = SubmittedShare::Result::OK;
|
||||
@@ -760,6 +777,7 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
|
||||
StratumServer::StratumClient::StratumClient()
|
||||
: m_rpcId(0)
|
||||
, m_connectedTime(0)
|
||||
, m_jobs{}
|
||||
, m_perConnectionJobId(0)
|
||||
, m_customDiff{}
|
||||
@@ -776,12 +794,19 @@ void StratumServer::StratumClient::reset()
|
||||
{
|
||||
Client::reset();
|
||||
m_rpcId = 0;
|
||||
m_connectedTime = 0;
|
||||
memset(m_jobs, 0, sizeof(m_jobs));
|
||||
m_perConnectionJobId = 0;
|
||||
m_customDiff = {};
|
||||
m_customUser.clear();
|
||||
}
|
||||
|
||||
bool StratumServer::StratumClient::on_connect()
|
||||
{
|
||||
m_connectedTime = seconds_since_epoch();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StratumServer::StratumClient::on_read(char* data, uint32_t size)
|
||||
{
|
||||
if ((data != m_readBuf + m_numRead) || (data + size > m_readBuf + sizeof(m_readBuf))) {
|
||||
@@ -968,7 +993,7 @@ bool StratumServer::StratumClient::process_submit(rapidjson::Document& doc, uint
|
||||
return static_cast<StratumServer*>(m_owner)->on_submit(this, id, job_id.GetString(), nonce.GetString(), result.GetString());
|
||||
}
|
||||
|
||||
void StratumServer::api_update_local_stats(time_t timestamp)
|
||||
void StratumServer::api_update_local_stats(uint64_t timestamp)
|
||||
{
|
||||
if (!m_pool->api() || !m_pool->params().m_localStats) {
|
||||
return;
|
||||
@@ -1013,8 +1038,9 @@ void StratumServer::api_update_local_stats(time_t timestamp)
|
||||
const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0;
|
||||
|
||||
double average_effort = 0.0;
|
||||
if (m_cumulativeFoundSharesDiff > 0.0) {
|
||||
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / m_cumulativeFoundSharesDiff;
|
||||
const double diff = m_cumulativeFoundSharesDiff;
|
||||
if (diff > 0.0) {
|
||||
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / diff;
|
||||
}
|
||||
|
||||
int shares_found = m_totalFoundShares;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "tcp_server.h"
|
||||
#include <rapidjson/document.h>
|
||||
#include <random>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
@@ -45,7 +44,7 @@ public:
|
||||
static Client* allocate() { return new StratumClient(); }
|
||||
|
||||
void reset() override;
|
||||
bool on_connect() override { return true; }
|
||||
bool on_connect() override;
|
||||
bool on_read(char* data, uint32_t size) override;
|
||||
|
||||
bool process_request(char* data, uint32_t size);
|
||||
@@ -53,6 +52,7 @@ public:
|
||||
bool process_submit(rapidjson::Document& doc, uint32_t id);
|
||||
|
||||
uint32_t m_rpcId;
|
||||
uint64_t m_connectedTime;
|
||||
|
||||
uv_mutex_t m_jobsLock;
|
||||
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
|
||||
void print_status() override;
|
||||
|
||||
void reset_share_counters();
|
||||
|
||||
private:
|
||||
void print_stratum_status() const;
|
||||
|
||||
@@ -106,7 +108,6 @@ private:
|
||||
std::atomic<uint32_t> m_extraNonce;
|
||||
|
||||
uv_mutex_t m_rngLock;
|
||||
std::random_device m_rd;
|
||||
std::mt19937_64 m_rng;
|
||||
|
||||
struct SubmittedShare
|
||||
@@ -139,7 +140,7 @@ private:
|
||||
|
||||
struct HashrateData
|
||||
{
|
||||
time_t m_timestamp;
|
||||
uint64_t m_timestamp;
|
||||
uint64_t m_cumulativeHashes;
|
||||
};
|
||||
|
||||
@@ -156,10 +157,10 @@ private:
|
||||
double m_cumulativeFoundSharesDiff;
|
||||
uint32_t m_totalFoundShares;
|
||||
|
||||
time_t m_apiLastUpdateTime;
|
||||
uint64_t m_apiLastUpdateTime;
|
||||
|
||||
void update_hashrate_data(uint64_t hashes, time_t timestamp);
|
||||
void api_update_local_stats(time_t timestamp);
|
||||
void update_hashrate_data(uint64_t hashes, uint64_t timestamp);
|
||||
void api_update_local_stats(uint64_t timestamp);
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+16
-50
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,8 +18,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "uv_util.h"
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
@@ -46,45 +44,16 @@ public:
|
||||
|
||||
int listen_port() const { return m_listenPort; }
|
||||
|
||||
struct raw_ip
|
||||
{
|
||||
alignas(8) uint8_t data[16];
|
||||
|
||||
FORCEINLINE bool operator<(const raw_ip& other) const
|
||||
{
|
||||
const uint64_t* a = reinterpret_cast<const uint64_t*>(data);
|
||||
const uint64_t* b = reinterpret_cast<const uint64_t*>(other.data);
|
||||
|
||||
if (a[1] < b[1]) return true;
|
||||
if (a[1] > b[1]) return false;
|
||||
|
||||
return a[0] < b[0];
|
||||
}
|
||||
|
||||
FORCEINLINE bool operator==(const raw_ip& other) const
|
||||
{
|
||||
const uint64_t* a = reinterpret_cast<const uint64_t*>(data);
|
||||
const uint64_t* b = reinterpret_cast<const uint64_t*>(other.data);
|
||||
|
||||
return (a[0] == b[0]) && (a[1] == b[1]);
|
||||
}
|
||||
|
||||
FORCEINLINE bool operator!=(const raw_ip& other) const { return !operator==(other); }
|
||||
};
|
||||
|
||||
static_assert(sizeof(raw_ip) == 16, "struct raw_ip has invalid size");
|
||||
static_assert(sizeof(in6_addr) == 16, "struct in6_addr has invalid size");
|
||||
static_assert(sizeof(in_addr) == 4, "struct in_addr has invalid size");
|
||||
|
||||
bool connect_to_peer(bool is_v6, const raw_ip& ip, int port);
|
||||
virtual void on_connect_failed(bool is_v6, const raw_ip& ip, int port);
|
||||
|
||||
void ban(const raw_ip& ip, uint64_t seconds);
|
||||
virtual void print_bans();
|
||||
|
||||
struct Client
|
||||
{
|
||||
Client();
|
||||
virtual ~Client();
|
||||
virtual ~Client() {}
|
||||
|
||||
virtual void reset();
|
||||
virtual bool on_connect() = 0;
|
||||
@@ -108,7 +77,6 @@ public:
|
||||
Client* m_next;
|
||||
|
||||
uv_tcp_t m_socket;
|
||||
uv_write_t m_write;
|
||||
uv_connect_t m_connectRequest;
|
||||
|
||||
bool m_isV6;
|
||||
@@ -121,21 +89,19 @@ public:
|
||||
char m_readBuf[READ_BUF_SIZE];
|
||||
uint32_t m_numRead;
|
||||
|
||||
struct WriteBuf
|
||||
{
|
||||
Client* m_client;
|
||||
uv_write_t m_write;
|
||||
char m_data[WRITE_BUF_SIZE];
|
||||
};
|
||||
|
||||
uv_mutex_t m_writeBuffersLock;
|
||||
std::vector<WriteBuf*> m_writeBuffers;
|
||||
|
||||
std::atomic<uint32_t> m_resetCounter{ 0 };
|
||||
|
||||
uv_mutex_t m_sendLock;
|
||||
};
|
||||
|
||||
struct WriteBuf
|
||||
{
|
||||
Client* m_client = nullptr;
|
||||
uv_write_t m_write = {};
|
||||
std::vector<uint8_t> m_data;
|
||||
};
|
||||
|
||||
uv_mutex_t m_writeBuffersLock;
|
||||
std::vector<WriteBuf*> m_writeBuffers;
|
||||
|
||||
struct SendCallbackBase
|
||||
{
|
||||
virtual ~SendCallbackBase() {}
|
||||
@@ -184,7 +150,7 @@ protected:
|
||||
int m_listenPort;
|
||||
|
||||
uv_loop_t m_loop;
|
||||
volatile bool m_loopStopped;
|
||||
std::atomic<bool> m_loopStopped;
|
||||
|
||||
uv_mutex_t m_clientsListLock;
|
||||
std::vector<Client*> m_preallocatedClients;
|
||||
@@ -193,12 +159,12 @@ protected:
|
||||
uint32_t m_numIncomingConnections;
|
||||
|
||||
uv_mutex_t m_bansLock;
|
||||
std::map<raw_ip, time_t> m_bans;
|
||||
unordered_map<raw_ip, std::chrono::steady_clock::time_point> m_bans;
|
||||
|
||||
bool is_banned(const raw_ip& ip);
|
||||
|
||||
uv_mutex_t m_pendingConnectionsLock;
|
||||
std::set<raw_ip> m_pendingConnections;
|
||||
unordered_set<raw_ip> m_pendingConnections;
|
||||
|
||||
uv_async_t m_dropConnectionsAsync;
|
||||
static void on_drop_connections(uv_async_t* async) { reinterpret_cast<TCPServer*>(async->data)->close_sockets(false); }
|
||||
|
||||
+81
-51
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -27,7 +27,7 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
|
||||
, m_loopThread{}
|
||||
, m_finished(0)
|
||||
, m_listenPort(-1)
|
||||
, m_loopStopped(false)
|
||||
, m_loopStopped{false}
|
||||
, m_numConnections(0)
|
||||
, m_numIncomingConnections(0)
|
||||
{
|
||||
@@ -46,6 +46,12 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
|
||||
uv_mutex_init_checked(&m_clientsListLock);
|
||||
uv_mutex_init_checked(&m_bansLock);
|
||||
uv_mutex_init_checked(&m_pendingConnectionsLock);
|
||||
uv_mutex_init_checked(&m_writeBuffersLock);
|
||||
|
||||
m_writeBuffers.resize(DEFAULT_BACKLOG);
|
||||
for (size_t i = 0; i < m_writeBuffers.size(); ++i) {
|
||||
m_writeBuffers[i] = new WriteBuf();
|
||||
}
|
||||
|
||||
m_preallocatedClients.reserve(DEFAULT_BACKLOG);
|
||||
for (int i = 0; i < DEFAULT_BACKLOG; ++i) {
|
||||
@@ -316,11 +322,17 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connect_failed(bool, const raw
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::is_banned(const raw_ip& ip)
|
||||
{
|
||||
const auto cur_time = std::chrono::steady_clock::now();
|
||||
|
||||
MutexLock lock(m_bansLock);
|
||||
|
||||
auto it = m_bans.find(ip);
|
||||
if ((it != m_bans.end()) && (time(nullptr) < it->second)) {
|
||||
return true;
|
||||
if (it != m_bans.end()) {
|
||||
const bool banned = (cur_time < it->second);
|
||||
if (!banned) {
|
||||
m_bans.erase(it);
|
||||
}
|
||||
return banned;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -425,14 +437,14 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::shutdown_tcp()
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
const system_clock::time_point start_time = system_clock::now();
|
||||
const auto start_time = steady_clock::now();
|
||||
int64_t counter = 0;
|
||||
uv_async_t asy;
|
||||
|
||||
constexpr uint32_t timeout_seconds = 30;
|
||||
|
||||
while (!m_loopStopped) {
|
||||
const int64_t elapsed_time = duration_cast<milliseconds>(system_clock::now() - start_time).count();
|
||||
const int64_t elapsed_time = duration_cast<milliseconds>(steady_clock::now() - start_time).count();
|
||||
|
||||
if (elapsed_time >= (counter + 1) * 1000) {
|
||||
++counter;
|
||||
@@ -461,6 +473,14 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::shutdown_tcp()
|
||||
uv_mutex_destroy(&m_bansLock);
|
||||
uv_mutex_destroy(&m_pendingConnectionsLock);
|
||||
|
||||
{
|
||||
MutexLock lock(m_writeBuffersLock);
|
||||
for (WriteBuf* buf : m_writeBuffers) {
|
||||
delete buf;
|
||||
}
|
||||
}
|
||||
uv_mutex_destroy(&m_writeBuffersLock);
|
||||
|
||||
LOGINFO(1, "stopped");
|
||||
}
|
||||
|
||||
@@ -475,8 +495,34 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::print_status()
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::ban(const raw_ip& ip, uint64_t seconds)
|
||||
{
|
||||
const auto ban_time = std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
|
||||
|
||||
MutexLock lock(m_bansLock);
|
||||
m_bans[ip] = time(nullptr) + seconds;
|
||||
m_bans[ip] = ban_time;
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::print_bans()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
const auto cur_time = steady_clock::now();
|
||||
|
||||
std::vector<std::pair<raw_ip, std::chrono::steady_clock::time_point>> bans;
|
||||
{
|
||||
MutexLock lock(m_bansLock);
|
||||
|
||||
bans.reserve(m_bans.size());
|
||||
for (const auto& b : m_bans) {
|
||||
bans.emplace_back(std::make_pair(b.first, b.second));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& b : bans) {
|
||||
if (cur_time < b.second) {
|
||||
const uint64_t t = duration_cast<seconds>(b.second - cur_time).count();
|
||||
LOGINFO(0, b.first << " is banned (" << t << " seconds left)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
@@ -486,50 +532,52 @@ 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");
|
||||
}
|
||||
|
||||
MutexLock lock0(client->m_sendLock);
|
||||
|
||||
typename Client::WriteBuf* buf = nullptr;
|
||||
WriteBuf* buf = nullptr;
|
||||
|
||||
{
|
||||
MutexLock lock(client->m_writeBuffersLock);
|
||||
if (!client->m_writeBuffers.empty()) {
|
||||
buf = client->m_writeBuffers.back();
|
||||
client->m_writeBuffers.pop_back();
|
||||
MutexLock lock(m_writeBuffersLock);
|
||||
if (!m_writeBuffers.empty()) {
|
||||
buf = m_writeBuffers.back();
|
||||
m_writeBuffers.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
if (!buf) {
|
||||
buf = new typename Client::WriteBuf();
|
||||
buf = new WriteBuf();
|
||||
}
|
||||
|
||||
const size_t bytes_written = callback(buf->m_data);
|
||||
// callback_buf is used in only 1 thread, so it's safe
|
||||
static uint8_t callback_buf[WRITE_BUF_SIZE];
|
||||
const size_t bytes_written = callback(callback_buf);
|
||||
|
||||
if (bytes_written > sizeof(buf->m_data)) {
|
||||
LOGERR(0, "send callback wrote " << bytes_written << " bytes, expected no more than " << sizeof(buf->m_data) << " bytes");
|
||||
if (bytes_written > WRITE_BUF_SIZE) {
|
||||
LOGERR(0, "send callback wrote " << bytes_written << " bytes, expected no more than " << WRITE_BUF_SIZE << " bytes");
|
||||
panic();
|
||||
}
|
||||
|
||||
if (bytes_written == 0) {
|
||||
LOGWARN(1, "send callback wrote 0 bytes, nothing to do");
|
||||
{
|
||||
MutexLock lock(client->m_writeBuffersLock);
|
||||
client->m_writeBuffers.push_back(buf);
|
||||
MutexLock lock(m_writeBuffersLock);
|
||||
m_writeBuffers.push_back(buf);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
buf->m_client = client;
|
||||
buf->m_write.data = buf;
|
||||
buf->m_data.reserve(round_up(bytes_written, 64));
|
||||
buf->m_data.assign(callback_buf, callback_buf + bytes_written);
|
||||
|
||||
uv_buf_t bufs[1];
|
||||
bufs[0].base = buf->m_data;
|
||||
bufs[0].base = reinterpret_cast<char*>(buf->m_data.data());
|
||||
bufs[0].len = static_cast<int>(bytes_written);
|
||||
|
||||
const int err = uv_write(&buf->m_write, reinterpret_cast<uv_stream_t*>(&client->m_socket), bufs, 1, Client::on_write);
|
||||
if (err) {
|
||||
{
|
||||
MutexLock lock(client->m_writeBuffersLock);
|
||||
client->m_writeBuffers.push_back(buf);
|
||||
MutexLock lock(m_writeBuffersLock);
|
||||
m_writeBuffers.push_back(buf);
|
||||
}
|
||||
LOGWARN(1, "failed to start writing data to client connection " << static_cast<const char*>(client->m_addrString) << ", error " << uv_err_name(err));
|
||||
return false;
|
||||
@@ -546,6 +594,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::loop(void* data)
|
||||
TCPServer* server = static_cast<TCPServer*>(data);
|
||||
uv_run(&server->m_loop, UV_RUN_DEFAULT);
|
||||
uv_loop_close(&server->m_loop);
|
||||
LOGINFO(1, "event loop stopped");
|
||||
server->m_loopStopped = true;
|
||||
}
|
||||
|
||||
@@ -569,9 +618,11 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_connection(uv_stream_t* se
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connection_close(uv_handle_t* handle)
|
||||
{
|
||||
Client* client = static_cast<Client*>(handle->data);
|
||||
MutexLock lock0(client->m_sendLock);
|
||||
if (!server_event_loop_thread) {
|
||||
LOGERR(1, "on_connection_close called from another thread, this is not thread safe");
|
||||
}
|
||||
|
||||
Client* client = static_cast<Client*>(handle->data);
|
||||
TCPServer* owner = client->m_owner;
|
||||
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(client->m_addrString) << log::NoColor() << " disconnected");
|
||||
@@ -760,28 +811,7 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::Client()
|
||||
{
|
||||
Client::reset();
|
||||
|
||||
uv_mutex_init_checked(&m_writeBuffersLock);
|
||||
uv_mutex_init_checked(&m_sendLock);
|
||||
|
||||
m_readBuf[0] = '\0';
|
||||
|
||||
m_writeBuffers.resize(2);
|
||||
for (size_t i = 0; i < m_writeBuffers.size(); ++i) {
|
||||
m_writeBuffers[i] = new WriteBuf();
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::~Client()
|
||||
{
|
||||
{
|
||||
MutexLock lock(m_writeBuffersLock);
|
||||
for (WriteBuf* buf : m_writeBuffers) {
|
||||
delete buf;
|
||||
}
|
||||
}
|
||||
uv_mutex_destroy(&m_writeBuffersLock);
|
||||
uv_mutex_destroy(&m_sendLock);
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
@@ -793,7 +823,6 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::reset()
|
||||
m_prev = nullptr;
|
||||
m_next = nullptr;
|
||||
memset(&m_socket, 0, sizeof(m_socket));
|
||||
memset(&m_write, 0, sizeof(m_write));
|
||||
memset(&m_connectRequest, 0, sizeof(m_connectRequest));
|
||||
m_isV6 = false;
|
||||
m_isIncoming = false;
|
||||
@@ -857,12 +886,13 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::on_read(uv_stream_t* stre
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::on_write(uv_write_t* req, int status)
|
||||
{
|
||||
Client::WriteBuf* buf = static_cast<Client::WriteBuf*>(req->data);
|
||||
WriteBuf* buf = static_cast<WriteBuf*>(req->data);
|
||||
Client* client = buf->m_client;
|
||||
TCPServer* server = client->m_owner;
|
||||
|
||||
{
|
||||
MutexLock lock(client->m_writeBuffersLock);
|
||||
client->m_writeBuffers.push_back(buf);
|
||||
if (server) {
|
||||
MutexLock lock(server->m_writeBuffersLock);
|
||||
server->m_writeBuffers.push_back(buf);
|
||||
}
|
||||
|
||||
if (status != 0) {
|
||||
|
||||
+10
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -32,7 +32,7 @@ namespace p2pool {
|
||||
#define STR2(X) STR(X)
|
||||
#define STR(X) #X
|
||||
|
||||
const char* VERSION = "v1.3 (built"
|
||||
const char* VERSION = "v2.0 (built"
|
||||
#if defined(__clang__)
|
||||
" with clang/" __clang_version__
|
||||
#elif defined(__GNUC__)
|
||||
@@ -66,7 +66,7 @@ void make_thread_background()
|
||||
SetThreadPriorityBoost(GetCurrentThread(), true);
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN);
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
|
||||
#elif !defined(__APPLE__)
|
||||
#elif !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||
sched_param param;
|
||||
param.sched_priority = 0;
|
||||
if (sched_setscheduler(0, SCHED_IDLE, ¶m) != 0) {
|
||||
@@ -225,7 +225,7 @@ void uv_rwlock_init_checked(uv_rwlock_t* lock)
|
||||
|
||||
uv_loop_t* uv_default_loop_checked()
|
||||
{
|
||||
if (!is_main_thread) {
|
||||
if (!is_main_thread()) {
|
||||
LOGERR(1, "uv_default_loop() can only be used by the main thread. Fix the code!");
|
||||
#ifdef _WIN32
|
||||
if (IsDebuggerPresent()) {
|
||||
@@ -339,7 +339,10 @@ void BackgroundJobTracker::print_status()
|
||||
}
|
||||
|
||||
BackgroundJobTracker bkg_jobs_tracker;
|
||||
thread_local bool is_main_thread = false;
|
||||
|
||||
static thread_local bool main_thread = false;
|
||||
void set_main_thread() { main_thread = true; }
|
||||
bool is_main_thread() { return main_thread; }
|
||||
|
||||
bool resolve_host(std::string& host, bool& is_v6)
|
||||
{
|
||||
@@ -379,4 +382,6 @@ bool resolve_host(std::string& host, bool& is_v6)
|
||||
return true;
|
||||
}
|
||||
|
||||
RandomDeviceSeed RandomDeviceSeed::instance;
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+82
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 5027)
|
||||
#pragma warning(disable : 4623 5026 5027)
|
||||
#endif
|
||||
|
||||
#define ROBIN_HOOD_MALLOC(size) p2pool::malloc_hook(size)
|
||||
@@ -109,6 +109,40 @@ FORCEINLINE void writeVarint(T value, std::vector<uint8_t>& out)
|
||||
writeVarint(value, [&out](uint8_t b) { out.emplace_back(b); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const uint8_t* readVarint(const uint8_t* data, const uint8_t* data_end, T& b)
|
||||
{
|
||||
uint64_t result = 0;
|
||||
int k = 0;
|
||||
|
||||
while (data < data_end) {
|
||||
if (k >= static_cast<int>(sizeof(T)) * 8) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint64_t cur_byte = *(data++);
|
||||
result |= (cur_byte & 0x7F) << k;
|
||||
k += 7;
|
||||
|
||||
if ((cur_byte & 0x80) == 0) {
|
||||
b = result;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE T read_unaligned(const T* p)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value, "T must be an integer type");
|
||||
|
||||
T result;
|
||||
memcpy(&result, p, sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, size_t N> FORCEINLINE constexpr size_t array_size(T(&)[N]) { return N; }
|
||||
template<typename T, typename U, size_t N> FORCEINLINE constexpr size_t array_size(T(U::*)[N]) { return N; }
|
||||
|
||||
@@ -133,7 +167,9 @@ private:
|
||||
};
|
||||
|
||||
extern BackgroundJobTracker bkg_jobs_tracker;
|
||||
extern thread_local bool is_main_thread;
|
||||
|
||||
void set_main_thread();
|
||||
bool is_main_thread();
|
||||
|
||||
bool resolve_host(std::string& host, bool& is_v6);
|
||||
|
||||
@@ -143,6 +179,30 @@ using unordered_map = robin_hood::detail::Table<false, 80, Key, T, robin_hood::h
|
||||
template <typename Key>
|
||||
using unordered_set = robin_hood::detail::Table<false, 80, Key, void, robin_hood::hash<Key>, std::equal_to<Key>>;
|
||||
|
||||
// Fills the whole initial MT19937-64 state with non-deterministic random numbers
|
||||
struct RandomDeviceSeed
|
||||
{
|
||||
using result_type = std::random_device::result_type;
|
||||
static_assert(sizeof(result_type) >= 4, "result_type must have at least 32 bits");
|
||||
|
||||
template<typename T>
|
||||
static void generate(T begin, T end)
|
||||
{
|
||||
std::random_device rd;
|
||||
for (T i = begin; i != end; ++i) {
|
||||
*i = rd();
|
||||
}
|
||||
}
|
||||
|
||||
static RandomDeviceSeed instance;
|
||||
};
|
||||
|
||||
FORCEINLINE uint64_t seconds_since_epoch()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
return duration_cast<seconds>(steady_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
namespace robin_hood {
|
||||
@@ -165,4 +225,23 @@ struct hash<std::array<uint8_t, N>>
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<p2pool::raw_ip>
|
||||
{
|
||||
FORCEINLINE size_t operator()(const p2pool::raw_ip& value) const noexcept
|
||||
{
|
||||
return hash_bytes(value.data, sizeof(value.data));
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<std::pair<uint64_t, uint64_t>>
|
||||
{
|
||||
FORCEINLINE size_t operator()(const std::pair<uint64_t, uint64_t>& value) const noexcept
|
||||
{
|
||||
static_assert(sizeof(value) == sizeof(uint64_t) * 2, "Invalid std::pair<uint64_t, uint64_t> size");
|
||||
return hash_bytes(&value, sizeof(value));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace robin_hood
|
||||
|
||||
+4
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,6 +19,9 @@
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
static_assert(sizeof(in6_addr) == 16, "struct in6_addr has invalid size");
|
||||
static_assert(sizeof(in_addr) == 4, "struct in_addr has invalid size");
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
struct MutexLock : public nocopy_nomove
|
||||
|
||||
+19
-8
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
* Portions Copyright (c) 2012-2013 The Cryptonote developers
|
||||
* Portions Copyright (c) 2014-2021 The Monero Project
|
||||
* Portions Copyright (c) 2021 XMRig <https://github.com/xmrig>
|
||||
@@ -79,15 +79,11 @@ static_assert(rev_alphabet.num_symbols == 58, "Check alphabet");
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
Wallet::Wallet(const char* address)
|
||||
Wallet::Wallet(const char* address) : m_prefix(0), m_checksum(0), m_type(NetworkType::Invalid)
|
||||
{
|
||||
decode(address);
|
||||
}
|
||||
|
||||
Wallet::~Wallet()
|
||||
{
|
||||
}
|
||||
|
||||
Wallet::Wallet(const Wallet& w)
|
||||
{
|
||||
operator=(w);
|
||||
@@ -199,10 +195,25 @@ bool Wallet::assign(const hash& spend_pub_key, const hash& view_pub_key, Network
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wallet::get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key) const
|
||||
bool Wallet::get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag) const
|
||||
{
|
||||
hash derivation;
|
||||
if (!generate_key_derivation(m_viewPublicKey, txkey_sec, derivation)) {
|
||||
if (!generate_key_derivation(m_viewPublicKey, txkey_sec, output_index, derivation, view_tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!derive_public_key(derivation, output_index, m_spendPublicKey, eph_public_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wallet::get_eph_public_key_with_view_tag(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t expected_view_tag) const
|
||||
{
|
||||
hash derivation;
|
||||
uint8_t view_tag;
|
||||
if (!generate_key_derivation(m_viewPublicKey, txkey_sec, output_index, derivation, view_tag) || (view_tag != expected_view_tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -25,7 +25,6 @@ class Wallet
|
||||
{
|
||||
public:
|
||||
explicit Wallet(const char* address);
|
||||
~Wallet();
|
||||
|
||||
Wallet(const Wallet& w);
|
||||
Wallet& operator=(const Wallet& w);
|
||||
@@ -39,7 +38,8 @@ public:
|
||||
FORCEINLINE const hash& spend_public_key() const { return m_spendPublicKey; }
|
||||
FORCEINLINE const hash& view_public_key() const { return m_viewPublicKey; }
|
||||
|
||||
bool get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key) const;
|
||||
bool get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag) const;
|
||||
bool get_eph_public_key_with_view_tag(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t expected_view_tag) const;
|
||||
|
||||
FORCEINLINE bool operator<(const Wallet& w) const { return m_spendPublicKey < w.m_spendPublicKey; }
|
||||
FORCEINLINE bool operator==(const Wallet& w) const { return m_spendPublicKey == w.m_spendPublicKey; }
|
||||
|
||||
+28
-10
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -88,10 +88,14 @@ void ZMQReader::run()
|
||||
char addr[32];
|
||||
|
||||
snprintf(addr, sizeof(addr), "tcp://%s:%u", m_address, m_zmqPort);
|
||||
while (!connect(addr, m_zmqPort)) { if (m_finished.load()) return; }
|
||||
if (!connect(addr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(addr, sizeof(addr), "tcp://127.0.0.1:%u", m_publisherPort);
|
||||
while (!connect(addr, m_publisherPort)) { if (m_finished.load()) return; }
|
||||
if (!connect(addr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_subscriber.set(zmq::sockopt::subscribe, "json-full-chain_main");
|
||||
m_subscriber.set(zmq::sockopt::subscribe, "json-full-miner_data");
|
||||
@@ -126,7 +130,7 @@ void ZMQReader::run()
|
||||
}
|
||||
}
|
||||
|
||||
bool ZMQReader::connect(const char* address, uint32_t id)
|
||||
bool ZMQReader::connect(const char* address)
|
||||
{
|
||||
struct ConnectMonitor : public zmq::monitor_t
|
||||
{
|
||||
@@ -139,19 +143,33 @@ bool ZMQReader::connect(const char* address, uint32_t id)
|
||||
bool connected = false;
|
||||
} monitor;
|
||||
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "inproc://connect-mon-%u", id);
|
||||
static uint64_t id = 0;
|
||||
|
||||
if (!id) {
|
||||
std::random_device rd;
|
||||
id = (static_cast<uint64_t>(rd()) << 32) | static_cast<uint32_t>(rd());
|
||||
}
|
||||
|
||||
char buf[log::Stream::BUF_SIZE + 1];
|
||||
log::Stream s(buf);
|
||||
s << "inproc://p2pool-connect-mon-" << id << '\0';
|
||||
++id;
|
||||
|
||||
monitor.init(m_subscriber, buf);
|
||||
m_subscriber.connect(address);
|
||||
|
||||
using namespace std::chrono;
|
||||
const system_clock::time_point start_time = system_clock::now();
|
||||
steady_clock::time_point start_time = steady_clock::now();
|
||||
|
||||
while (!monitor.connected && monitor.check_event(-1)) {
|
||||
const int64_t elapsed_time = duration_cast<milliseconds>(system_clock::now() - start_time).count();
|
||||
const steady_clock::time_point cur_time = steady_clock::now();
|
||||
const int64_t elapsed_time = duration_cast<milliseconds>(cur_time - start_time).count();
|
||||
if (elapsed_time >= 3000) {
|
||||
LOGERR(1, "failed to connect to " << address);
|
||||
return false;
|
||||
if (m_finished.load()) {
|
||||
return false;
|
||||
}
|
||||
start_time = cur_time;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +207,7 @@ void ZMQReader::parse(char* data, size_t size)
|
||||
return;
|
||||
}
|
||||
|
||||
m_tx.time_received = time(nullptr);
|
||||
m_tx.time_received = seconds_since_epoch();
|
||||
|
||||
for (SizeType i = 0, n = doc.Size(); i < n; ++i) {
|
||||
const auto& v = doc[i];
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
private:
|
||||
static void run_wrapper(void* arg);
|
||||
void run();
|
||||
bool connect(const char* address, uint32_t id);
|
||||
bool connect(const char* address);
|
||||
|
||||
void parse(char* data, size_t size);
|
||||
|
||||
|
||||
+28
-15
@@ -1,6 +1,8 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
project(p2pool_tests)
|
||||
|
||||
option(STATIC_LIBS "Use locally built libuv and libzmq static libs" OFF)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
@@ -17,6 +19,7 @@ set(LIBS gtest)
|
||||
|
||||
add_subdirectory(../external/src/RandomX RandomX)
|
||||
set(LIBS ${LIBS} randomx)
|
||||
add_definitions(-DWITH_RANDOMX)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||
set(WARNING_FLAGS "")
|
||||
@@ -71,6 +74,7 @@ set(SOURCES
|
||||
src/keccak_tests.cpp
|
||||
src/main.cpp
|
||||
src/pool_block_tests.cpp
|
||||
src/varint_tests.cpp
|
||||
src/wallet_tests.cpp
|
||||
../external/src/cryptonote/crypto-ops-data.c
|
||||
../external/src/cryptonote/crypto-ops.c
|
||||
@@ -86,6 +90,7 @@ set(SOURCES
|
||||
../src/log.cpp
|
||||
../src/memory_leak_debug.cpp
|
||||
../src/mempool.cpp
|
||||
../src/miner.cpp
|
||||
../src/p2p_server.cpp
|
||||
../src/p2pool.cpp
|
||||
../src/p2pool_api.cpp
|
||||
@@ -118,17 +123,28 @@ elseif (NOT APPLE)
|
||||
set(LIBS ${LIBS} pthread gss dl)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
||||
find_library(ZMQ_LIBRARY_DEBUG NAMES libzmq-v142-mt-sgd-4_3_5 PATHS "../external/lib/libzmq/Debug")
|
||||
find_library(ZMQ_LIBRARY NAMES libzmq-v142-mt-s-4_3_5 PATHS "../external/lib/libzmq/Release")
|
||||
find_library(UV_LIBRARY_DEBUG NAMES uv_a PATHS "../external/lib/libuv/Debug")
|
||||
find_library(UV_LIBRARY NAMES uv_a PATHS "../external/lib/libuv/Release")
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
|
||||
find_library(ZMQ_LIBRARY_DEBUG NAMES zmq libzmq.a)
|
||||
find_library(ZMQ_LIBRARY NAMES zmq libzmq.a)
|
||||
find_library(UV_LIBRARY_DEBUG NAMES uv libuv.a)
|
||||
find_library(UV_LIBRARY NAMES uv libuv.a)
|
||||
find_library(SODIUM_LIBRARY sodium)
|
||||
if (STATIC_LIBS)
|
||||
set(ZMQ_LIBRARY "${CMAKE_SOURCE_DIR}/../external/src/libzmq/build/lib/libzmq.a")
|
||||
set(ZMQ_LIBRARY_DEBUG "${CMAKE_SOURCE_DIR}/../external/src/libzmq/build/lib/libzmq.a")
|
||||
set(UV_LIBRARY_DEBUG "${CMAKE_SOURCE_DIR}/../external/src/libuv/build/libuv_a.a")
|
||||
set(UV_LIBRARY "${CMAKE_SOURCE_DIR}/../external/src/libuv/build/libuv_a.a")
|
||||
else()
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
||||
find_library(ZMQ_LIBRARY_DEBUG NAMES libzmq-v142-mt-sgd-4_3_5 PATHS "../external/lib/libzmq/Debug")
|
||||
find_library(ZMQ_LIBRARY NAMES libzmq-v142-mt-s-4_3_5 PATHS "../external/lib/libzmq/Release")
|
||||
find_library(UV_LIBRARY_DEBUG NAMES uv_a PATHS "../external/lib/libuv/Debug")
|
||||
find_library(UV_LIBRARY NAMES uv_a PATHS "../external/lib/libuv/Release")
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
|
||||
find_library(ZMQ_LIBRARY_DEBUG NAMES zmq libzmq.a)
|
||||
find_library(ZMQ_LIBRARY NAMES zmq libzmq.a)
|
||||
find_library(UV_LIBRARY_DEBUG NAMES uv libuv.a)
|
||||
find_library(UV_LIBRARY NAMES uv libuv.a)
|
||||
find_library(SODIUM_LIBRARY sodium)
|
||||
endif()
|
||||
|
||||
if (SODIUM_LIBRARY)
|
||||
set(LIBS ${LIBS} ${SODIUM_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_library(PGM_LIBRARY pgm)
|
||||
@@ -142,13 +158,10 @@ if (NORM_LIBRARY)
|
||||
set(LIBS ${LIBS} ${NORM_LIBRARY})
|
||||
endif()
|
||||
|
||||
if (SODIUM_LIBRARY)
|
||||
set(LIBS ${LIBS} ${SODIUM_LIBRARY})
|
||||
endif()
|
||||
|
||||
add_definitions(/DZMQ_STATIC /DP2POOL_LOG_DISABLE)
|
||||
|
||||
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} ${LIBS})
|
||||
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/crypto_tests.txt" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
|
||||
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}>)
|
||||
|
||||
@@ -42,7 +42,8 @@ TEST(crypto, derivation)
|
||||
if (result) {
|
||||
f >> expected_derivation;
|
||||
}
|
||||
ASSERT_EQ(p2pool::generate_key_derivation(key1, key2, derivation), result);
|
||||
uint8_t view_tag;
|
||||
ASSERT_EQ(p2pool::generate_key_derivation(key1, key2, 0, derivation, view_tag), result);
|
||||
if (result) {
|
||||
ASSERT_EQ(derivation, expected_derivation);
|
||||
}
|
||||
@@ -61,6 +62,20 @@ TEST(crypto, derivation)
|
||||
ASSERT_EQ(derived_key, expected_derived_key);
|
||||
}
|
||||
}
|
||||
else if (name == "derive_view_tag") {
|
||||
hash derivation;
|
||||
uint64_t output_index;
|
||||
std::string result_str;
|
||||
f >> derivation >> output_index >> result_str;
|
||||
uint8_t view_tag;
|
||||
p2pool::derive_view_tag(derivation, output_index, view_tag);
|
||||
|
||||
char buf[log::Stream::BUF_SIZE + 1];
|
||||
log::Stream s(buf);
|
||||
s << log::hex_buf(&view_tag, 1) << '\0';
|
||||
|
||||
ASSERT_EQ(buf, result_str);
|
||||
}
|
||||
} while (!f.eof());
|
||||
}
|
||||
|
||||
|
||||
@@ -542,3 +542,73 @@ derive_public_key 5ea95a51ab11b80c7d09d0c8f8952b70c67e81d0fd421bbed43ab77c1b7b30
|
||||
derive_public_key 2c312ef971def53361274c37a90bfde86f959d877a636ea641a9c976ee80c7e3 121 b611ebd2bcfefc81cb772e35e3dd0204575cb0da644f68d4f9828a2683861e6c false
|
||||
derive_public_key 6fa161dd958022caf185faf873dd9adbc5578352cda505e84fff7cc99a8762a7 333934910 c2b56e207862958751d49643f23079009092c32bf82179a1295e3b85a385c1c3 false
|
||||
derive_public_key d85725562544e1984048391413a6112eb221bd217db3baaa9843bee331000e8e 21880446 f3148c3041e634d829e5df463ba3b1d64df282d620d63485e1f10024e003b939 false
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 0 76
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 1 d6
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 2 87
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 3 1b
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 12 d6
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 13 e9
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 14 12
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 15 26
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 127 2c
|
||||
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 128 11
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 0 70
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 1 81
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 2 a0
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 3 ec
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 12 22
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 13 0a
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 14 87
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 15 76
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 127 4b
|
||||
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 128 39
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 0 93
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 1 67
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 2 9d
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 3 2d
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 12 63
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 13 cf
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 14 ef
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 15 10
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 127 fc
|
||||
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 128 73
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 0 90
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 1 5a
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 2 de
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 3 21
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 12 57
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 13 52
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 14 6f
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 15 eb
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 127 93
|
||||
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 128 dc
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 0 c6
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 1 60
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 2 f0
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 3 71
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 12 0e
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 13 42
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 14 b2
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 15 61
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 127 c3
|
||||
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 128 0a
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 0 4c
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 1 9b
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 2 64
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 3 ff
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 12 e3
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 13 24
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 14 ea
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 15 3b
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 127 47
|
||||
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 128 5a
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 0 74
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 1 77
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 2 a9
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 3 44
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 12 75
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 13 05
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 14 ca
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 15 00
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 127 a6
|
||||
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 128 0d
|
||||
|
||||
@@ -254,7 +254,7 @@ TEST(difficulty_type, check_pow)
|
||||
|
||||
// Max hash value that passes this difficulty
|
||||
memcpy(h.h, data, HASH_SIZE);
|
||||
EXPECT_EQ(diff.check_pow(h), true);
|
||||
ASSERT_EQ(diff.check_pow(h), true);
|
||||
|
||||
// Add 1 to data (256-bit number)
|
||||
for (int j = 0; j <= 3; ++j) {
|
||||
@@ -267,7 +267,7 @@ TEST(difficulty_type, check_pow)
|
||||
|
||||
// Min hash value that fails this difficulty
|
||||
memcpy(h.h, data, HASH_SIZE);
|
||||
EXPECT_EQ(diff.check_pow(h), false);
|
||||
ASSERT_EQ(diff.check_pow(h), false);
|
||||
}
|
||||
|
||||
const uint64_t target = diff.target();
|
||||
@@ -276,14 +276,14 @@ TEST(difficulty_type, check_pow)
|
||||
for (int j = 0; j < 10000; ++j) {
|
||||
const uint64_t data[4] = { r(), r(), r(), r() % target };
|
||||
memcpy(h.h, data, HASH_SIZE);
|
||||
EXPECT_EQ(diff.check_pow(h), true);
|
||||
ASSERT_EQ(diff.check_pow(h), true);
|
||||
}
|
||||
|
||||
// Random values that fail
|
||||
for (int j = 0; j < 10000; ++j) {
|
||||
const uint64_t data[4] = { r(), r(), r(), target + (r() % (std::numeric_limits<uint64_t>::max() - target + 1)) };
|
||||
memcpy(h.h, data, HASH_SIZE);
|
||||
EXPECT_EQ(diff.check_pow(h), false);
|
||||
ASSERT_EQ(diff.check_pow(h), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
void p2pool_usage() {}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "crypto.h"
|
||||
#include "pool_block.h"
|
||||
#include "pow_hash.h"
|
||||
#include "side_chain.h"
|
||||
@@ -72,7 +73,6 @@ TEST(pool_block, deserialize)
|
||||
ASSERT_EQ(b.m_cumulativeDifficulty.lo, 12544665764606ull);
|
||||
ASSERT_EQ(b.m_cumulativeDifficulty.hi, 0);
|
||||
ASSERT_EQ(b.m_tmpTxExtra.size(), 0);
|
||||
ASSERT_EQ(b.m_tmpInts.size(), 0);
|
||||
ASSERT_EQ(b.m_depth, 0);
|
||||
ASSERT_EQ(b.m_verified, false);
|
||||
ASSERT_EQ(b.m_invalid, false);
|
||||
@@ -91,7 +91,7 @@ TEST(pool_block, deserialize)
|
||||
hasher.set_seed(seed);
|
||||
|
||||
hash pow_hash;
|
||||
ASSERT_EQ(b.get_pow_hash(&hasher, seed, pow_hash), true);
|
||||
ASSERT_EQ(b.get_pow_hash(&hasher, 0, seed, pow_hash), true);
|
||||
|
||||
std::stringstream s;
|
||||
s << pow_hash;
|
||||
@@ -100,4 +100,43 @@ TEST(pool_block, deserialize)
|
||||
ASSERT_EQ(b.m_difficulty.check_pow(pow_hash), true);
|
||||
}
|
||||
|
||||
TEST(pool_block, verify)
|
||||
{
|
||||
init_crypto_cache();
|
||||
|
||||
PoolBlock b;
|
||||
SideChain sidechain(nullptr, NetworkType::Mainnet);
|
||||
|
||||
std::ifstream f("sidechain_dump.dat", std::ios::binary | std::ios::ate);
|
||||
ASSERT_EQ(f.good() && f.is_open(), true);
|
||||
|
||||
std::vector<uint8_t> buf(f.tellg());
|
||||
f.seekg(0);
|
||||
f.read(reinterpret_cast<char*>(buf.data()), buf.size());
|
||||
ASSERT_EQ(f.good(), true);
|
||||
|
||||
for (const uint8_t *p = buf.data(), *e = buf.data() + buf.size(); p < e;) {
|
||||
ASSERT_TRUE(p + sizeof(uint32_t) <= e);
|
||||
const uint32_t n = *reinterpret_cast<const uint32_t*>(p);
|
||||
p += sizeof(uint32_t);
|
||||
|
||||
ASSERT_TRUE(p + n <= e);
|
||||
ASSERT_EQ(b.deserialize(p, n, sidechain), 0);
|
||||
p += n;
|
||||
|
||||
sidechain.add_block(b);
|
||||
ASSERT_TRUE(sidechain.find_block(b.m_sidechainId) != nullptr);
|
||||
}
|
||||
|
||||
const PoolBlock* tip = sidechain.chainTip();
|
||||
ASSERT_TRUE(tip != nullptr);
|
||||
ASSERT_TRUE(tip->m_verified);
|
||||
ASSERT_FALSE(tip->m_invalid);
|
||||
|
||||
ASSERT_EQ(tip->m_txinGenHeight, 2483901);
|
||||
ASSERT_EQ(tip->m_sidechainHeight, 522805);
|
||||
|
||||
destroy_crypto_cache();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "util.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
TEST(varint, read_write)
|
||||
{
|
||||
std::vector<uint8_t> v;
|
||||
v.reserve(16);
|
||||
|
||||
uint64_t check;
|
||||
|
||||
// 0...2^7 - 1
|
||||
for (uint64_t value = 0; value < 0x80; ++value) {
|
||||
v.clear();
|
||||
writeVarint(value, v);
|
||||
ASSERT_EQ(v.size(), 1);
|
||||
ASSERT_EQ(v[0], value);
|
||||
ASSERT_EQ(readVarint(v.data(), v.data() + v.size(), check), v.data() + v.size());
|
||||
ASSERT_EQ(check, value);
|
||||
}
|
||||
|
||||
// 2^7...2^14 - 1
|
||||
for (uint64_t value = 0x80; value < 0x4000; ++value) {
|
||||
v.clear();
|
||||
writeVarint(value, v);
|
||||
ASSERT_EQ(v.size(), 2);
|
||||
ASSERT_EQ(v[0], (value & 0x7F) | 0x80);
|
||||
ASSERT_EQ(v[1], value >> 7);
|
||||
ASSERT_EQ(readVarint(v.data(), v.data() + v.size(), check), v.data() + v.size());
|
||||
ASSERT_EQ(check, value);
|
||||
}
|
||||
|
||||
// 2^14...2^21 - 1
|
||||
for (uint64_t value = 0x4000; value < 0x200000; ++value) {
|
||||
v.clear();
|
||||
writeVarint(value, v);
|
||||
ASSERT_EQ(v.size(), 3);
|
||||
ASSERT_EQ(v[0], (value & 0x7F) | 0x80);
|
||||
ASSERT_EQ(v[1], ((value >> 7) & 0x7F) | 0x80);
|
||||
ASSERT_EQ(v[2], value >> 14);
|
||||
ASSERT_EQ(readVarint(v.data(), v.data() + v.size(), check), v.data() + v.size());
|
||||
ASSERT_EQ(check, value);
|
||||
}
|
||||
|
||||
// 2^64 - 1
|
||||
v.clear();
|
||||
writeVarint(std::numeric_limits<uint64_t>::max(), v);
|
||||
ASSERT_EQ(v.size(), 10);
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
ASSERT_EQ(v[i], 0xFF);
|
||||
}
|
||||
ASSERT_EQ(v[9], 1);
|
||||
ASSERT_EQ(readVarint(v.data(), v.data() + v.size(), check), v.data() + v.size());
|
||||
ASSERT_EQ(check, std::numeric_limits<uint64_t>::max());
|
||||
|
||||
// Invalid value 1
|
||||
uint8_t buf[16];
|
||||
memset(buf, -1, sizeof(buf));
|
||||
ASSERT_EQ(readVarint(buf, buf + sizeof(buf), check), nullptr);
|
||||
|
||||
// Invalid value 2
|
||||
uint8_t buf2[1] = { 0x80 };
|
||||
ASSERT_EQ(readVarint(buf2, buf2 + 1, check), nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user