Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 2071a24a9f | |||
| 1c0da4513f | |||
| 6a002cda46 | |||
| c21c9615a3 | |||
| c0c210664a | |||
| 6859d11445 | |||
| 111324b6e0 | |||
| 148b9dd294 | |||
| 04d18cdf1d | |||
| 352ad81a0a | |||
| d26902468d | |||
| 0382027d70 | |||
| 966b499e5c | |||
| 9885f82e9e | |||
| 32a58d1534 | |||
| bc341918f6 |
@@ -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:
|
||||
@@ -102,7 +107,7 @@ jobs:
|
||||
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 17 2022, os: 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\"}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -145,7 +150,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, macos-11]
|
||||
os: [macos-10.15, macos-11]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
#
|
||||
# Find more information at:
|
||||
# https://github.com/microsoft/msvc-code-analysis-action
|
||||
|
||||
name: Microsoft C++ Code Analysis
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '40 10 * * 0'
|
||||
|
||||
env:
|
||||
# Path to the CMake build directory.
|
||||
build: '${{ github.workspace }}/build'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{ env.build }}
|
||||
|
||||
# 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
|
||||
# Provide a unique ID to access the sarif output path
|
||||
id: run-analysis
|
||||
with:
|
||||
cmakeBuildDirectory: ${{ env.build }}
|
||||
# Ruleset file that will determine what checks will be run
|
||||
ruleset: NativeRecommendedRules.ruleset
|
||||
|
||||
# Upload SARIF file to GitHub Code Scanning Alerts
|
||||
- name: Upload SARIF to GitHub
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: ${{ steps.run-analysis.outputs.sarif }}
|
||||
|
||||
# Upload SARIF file as an Artifact to download and view
|
||||
- name: Upload SARIF as an Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: sarif-file
|
||||
path: ${{ steps.run-analysis.outputs.sarif }}
|
||||
@@ -19,3 +19,6 @@
|
||||
[submodule "external/src/libzmq"]
|
||||
path = external/src/libzmq
|
||||
url = https://github.com/SChernykh/libzmq
|
||||
[submodule "external/src/robin-hood-hashing"]
|
||||
path = external/src/robin-hood-hashing
|
||||
url = https://github.com/SChernykh/robin-hood-hashing
|
||||
|
||||
@@ -82,6 +82,7 @@ include_directories(external/src/libzmq/include)
|
||||
include_directories(external/src/llhttp)
|
||||
include_directories(external/src/RandomX/src)
|
||||
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)
|
||||
|
||||
+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,12 +2,13 @@
|
||||
|
||||
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/ and https://p2pool.observer/
|
||||
|
||||
### Build status
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
<a href="https://scan.coverity.com/projects/schernykh-p2pool">
|
||||
<img alt="Coverity Scan Build Status"
|
||||
@@ -65,16 +66,6 @@ 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/)
|
||||
@@ -111,16 +102,6 @@ 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):
|
||||
@@ -136,15 +117,6 @@ 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:
|
||||
```
|
||||
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
|
||||
```
|
||||
then follow the instructions from https://github.com/monero-project/monero/#on-windows
|
||||
|
||||
## How to mine on P2Pool
|
||||
|
||||
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**.
|
||||
@@ -167,14 +139,14 @@ 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)
|
||||
- Alternatively, grab the latest source code for p2pool and build it
|
||||
- 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
|
||||
- Use the `monerod` binary v0.17.3.0 or newer
|
||||
- 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!
|
||||
- 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>
|
||||
@@ -197,7 +169,7 @@ Step-by-step guide:
|
||||
*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)*
|
||||
- Download xmrig binary from https://github.com/xmrig/xmrig/releases/latest *(xmrig-6.15.2-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):
|
||||
@@ -206,7 +178,6 @@ Step-by-step guide:
|
||||
- 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!**
|
||||
|
||||
+4
-4
@@ -9,10 +9,10 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||
set(WARNING_FLAGS "-Wall -Wextra -Werror")
|
||||
set(OPTIMIZATION_FLAGS "-Ofast -s")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||
|
||||
if (WIN32)
|
||||
@@ -42,9 +42,9 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
|
||||
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal -Werror")
|
||||
set(OPTIMIZATION_FLAGS "-Ofast -funroll-loops -fmerge-all-constants")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||
endif()
|
||||
|
||||
@@ -7,3 +7,4 @@
|
||||
../external/src/llhttp/
|
||||
../external/src/RandomX/src/
|
||||
../external/src/rapidjson/include
|
||||
../external/src/robin-hood-hashing/src/include
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
cppcheck ../src -DZMQ_STATIC --platform=unix64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" -I ../src/ -I ../external/src/ -I ../external/src/cryptonote/ -I ../external/src/libuv/ -I ../external/src/cppzmq/ -I ../external/src/libzmq/ -I ../external/src/llhttp/ -I ../external/src/RandomX/src/ -I ../external/src/rapidjson/include --suppressions-list=suppressions.txt --output-file=errors_full.txt
|
||||
cppcheck ../src -DZMQ_STATIC --platform=unix64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" -I ../src/ -I ../external/src/ -I ../external/src/cryptonote/ -I ../external/src/libuv/ -I ../external/src/cppzmq/ -I ../external/src/libzmq/ -I ../external/src/llhttp/ -I ../external/src/RandomX/src/ -I ../external/src/rapidjson/include -I ../external/src/robin-hood-hashing/src/include --suppressions-list=suppressions.txt --output-file=errors_full.txt
|
||||
grep -v 'external' errors_full.txt | grep -v 'unmatchedSuppression' > errors_filtered.txt
|
||||
if [ -s errors_filtered.txt ]; then
|
||||
cat errors_filtered.txt
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
##
|
||||
# Monero Wallet Address for mining rewards (replace this with your own address)
|
||||
WALLET_ADDRESS="44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg"
|
||||
|
||||
##
|
||||
# p2pool settings
|
||||
#
|
||||
# Which port to listen for miner connections
|
||||
P2POOL_STRATUM_PORT=3333
|
||||
#
|
||||
# How much logging - (Less) 0 - 6 (More)
|
||||
P2POOL_LOGLEVEL=2
|
||||
#
|
||||
# The name of docker volume to hold p2pool data (created automatically if it doesnt exist)
|
||||
P2POOL_VOLUME=p2pool
|
||||
|
||||
|
||||
##
|
||||
# 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"
|
||||
#
|
||||
# The name of docker volume to hold p2pool data (created automatically if it doesnt exist)
|
||||
MONERO_VOLUME=monero
|
||||
|
||||
|
||||
##
|
||||
# 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"
|
||||
#
|
||||
# Limit the number of CPU cores to use for mining (uncomment to enable, edit to customize)
|
||||
#NUM_MINING_CPU="-t 4"
|
||||
@@ -0,0 +1,49 @@
|
||||
# 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) 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)
|
||||
|
||||
|
||||
#### 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```
|
||||
|
||||
|
||||
#### 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,83 @@
|
||||
---
|
||||
version: '3.4'
|
||||
|
||||
networks:
|
||||
p2pool:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
p2pool:
|
||||
name: ${P2POOL_VOLUME}
|
||||
monero:
|
||||
name: ${MONERO_VOLUME}
|
||||
|
||||
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}
|
||||
|
||||
p2pool:
|
||||
image: p2pool:latest
|
||||
build: ../
|
||||
container_name: p2pool-p2pool
|
||||
networks:
|
||||
- p2pool
|
||||
ports:
|
||||
- ${P2POOL_STRATUM_PORT}:3333/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}
|
||||
|
||||
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}
|
||||
${NUM_MINING_CPU}
|
||||
--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
+1
-1
Submodule external/src/cppzmq updated: 286d8be432...a98fa4a91d
Vendored
+1
-1
Submodule external/src/libsodium updated: 64129657a5...5df53ce62b
Vendored
+1
-1
Submodule external/src/libuv updated: a39009a5a9...0a47e4c771
Vendored
+1
-1
Submodule external/src/libzmq updated: 056f37f352...ab9c7add58
Vendored
+1
-1
Submodule external/src/rapidjson updated: 5bbae3e50b...a115f5d775
+1
Submodule external/src/robin-hood-hashing added at 90687c508a
@@ -1018,7 +1018,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)) {
|
||||
|
||||
@@ -279,6 +279,39 @@ 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;
|
||||
void free_hook(void* p) noexcept;
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
#include "util.h"
|
||||
|
||||
@@ -69,7 +69,7 @@ typedef struct cmd {
|
||||
cmdfunc *func;
|
||||
} cmd;
|
||||
|
||||
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_exit;
|
||||
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_showpeers, do_outpeers, do_inpeers, do_exit;
|
||||
|
||||
static cmd cmds[] = {
|
||||
{ STRCONST("help"), "", "display list of commands", do_help },
|
||||
@@ -77,6 +77,9 @@ static cmd cmds[] = {
|
||||
{ STRCONST("loglevel"), "<level>", "set log level", do_loglevel },
|
||||
{ STRCONST("addpeers"), "<peeraddr>", "add peer", do_addpeers },
|
||||
{ STRCONST("droppeers"), "", "disconnect all peers", do_droppeers },
|
||||
{ STRCONST("peers"), "", "show all peers", do_showpeers },
|
||||
{ STRCONST("outpeers"), "", "set maximum number of outgoing connections", do_outpeers },
|
||||
{ STRCONST("inpeers"), "", "set maximum number of incoming connections", do_inpeers },
|
||||
{ STRCONST("exit"), "", "terminate p2pool", do_exit },
|
||||
{ STRCNULL, NULL, NULL, NULL }
|
||||
};
|
||||
@@ -129,6 +132,32 @@ static int do_droppeers(p2pool *m_pool, const char * /* args */)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_showpeers(p2pool* m_pool, const char* /* args */)
|
||||
{
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->show_peers();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
static int do_exit(p2pool *m_pool, const char * /* args */)
|
||||
{
|
||||
bkg_jobs_tracker.wait();
|
||||
|
||||
+2
-3
@@ -20,7 +20,6 @@
|
||||
#include "keccak.h"
|
||||
#include "uv_util.h"
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
|
||||
extern "C" {
|
||||
#include "crypto-ops.h"
|
||||
@@ -237,8 +236,8 @@ public:
|
||||
|
||||
private:
|
||||
uv_mutex_t m;
|
||||
std::unordered_map<std::array<uint8_t, HASH_SIZE * 2>, hash> derivations;
|
||||
std::unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
|
||||
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, hash> derivations;
|
||||
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
|
||||
};
|
||||
|
||||
static Cache* cache = nullptr;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
+16
-2
@@ -28,6 +28,7 @@ namespace p2pool {
|
||||
namespace log {
|
||||
|
||||
int GLOBAL_LOG_LEVEL = 3;
|
||||
bool CONSOLE_COLORS = true;
|
||||
|
||||
#ifndef P2POOL_LOG_DISABLE
|
||||
|
||||
@@ -35,6 +36,7 @@ static volatile bool stopped = false;
|
||||
static volatile bool worker_started = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
static const HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
static const HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
static const HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
#endif
|
||||
@@ -52,7 +54,7 @@ public:
|
||||
: 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);
|
||||
|
||||
@@ -69,6 +71,9 @@ public:
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD dwConsoleMode;
|
||||
if (GetConsoleMode(hStdIn, &dwConsoleMode)) {
|
||||
SetConsoleMode(hStdIn, dwConsoleMode & ~ENABLE_QUICK_EDIT_MODE);
|
||||
}
|
||||
if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
|
||||
SetConsoleMode(hStdOut, dwConsoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
}
|
||||
@@ -155,6 +160,13 @@ private:
|
||||
p += 3;
|
||||
size -= 3;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD k;
|
||||
WriteConsole((severity == 1) ? hStdOut : hStdErr, p, size, &k, nullptr);
|
||||
@@ -172,7 +184,9 @@ private:
|
||||
}
|
||||
|
||||
if (m_logFile.is_open()) {
|
||||
strip_colors(p, size);
|
||||
if (c) {
|
||||
strip_colors(p, size);
|
||||
}
|
||||
|
||||
if (severity == 1) {
|
||||
m_logFile.write("NOTICE ", 8);
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace p2pool {
|
||||
namespace log {
|
||||
|
||||
extern int GLOBAL_LOG_LEVEL;
|
||||
extern bool CONSOLE_COLORS;
|
||||
constexpr int MAX_GLOBAL_LOG_LEVEL = 6;
|
||||
|
||||
enum class Severity {
|
||||
|
||||
@@ -38,6 +38,10 @@ static void usage()
|
||||
"--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"
|
||||
"--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 Maximum number of outgoing connections for p2p server (any value between 10 and 1000)\n"
|
||||
"--in-peers Maximum number of incoming connections for p2p server (any value between 10 and 1000)\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",
|
||||
|
||||
+40
-10
@@ -120,9 +120,9 @@ FORCEINLINE static void remove_allocation(void* p)
|
||||
__debugbreak();
|
||||
}
|
||||
|
||||
FORCEINLINE static void* allocate_noexcept(size_t n) noexcept
|
||||
void* malloc_hook(size_t n) noexcept
|
||||
{
|
||||
void* p = malloc(n + sizeof(TrackedAllocation));
|
||||
void* p = malloc(n);
|
||||
if (p) {
|
||||
add_alocation(p, n);
|
||||
}
|
||||
@@ -131,25 +131,46 @@ FORCEINLINE static void* allocate_noexcept(size_t n) noexcept
|
||||
|
||||
FORCEINLINE static void* allocate(size_t n)
|
||||
{
|
||||
void* p = allocate_noexcept(n);
|
||||
void* p = malloc_hook(n);
|
||||
if (!p) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
FORCEINLINE static void deallocate(void* p)
|
||||
void free_hook(void* p) noexcept
|
||||
{
|
||||
remove_allocation(p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
void* realloc_hook(void* ptr, size_t size) noexcept
|
||||
{
|
||||
remove_allocation(ptr);
|
||||
|
||||
void* p = realloc(ptr, size);
|
||||
if (p) {
|
||||
add_alocation(p, size);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void* calloc_hook(size_t count, size_t size) noexcept
|
||||
{
|
||||
void* p = calloc(count, size);
|
||||
if (p) {
|
||||
add_alocation(p, size);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
} // p2pool
|
||||
|
||||
void memory_tracking_start()
|
||||
{
|
||||
using namespace p2pool;
|
||||
|
||||
uv_replace_allocator(malloc_hook, realloc_hook, calloc_hook, free_hook);
|
||||
uv_mutex_init_checked(&allocation_lock);
|
||||
track_memory = true;
|
||||
}
|
||||
@@ -207,14 +228,23 @@ void memory_tracking_stop()
|
||||
|
||||
NOINLINE void* operator new(size_t n) { return p2pool::allocate(n); }
|
||||
NOINLINE void* operator new[](size_t n) { return p2pool::allocate(n); }
|
||||
NOINLINE void* operator new(size_t n, const std::nothrow_t&) noexcept { return p2pool::allocate_noexcept(n); }
|
||||
NOINLINE void* operator new[](size_t n, const std::nothrow_t&) noexcept { return p2pool::allocate_noexcept(n); }
|
||||
NOINLINE void operator delete(void* p) noexcept { p2pool::deallocate(p); }
|
||||
NOINLINE void operator delete[](void* p) noexcept { p2pool::deallocate(p); }
|
||||
NOINLINE void operator delete(void* p, size_t) noexcept { p2pool::deallocate(p); }
|
||||
NOINLINE void operator delete[](void* p, size_t) noexcept { p2pool::deallocate(p); }
|
||||
NOINLINE void* operator new(size_t n, const std::nothrow_t&) noexcept { return p2pool::malloc_hook(n); }
|
||||
NOINLINE void* operator new[](size_t n, const std::nothrow_t&) noexcept { return p2pool::malloc_hook(n); }
|
||||
NOINLINE void operator delete(void* p) noexcept { p2pool::free_hook(p); }
|
||||
NOINLINE void operator delete[](void* p) noexcept { p2pool::free_hook(p); }
|
||||
NOINLINE void operator delete(void* p, size_t) noexcept { p2pool::free_hook(p); }
|
||||
NOINLINE void operator delete[](void* p, size_t) noexcept { p2pool::free_hook(p); }
|
||||
|
||||
#else
|
||||
void memory_tracking_start() {}
|
||||
void memory_tracking_stop() {}
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
void* malloc_hook(size_t n) noexcept { return malloc(n); }
|
||||
void* realloc_hook(void* ptr, size_t size) noexcept { return realloc(ptr, size); }
|
||||
void* calloc_hook(size_t count, size_t size) noexcept { return calloc(count, size); }
|
||||
void free_hook(void* p) noexcept { free(p); }
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
+1
-2
@@ -18,7 +18,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "uv_util.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
@@ -35,7 +34,7 @@ public:
|
||||
|
||||
public:
|
||||
mutable uv_rwlock_t m_lock;
|
||||
std::unordered_map<hash, TxMempoolData> m_transactions;
|
||||
unordered_map<hash, TxMempoolData> m_transactions;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+79
-20
@@ -28,9 +28,8 @@
|
||||
|
||||
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;
|
||||
@@ -53,6 +52,9 @@ P2PServer::P2PServer(p2pool* pool)
|
||||
, m_peerId(m_rng())
|
||||
, m_peerListLastSaved(0)
|
||||
{
|
||||
set_max_outgoing_peers(pool->params().m_maxOutgoingPeers);
|
||||
set_max_incoming_peers(pool->params().m_maxIncomingPeers);
|
||||
|
||||
uv_mutex_init_checked(&m_rngLock);
|
||||
uv_mutex_init_checked(&m_blockLock);
|
||||
uv_mutex_init_checked(&m_peerListLock);
|
||||
@@ -94,6 +96,7 @@ P2PServer::P2PServer(p2pool* pool)
|
||||
P2PServer::~P2PServer()
|
||||
{
|
||||
uv_timer_stop(&m_timer);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_broadcastAsync), nullptr);
|
||||
|
||||
shutdown_tcp();
|
||||
@@ -225,8 +228,8 @@ void P2PServer::update_peer_connections()
|
||||
peer_list = m_peerList;
|
||||
}
|
||||
|
||||
// Try to have at least 8 outgoing connections
|
||||
for (uint32_t i = m_numConnections - m_numIncomingConnections; (i < 8) && !peer_list.empty();) {
|
||||
// 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 < m_maxOutgoingPeers) && !peer_list.empty();) {
|
||||
const uint64_t k = get_random64() % peer_list.size();
|
||||
const Peer& peer = peer_list[k];
|
||||
|
||||
@@ -277,6 +280,7 @@ void P2PServer::update_peer_list()
|
||||
});
|
||||
|
||||
if (result) {
|
||||
client->m_lastPeerListRequestTime = std::chrono::system_clock::now();
|
||||
++client->m_peerListPendingRequests;
|
||||
}
|
||||
}
|
||||
@@ -314,7 +318,7 @@ void P2PServer::save_peer_list_async()
|
||||
});
|
||||
|
||||
if (err) {
|
||||
LOGERR(1, "update_peer_list: uv_queue_work failed, error " << uv_err_name(err));
|
||||
LOGERR(1, "save_peer_list_async: uv_queue_work failed, error " << uv_err_name(err));
|
||||
delete work;
|
||||
}
|
||||
}
|
||||
@@ -366,12 +370,10 @@ void P2PServer::load_peer_list()
|
||||
{
|
||||
std::string saved_list;
|
||||
|
||||
// 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;
|
||||
@@ -379,7 +381,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;
|
||||
@@ -402,7 +404,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 += ',';
|
||||
}
|
||||
@@ -412,9 +414,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
|
||||
@@ -686,12 +695,39 @@ uint64_t P2PServer::get_random64()
|
||||
|
||||
void P2PServer::print_status()
|
||||
{
|
||||
const int64_t uptime = time(nullptr) - m_pool->start_time();
|
||||
|
||||
const int64_t s = uptime % 60;
|
||||
const int64_t m = (uptime / 60) % 60;
|
||||
const int64_t h = (uptime / 3600) % 24;
|
||||
const int64_t d = uptime / 86400;
|
||||
|
||||
char buf[log::Stream::BUF_SIZE + 1];
|
||||
log::Stream s1(buf);
|
||||
|
||||
if (d > 0) {
|
||||
s1 << d << "d ";
|
||||
}
|
||||
s1 << h << "h " << m << "m " << s << 's';
|
||||
|
||||
LOGINFO(0, "status" <<
|
||||
"\nConnections = " << m_numConnections << " (" << m_numIncomingConnections << " incoming)" <<
|
||||
"\nPeer list size = " << m_peerList.size()
|
||||
"\nPeer list size = " << m_peerList.size() <<
|
||||
"\nUptime = " << log::const_buf(buf, s1.m_pos)
|
||||
);
|
||||
}
|
||||
|
||||
void P2PServer::show_peers()
|
||||
{
|
||||
MutexLock lock(m_clientsListLock);
|
||||
|
||||
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
if (client->m_listenPort >= 0) {
|
||||
LOGINFO(0, (client->m_isIncoming ? "I " : "O ") << client->m_pingTime << " ms\t" << static_cast<char*>(client->m_addrString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::on_timer()
|
||||
{
|
||||
++m_timerCounter;
|
||||
@@ -711,7 +747,7 @@ void P2PServer::on_timer()
|
||||
|
||||
void P2PServer::flush_cache()
|
||||
{
|
||||
if (!m_cache) {
|
||||
if (!m_cache || ((m_timerCounter % 30) != 2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -831,7 +867,9 @@ P2PServer::P2PClient::P2PClient()
|
||||
, m_handshakeInvalid(false)
|
||||
, m_listenPort(-1)
|
||||
, m_nextPeerListRequest(0)
|
||||
, m_lastPeerListRequestTime{}
|
||||
, m_peerListPendingRequests(0)
|
||||
, m_pingTime(0)
|
||||
, m_lastAlive(0)
|
||||
, m_lastBroadcastTimestamp(0)
|
||||
, m_lastBlockrequestTimestamp(0)
|
||||
@@ -855,7 +893,9 @@ void P2PServer::P2PClient::reset()
|
||||
m_handshakeInvalid = false;
|
||||
m_listenPort = -1;
|
||||
m_nextPeerListRequest = 0;
|
||||
m_lastPeerListRequestTime = {};
|
||||
m_peerListPendingRequests = 0;
|
||||
m_pingTime = 0;
|
||||
m_lastAlive = 0;
|
||||
m_lastBroadcastTimestamp = 0;
|
||||
m_lastBlockrequestTimestamp = 0;
|
||||
@@ -870,6 +910,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)) {
|
||||
@@ -1041,7 +1090,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent PEER_LIST_RESPONSE");
|
||||
|
||||
if (bytes_left >= 2) {
|
||||
const uint8_t num_peers = buf[1];
|
||||
const uint32_t num_peers = buf[1];
|
||||
if (num_peers > PEER_LIST_RESPONSE_MAX_PEERS) {
|
||||
LOGWARN(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent too long peer list (" << num_peers << ')');
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
@@ -1049,8 +1098,11 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bytes_left >= 2u + num_peers * 19) {
|
||||
bytes_read = 2u + num_peers * 19;
|
||||
if (bytes_left >= 2u + num_peers * 19u) {
|
||||
bytes_read = 2u + num_peers * 19u;
|
||||
|
||||
using namespace std::chrono;
|
||||
m_pingTime = duration_cast<milliseconds>(system_clock::now() - m_lastPeerListRequestTime).count();
|
||||
|
||||
--m_peerListPendingRequests;
|
||||
if (!on_peer_list_response(buf + 1)) {
|
||||
@@ -1609,6 +1661,13 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
||||
memcpy(ip.data, buf, sizeof(ip.data));
|
||||
buf += sizeof(ip.data);
|
||||
|
||||
// Fill in default bytes for IPv4 addresses
|
||||
if (!is_v6) {
|
||||
memset(ip.data, 0, 10);
|
||||
ip.data[10] = 0xFF;
|
||||
ip.data[11] = 0xFF;
|
||||
}
|
||||
|
||||
int port = 0;
|
||||
memcpy(&port, buf, 2);
|
||||
buf += 2;
|
||||
|
||||
+17
-3
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "tcp_server.h"
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
@@ -30,6 +29,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,8 +109,12 @@ public:
|
||||
bool m_handshakeComplete;
|
||||
bool m_handshakeInvalid;
|
||||
int m_listenPort;
|
||||
|
||||
time_t m_nextPeerListRequest;
|
||||
std::chrono::system_clock::time_point m_lastPeerListRequestTime;
|
||||
int m_peerListPendingRequests;
|
||||
int64_t m_pingTime;
|
||||
|
||||
time_t m_lastAlive;
|
||||
time_t m_lastBroadcastTimestamp;
|
||||
time_t m_lastBlockrequestTimestamp;
|
||||
@@ -124,15 +128,25 @@ public:
|
||||
uint64_t get_peerId() const { return m_peerId; }
|
||||
|
||||
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;
|
||||
std::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(); }
|
||||
@@ -188,7 +202,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();
|
||||
|
||||
+103
-13
@@ -49,6 +49,8 @@ p2pool::p2pool(int argc, char* argv[])
|
||||
, m_params(new Params(argc, argv))
|
||||
, m_updateSeed(true)
|
||||
, m_submitBlockData{}
|
||||
, m_zmqLastActive(0)
|
||||
, m_startTime(time(nullptr))
|
||||
{
|
||||
LOGINFO(1, log::LightCyan() << VERSION);
|
||||
|
||||
@@ -108,7 +110,24 @@ p2pool::p2pool(int argc, char* argv[])
|
||||
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_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;
|
||||
}
|
||||
|
||||
if (m_params->m_disableRandomX) {
|
||||
m_hasher = new RandomX_Hasher_RPC(this);
|
||||
}
|
||||
else {
|
||||
m_hasher = new RandomX_Hasher(this);
|
||||
}
|
||||
|
||||
m_blockTemplate = new BlockTemplate(this);
|
||||
m_mempool = new Mempool();
|
||||
m_consoleCommands = new ConsoleCommands(this);
|
||||
@@ -129,9 +148,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)
|
||||
@@ -201,6 +220,8 @@ void p2pool::handle_miner_data(MinerData& data)
|
||||
c.reward = 0;
|
||||
|
||||
m_mainchainByHash[c.id] = c;
|
||||
|
||||
cleanup_mainchain_data(data.height);
|
||||
}
|
||||
|
||||
data.tx_backlog.clear();
|
||||
@@ -222,7 +243,7 @@ void p2pool::handle_miner_data(MinerData& data)
|
||||
"\n---------------------------------------------------------------------------------------------------------------"
|
||||
);
|
||||
|
||||
if (!is_main_thread) {
|
||||
if (!is_main_thread()) {
|
||||
update_block_template_async();
|
||||
}
|
||||
else {
|
||||
@@ -230,6 +251,41 @@ void p2pool::handle_miner_data(MinerData& data)
|
||||
}
|
||||
|
||||
m_zmqLastActive = time(nullptr);
|
||||
|
||||
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\
|
||||
@@ -531,6 +587,7 @@ void p2pool::download_block_headers(uint64_t current_height)
|
||||
if (parse_block_headers_range(data, size) == BLOCK_HEADERS_REQUIRED) {
|
||||
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);
|
||||
api_update_network_stats();
|
||||
@@ -686,18 +743,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;
|
||||
@@ -767,10 +828,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();
|
||||
}
|
||||
|
||||
@@ -969,7 +1034,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;
|
||||
@@ -1032,7 +1097,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();
|
||||
@@ -1061,6 +1126,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();
|
||||
@@ -1216,11 +1306,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();
|
||||
|
||||
+16
-10
@@ -19,12 +19,11 @@
|
||||
|
||||
#include "uv_util.h"
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
struct Params;
|
||||
class RandomX_Hasher;
|
||||
class RandomX_Hasher_Base;
|
||||
class BlockTemplate;
|
||||
class Mempool;
|
||||
class SideChain;
|
||||
@@ -32,6 +31,7 @@ class StratumServer;
|
||||
class P2PServer;
|
||||
class ConsoleCommands;
|
||||
class p2pool_api;
|
||||
class ZMQReader;
|
||||
|
||||
class p2pool : public MinerCallbackHandler
|
||||
{
|
||||
@@ -51,8 +51,8 @@ public:
|
||||
|
||||
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;
|
||||
|
||||
@@ -79,6 +79,7 @@ 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; }
|
||||
|
||||
private:
|
||||
p2pool(const p2pool&) = delete;
|
||||
@@ -96,7 +97,7 @@ 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;
|
||||
@@ -104,7 +105,7 @@ private:
|
||||
|
||||
mutable uv_rwlock_t m_mainchainLock;
|
||||
std::map<uint64_t, ChainMain> m_mainchainByHeight;
|
||||
std::unordered_map<hash, ChainMain> m_mainchainByHash;
|
||||
unordered_map<hash, ChainMain> m_mainchainByHash;
|
||||
|
||||
enum { TIMESTAMP_WINDOW = 60 };
|
||||
bool get_timestamps(uint64_t (×tamps)[TIMESTAMP_WINDOW]) const;
|
||||
@@ -129,6 +130,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)
|
||||
@@ -157,9 +160,9 @@ private:
|
||||
|
||||
struct SubmitBlockData
|
||||
{
|
||||
uint32_t template_id;
|
||||
uint32_t nonce;
|
||||
uint32_t extra_nonce;
|
||||
uint32_t template_id = 0;
|
||||
uint32_t nonce = 0;
|
||||
uint32_t extra_nonce = 0;
|
||||
std::vector<uint8_t> blob;
|
||||
};
|
||||
|
||||
@@ -170,7 +173,10 @@ private:
|
||||
uv_async_t m_blockTemplateAsync;
|
||||
uv_async_t m_stopAsync;
|
||||
|
||||
time_t m_zmqLastActive = 0;
|
||||
time_t m_zmqLastActive;
|
||||
time_t m_startTime;
|
||||
|
||||
ZMQReader* m_ZMQReader = nullptr;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+1
-1
@@ -127,7 +127,7 @@ void p2pool_api::dump_to_file_async_internal(const Category& category, const cha
|
||||
|
||||
void p2pool_api::dump_to_file()
|
||||
{
|
||||
std::unordered_map<std::string, std::vector<char>> data;
|
||||
unordered_map<std::string, std::vector<char>> data;
|
||||
{
|
||||
MutexLock lock(m_dumpDataLock);
|
||||
data = std::move(m_dumpData);
|
||||
|
||||
+1
-2
@@ -18,7 +18,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "uv_util.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
@@ -84,7 +83,7 @@ private:
|
||||
std::string m_localPath;
|
||||
|
||||
uv_mutex_t m_dumpDataLock;
|
||||
std::unordered_map<std::string, std::vector<char>> m_dumpData;
|
||||
unordered_map<std::string, std::vector<char>> m_dumpData;
|
||||
|
||||
uv_async_t m_dumpToFileAsync;
|
||||
};
|
||||
|
||||
+19
-13
@@ -30,11 +30,11 @@ Params::Params(int argc, char* argv[])
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--light-mode") == 0) {
|
||||
@@ -58,7 +58,7 @@ Params::Params(int argc, char* argv[])
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,22 @@ Params::Params(int argc, char* argv[])
|
||||
if (strcmp(argv[i], "--no-cache") == 0) {
|
||||
m_blockCache = false;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--no-color") == 0) {
|
||||
log::CONSOLE_COLORS = false;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--no-randomx") == 0) {
|
||||
m_disableRandomX = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--out-peers") == 0) && (i + 1 < argc)) {
|
||||
m_maxOutgoingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 1000UL);
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--in-peers") == 0) && (i + 1 < argc)) {
|
||||
m_maxIncomingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 1000UL);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_stratumAddresses.empty()) {
|
||||
@@ -88,16 +104,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
|
||||
|
||||
@@ -39,6 +39,9 @@ struct Params
|
||||
std::string m_apiPath;
|
||||
bool m_localStats = false;
|
||||
bool m_blockCache = true;
|
||||
bool m_disableRandomX = false;
|
||||
uint32_t m_maxOutgoingPeers = 10;
|
||||
uint32_t m_maxIncomingPeers = 1000;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+7
-9
@@ -60,7 +60,6 @@ PoolBlock::PoolBlock()
|
||||
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,7 +109,6 @@ 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;
|
||||
@@ -227,7 +225,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 +274,28 @@ 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);
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+2
-3
@@ -28,7 +28,7 @@
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
class RandomX_Hasher;
|
||||
class RandomX_Hasher_Base;
|
||||
class SideChain;
|
||||
|
||||
/*
|
||||
@@ -121,7 +121,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;
|
||||
|
||||
@@ -137,7 +136,7 @@ struct PoolBlock
|
||||
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);
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
@@ -341,7 +341,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;
|
||||
|
||||
|
||||
+113
-1
@@ -22,6 +22,9 @@
|
||||
#include "randomx.h"
|
||||
#include "configuration.h"
|
||||
#include "virtual_machine.hpp"
|
||||
#include "json_rpc_request.h"
|
||||
#include "json_parsers.h"
|
||||
#include <rapidjson/document.h>
|
||||
#include <thread>
|
||||
|
||||
static constexpr char log_category_prefix[] = "RandomX_Hasher ";
|
||||
@@ -302,7 +305,7 @@ 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)
|
||||
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) {
|
||||
@@ -348,4 +351,113 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, const hash& seed,
|
||||
return false;
|
||||
}
|
||||
|
||||
RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
|
||||
: m_pool(pool)
|
||||
, m_loopStopped(false)
|
||||
, 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);
|
||||
|
||||
using namespace std::chrono;
|
||||
while (!m_loopStopped) {
|
||||
std::this_thread::sleep_for(milliseconds(1));
|
||||
}
|
||||
|
||||
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");
|
||||
hasher->m_loopStopped = true;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
+50
-7
@@ -27,25 +27,36 @@ 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 bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) = 0;
|
||||
};
|
||||
|
||||
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);
|
||||
bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) override;
|
||||
|
||||
private:
|
||||
|
||||
struct ThreadSafeVM
|
||||
{
|
||||
uv_mutex_t mutex;
|
||||
randomx_vm* vm;
|
||||
uv_mutex_t mutex{};
|
||||
randomx_vm* vm = nullptr;
|
||||
};
|
||||
|
||||
p2pool* m_pool;
|
||||
@@ -62,7 +73,7 @@ private:
|
||||
// 1: light VM for the previous seed
|
||||
// 2: full dataset VM for the current seed
|
||||
enum { FULL_DATASET_VM = 2 };
|
||||
ThreadSafeVM m_vm[3];
|
||||
ThreadSafeVM m_vm[3]{};
|
||||
|
||||
hash m_seed[2];
|
||||
uint32_t m_index;
|
||||
@@ -70,4 +81,36 @@ private:
|
||||
std::atomic<uint32_t> m_setSeedCounter;
|
||||
};
|
||||
|
||||
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;
|
||||
volatile bool m_loopStopped;
|
||||
|
||||
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
|
||||
|
||||
+83
-45
@@ -51,9 +51,8 @@ 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)
|
||||
@@ -77,8 +76,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,11 +96,16 @@ 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 {
|
||||
const randomx_flags flags = randomx_get_flags();
|
||||
randomx_cache* cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
|
||||
@@ -147,6 +154,7 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
|
||||
SideChain::~SideChain()
|
||||
{
|
||||
uv_mutex_destroy(&m_sidechainLock);
|
||||
uv_mutex_destroy(&m_seenBlocksLock);
|
||||
for (auto& it : m_blocksById) {
|
||||
delete it.second;
|
||||
}
|
||||
@@ -259,6 +267,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 +356,21 @@ 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 +383,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 +391,25 @@ 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;
|
||||
|
||||
for (PoolBlock* tmp = m_chainTip; tmp && (tmp->m_sidechainHeight + m_chainWindowSize > m_chainTip->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,7 +433,7 @@ 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;
|
||||
@@ -486,8 +501,8 @@ void SideChain::add_block(const PoolBlock& 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 (p2pServer()) {
|
||||
p2pServer()->store_in_cache(block);
|
||||
}
|
||||
|
||||
PoolBlock* new_block = new PoolBlock(block);
|
||||
@@ -564,21 +579,34 @@ 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 * 38 + 64);
|
||||
writeVarint(n, blob);
|
||||
|
||||
for (const PoolBlock::TxOutput& output : b->m_outputs) {
|
||||
writeVarint(output.m_reward, blob);
|
||||
blob.emplace_back(TXOUT_TO_KEY);
|
||||
blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
writeVarint(n, blob);
|
||||
@@ -588,16 +616,16 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
|
||||
|
||||
hash eph_public_key;
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
writeVarint(rewards[i], blob);
|
||||
writeVarint(m_tmpRewards[i], blob);
|
||||
|
||||
blob.emplace_back(TXOUT_TO_KEY);
|
||||
|
||||
if (!shares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key)) {
|
||||
if (!m_tmpShares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key)) {
|
||||
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);
|
||||
block->m_outputs.emplace_back(m_tmpRewards[i], eph_public_key);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -738,9 +766,9 @@ uint64_t SideChain::miner_count()
|
||||
|
||||
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 {
|
||||
@@ -761,6 +789,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();
|
||||
@@ -954,14 +987,14 @@ void SideChain::verify_loop(PoolBlock* block)
|
||||
// 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 (p2pServer() && (block->m_depth < UNCLE_BLOCK_DEPTH)) {
|
||||
p2pServer()->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 (p2pServer()) {
|
||||
p2pServer()->store_in_cache(*block);
|
||||
}
|
||||
|
||||
// Try to verify blocks on top of this one
|
||||
@@ -1297,7 +1330,9 @@ void SideChain::update_chain_tip(PoolBlock* block)
|
||||
", main chain height = " << log::Gray() << m_chainTip->m_txinGenHeight);
|
||||
|
||||
block->m_wantBroadcast = true;
|
||||
m_pool->update_block_template_async();
|
||||
if (m_pool) {
|
||||
m_pool->update_block_template_async();
|
||||
}
|
||||
prune_old_blocks();
|
||||
}
|
||||
}
|
||||
@@ -1313,7 +1348,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
|
||||
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 +1359,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 +1378,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
|
||||
}
|
||||
}
|
||||
@@ -1539,6 +1574,7 @@ void SideChain::prune_old_blocks()
|
||||
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 +1599,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-5
@@ -19,12 +19,11 @@
|
||||
|
||||
#include "uv_util.h"
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
class p2pool;
|
||||
class P2PServer;
|
||||
struct DifficultyData;
|
||||
struct PoolBlock;
|
||||
class Wallet;
|
||||
@@ -72,11 +71,15 @@ public:
|
||||
uint64_t miner_count();
|
||||
time_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:
|
||||
@@ -98,9 +101,13 @@ private:
|
||||
mutable uv_mutex_t m_sidechainLock;
|
||||
PoolBlock* m_chainTip;
|
||||
std::map<uint64_t, std::vector<PoolBlock*>> m_blocksByHeight;
|
||||
std::unordered_map<hash, PoolBlock*> m_blocksById;
|
||||
std::unordered_set<hash> m_seenBlocks;
|
||||
std::unordered_map<hash, time_t> m_seenWallets;
|
||||
unordered_map<hash, PoolBlock*> m_blocksById;
|
||||
unordered_map<hash, time_t> m_seenWallets;
|
||||
std::vector<MinerShare> m_tmpShares;
|
||||
std::vector<uint64_t> m_tmpRewards;
|
||||
|
||||
uv_mutex_t m_seenBlocksLock;
|
||||
unordered_set<hash> m_seenBlocks;
|
||||
|
||||
std::vector<DifficultyData> m_difficultyData;
|
||||
|
||||
|
||||
+44
-6
@@ -176,6 +176,28 @@ void StratumServer::on_block(const BlockTemplate& block)
|
||||
}
|
||||
}
|
||||
|
||||
bool StratumServer::get_custom_user(const char* s, std::string& user)
|
||||
{
|
||||
user.clear();
|
||||
// Find first of '+' or '.', drop non-printable characters
|
||||
while (s && (user.length() < 64)) {
|
||||
const char c = *s;
|
||||
if (!c) {
|
||||
break;
|
||||
}
|
||||
if ((c == '+') || (c == '.')) {
|
||||
break;
|
||||
}
|
||||
// Limit to printable ASCII characters
|
||||
if (c >= ' ' && c <= '~') {
|
||||
user += c;
|
||||
}
|
||||
++s;
|
||||
}
|
||||
|
||||
return !user.empty();
|
||||
}
|
||||
|
||||
bool StratumServer::get_custom_diff(const char* s, difficulty_type& diff)
|
||||
{
|
||||
const char* diff_str = nullptr;
|
||||
@@ -225,6 +247,10 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
|
||||
target = std::max(target, client->m_customDiff.target());
|
||||
}
|
||||
|
||||
if (get_custom_user(login, client->m_customUser)) {
|
||||
LOGINFO(5, "client " << log::Gray() << static_cast<char*>(client->m_addrString) << " set custom user " << client->m_customUser);
|
||||
}
|
||||
|
||||
uint32_t job_id;
|
||||
{
|
||||
MutexLock lock(client->m_jobsLock);
|
||||
@@ -334,7 +360,8 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
}
|
||||
|
||||
if (mainchain_diff.check_pow(resultHash)) {
|
||||
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << " found a mainchain block, submitting it");
|
||||
const std::string& s = client->m_customUser;
|
||||
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (!s.empty() ? " user " : "") << s << " found a mainchain block, submitting it");
|
||||
m_pool->submit_block_async(template_id, nonce, extra_nonce);
|
||||
block.update_tx_keys();
|
||||
}
|
||||
@@ -367,12 +394,21 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
share->m_resultHash = resultHash;
|
||||
share->m_sidechainDifficulty = sidechain_diff;
|
||||
|
||||
// If this share is below sidechain difficulty, process it in this thread because it'll be quick
|
||||
if (!share->m_sidechainDifficulty.check_pow(share->m_resultHash)) {
|
||||
on_share_found(&share->m_req);
|
||||
on_after_share_found(&share->m_req, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Else switch to a worker thread to check PoW which can take a long time
|
||||
const int err = uv_queue_work(&m_loop, &share->m_req, on_share_found, on_after_share_found);
|
||||
if (err) {
|
||||
LOGERR(1, "uv_queue_work failed, error " << uv_err_name(err));
|
||||
MutexLock lock(m_submittedSharesPoolLock);
|
||||
m_submittedSharesPool.push_back(share);
|
||||
return false;
|
||||
|
||||
// If uv_queue_work failed, process this share here anyway
|
||||
on_share_found(&share->m_req);
|
||||
on_after_share_found(&share->m_req, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -625,7 +661,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;
|
||||
@@ -645,7 +681,8 @@ void StratumServer::on_share_found(uv_work_t* req)
|
||||
server->m_cumulativeFoundSharesDiff += diff;
|
||||
++server->m_totalFoundShares;
|
||||
|
||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", effort " << effort << '%');
|
||||
const std::string& s = client->m_customUser;
|
||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", client " << static_cast<char*>(client->m_addrString) << (!s.empty() ? " user " : "") << s << ", effort " << effort << '%');
|
||||
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
|
||||
}
|
||||
|
||||
@@ -742,6 +779,7 @@ void StratumServer::StratumClient::reset()
|
||||
memset(m_jobs, 0, sizeof(m_jobs));
|
||||
m_perConnectionJobId = 0;
|
||||
m_customDiff = {};
|
||||
m_customUser.clear();
|
||||
}
|
||||
|
||||
bool StratumServer::StratumClient::on_read(char* data, uint32_t size)
|
||||
|
||||
@@ -65,6 +65,7 @@ public:
|
||||
|
||||
uint32_t m_perConnectionJobId;
|
||||
difficulty_type m_customDiff;
|
||||
std::string m_customUser;
|
||||
};
|
||||
|
||||
bool on_login(StratumClient* client, uint32_t id, const char* login);
|
||||
@@ -77,6 +78,7 @@ private:
|
||||
void print_stratum_status() const;
|
||||
|
||||
static bool get_custom_diff(const char* s, difficulty_type& diff);
|
||||
static bool get_custom_user(const char* s, std::string& user);
|
||||
|
||||
static void on_share_found(uv_work_t* req);
|
||||
static void on_after_share_found(uv_work_t* req, int status);
|
||||
|
||||
+12
-45
@@ -18,8 +18,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "uv_util.h"
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
@@ -46,36 +44,6 @@ 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);
|
||||
|
||||
@@ -108,7 +76,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 +88,21 @@ 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;
|
||||
uv_write_t m_write;
|
||||
char m_data[WRITE_BUF_SIZE];
|
||||
};
|
||||
|
||||
uv_mutex_t m_writeBuffersLock;
|
||||
std::vector<WriteBuf*> m_writeBuffers;
|
||||
|
||||
struct SendCallbackBase
|
||||
{
|
||||
virtual ~SendCallbackBase() {}
|
||||
@@ -193,12 +160,12 @@ protected:
|
||||
uint32_t m_numIncomingConnections;
|
||||
|
||||
uv_mutex_t m_bansLock;
|
||||
std::map<raw_ip, time_t> m_bans;
|
||||
unordered_map<raw_ip, time_t> 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); }
|
||||
|
||||
+36
-31
@@ -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) {
|
||||
@@ -308,7 +314,6 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(bool is_v6, const
|
||||
return connect_to_peer_nolock(client, is_v6, reinterpret_cast<sockaddr*>(&addr));
|
||||
}
|
||||
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connect_failed(bool, const raw_ip&, int)
|
||||
{
|
||||
@@ -320,8 +325,12 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::is_banned(const raw_ip& ip)
|
||||
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 = (time(nullptr) < it->second);
|
||||
if (!banned) {
|
||||
m_bans.erase(it);
|
||||
}
|
||||
return banned;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -462,6 +471,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");
|
||||
}
|
||||
|
||||
@@ -489,18 +506,18 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
|
||||
|
||||
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);
|
||||
@@ -513,8 +530,8 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
|
||||
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;
|
||||
}
|
||||
@@ -529,8 +546,8 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
|
||||
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;
|
||||
@@ -547,6 +564,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;
|
||||
}
|
||||
|
||||
@@ -761,27 +779,14 @@ 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);
|
||||
}
|
||||
|
||||
@@ -794,7 +799,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;
|
||||
@@ -858,12 +862,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) {
|
||||
|
||||
+6
-3
@@ -32,7 +32,7 @@ namespace p2pool {
|
||||
#define STR2(X) STR(X)
|
||||
#define STR(X) #X
|
||||
|
||||
const char* VERSION = "v1.2 (built"
|
||||
const char* VERSION = "v1.5 (built"
|
||||
#if defined(__clang__)
|
||||
" with clang/" __clang_version__
|
||||
#elif defined(__GNUC__)
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
+47
-13
@@ -17,6 +17,21 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4623 5026 5027)
|
||||
#endif
|
||||
|
||||
#define ROBIN_HOOD_MALLOC(size) p2pool::malloc_hook(size)
|
||||
#define ROBIN_HOOD_CALLOC(count, size) p2pool::calloc_hook((count), (size))
|
||||
#define ROBIN_HOOD_FREE(ptr) p2pool::free_hook(ptr)
|
||||
|
||||
#include "robin_hood.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
extern const char* VERSION;
|
||||
@@ -118,24 +133,28 @@ 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);
|
||||
|
||||
template <typename Key, typename T>
|
||||
using unordered_map = robin_hood::detail::Table<false, 80, Key, T, robin_hood::hash<Key>, std::equal_to<Key>>;
|
||||
|
||||
template <typename Key>
|
||||
using unordered_set = robin_hood::detail::Table<false, 80, Key, void, robin_hood::hash<Key>, std::equal_to<Key>>;
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
namespace std {
|
||||
namespace robin_hood {
|
||||
|
||||
template<>
|
||||
struct hash<p2pool::hash>
|
||||
{
|
||||
FORCEINLINE size_t operator()(const p2pool::hash& value) const noexcept
|
||||
{
|
||||
uint64_t result = 0xcbf29ce484222325ull;
|
||||
for (size_t i = 0; i < p2pool::HASH_SIZE; ++i) {
|
||||
result = (result ^ value.h[i]) * 0x100000001b3ull;
|
||||
}
|
||||
return static_cast<size_t>(result);
|
||||
return hash_bytes(value.h, p2pool::HASH_SIZE);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -144,12 +163,27 @@ struct hash<std::array<uint8_t, N>>
|
||||
{
|
||||
FORCEINLINE size_t operator()(const std::array<uint8_t, N>& value) const noexcept
|
||||
{
|
||||
uint64_t result = 0xcbf29ce484222325ull;
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
result = (result ^ value[i]) * 0x100000001b3ull;
|
||||
}
|
||||
return static_cast<size_t>(result);
|
||||
return hash_bytes(value.data(), N);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
+31
-11
@@ -19,6 +19,7 @@
|
||||
#include "zmq_reader.h"
|
||||
#include "json_parsers.h"
|
||||
#include <rapidjson/document.h>
|
||||
#include <random>
|
||||
|
||||
static constexpr char log_category_prefix[] = "ZMQReader ";
|
||||
|
||||
@@ -76,19 +77,25 @@ ZMQReader::~ZMQReader()
|
||||
}
|
||||
}
|
||||
|
||||
void ZMQReader::run_wrapper(void* arg)
|
||||
{
|
||||
reinterpret_cast<ZMQReader*>(arg)->run();
|
||||
LOGINFO(1, "worker thread stopped");
|
||||
}
|
||||
|
||||
void ZMQReader::run()
|
||||
{
|
||||
try {
|
||||
char addr[32];
|
||||
|
||||
snprintf(addr, sizeof(addr), "tcp://%s:%u", m_address, m_zmqPort);
|
||||
if (!connect(addr, m_zmqPort)) {
|
||||
throw zmq::error_t();
|
||||
if (!connect(addr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(addr, sizeof(addr), "tcp://127.0.0.1:%u", m_publisherPort);
|
||||
if (!connect(addr, m_publisherPort)) {
|
||||
throw zmq::error_t();
|
||||
if (!connect(addr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_subscriber.set(zmq::sockopt::subscribe, "json-full-chain_main");
|
||||
@@ -122,10 +129,9 @@ void ZMQReader::run()
|
||||
LOGERR(1, "exception " << e.what() << ", aborting");
|
||||
panic();
|
||||
}
|
||||
LOGINFO(1, "worker thread stopped");
|
||||
}
|
||||
|
||||
bool ZMQReader::connect(const char* address, uint32_t id)
|
||||
bool ZMQReader::connect(const char* address)
|
||||
{
|
||||
struct ConnectMonitor : public zmq::monitor_t
|
||||
{
|
||||
@@ -138,19 +144,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();
|
||||
system_clock::time_point start_time = system_clock::now();
|
||||
|
||||
while (!monitor.connected && monitor.check_event(-1)) {
|
||||
const int64_t elapsed_time = duration_cast<milliseconds>(system_clock::now() - start_time).count();
|
||||
const system_clock::time_point cur_time = system_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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -28,9 +28,9 @@ public:
|
||||
~ZMQReader();
|
||||
|
||||
private:
|
||||
static void run_wrapper(void* arg) { reinterpret_cast<ZMQReader*>(arg)->run(); }
|
||||
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);
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ set(SOURCES
|
||||
../src/json_rpc_request.cpp
|
||||
../src/keccak.cpp
|
||||
../src/log.cpp
|
||||
../src/memory_leak_debug.cpp
|
||||
../src/mempool.cpp
|
||||
../src/p2p_server.cpp
|
||||
../src/p2pool.cpp
|
||||
@@ -107,6 +108,7 @@ include_directories(../external/src/libzmq/include)
|
||||
include_directories(../external/src/llhttp)
|
||||
include_directories(../external/src/RandomX/src)
|
||||
include_directories(../external/src/rapidjson/include)
|
||||
include_directories(../external/src/robin-hood-hashing/src/include)
|
||||
include_directories(src)
|
||||
include_directories(googletest/googletest/include)
|
||||
|
||||
@@ -150,3 +152,4 @@ 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}>)
|
||||
|
||||
@@ -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.has_block(b.m_sidechainId));
|
||||
}
|
||||
|
||||
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.
Reference in New Issue
Block a user