Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cf184295b4 | |||
| 0cd9876a46 | |||
| 1e4e4bd08e | |||
| a640e41d74 | |||
| f90e00edea | |||
| 38ffd73b54 | |||
| c10a5ce323 | |||
| e2931f94b5 | |||
| 95c268be4c | |||
| 4ac4f8f860 | |||
| 248fcee174 | |||
| 89bcceb986 | |||
| 2b01350add | |||
| f1b6212c82 | |||
| 7d961ab3b9 | |||
| 862e7d965b | |||
| da2d9f7e2e | |||
| d8bba4dc82 | |||
| 3a8a24a5ee | |||
| eaf29872cb | |||
| 74f553274f | |||
| d0f1e6eb2e | |||
| f9def6cb73 | |||
| 2f659c8ecf | |||
| 2d559ca764 | |||
| 1328ea6aef | |||
| 07cfb54bdc | |||
| 5dd8d11b72 | |||
| dbab191bfc | |||
| e44baad73c | |||
| bb7a5b4d6d | |||
| 64116643c2 | |||
| 1e4c6ef5f4 | |||
| 61edc6ed70 | |||
| d283f91eb0 | |||
| dff2413cae | |||
| 998c2ba72f | |||
| b56d027dd5 | |||
| eaed654012 | |||
| 981b161cc9 | |||
| f59e9171e8 | |||
| b45540ca08 | |||
| 806e1ca0a3 | |||
| 113e39817d | |||
| 0869326f39 | |||
| 75d62c0303 | |||
| da4686b88f | |||
| a008eac8c6 | |||
| 3d60ae8c32 | |||
| 88f346414a | |||
| 2071a24a9f | |||
| 1c0da4513f | |||
| 6a002cda46 | |||
| c21c9615a3 | |||
| c0c210664a | |||
| 6859d11445 | |||
| 111324b6e0 | |||
| 148b9dd294 | |||
| 04d18cdf1d | |||
| 352ad81a0a | |||
| d26902468d | |||
| 0382027d70 | |||
| 966b499e5c | |||
| 9885f82e9e | |||
| 32a58d1534 | |||
| bc341918f6 | |||
| f17c23528a | |||
| a06f4dcdcd | |||
| 3bc03e4801 | |||
| 816a29c5ab | |||
| 285560e120 | |||
| 39ba5c4131 | |||
| 9b86f8e81f | |||
| 9e90e988fa | |||
| 39b2167c98 | |||
| f3024d3556 | |||
| 61ac90f1d5 | |||
| 60a8538e0c | |||
| 77a3a5857c | |||
| af2a8eeb08 | |||
| 54d0ab3658 | |||
| c9faa92098 | |||
| 52e5008bf4 | |||
| a0a31b0f3b | |||
| 7742d163f7 |
+24
-16
@@ -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:
|
||||
@@ -96,7 +101,13 @@ jobs:
|
||||
|
||||
build-windows-msbuild:
|
||||
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-${{ matrix.config.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\"}
|
||||
- {vs: Visual Studio 17 2022, os: 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\"}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -107,23 +118,20 @@ jobs:
|
||||
- name: Setup cmake
|
||||
uses: lukka/get-cmake@latest
|
||||
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1.0.3
|
||||
|
||||
- name: Build p2pool
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Visual Studio 16 2019"
|
||||
msbuild /m /p:Configuration=Release p2pool.vcxproj
|
||||
cmake .. -G "${{ matrix.config.vs }}"
|
||||
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool.vcxproj
|
||||
|
||||
- name: Build tests
|
||||
run: |
|
||||
cd tests
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Visual Studio 16 2019"
|
||||
msbuild /m /p:Configuration=Release p2pool_tests.vcxproj
|
||||
cmake .. -G "${{ matrix.config.vs }}"
|
||||
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool_tests.vcxproj
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
@@ -133,7 +141,7 @@ jobs:
|
||||
- name: Archive binary
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: p2pool-msbuild.exe
|
||||
name: p2pool-msbuild-${{ matrix.config.os }}.exe
|
||||
path: build/Release/p2pool.exe
|
||||
|
||||
build-macos:
|
||||
@@ -142,7 +150,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, macos-11]
|
||||
os: [macos-10.15, macos-11]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# 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'
|
||||
config: 'Release'
|
||||
|
||||
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 }} -DCMAKE_BUILD_TYPE=${{ env.config }}
|
||||
|
||||
# Build is not required unless generated source files are used
|
||||
# - name: Build CMake
|
||||
# run: cmake --build ${{ env.build }}
|
||||
|
||||
- name: Initialize MSVC Code Analysis
|
||||
uses: microsoft/msvc-code-analysis-action@47ecec99bf7ce0399ed85cbb5012b6f24eab5959
|
||||
# Provide a unique ID to access the sarif output path
|
||||
id: run-analysis
|
||||
with:
|
||||
cmakeBuildDirectory: ${{ env.build }}
|
||||
buildConfiguration: ${{ env.config }}
|
||||
# Ruleset file that will determine what checks will be run
|
||||
ruleset: NativeRecommendedRules.ruleset
|
||||
|
||||
# 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
|
||||
|
||||
@@ -27,6 +27,7 @@ set(HEADERS
|
||||
src/keccak.h
|
||||
src/log.h
|
||||
src/mempool.h
|
||||
src/miner.h
|
||||
src/p2p_server.h
|
||||
src/p2pool.h
|
||||
src/p2pool_api.h
|
||||
@@ -60,6 +61,7 @@ set(SOURCES
|
||||
src/main.cpp
|
||||
src/memory_leak_debug.cpp
|
||||
src/mempool.cpp
|
||||
src/miner.cpp
|
||||
src/p2p_server.cpp
|
||||
src/p2pool.cpp
|
||||
src/p2pool_api.cpp
|
||||
@@ -82,6 +84,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"
|
||||
@@ -22,7 +23,7 @@ Here's the comparison table of the different ways of mining. While pool mining i
|
||||
|-|-|-|-|-|-|-|-|
|
||||
|Centralized pool|Regular|0-3%|0.001-0.01 XMR|Yes|Less stable due to pool server outages|Pool admin controls your mined funds, what you mine and can execute network attacks|Only miner software is required
|
||||
|Solo|Rare|0%|0.6 XMR or more|No|As stable as your Monero node|100% under your control|Monero node + optional miner
|
||||
|**P2Pool**|Regular|0%|less than 0.0004 XMR|No|As stable as your Monero node|100% under your control|Monero node + P2Pool node + miner
|
||||
|**P2Pool**|Regular|0%|~0.0003 XMR|No|As stable as your Monero node|100% under your control|Monero node + P2Pool node + miner
|
||||
|
||||
## Features
|
||||
|
||||
@@ -32,7 +33,7 @@ Here's the comparison table of the different ways of mining. While pool mining i
|
||||
* PPLNS payout scheme
|
||||
* **0% fee**
|
||||
* **0 XMR payout fee**
|
||||
* **Less than 0.0004 XMR minimal payout**
|
||||
* **~0.0003 XMR minimal payout**
|
||||
* Fast block times, down to 1 second
|
||||
* Uncle blocks are supported to avoid orphans - all your shares will be accounted for!
|
||||
* Configurable PPLNS window size and block time
|
||||
@@ -49,7 +50,84 @@ First you need to find a pool share. This share will stay in PPLNS window for 21
|
||||
|
||||
* Block time: 10 seconds
|
||||
* PPLNS window: 2160 blocks (6 hours)
|
||||
* Minimum payout = Monero block reward/2160, currently ~0.0004 XMR
|
||||
* Minimum payout = Monero block reward/2160, ~0.0003 XMR
|
||||
|
||||
## 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**.
|
||||
|
||||
**Wallet software compatible with p2pool payouts**
|
||||
- Official Monero CLI and GUI v0.17.2.3 and newer
|
||||
- Monerujo v2.1.0 "Vertant" and newer
|
||||
- Cake Wallet v4.2.7 and newer
|
||||
- MyMonero
|
||||
|
||||
**General Considerations**
|
||||
|
||||
- Create a separate restricted user account for mining. p2pool is relatively new and may still have serious bugs/vulnerabilities!
|
||||
- You have to use the primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
|
||||
- Check that ports 18080 (Monero p2p port) and 37889 (p2pool p2p port) are open in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine.
|
||||
- You can connect multiple miners to the same p2pool node. The more the better!
|
||||
|
||||
Step-by-step guide:
|
||||
|
||||
### GNU/Linux
|
||||
|
||||
- Download binaries from https://github.com/SChernykh/p2pool/releases/latest
|
||||
- Alternatively, grab the latest source code for 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**
|
||||
- Run `./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
|
||||
- p2pool has verbose logging by default, you can reduce it by using "loglevel N" command where N is between 0 and 6. Default loglevel is 3.
|
||||
- You can use `logrotate` with a config like this to control logfile growth:
|
||||
```
|
||||
<path-to-logfile>
|
||||
{
|
||||
rotate 7
|
||||
daily
|
||||
missingok
|
||||
delaycompress
|
||||
nocreate
|
||||
}
|
||||
```
|
||||
- Wait until initial p2pool sync is finished, it shouldn't take more than 5-10 minutes, once completed xmrig should be able to connect to the stratum server on port 3333.
|
||||
- Run `./xmrig -o 127.0.0.1:3333`. Note that you don't need to specify wallet address for xmrig. **Wallet address set in xmrig config will be ignored!**
|
||||
- To set custom fixed difficulty for your miner (for example, 10000), run `./xmrig -u x+10000 -o 127.0.0.1:3333`
|
||||
- xmrig should connect and start mining
|
||||
- Also check p2pool.log for any warnings and errors: `grep -E 'WARNING|ERROR' p2pool.log`
|
||||
|
||||
### Windows
|
||||
|
||||
*NOTE: Windows SmartScreen may block incoming connections by files that are "Downloaded from the Internet". You can allow 'p2pool.exe' and 'monerod.exe' by double-clicking them, clicking "More Info", then click "Run Anyway" and then closing them immediately so you can run them from the command line. Advanced users can use the PowerShell cmdlet `Unblock-File` to remove this flag.*
|
||||
|
||||
- Download p2pool binaries from https://github.com/SChernykh/p2pool/releases/latest
|
||||
- Download xmrig binary from https://github.com/xmrig/xmrig/releases/latest *(xmrig-6.15.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):
|
||||
- On Windows 10 or above, run xmrig at least once as Administrator (right-click Run As Administrator)
|
||||
- On earlier versions of Windows, you'll need to run it as admin at least once per login.
|
||||
- Open a command prompt and navigate to the folder where you extracted p2pool.
|
||||
- *When running these commands, Windows Firewall may prompt to allow connections, click "Allow"*
|
||||
- Run `.\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist` *NOTE: don't forget --zmq-pub parameter in the command line*
|
||||
- Run `.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
|
||||
- Wait until initial p2pool sync is finished, it shouldn't take more than 5-10 minutes, once completed xmrig should be able to connect to the stratum server on port 3333.
|
||||
- Run `.\xmrig.exe -o 127.0.0.1:3333`. Note that you don't need to specify wallet address for xmrig. **Wallet address set in xmrig config will be ignored!**
|
||||
- To set custom fixed difficulty for your miner (for example, 10000), run `xmrig.exe -u x+10000 -o 127.0.0.1:3333`
|
||||
- Windows Quickstart: Create a batch (.bat) file with the following contents and place it in your p2pool directory along with xmrig.exe.
|
||||
```
|
||||
@ECHO OFF
|
||||
start cmd /k %~dp0\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
|
||||
ECHO Wait until the Monero daemon shows fully synced before continuing. This can take some time. Type 'status' in other window to check progress.
|
||||
PAUSE
|
||||
start cmd /k %~dp0\p2pool.exe --wallet YOUR_WALLET_ADDRESS
|
||||
ECHO Wait until the daemon shows fully synced before continuing. This can take some time.
|
||||
PAUSE
|
||||
%~dp0\xmrig.exe -u x+30000 -o 127.0.0.1
|
||||
```
|
||||
|
||||
## Build instructions
|
||||
|
||||
@@ -65,16 +143,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 +179,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,92 +194,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**.
|
||||
|
||||
**Wallet software compatible with p2pool payouts**
|
||||
- Official Monero CLI and GUI v0.17.2.3 and newer
|
||||
- Monerujo v2.1.0 "Vertant" and newer
|
||||
- Cake Wallet v4.2.7 and newer
|
||||
- MyMonero
|
||||
|
||||
**General Considerations**
|
||||
|
||||
- Create a separate restricted user account for mining. p2pool is relatively new and may still have serious bugs/vulnerabilities!
|
||||
- You have to use the primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
|
||||
- Check that ports 18080 (Monero p2p port) and 37889 (p2pool p2p port) are open in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine.
|
||||
- You can connect multiple miners to the same p2pool node. The more the better!
|
||||
|
||||
Step-by-step guide:
|
||||
|
||||
### GNU/Linux
|
||||
|
||||
- Download binaries from https://github.com/SChernykh/p2pool/releases/latest
|
||||
- Alternatively, grab the latest source code for both p2pool and monerod and build them (see above, also notice that the branch name for monerod changed, you'll need to checkout p2pool-api-v0.17)
|
||||
- Prepare enough huge pages (each of monerod/p2pool/xmrig needs them): `sudo sysctl vm.nr_hugepages=3072`
|
||||
- Get xmrig (linux-static-x64) binary from https://github.com/xmrig/xmrig/releases/latest
|
||||
- Check that ports 18080 (Monero p2p port) and 37889 (p2pool p2p port) are open in your firewall to ensure better connectivity
|
||||
- Run `./monerod --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist` **don't forget --zmq-pub parameter in the command line**
|
||||
- Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-7dbb0d1fc)** on startup. Wait until it's synchronized.
|
||||
- Run `./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
|
||||
- p2pool has _very_ verbose logging by default, it will spam a lot, no I mean A LOT in both console and in p2pool.log. Logs help testing immensely!
|
||||
- You can use `logrotate` with a config like this to control logfile growth:
|
||||
```
|
||||
<path-to-logfile>
|
||||
{
|
||||
rotate 7
|
||||
daily
|
||||
missingok
|
||||
delaycompress
|
||||
nocreate
|
||||
}
|
||||
```
|
||||
- Wait until initial p2pool sync is finished, it shouldn't take more than 5-10 minutes, once completed xmrig should be able to connect to the stratum server on port 3333.
|
||||
- Run `./xmrig -o 127.0.0.1:3333`. Note that you don't need to specify wallet address for xmrig. **Wallet address set in xmrig config will be ignored!**
|
||||
- To set custom fixed difficulty for your miner (for example, 10000), run `./xmrig -u x+10000 -o 127.0.0.1:3333`
|
||||
- xmrig should connect and start mining
|
||||
- Also check p2pool.log for any warnings and errors: `grep -E 'WARNING|ERROR' p2pool.log`
|
||||
|
||||
### Windows
|
||||
|
||||
*NOTE: Windows SmartScreen may block incoming connections by files that are "Downloaded from the Internet". You can allow 'p2pool.exe' and 'monerod.exe' by double-clicking them, clicking "More Info", then click "Run Anyway" and then closing them immediately so you can run them from the command line. Advanced users can use the PowerShell cmdlet `Unblock-File` to remove this flag.*
|
||||
|
||||
- Download p2pool binaries from https://github.com/SChernykh/p2pool/releases/latest
|
||||
- Download xmrig binary from https://github.com/xmrig/xmrig/releases/latest *(xmrig-6.15.0-gcc-win64.zip is the current version)*
|
||||
- Expand the p2pool binaries into an appropriate location (`%USERPROFILE%/bin` or `C:/bin/` are good options)
|
||||
- Expand xmrig binary into appropriate location (same folder as p2pool is fine)
|
||||
- Prepare huge pages (each of monerod/p2pool/xmrig needs them):
|
||||
- On Windows 10 or above, run xmrig at least once as Administrator (right-click Run As Administrator)
|
||||
- On earlier versions of Windows, you'll need to run it as admin at least once per login.
|
||||
- Open a command prompt and navigate to the folder where you extracted p2pool.
|
||||
- *When running these commands, Windows Firewall may prompt to allow connections, click "Allow"*
|
||||
- Run `.\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist` *NOTE: don't forget --zmq-pub parameter in the command line*
|
||||
- Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-7dbb0d1fc)** on startup. Wait until it's synchronized.
|
||||
- Run `.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
|
||||
- Wait until initial p2pool sync is finished, it shouldn't take more than 5-10 minutes, once completed xmrig should be able to connect to the stratum server on port 3333.
|
||||
- Run `.\xmrig.exe -o 127.0.0.1:3333`. Note that you don't need to specify wallet address for xmrig. **Wallet address set in xmrig config will be ignored!**
|
||||
- To set custom fixed difficulty for your miner (for example, 10000), run `xmrig.exe -u x+10000 -o 127.0.0.1:3333`
|
||||
- Windows Quickstart: Create a batch (.bat) file with the following contents and place it in your p2pool directory along with xmrig.exe.
|
||||
```
|
||||
@ECHO OFF
|
||||
start cmd /k %~dp0\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
|
||||
ECHO Wait until the Monero daemon shows fully synced before continuing. This can take some time. Type 'status' in other window to check progress.
|
||||
PAUSE
|
||||
start cmd /k %~dp0\p2pool.exe --wallet YOUR_WALLET_ADDRESS
|
||||
ECHO Wait until the daemon shows fully synced before continuing. This can take some time.
|
||||
PAUSE
|
||||
%~dp0\xmrig.exe -u x+30000 -o 127.0.0.1
|
||||
```
|
||||
|
||||
## Donations
|
||||
|
||||
|
||||
+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
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Vendored
+1
-1
Submodule external/src/cppzmq updated: 286d8be432...a98fa4a91d
Vendored
+1
-1
Submodule external/src/libsodium updated: 64129657a5...dce3bca3ba
Vendored
+1
-1
Submodule external/src/libuv updated: a39009a5a9...bb0b4bb783
Vendored
+1
-1
Submodule external/src/libzmq updated: 056f37f352...a01d259db3
Vendored
+1
-1
Submodule external/src/rapidjson updated: 5bbae3e50b...a115f5d775
+1
Submodule external/src/robin-hood-hashing added at 90687c508a
+27
-8
@@ -66,7 +66,7 @@ BlockTemplate::BlockTemplate(p2pool* pool)
|
||||
m_mempoolTxsOrder.reserve(1024);
|
||||
m_shares.reserve(m_pool->side_chain().chain_window_size() * 2);
|
||||
|
||||
for (size_t i = 0; i < array_size(m_oldTemplates); ++i) {
|
||||
for (size_t i = 0; i < array_size(&BlockTemplate::m_oldTemplates); ++i) {
|
||||
m_oldTemplates[i] = new BlockTemplate(*this);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ BlockTemplate::BlockTemplate(p2pool* pool)
|
||||
|
||||
BlockTemplate::~BlockTemplate()
|
||||
{
|
||||
for (size_t i = 0; i < array_size(m_oldTemplates); ++i) {
|
||||
for (size_t i = 0; i < array_size(&BlockTemplate::m_oldTemplates); ++i) {
|
||||
delete m_oldTemplates[i];
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
WriteLock lock(m_lock);
|
||||
|
||||
if (m_templateId > 0) {
|
||||
*m_oldTemplates[m_templateId % array_size(m_oldTemplates)] = *this;
|
||||
*m_oldTemplates[m_templateId % array_size(&BlockTemplate::m_oldTemplates)] = *this;
|
||||
}
|
||||
|
||||
++m_templateId;
|
||||
@@ -838,6 +838,25 @@ void BlockTemplate::calc_merkle_tree_main_branch()
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockTemplate::get_difficulties(const uint32_t template_id, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const
|
||||
{
|
||||
ReadLock lock(m_lock);
|
||||
|
||||
if (template_id == m_templateId) {
|
||||
mainchain_difficulty = m_difficulty;
|
||||
sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
|
||||
return true;
|
||||
}
|
||||
|
||||
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
|
||||
|
||||
if (old && (template_id == old->m_templateId)) {
|
||||
return old->get_difficulties(template_id, mainchain_difficulty, sidechain_difficulty);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t BlockTemplate::get_hashing_blob(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const
|
||||
{
|
||||
ReadLock lock(m_lock);
|
||||
@@ -852,7 +871,7 @@ uint32_t BlockTemplate::get_hashing_blob(const uint32_t template_id, uint32_t ex
|
||||
return get_hashing_blob_nolock(extra_nonce, blob);
|
||||
}
|
||||
|
||||
const BlockTemplate* old = m_oldTemplates[template_id % array_size(m_oldTemplates)];
|
||||
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
|
||||
|
||||
if (old && (template_id == old->m_templateId)) {
|
||||
return old->get_hashing_blob(template_id, extra_nonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset);
|
||||
@@ -953,7 +972,7 @@ std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id
|
||||
ReadLock lock(m_lock);
|
||||
|
||||
if (template_id != m_templateId) {
|
||||
const BlockTemplate* old = m_oldTemplates[template_id % array_size(m_oldTemplates)];
|
||||
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
|
||||
if (old && (template_id == old->m_templateId)) {
|
||||
return old->get_block_template_blob(template_id, nonce_offset, extra_nonce_offset);
|
||||
}
|
||||
@@ -983,7 +1002,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
|
||||
m_poolBlockTemplate->m_nonce = nonce;
|
||||
m_poolBlockTemplate->m_extraNonce = extra_nonce;
|
||||
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_nonceOffset, &nonce, NONCE_SIZE);
|
||||
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_extraNonceOffsetInTemplate, &extra_nonce, NONCE_SIZE);
|
||||
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_extraNonceOffsetInTemplate, &extra_nonce, EXTRA_NONCE_SIZE);
|
||||
|
||||
SideChain& side_chain = m_pool->side_chain();
|
||||
|
||||
@@ -999,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)) {
|
||||
@@ -1016,7 +1035,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
|
||||
return;
|
||||
}
|
||||
|
||||
BlockTemplate* old = m_oldTemplates[template_id % array_size(m_oldTemplates)];
|
||||
BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
|
||||
|
||||
if (old && (template_id == old->m_templateId)) {
|
||||
old->submit_sidechain_block(template_id, nonce, extra_nonce);
|
||||
|
||||
@@ -40,6 +40,7 @@ public:
|
||||
|
||||
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet);
|
||||
|
||||
bool get_difficulties(const uint32_t template_id, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
|
||||
uint32_t get_hashing_blob(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const;
|
||||
|
||||
uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "p2pool.h"
|
||||
#include "stratum_server.h"
|
||||
#include "p2p_server.h"
|
||||
#include "miner.h"
|
||||
#include "side_chain.h"
|
||||
#include <iostream>
|
||||
|
||||
@@ -69,7 +70,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_start_mining, do_stop_mining, do_exit;
|
||||
|
||||
static cmd cmds[] = {
|
||||
{ STRCONST("help"), "", "display list of commands", do_help },
|
||||
@@ -77,17 +78,21 @@ 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("start_mining"), "<threads>", "start mining", do_start_mining },
|
||||
{ STRCONST("stop_mining"), "", "stop mining", do_stop_mining },
|
||||
{ STRCONST("exit"), "", "terminate p2pool", do_exit },
|
||||
{ STRCNULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static int do_help(p2pool * /* m_pool */, const char * /* args */)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOGINFO(0, "List of commands");
|
||||
for (i=0; cmds[i].name.len; i++)
|
||||
for (int i = 0; cmds[i].name.len; ++i) {
|
||||
LOGINFO(0, cmds[i].name.str << " " << cmds[i].arg << "\t" << cmds[i].descr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -100,13 +105,16 @@ static int do_status(p2pool *m_pool, const char * /* args */)
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->print_status();
|
||||
}
|
||||
if (m_pool->miner()) {
|
||||
m_pool->miner()->print_status();
|
||||
}
|
||||
bkg_jobs_tracker.print_status();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_loglevel(p2pool * /* m_pool */, const char *args)
|
||||
{
|
||||
int level = atoi(args);
|
||||
int level = strtol(args, nullptr, 10);
|
||||
level = std::min(std::max(level, 0), log::MAX_GLOBAL_LOG_LEVEL);
|
||||
log::GLOBAL_LOG_LEVEL = level;
|
||||
LOGINFO(0, "log level set to " << level);
|
||||
@@ -129,6 +137,46 @@ 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_start_mining(p2pool* m_pool, const char* args)
|
||||
{
|
||||
uint32_t threads = strtoul(args, nullptr, 10);
|
||||
threads = std::min(std::max(threads, 1u), 64u);
|
||||
m_pool->start_mining(threads);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_stop_mining(p2pool* m_pool, const char* /*args*/)
|
||||
{
|
||||
m_pool->stop_mining();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_exit(p2pool *m_pool, const char * /* args */)
|
||||
{
|
||||
bkg_jobs_tracker.wait();
|
||||
|
||||
+22
-5
@@ -20,7 +20,6 @@
|
||||
#include "keccak.h"
|
||||
#include "uv_util.h"
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
|
||||
extern "C" {
|
||||
#include "crypto-ops.h"
|
||||
@@ -33,9 +32,12 @@ namespace {
|
||||
class RandomBytes
|
||||
{
|
||||
public:
|
||||
RandomBytes() : rd(), rng(rd()), dist(0, 255)
|
||||
RandomBytes() : rng(s), dist(0, 255)
|
||||
{
|
||||
uv_mutex_init_checked(&m);
|
||||
|
||||
// Diffuse the initial state in case it has low quality
|
||||
rng.discard(10000);
|
||||
}
|
||||
|
||||
~RandomBytes()
|
||||
@@ -55,7 +57,22 @@ public:
|
||||
private:
|
||||
uv_mutex_t m;
|
||||
|
||||
std::random_device rd;
|
||||
// Fills the whole initial MT19937-64 state with non-deterministic random numbers
|
||||
struct SeedSequence
|
||||
{
|
||||
using result_type = std::random_device::result_type;
|
||||
|
||||
template<typename T>
|
||||
static void generate(T begin, T end)
|
||||
{
|
||||
std::random_device rd;
|
||||
for (T i = begin; i != end; ++i) {
|
||||
*i = rd();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SeedSequence s;
|
||||
std::mt19937_64 rng;
|
||||
std::uniform_int_distribution<> dist;
|
||||
};
|
||||
@@ -237,8 +254,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);
|
||||
|
||||
+22
-3
@@ -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);
|
||||
|
||||
@@ -63,12 +65,20 @@ public:
|
||||
|
||||
uv_cond_init(&m_cond);
|
||||
uv_mutex_init(&m_mutex);
|
||||
uv_thread_create(&m_worker, run_wrapper, this);
|
||||
|
||||
const int err = uv_thread_create(&m_worker, run_wrapper, this);
|
||||
if (err) {
|
||||
fprintf(stderr, "failed to start logger thread (%s), aborting\n", uv_err_name(err));
|
||||
abort();
|
||||
}
|
||||
|
||||
do {} while (!worker_started);
|
||||
|
||||
#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 +165,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 +189,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 {
|
||||
@@ -371,6 +372,7 @@ template<> struct log::Stream::Entry<XMRAmount>
|
||||
|
||||
template<> struct log::Stream::Entry<NetworkType>
|
||||
{
|
||||
// cppcheck-suppress constParameter
|
||||
static NOINLINE void put(const NetworkType& value, Stream* wrapper)
|
||||
{
|
||||
switch (value) {
|
||||
|
||||
+10
-1
@@ -33,13 +33,22 @@ static void usage()
|
||||
"--p2p Comma-separated list of IP:port for p2p server to listen on\n"
|
||||
"--addpeers Comma-separated list of IP:port of other p2pool nodes to connect to\n"
|
||||
"--light-mode Don't allocate RandomX dataset, saves 2GB of RAM\n"
|
||||
"--loglevel Verbosity of the log, integer number between 0 and 5\n"
|
||||
"--loglevel Verbosity of the log, integer number between 0 and %d\n"
|
||||
"--config Name of the p2pool config file\n"
|
||||
"--data-api Path to the p2pool JSON data (use it in tandem with an external web-server)\n"
|
||||
"--local-api Enable /local/ path in api path for Stratum Server and built-in miner statistics\n"
|
||||
"--stratum-api An alias for --local-api\n"
|
||||
"--no-cache Disable p2pool.cache\n"
|
||||
"--no-color Disable colors in console output\n"
|
||||
"--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes\n"
|
||||
"--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 1000)\n"
|
||||
"--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 1000)\n"
|
||||
"--start-mining N Start built-in miner using N threads (any value between 1 and 64)\n"
|
||||
"--help Show this help message\n\n"
|
||||
"Example command line:\n\n"
|
||||
"%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",
|
||||
p2pool::VERSION,
|
||||
p2pool::log::MAX_GLOBAL_LOG_LEVEL,
|
||||
#ifdef _WIN32
|
||||
"p2pool.exe"
|
||||
#else
|
||||
|
||||
+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
|
||||
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "miner.h"
|
||||
#include "p2pool.h"
|
||||
#include "stratum_server.h"
|
||||
#include "block_template.h"
|
||||
#include "pow_hash.h"
|
||||
#include "randomx.h"
|
||||
#include "params.h"
|
||||
#include "p2pool_api.h"
|
||||
#include <thread>
|
||||
|
||||
static constexpr char log_category_prefix[] = "Miner ";
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
Miner::Miner(p2pool* pool, uint32_t threads)
|
||||
: m_pool(pool)
|
||||
, m_threads(threads)
|
||||
, m_stopped(false)
|
||||
, m_startTimestamp(high_resolution_clock::now())
|
||||
, m_nonce(0)
|
||||
, m_nonceTimestamp(m_startTimestamp)
|
||||
, m_extraNonce(0xF19E3779U)
|
||||
, m_totalHashes(0)
|
||||
, m_job{}
|
||||
, m_jobIndex(0)
|
||||
{
|
||||
on_block(m_pool->block_template());
|
||||
|
||||
m_minerThreads.reserve(threads);
|
||||
|
||||
for (uint32_t i = 0; i < threads; ++i) {
|
||||
WorkerData* data = new WorkerData{ this, i + 1, threads, {} };
|
||||
const int err = uv_thread_create(&data->m_worker, run, data);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to start worker thread " << data->m_index << '/' << threads << ", error " << uv_err_name(err));
|
||||
delete data;
|
||||
continue;
|
||||
}
|
||||
m_minerThreads.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
Miner::~Miner()
|
||||
{
|
||||
m_stopped = true;
|
||||
|
||||
for (WorkerData* data : m_minerThreads) {
|
||||
uv_thread_join(&data->m_worker);
|
||||
delete data;
|
||||
}
|
||||
}
|
||||
|
||||
void Miner::print_status()
|
||||
{
|
||||
const uint32_t hash_count = 0 - m_nonce.load();
|
||||
|
||||
const double dt = static_cast<double>(duration_cast<nanoseconds>(high_resolution_clock::now() - m_nonceTimestamp).count()) / 1e9;
|
||||
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
|
||||
|
||||
LOGINFO(0, "status" <<
|
||||
"\nThreads = " << m_threads <<
|
||||
"\nHashrate = " << log::Hashrate(hr)
|
||||
);
|
||||
}
|
||||
|
||||
void Miner::on_block(const BlockTemplate& block)
|
||||
{
|
||||
const uint32_t next_index = m_jobIndex ^ 1;
|
||||
Job& j = m_job[next_index];
|
||||
hash seed;
|
||||
j.m_blobSize = block.get_hashing_blob(m_extraNonce, j.m_blob, j.m_height, j.m_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
|
||||
|
||||
const uint32_t hash_count = 0 - m_nonce.exchange(0);
|
||||
m_jobIndex = next_index;
|
||||
|
||||
const auto cur_ts = high_resolution_clock::now();
|
||||
const double dt = static_cast<double>(duration_cast<nanoseconds>(cur_ts - m_nonceTimestamp).count()) / 1e9;
|
||||
|
||||
m_nonceTimestamp = cur_ts;
|
||||
m_totalHashes += hash_count;
|
||||
|
||||
if (m_pool->api() && m_pool->params().m_localStats) {
|
||||
m_pool->api()->set(p2pool_api::Category::LOCAL, "miner",
|
||||
[cur_ts, hash_count, dt, this](log::Stream& s)
|
||||
{
|
||||
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
|
||||
const double time_running = static_cast<double>(duration_cast<milliseconds>(cur_ts - m_startTimestamp).count()) / 1e3;
|
||||
|
||||
s << "{\"current_hashrate\":" << hr
|
||||
<< ",\"total_hashes\":" << m_totalHashes
|
||||
<< ",\"time_running\":" << time_running
|
||||
<< ",\"shares_found\":" << m_sharesFound.load()
|
||||
<< ",\"threads\":" << m_threads
|
||||
<< "}";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Miner::run(void* data)
|
||||
{
|
||||
WorkerData* d = static_cast<WorkerData*>(data);
|
||||
LOGINFO(1, "worker thread " << d->m_index << '/' << d->m_count << " started");
|
||||
make_thread_background();
|
||||
d->m_miner->run(d);
|
||||
LOGINFO(1, "worker thread " << d->m_index << '/' << d->m_count << " stopped");
|
||||
}
|
||||
|
||||
void Miner::run(WorkerData* data)
|
||||
{
|
||||
RandomX_Hasher_Base* hasher = m_pool->hasher();
|
||||
randomx_cache* cache = hasher->cache();
|
||||
randomx_dataset* dataset = hasher->dataset();
|
||||
|
||||
if (!cache && !dataset) {
|
||||
LOGERR(1, "worker thread " << data->m_index << '/' << data->m_count << ": RandomX cache and dataset are not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
randomx_flags flags = randomx_get_flags();
|
||||
if (dataset) {
|
||||
flags |= RANDOMX_FLAG_FULL_MEM;
|
||||
}
|
||||
|
||||
randomx_vm* vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, dataset ? nullptr : cache, dataset);
|
||||
if (!vm) {
|
||||
LOGWARN(1, "couldn't allocate RandomX VM using large pages");
|
||||
vm = randomx_create_vm(flags, dataset ? nullptr : cache, dataset);
|
||||
if (!vm) {
|
||||
LOGERR(1, "couldn't allocate RandomX VM");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t index = 0;
|
||||
Job job[2];
|
||||
|
||||
uint32_t seed_counter = 0;
|
||||
bool first = true;
|
||||
|
||||
Miner* miner = data->m_miner;
|
||||
|
||||
while (!m_stopped) {
|
||||
if (hasher->seed_counter() != seed_counter) {
|
||||
LOGINFO(5, "worker thread " << data->m_index << '/' << data->m_count << " paused (waiting for RandomX cache/dataset update)");
|
||||
hasher->sync_wait();
|
||||
seed_counter = hasher->seed_counter();
|
||||
if (flags & RANDOMX_FLAG_FULL_MEM) {
|
||||
dataset = hasher->dataset();
|
||||
randomx_vm_set_dataset(vm, dataset);
|
||||
}
|
||||
else {
|
||||
cache = hasher->cache();
|
||||
randomx_vm_set_cache(vm, cache);
|
||||
}
|
||||
LOGINFO(5, "worker thread " << data->m_index << '/' << data->m_count << " resumed");
|
||||
}
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job));
|
||||
job[index].set_nonce(miner->m_nonce.fetch_sub(1), miner->m_extraNonce);
|
||||
randomx_calculate_hash_first(vm, job[index].m_blob, job[index].m_blobSize);
|
||||
}
|
||||
|
||||
const Job& j = job[index];
|
||||
index ^= 1;
|
||||
memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job));
|
||||
job[index].set_nonce(miner->m_nonce.fetch_sub(1), miner->m_extraNonce);
|
||||
|
||||
hash h;
|
||||
randomx_calculate_hash_next(vm, job[index].m_blob, job[index].m_blobSize, &h);
|
||||
|
||||
if (j.m_diff.check_pow(h)) {
|
||||
LOGINFO(0, log::Green() << "worker thread " << data->m_index << '/' << data->m_count << " found a mainchain block, submitting it");
|
||||
m_pool->submit_block_async(j.m_templateId, j.m_nonce, j.m_extraNonce);
|
||||
m_pool->block_template().update_tx_keys();
|
||||
}
|
||||
|
||||
if (j.m_sidechainDiff.check_pow(h)) {
|
||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << j.m_height << ", diff " << j.m_sidechainDiff << ", worker thread " << data->m_index << '/' << data->m_count);
|
||||
m_pool->submit_sidechain_block(j.m_templateId, j.m_nonce, j.m_extraNonce);
|
||||
++m_sharesFound;
|
||||
}
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
randomx_destroy_vm(vm);
|
||||
}
|
||||
|
||||
void Miner::Job::set_nonce(uint32_t nonce, uint32_t extra_nonce)
|
||||
{
|
||||
m_nonce = nonce;
|
||||
m_extraNonce = extra_nonce;
|
||||
|
||||
uint8_t* p = m_blob + m_nonceOffset;
|
||||
p[0] = static_cast<uint8_t>(nonce);
|
||||
p[1] = static_cast<uint8_t>(nonce >> 8);
|
||||
p[2] = static_cast<uint8_t>(nonce >> 16);
|
||||
p[3] = static_cast<uint8_t>(nonce >> 24);
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "uv_util.h"
|
||||
#include <chrono>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
class p2pool;
|
||||
class BlockTemplate;
|
||||
|
||||
class Miner
|
||||
{
|
||||
public:
|
||||
Miner(p2pool* pool, uint32_t threads);
|
||||
~Miner();
|
||||
|
||||
void print_status();
|
||||
void on_block(const BlockTemplate& block);
|
||||
|
||||
private:
|
||||
static void run(void* data);
|
||||
|
||||
p2pool* m_pool;
|
||||
uint32_t m_threads;
|
||||
|
||||
struct WorkerData
|
||||
{
|
||||
Miner* m_miner;
|
||||
uint32_t m_index;
|
||||
uint32_t m_count;
|
||||
uv_thread_t m_worker;
|
||||
};
|
||||
|
||||
std::vector<WorkerData*> m_minerThreads;
|
||||
volatile bool m_stopped;
|
||||
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_startTimestamp;
|
||||
|
||||
std::atomic<uint32_t> m_nonce;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_nonceTimestamp;
|
||||
const uint32_t m_extraNonce;
|
||||
|
||||
uint64_t m_totalHashes;
|
||||
std::atomic<uint32_t> m_sharesFound;
|
||||
|
||||
struct Job
|
||||
{
|
||||
uint8_t m_blob[128] = {};
|
||||
uint32_t m_blobSize = 0;
|
||||
uint32_t m_templateId = 0;
|
||||
difficulty_type m_diff = {};
|
||||
difficulty_type m_sidechainDiff = {};
|
||||
uint64_t m_height = 0;
|
||||
size_t m_nonceOffset = 0;
|
||||
uint32_t m_nonce = 0;
|
||||
uint32_t m_extraNonce = 0;
|
||||
|
||||
void set_nonce(uint32_t nonce, uint32_t extra_nonce);
|
||||
};
|
||||
Job m_job[2];
|
||||
volatile uint32_t m_jobIndex;
|
||||
|
||||
void run(WorkerData* data);
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
+203
-65
@@ -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;
|
||||
@@ -42,16 +41,20 @@ namespace p2pool {
|
||||
P2PServer::P2PServer(p2pool* pool)
|
||||
: TCPServer(P2PClient::allocate)
|
||||
, m_pool(pool)
|
||||
, m_cache(new BlockCache())
|
||||
, m_cache(pool->params().m_blockCache ? new BlockCache() : nullptr)
|
||||
, m_cacheLoaded(false)
|
||||
, m_initialPeerList(pool->params().m_p2pPeerList)
|
||||
, m_rd{}
|
||||
, m_rng(m_rd())
|
||||
, m_block(new PoolBlock())
|
||||
, m_timer{}
|
||||
, m_timerCounter(0)
|
||||
, 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);
|
||||
@@ -93,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();
|
||||
@@ -166,12 +170,6 @@ void P2PServer::on_connect_failed(bool is_v6, const raw_ip& ip, int port)
|
||||
|
||||
void P2PServer::update_peer_connections()
|
||||
{
|
||||
std::vector<Peer> peer_list;
|
||||
{
|
||||
MutexLock lock(m_peerListLock);
|
||||
peer_list = m_peerList;
|
||||
}
|
||||
|
||||
const time_t cur_time = time(nullptr);
|
||||
const time_t last_updated = m_pool->side_chain().last_updated();
|
||||
|
||||
@@ -180,13 +178,14 @@ void P2PServer::update_peer_connections()
|
||||
MutexLock lock(m_clientsListLock);
|
||||
connected_clients.reserve(m_numConnections);
|
||||
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
connected_clients.emplace_back(client->m_addr);
|
||||
bool disconnected = false;
|
||||
|
||||
const int timeout = client->m_handshakeComplete ? 300 : 10;
|
||||
if (cur_time >= client->m_lastAlive + timeout) {
|
||||
const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive);
|
||||
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting");
|
||||
client->close();
|
||||
disconnected = true;
|
||||
}
|
||||
|
||||
if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) {
|
||||
@@ -200,13 +199,37 @@ void P2PServer::update_peer_connections()
|
||||
client->ban(DEFAULT_BAN_TIME);
|
||||
remove_peer_from_list(client);
|
||||
client->close();
|
||||
disconnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!disconnected) {
|
||||
connected_clients.emplace_back(client->m_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to have at least 8 outgoing connections
|
||||
for (uint32_t i = m_numConnections - m_numIncomingConnections; (i < 8) && !peer_list.empty();) {
|
||||
std::vector<Peer> peer_list;
|
||||
{
|
||||
MutexLock lock(m_peerListLock);
|
||||
|
||||
if ((m_timerCounter % 30) == 1) {
|
||||
// Update last seen time for currently connected peers
|
||||
for (Peer& p : m_peerList) {
|
||||
if (std::find_if(connected_clients.begin(), connected_clients.end(), [&p](const raw_ip& addr) { return p.m_addr == addr; }) != connected_clients.end()) {
|
||||
p.m_lastSeen = cur_time;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all peers that weren't seen for more than 1 hour
|
||||
m_peerList.erase(std::remove_if(m_peerList.begin(), m_peerList.end(), [cur_time](const Peer& p) { return p.m_lastSeen + 3600 < cur_time; }), m_peerList.end());
|
||||
}
|
||||
|
||||
peer_list = m_peerList;
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
||||
@@ -227,6 +250,10 @@ void P2PServer::update_peer_connections()
|
||||
}
|
||||
peer_list.pop_back();
|
||||
}
|
||||
|
||||
if ((m_numConnections == 0) && ((m_timerCounter % 30) == 0)) {
|
||||
LOGERR(1, "no connections to other p2pool nodes, check your monerod/p2pool/network/firewall setup!!!");
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::update_peer_list()
|
||||
@@ -244,13 +271,18 @@ void P2PServer::update_peer_list()
|
||||
// Send peer list requests at random intervals (60-120 seconds)
|
||||
client->m_nextPeerListRequest = cur_time + 60 + (get_random64() % 61);
|
||||
|
||||
send(client,
|
||||
const bool result = send(client,
|
||||
[](void* buf)
|
||||
{
|
||||
LOGINFO(5, "sending PEER_LIST_REQUEST");
|
||||
*reinterpret_cast<uint8_t*>(buf) = static_cast<uint8_t>(MessageId::PEER_LIST_REQUEST);
|
||||
return 1;
|
||||
});
|
||||
|
||||
if (result) {
|
||||
client->m_lastPeerListRequestTime = std::chrono::system_clock::now();
|
||||
++client->m_peerListPendingRequests;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,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;
|
||||
}
|
||||
}
|
||||
@@ -338,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;
|
||||
@@ -351,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;
|
||||
@@ -374,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 += ',';
|
||||
}
|
||||
@@ -384,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
|
||||
@@ -424,8 +461,6 @@ void P2PServer::load_peer_list()
|
||||
}
|
||||
p.m_isV6 = true;
|
||||
memcpy(p.m_addr.data, &addr6.sin6_addr, sizeof(in6_addr));
|
||||
p.m_port = port;
|
||||
p.m_numFailedConnections = 0;
|
||||
}
|
||||
else {
|
||||
sockaddr_in addr4;
|
||||
@@ -439,8 +474,6 @@ void P2PServer::load_peer_list()
|
||||
p.m_addr.data[10] = 0xFF;
|
||||
p.m_addr.data[11] = 0xFF;
|
||||
memcpy(p.m_addr.data + 12, &addr4.sin_addr, sizeof(in_addr));
|
||||
p.m_port = port;
|
||||
p.m_numFailedConnections = 0;
|
||||
}
|
||||
|
||||
bool already_added = false;
|
||||
@@ -451,6 +484,10 @@ void P2PServer::load_peer_list()
|
||||
}
|
||||
}
|
||||
|
||||
p.m_port = port;
|
||||
p.m_numFailedConnections = 0;
|
||||
p.m_lastSeen = time(nullptr);
|
||||
|
||||
if (!already_added && !is_banned(p.m_addr)) {
|
||||
m_peerList.push_back(p);
|
||||
}
|
||||
@@ -461,18 +498,21 @@ void P2PServer::load_peer_list()
|
||||
|
||||
void P2PServer::update_peer_in_list(bool is_v6, const raw_ip& ip, int port)
|
||||
{
|
||||
const time_t cur_time = time(nullptr);
|
||||
|
||||
MutexLock lock(m_peerListLock);
|
||||
|
||||
for (Peer& p : m_peerList) {
|
||||
if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) {
|
||||
p.m_port = port;
|
||||
p.m_numFailedConnections = 0;
|
||||
p.m_lastSeen = cur_time;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_banned(ip)) {
|
||||
m_peerList.emplace_back(Peer{ is_v6, ip, port, 0 });
|
||||
m_peerList.emplace_back(Peer{ is_v6, ip, port, 0, cur_time });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,18 +649,19 @@ void P2PServer::on_broadcast()
|
||||
uint8_t* p = p0;
|
||||
|
||||
bool send_pruned = true;
|
||||
{
|
||||
ReadLock lock(client->m_broadcastedHashesLock);
|
||||
for (const hash& id : data->ancestor_hashes) {
|
||||
if (client->m_broadcastedHashes.find(id) == client->m_broadcastedHashes.end()) {
|
||||
send_pruned = false;
|
||||
break;
|
||||
}
|
||||
|
||||
const hash* a = client->m_broadcastedHashes;
|
||||
const hash* b = client->m_broadcastedHashes + array_size(&P2PClient::m_broadcastedHashes);
|
||||
|
||||
for (const hash& id : data->ancestor_hashes) {
|
||||
if (std::find(a, b, id) == b) {
|
||||
send_pruned = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (send_pruned) {
|
||||
LOGINFO(5, "sending BLOCK_BROADCAST (pruned) to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
LOGINFO(6, "sending BLOCK_BROADCAST (pruned) to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
|
||||
|
||||
*reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(data->pruned_blob.size());
|
||||
@@ -654,14 +695,43 @@ 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;
|
||||
|
||||
if (!m_initialPeerList.empty()) {
|
||||
connect_to_peers(m_initialPeerList);
|
||||
m_initialPeerList.clear();
|
||||
@@ -672,11 +742,12 @@ void P2PServer::on_timer()
|
||||
update_peer_list();
|
||||
save_peer_list_async();
|
||||
update_peer_connections();
|
||||
check_zmq();
|
||||
}
|
||||
|
||||
void P2PServer::flush_cache()
|
||||
{
|
||||
if (!m_cache) {
|
||||
if (!m_cache || ((m_timerCounter % 30) != 2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -772,6 +843,21 @@ void P2PServer::download_missing_blocks()
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::check_zmq()
|
||||
{
|
||||
if ((m_timerCounter % 30) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const time_t cur_time = time(nullptr);
|
||||
const time_t last_active = m_pool->zmq_last_active();
|
||||
|
||||
if (cur_time >= last_active + 300) {
|
||||
const uint64_t dt = static_cast<uint64_t>(cur_time - last_active);
|
||||
LOGERR(1, "no ZMQ messages received from monerod in the last " << dt << " seconds, check your monerod/p2pool/network/firewall setup!!!");
|
||||
}
|
||||
}
|
||||
|
||||
P2PServer::P2PClient::P2PClient()
|
||||
: m_peerId(0)
|
||||
, m_expectedMessage(MessageId::HANDSHAKE_CHALLENGE)
|
||||
@@ -781,16 +867,18 @@ 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)
|
||||
, m_broadcastedHashes{}
|
||||
{
|
||||
uv_rwlock_init_checked(&m_broadcastedHashesLock);
|
||||
}
|
||||
|
||||
P2PServer::P2PClient::~P2PClient()
|
||||
{
|
||||
uv_rwlock_destroy(&m_broadcastedHashesLock);
|
||||
}
|
||||
|
||||
void P2PServer::P2PClient::reset()
|
||||
@@ -805,18 +893,32 @@ 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;
|
||||
|
||||
WriteLock lock(m_broadcastedHashesLock);
|
||||
m_broadcastedHashes.clear();
|
||||
for (hash& h : m_broadcastedHashes) {
|
||||
h = {};
|
||||
}
|
||||
m_broadcastedHashesIndex = 0;
|
||||
}
|
||||
|
||||
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)) {
|
||||
@@ -866,6 +968,13 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
switch (id)
|
||||
{
|
||||
case MessageId::HANDSHAKE_CHALLENGE:
|
||||
if (m_handshakeComplete) {
|
||||
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected HANDSHAKE_CHALLENGE");
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent HANDSHAKE_CHALLENGE");
|
||||
|
||||
if (bytes_left >= 1 + CHALLENGE_SIZE + sizeof(uint64_t)) {
|
||||
@@ -880,6 +989,13 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
break;
|
||||
|
||||
case MessageId::HANDSHAKE_SOLUTION:
|
||||
if (m_handshakeComplete) {
|
||||
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected HANDSHAKE_SOLUTION");
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent HANDSHAKE_SOLUTION");
|
||||
|
||||
if (bytes_left >= 1 + HASH_SIZE + CHALLENGE_SIZE) {
|
||||
@@ -935,7 +1051,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
break;
|
||||
|
||||
case MessageId::BLOCK_BROADCAST:
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_BROADCAST");
|
||||
LOGINFO(6, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_BROADCAST");
|
||||
|
||||
if (bytes_left >= 1 + sizeof(uint32_t)) {
|
||||
const uint32_t block_size = *reinterpret_cast<uint32_t*>(buf + 1);
|
||||
@@ -964,10 +1080,17 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
break;
|
||||
|
||||
case MessageId::PEER_LIST_RESPONSE:
|
||||
if (m_peerListPendingRequests <= 0) {
|
||||
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected PEER_LIST_RESPONSE");
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent 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);
|
||||
@@ -975,8 +1098,13 @@ 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)) {
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
@@ -1413,10 +1541,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
WriteLock lock2(m_broadcastedHashesLock);
|
||||
m_broadcastedHashes.insert(server->m_block->m_sidechainId);
|
||||
}
|
||||
m_broadcastedHashes[m_broadcastedHashesIndex.fetch_add(1) % array_size(&P2PClient::m_broadcastedHashes)] = server->m_block->m_sidechainId;
|
||||
|
||||
const MinerData& miner_data = server->m_pool->miner_data();
|
||||
|
||||
@@ -1444,8 +1569,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
}
|
||||
else if (peer_height > our_height) {
|
||||
if (peer_height >= our_height + 2) {
|
||||
const int level = (peer_height >= our_height + 3) ? 3 : 4;
|
||||
LOGWARN(level , "peer " << static_cast<char*>(m_addrString) << " is ahead on mainchain (height " << peer_height << ", your height " << our_height << "). Is your monerod stuck or lagging?");
|
||||
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " is ahead on mainchain (height " << peer_height << ", your height " << our_height << "). Is your monerod stuck or lagging?");
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1470,23 +1594,28 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||
MutexLock lock(server->m_clientsListLock);
|
||||
|
||||
// Send every 4th peer on average, selected at random
|
||||
const uint32_t n = server->m_numConnections;
|
||||
const uint32_t peers_to_send_target = std::min<uint32_t>(PEER_LIST_RESPONSE_MAX_PEERS, std::max<uint32_t>(1, n / 4));
|
||||
const uint32_t peers_to_send_target = std::min<uint32_t>(PEER_LIST_RESPONSE_MAX_PEERS, std::max<uint32_t>(1, server->m_numConnections / 4));
|
||||
uint32_t n = 0;
|
||||
|
||||
for (P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
if (client->m_listenPort < 0) {
|
||||
if ((client->m_listenPort < 0) || (client->m_addr == m_addr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t hi;
|
||||
umul128(server->get_random64(), n, &hi);
|
||||
const Peer p{ client->m_isV6, client->m_addr, client->m_listenPort, 0, 0 };
|
||||
++n;
|
||||
|
||||
if ((hi < peers_to_send_target) && (client->m_addr != m_addr)) {
|
||||
peers[num_selected_peers++] = { client->m_isV6, client->m_addr, client->m_listenPort, 0 };
|
||||
// Use https://en.wikipedia.org/wiki/Reservoir_sampling algorithm
|
||||
if (num_selected_peers < peers_to_send_target) {
|
||||
peers[num_selected_peers++] = p;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (num_selected_peers >= PEER_LIST_RESPONSE_MAX_PEERS) {
|
||||
break;
|
||||
}
|
||||
uint64_t k;
|
||||
umul128(server->get_random64(), n, &k);
|
||||
|
||||
if (k < peers_to_send_target) {
|
||||
peers[k] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1520,6 +1649,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||
bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
||||
{
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
const time_t cur_time = time(nullptr);
|
||||
|
||||
MutexLock lock(server->m_peerListLock);
|
||||
|
||||
@@ -1531,20 +1661,28 @@ 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;
|
||||
|
||||
bool already_added = false;
|
||||
for (const Peer& p : server->m_peerList) {
|
||||
for (Peer& p : server->m_peerList) {
|
||||
if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) {
|
||||
already_added = true;
|
||||
p.m_lastSeen = cur_time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!already_added && !server->is_banned(ip)) {
|
||||
server->m_peerList.emplace_back(Peer{ is_v6, ip, port, 0 });
|
||||
server->m_peerList.emplace_back(Peer{ is_v6, ip, port, 0, cur_time });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-5
@@ -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,13 +109,18 @@ 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;
|
||||
|
||||
uv_rwlock_t m_broadcastedHashesLock;
|
||||
std::set<hash> m_broadcastedHashes;
|
||||
hash m_broadcastedHashes[8];
|
||||
std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 };
|
||||
};
|
||||
|
||||
void broadcast(const PoolBlock& block);
|
||||
@@ -123,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(); }
|
||||
@@ -139,6 +154,7 @@ private:
|
||||
|
||||
void flush_cache();
|
||||
void download_missing_blocks();
|
||||
void check_zmq();
|
||||
void update_peer_connections();
|
||||
void update_peer_list();
|
||||
void save_peer_list_async();
|
||||
@@ -156,6 +172,7 @@ private:
|
||||
PoolBlock* m_block;
|
||||
|
||||
uv_timer_t m_timer;
|
||||
uint32_t m_timerCounter;
|
||||
|
||||
uint64_t m_peerId;
|
||||
|
||||
@@ -167,6 +184,7 @@ private:
|
||||
raw_ip m_addr;
|
||||
int m_port;
|
||||
uint32_t m_numFailedConnections;
|
||||
time_t m_lastSeen;
|
||||
};
|
||||
|
||||
std::vector<Peer> m_peerList;
|
||||
@@ -184,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();
|
||||
|
||||
+133
-27
@@ -27,6 +27,7 @@
|
||||
#include "side_chain.h"
|
||||
#include "stratum_server.h"
|
||||
#include "p2p_server.h"
|
||||
#include "miner.h"
|
||||
#include "params.h"
|
||||
#include "console_commands.h"
|
||||
#include "crypto.h"
|
||||
@@ -49,6 +50,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);
|
||||
|
||||
@@ -105,10 +108,27 @@ p2pool::p2pool(int argc, char* argv[])
|
||||
uv_mutex_init_checked(&m_foundBlocksLock);
|
||||
uv_mutex_init_checked(&m_submitBlockDataLock);
|
||||
|
||||
m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath);
|
||||
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 +149,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)
|
||||
@@ -173,6 +193,8 @@ void p2pool::handle_tx(TxMempoolData& tx)
|
||||
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
||||
m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet);
|
||||
#endif
|
||||
|
||||
m_zmqLastActive = time(nullptr);
|
||||
}
|
||||
|
||||
void p2pool::handle_miner_data(MinerData& data)
|
||||
@@ -199,6 +221,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();
|
||||
@@ -220,12 +244,49 @@ void p2pool::handle_miner_data(MinerData& data)
|
||||
"\n---------------------------------------------------------------------------------------------------------------"
|
||||
);
|
||||
|
||||
if (!is_main_thread) {
|
||||
if (!is_main_thread()) {
|
||||
update_block_template_async();
|
||||
}
|
||||
else {
|
||||
update_block_template();
|
||||
}
|
||||
|
||||
m_zmqLastActive = time(nullptr);
|
||||
|
||||
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\
|
||||
@@ -288,6 +349,8 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
|
||||
}
|
||||
|
||||
api_update_network_stats();
|
||||
|
||||
m_zmqLastActive = time(nullptr);
|
||||
}
|
||||
|
||||
void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
|
||||
@@ -525,8 +588,12 @@ 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);
|
||||
if (m_params->m_minerThreads) {
|
||||
start_mining(m_params->m_minerThreads);
|
||||
}
|
||||
api_update_network_stats();
|
||||
}
|
||||
}
|
||||
@@ -593,22 +660,12 @@ void p2pool::update_median_timestamp()
|
||||
|
||||
void p2pool::stratum_on_block()
|
||||
{
|
||||
#if 0
|
||||
uint8_t hashing_blob[128];
|
||||
uint64_t height;
|
||||
difficulty_type difficulty;
|
||||
difficulty_type sidechain_difficulty;
|
||||
hash seed_hash;
|
||||
size_t nonce_offset;
|
||||
uint32_t template_id;
|
||||
|
||||
m_blockTemplate->get_hashing_blob(0, hashing_blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset, template_id);
|
||||
submit_block(template_id, 0, 0);
|
||||
#else
|
||||
if (m_miner) {
|
||||
m_miner->on_block(*m_blockTemplate);
|
||||
}
|
||||
if (m_stratumServer) {
|
||||
m_stratumServer->on_block(*m_blockTemplate);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void p2pool::get_info()
|
||||
@@ -680,18 +737,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;
|
||||
@@ -761,10 +822,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();
|
||||
}
|
||||
|
||||
@@ -963,7 +1028,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;
|
||||
@@ -1026,7 +1091,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();
|
||||
@@ -1055,6 +1120,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();
|
||||
@@ -1118,6 +1208,21 @@ bool p2pool::get_difficulty_at_height(uint64_t height, difficulty_type& diff)
|
||||
return true;
|
||||
}
|
||||
|
||||
void p2pool::start_mining(uint32_t threads)
|
||||
{
|
||||
stop_mining();
|
||||
m_miner = new Miner(this, threads);
|
||||
}
|
||||
|
||||
void p2pool::stop_mining()
|
||||
{
|
||||
Miner* miner = m_miner;
|
||||
if (miner) {
|
||||
m_miner = nullptr;
|
||||
delete miner;
|
||||
}
|
||||
}
|
||||
|
||||
static void on_signal(uv_signal_t* handle, int signum)
|
||||
{
|
||||
p2pool* pool = reinterpret_cast<p2pool*>(handle->data);
|
||||
@@ -1210,11 +1315,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();
|
||||
@@ -1226,6 +1331,7 @@ int p2pool::run()
|
||||
|
||||
bkg_jobs_tracker.wait();
|
||||
|
||||
delete m_miner;
|
||||
delete m_stratumServer;
|
||||
delete m_p2pServer;
|
||||
|
||||
|
||||
+27
-9
@@ -19,19 +19,20 @@
|
||||
|
||||
#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;
|
||||
class StratumServer;
|
||||
class P2PServer;
|
||||
class Miner;
|
||||
class ConsoleCommands;
|
||||
class p2pool_api;
|
||||
class ZMQReader;
|
||||
|
||||
class p2pool : public MinerCallbackHandler
|
||||
{
|
||||
@@ -49,13 +50,16 @@ public:
|
||||
SideChain& side_chain() { return *m_sideChain; }
|
||||
const MinerData& miner_data() const { return m_minerData; }
|
||||
|
||||
RandomX_Hasher* hasher() const { return m_hasher; }
|
||||
bool calculate_hash(const void* data, size_t size, const hash& seed, hash& result);
|
||||
p2pool_api* api() const { return m_api; }
|
||||
|
||||
RandomX_Hasher_Base* hasher() const { return m_hasher; }
|
||||
bool calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result);
|
||||
static uint64_t get_seed_height(uint64_t height);
|
||||
bool get_seed(uint64_t height, hash& seed) const;
|
||||
|
||||
StratumServer* stratum_server() const { return m_stratumServer; }
|
||||
P2PServer* p2p_server() const { return m_p2pServer; }
|
||||
Miner* miner() const { return m_miner; }
|
||||
|
||||
virtual void handle_tx(TxMempoolData& tx) override;
|
||||
virtual void handle_miner_data(MinerData& data) override;
|
||||
@@ -76,6 +80,12 @@ public:
|
||||
|
||||
bool get_difficulty_at_height(uint64_t height, difficulty_type& diff);
|
||||
|
||||
void start_mining(uint32_t threads);
|
||||
void stop_mining();
|
||||
|
||||
time_t zmq_last_active() const { return m_zmqLastActive; }
|
||||
time_t start_time() const { return m_startTime; }
|
||||
|
||||
private:
|
||||
p2pool(const p2pool&) = delete;
|
||||
p2pool(p2pool&&) = delete;
|
||||
@@ -92,7 +102,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;
|
||||
@@ -100,7 +110,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;
|
||||
@@ -125,6 +135,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)
|
||||
@@ -148,14 +160,15 @@ private:
|
||||
std::atomic<uint32_t> m_serversStarted{ 0 };
|
||||
StratumServer* m_stratumServer = nullptr;
|
||||
P2PServer* m_p2pServer = nullptr;
|
||||
Miner* m_miner = nullptr;
|
||||
|
||||
ConsoleCommands* m_consoleCommands;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -165,6 +178,11 @@ private:
|
||||
uv_async_t m_submitBlockAsync;
|
||||
uv_async_t m_blockTemplateAsync;
|
||||
uv_async_t m_stopAsync;
|
||||
|
||||
time_t m_zmqLastActive;
|
||||
time_t m_startTime;
|
||||
|
||||
ZMQReader* m_ZMQReader = nullptr;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+8
-2
@@ -28,7 +28,7 @@ static constexpr char log_category_prefix[] = "P2Pool API ";
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
p2pool_api::p2pool_api(const std::string& api_path) : m_apiPath(api_path)
|
||||
p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats): m_apiPath(api_path)
|
||||
{
|
||||
if (m_apiPath.empty()) {
|
||||
LOGERR(1, "api path is empty");
|
||||
@@ -60,9 +60,14 @@ p2pool_api::p2pool_api(const std::string& api_path) : m_apiPath(api_path)
|
||||
|
||||
m_networkPath = m_apiPath + "network/";
|
||||
m_poolPath = m_apiPath + "pool/";
|
||||
m_localPath = m_apiPath + "local/";
|
||||
|
||||
create_dir(m_networkPath);
|
||||
create_dir(m_poolPath);
|
||||
|
||||
if (local_stats) {
|
||||
create_dir(m_localPath);
|
||||
}
|
||||
}
|
||||
|
||||
p2pool_api::~p2pool_api()
|
||||
@@ -109,6 +114,7 @@ void p2pool_api::dump_to_file_async_internal(const Category& category, const cha
|
||||
case Category::GLOBAL: path = m_apiPath + filename; break;
|
||||
case Category::NETWORK: path = m_networkPath + filename; break;
|
||||
case Category::POOL: path = m_poolPath + filename; break;
|
||||
case Category::LOCAL: path = m_localPath + filename; break;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -121,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);
|
||||
|
||||
+4
-3
@@ -18,20 +18,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "uv_util.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
class p2pool_api
|
||||
{
|
||||
public:
|
||||
explicit p2pool_api(const std::string& api_path);
|
||||
p2pool_api(const std::string& api_path, const bool local_stats);
|
||||
~p2pool_api();
|
||||
|
||||
enum class Category {
|
||||
GLOBAL,
|
||||
NETWORK,
|
||||
POOL,
|
||||
LOCAL,
|
||||
};
|
||||
|
||||
void on_stop();
|
||||
@@ -80,9 +80,10 @@ private:
|
||||
std::string m_apiPath;
|
||||
std::string m_networkPath;
|
||||
std::string m_poolPath;
|
||||
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;
|
||||
};
|
||||
|
||||
+31
-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;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,34 @@ Params::Params(int argc, char* argv[])
|
||||
if ((strcmp(argv[i], "--data-api") == 0) && (i + 1 < argc)) {
|
||||
m_apiPath = argv[++i];
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--local-api") == 0) || (strcmp(argv[i], "--stratum-api") == 0)) {
|
||||
m_localStats = true;
|
||||
}
|
||||
|
||||
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 ((strcmp(argv[i], "--start-mining") == 0) && (i + 1 < argc)) {
|
||||
m_minerThreads = std::min(std::max(strtoul(argv[++i], nullptr, 10), 1UL), 64UL);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_stratumAddresses.empty()) {
|
||||
@@ -80,16 +108,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
|
||||
|
||||
@@ -37,6 +37,12 @@ struct Params
|
||||
std::string m_p2pPeerList;
|
||||
std::string m_config;
|
||||
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;
|
||||
uint32_t m_minerThreads = 0;
|
||||
};
|
||||
|
||||
} // 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;
|
||||
|
||||
|
||||
+125
-12
@@ -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 ";
|
||||
@@ -34,7 +37,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
|
||||
, m_dataset(nullptr)
|
||||
, m_seed{}
|
||||
, m_index(0)
|
||||
, m_setSeedCounter(0)
|
||||
, m_seedCounter(0)
|
||||
{
|
||||
uint64_t memory_allocated = 0;
|
||||
|
||||
@@ -54,7 +57,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
|
||||
|
||||
const randomx_flags flags = randomx_get_flags();
|
||||
|
||||
for (size_t i = 0; i < array_size(m_cache); ++i) {
|
||||
for (size_t i = 0; i < array_size(&RandomX_Hasher::m_cache); ++i) {
|
||||
m_cache[i] = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
|
||||
if (!m_cache[i]) {
|
||||
LOGWARN(1, "couldn't allocate RandomX cache using large pages");
|
||||
@@ -70,7 +73,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
|
||||
uv_rwlock_init_checked(&m_datasetLock);
|
||||
uv_rwlock_init_checked(&m_cacheLock);
|
||||
|
||||
for (size_t i = 0; i < array_size(m_vm); ++i) {
|
||||
for (size_t i = 0; i < array_size(&RandomX_Hasher::m_vm); ++i) {
|
||||
uv_mutex_init_checked(&m_vm[i].mutex);
|
||||
m_vm[i].vm = nullptr;
|
||||
}
|
||||
@@ -91,7 +94,7 @@ RandomX_Hasher::~RandomX_Hasher()
|
||||
uv_rwlock_destroy(&m_datasetLock);
|
||||
uv_rwlock_destroy(&m_cacheLock);
|
||||
|
||||
for (size_t i = 0; i < array_size(m_vm); ++i) {
|
||||
for (size_t i = 0; i < array_size(&RandomX_Hasher::m_vm); ++i) {
|
||||
{
|
||||
MutexLock lock(m_vm[i].mutex);
|
||||
if (m_vm[i].vm) {
|
||||
@@ -105,7 +108,7 @@ RandomX_Hasher::~RandomX_Hasher()
|
||||
randomx_release_dataset(m_dataset);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < array_size(m_cache); ++i) {
|
||||
for (size_t i = 0; i < array_size(&RandomX_Hasher::m_cache); ++i) {
|
||||
if (m_cache[i]) {
|
||||
randomx_release_cache(m_cache[i]);
|
||||
}
|
||||
@@ -116,6 +119,10 @@ RandomX_Hasher::~RandomX_Hasher()
|
||||
|
||||
void RandomX_Hasher::set_seed_async(const hash& seed)
|
||||
{
|
||||
if (m_seed[m_index] == seed) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct Work
|
||||
{
|
||||
p2pool* pool;
|
||||
@@ -124,10 +131,7 @@ void RandomX_Hasher::set_seed_async(const hash& seed)
|
||||
uv_work_t req;
|
||||
};
|
||||
|
||||
Work* work = new Work{};
|
||||
work->pool = m_pool;
|
||||
work->hasher = this;
|
||||
work->seed = seed;
|
||||
Work* work = new Work{ m_pool, this, seed, {} };
|
||||
work->req.data = work;
|
||||
|
||||
const int err = uv_queue_work(uv_default_loop_checked(), &work->req,
|
||||
@@ -164,7 +168,7 @@ void RandomX_Hasher::set_seed(const hash& seed)
|
||||
WriteLock lock(m_datasetLock);
|
||||
uv_rwlock_wrlock(&m_cacheLock);
|
||||
|
||||
m_setSeedCounter.fetch_add(1);
|
||||
m_seedCounter.fetch_add(1);
|
||||
|
||||
if (m_seed[m_index] == seed) {
|
||||
uv_rwlock_wrunlock(&m_cacheLock);
|
||||
@@ -266,7 +270,7 @@ void RandomX_Hasher::set_seed(const hash& seed)
|
||||
void RandomX_Hasher::set_old_seed(const hash& seed)
|
||||
{
|
||||
// set_seed() must go first, wait for it
|
||||
while (m_setSeedCounter.load() == 0) {
|
||||
while (m_seedCounter.load() == 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
@@ -302,7 +306,13 @@ void RandomX_Hasher::set_old_seed(const hash& seed)
|
||||
LOGINFO(1, log::LightCyan() << "old cache updated");
|
||||
}
|
||||
|
||||
bool RandomX_Hasher::calculate(const void* data, size_t size, const hash& seed, hash& result)
|
||||
void RandomX_Hasher::sync_wait()
|
||||
{
|
||||
ReadLock lock(m_datasetLock);
|
||||
ReadLock lock2(m_cacheLock);
|
||||
}
|
||||
|
||||
bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*/, const hash& seed, hash& result)
|
||||
{
|
||||
// First try to use the dataset if it's ready
|
||||
if (uv_rwlock_tryrdlock(&m_datasetLock) == 0) {
|
||||
@@ -348,4 +358,107 @@ 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_loopThread{}
|
||||
{
|
||||
int err = uv_loop_init(&m_loop);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
|
||||
uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
|
||||
uv_async_init(&m_loop, &m_kickTheLoopAsync, nullptr);
|
||||
m_shutdownAsync.data = this;
|
||||
|
||||
uv_mutex_init_checked(&m_requestMutex);
|
||||
uv_mutex_init_checked(&m_condMutex);
|
||||
|
||||
err = uv_cond_init(&m_cond);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to create cond, error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
|
||||
err = uv_thread_create(&m_loopThread, loop, this);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
RandomX_Hasher_RPC::~RandomX_Hasher_RPC()
|
||||
{
|
||||
uv_async_send(&m_shutdownAsync);
|
||||
uv_thread_join(&m_loopThread);
|
||||
|
||||
uv_mutex_destroy(&m_requestMutex);
|
||||
uv_mutex_destroy(&m_condMutex);
|
||||
uv_cond_destroy(&m_cond);
|
||||
|
||||
LOGINFO(1, "stopped");
|
||||
}
|
||||
|
||||
void RandomX_Hasher_RPC::loop(void* data)
|
||||
{
|
||||
LOGINFO(1, "event loop started");
|
||||
RandomX_Hasher_RPC* hasher = static_cast<RandomX_Hasher_RPC*>(data);
|
||||
uv_run(&hasher->m_loop, UV_RUN_DEFAULT);
|
||||
uv_loop_close(&hasher->m_loop);
|
||||
LOGINFO(1, "event loop stopped");
|
||||
}
|
||||
|
||||
bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t height, const hash& /*seed*/, hash& h)
|
||||
{
|
||||
MutexLock lock(m_requestMutex);
|
||||
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(data_ptr);
|
||||
const uint8_t major_version = data[0];
|
||||
|
||||
char buf[log::Stream::BUF_SIZE + 1];
|
||||
log::Stream s(buf);
|
||||
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"calc_pow\",\"params\":{\"major_version\":" << major_version <<
|
||||
",\"height\":" << height <<
|
||||
",\"block_blob\":\"" << log::hex_buf(data, size) << '"' <<
|
||||
",\"seed_hash\":\"\"}}";
|
||||
|
||||
volatile int result = 0;
|
||||
volatile bool done = false;
|
||||
|
||||
JSONRPCRequest::call(m_pool->params().m_host.c_str(), m_pool->params().m_rpcPort, buf,
|
||||
[&result, &h](const char* data, size_t size)
|
||||
{
|
||||
rapidjson::Document doc;
|
||||
if (doc.Parse(data, size).HasParseError() || !parseValue(doc, "result", h)) {
|
||||
LOGWARN(3, "RPC calc_pow: invalid JSON response (parse error)");
|
||||
result = -1;
|
||||
return;
|
||||
}
|
||||
result = 1;
|
||||
},
|
||||
[this, &result, &done](const char* data, size_t size)
|
||||
{
|
||||
if (size > 0) {
|
||||
LOGWARN(3, "RPC calc_pow: server returned error " << log::const_buf(data, size));
|
||||
result = -1;
|
||||
}
|
||||
|
||||
MutexLock lock2(m_condMutex);
|
||||
done = true;
|
||||
uv_cond_signal(&m_cond);
|
||||
}, &m_loop);
|
||||
|
||||
uv_async_send(&m_kickTheLoopAsync);
|
||||
|
||||
{
|
||||
MutexLock lock2(m_condMutex);
|
||||
while (!done) {
|
||||
uv_cond_wait(&m_cond, &m_condMutex);
|
||||
}
|
||||
}
|
||||
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+60
-8
@@ -27,25 +27,46 @@ namespace p2pool {
|
||||
|
||||
class p2pool;
|
||||
|
||||
class RandomX_Hasher
|
||||
class RandomX_Hasher_Base
|
||||
{
|
||||
public:
|
||||
virtual ~RandomX_Hasher_Base() {}
|
||||
|
||||
virtual void set_seed_async(const hash&) {}
|
||||
virtual void set_old_seed(const hash&) {}
|
||||
|
||||
virtual randomx_cache* cache() const { return nullptr; }
|
||||
virtual randomx_dataset* dataset() const { return nullptr; }
|
||||
virtual uint32_t seed_counter() const { return 0; }
|
||||
virtual void sync_wait() {}
|
||||
|
||||
virtual bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) = 0;
|
||||
};
|
||||
|
||||
class RandomX_Hasher : public RandomX_Hasher_Base
|
||||
{
|
||||
public:
|
||||
explicit RandomX_Hasher(p2pool* pool);
|
||||
~RandomX_Hasher();
|
||||
|
||||
void set_seed_async(const hash& seed);
|
||||
void set_seed_async(const hash& seed) override;
|
||||
void set_seed(const hash& seed);
|
||||
|
||||
void set_old_seed(const hash& seed);
|
||||
void set_old_seed(const hash& seed) override;
|
||||
|
||||
bool calculate(const void* data, size_t size, const hash& seed, hash& result);
|
||||
randomx_cache* cache() const override { return m_cache[m_index]; }
|
||||
randomx_dataset* dataset() const override { return m_dataset; }
|
||||
uint32_t seed_counter() const override { return m_seedCounter.load(); }
|
||||
void sync_wait() override;
|
||||
|
||||
bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) override;
|
||||
|
||||
private:
|
||||
|
||||
struct ThreadSafeVM
|
||||
{
|
||||
uv_mutex_t mutex;
|
||||
randomx_vm* vm;
|
||||
uv_mutex_t mutex{};
|
||||
randomx_vm* vm = nullptr;
|
||||
};
|
||||
|
||||
p2pool* m_pool;
|
||||
@@ -62,12 +83,43 @@ 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;
|
||||
|
||||
std::atomic<uint32_t> m_setSeedCounter;
|
||||
std::atomic<uint32_t> m_seedCounter;
|
||||
};
|
||||
|
||||
class RandomX_Hasher_RPC : public RandomX_Hasher_Base
|
||||
{
|
||||
public:
|
||||
explicit RandomX_Hasher_RPC(p2pool* pool);
|
||||
~RandomX_Hasher_RPC();
|
||||
|
||||
bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) override;
|
||||
|
||||
private:
|
||||
static void loop(void* data);
|
||||
|
||||
p2pool* m_pool;
|
||||
|
||||
uv_mutex_t m_requestMutex;
|
||||
uv_loop_t m_loop;
|
||||
|
||||
uv_thread_t m_loopThread;
|
||||
uv_mutex_t m_condMutex;
|
||||
uv_cond_t m_cond;
|
||||
|
||||
uv_async_t m_shutdownAsync;
|
||||
uv_async_t m_kickTheLoopAsync;
|
||||
|
||||
static void on_shutdown(uv_async_t* async)
|
||||
{
|
||||
RandomX_Hasher_RPC* server = reinterpret_cast<RandomX_Hasher_RPC*>(async->data);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_shutdownAsync), nullptr);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_kickTheLoopAsync), nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+92
-46
@@ -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,18 +356,24 @@ 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_seenBlocksLock);
|
||||
m_seenBlocks.erase(block.m_sidechainId);
|
||||
}
|
||||
|
||||
extern const char* BLOCK_FOUND;
|
||||
|
||||
bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks)
|
||||
@@ -364,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()) {
|
||||
@@ -372,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;
|
||||
}
|
||||
|
||||
@@ -407,13 +428,15 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
hash seed;
|
||||
if (!m_pool->get_seed(block.m_txinGenHeight, seed)) {
|
||||
LOGWARN(3, "add_external_block: couldn't get seed hash for mainchain height " << block.m_txinGenHeight);
|
||||
unsee_block(block);
|
||||
return false;
|
||||
}
|
||||
|
||||
hash pow_hash;
|
||||
if (!block.get_pow_hash(m_pool->hasher(), seed, pow_hash)) {
|
||||
LOGWARN(3, "add_external_block: couldn't get PoW hash for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight);
|
||||
return false;
|
||||
if (!block.get_pow_hash(m_pool->hasher(), block.m_txinGenHeight, seed, pow_hash)) {
|
||||
LOGWARN(3, "add_external_block: couldn't get PoW hash for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight << ". Ignoring it.");
|
||||
unsee_block(block);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if it has the correct parent and difficulty to go right to monerod for checking
|
||||
@@ -478,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);
|
||||
@@ -556,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);
|
||||
@@ -580,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;
|
||||
@@ -730,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 {
|
||||
@@ -753,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();
|
||||
@@ -946,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
|
||||
@@ -1289,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();
|
||||
}
|
||||
}
|
||||
@@ -1305,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
|
||||
@@ -1316,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*)
|
||||
@@ -1335,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
|
||||
}
|
||||
}
|
||||
@@ -1531,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;
|
||||
}
|
||||
@@ -1555,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-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;
|
||||
@@ -47,6 +46,7 @@ public:
|
||||
void fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares);
|
||||
|
||||
bool block_seen(const PoolBlock& block);
|
||||
void unsee_block(const PoolBlock& block);
|
||||
bool add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks);
|
||||
void add_block(const PoolBlock& block);
|
||||
void get_missing_blocks(std::vector<hash>& missing_blocks);
|
||||
@@ -71,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:
|
||||
@@ -97,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;
|
||||
|
||||
|
||||
+175
-49
@@ -21,6 +21,7 @@
|
||||
#include "p2pool.h"
|
||||
#include "side_chain.h"
|
||||
#include "params.h"
|
||||
#include "p2pool_api.h"
|
||||
|
||||
static constexpr char log_category_prefix[] = "StratumServer ";
|
||||
|
||||
@@ -48,6 +49,7 @@ StratumServer::StratumServer(p2pool* pool)
|
||||
, m_hashrateDataTail_24h(0)
|
||||
, m_cumulativeFoundSharesDiff(0.0)
|
||||
, m_totalFoundShares(0)
|
||||
, m_apiLastUpdateTime(0)
|
||||
{
|
||||
m_hashrateData[0] = { time(nullptr), 0 };
|
||||
|
||||
@@ -174,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;
|
||||
@@ -223,13 +247,17 @@ 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);
|
||||
|
||||
job_id = client->m_perConnectionJobId++;
|
||||
|
||||
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(client->m_jobs)];
|
||||
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
|
||||
saved_job.job_id = job_id;
|
||||
saved_job.extra_nonce = extra_nonce;
|
||||
saved_job.template_id = template_id;
|
||||
@@ -307,7 +335,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
{
|
||||
MutexLock lock(client->m_jobsLock);
|
||||
|
||||
const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(client->m_jobs)];
|
||||
const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
|
||||
if (saved_job.job_id == job_id) {
|
||||
template_id = saved_job.template_id;
|
||||
extra_nonce = saved_job.extra_nonce;
|
||||
@@ -317,6 +345,27 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
}
|
||||
|
||||
if (found) {
|
||||
BlockTemplate& block = m_pool->block_template();
|
||||
difficulty_type mainchain_diff, sidechain_diff;
|
||||
|
||||
if (!block.get_difficulties(template_id, mainchain_diff, sidechain_diff)) {
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
|
||||
return send(client,
|
||||
[id](void* buf)
|
||||
{
|
||||
log::Stream s(reinterpret_cast<char*>(buf));
|
||||
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Stale share\"}}\n";
|
||||
return s.m_pos;
|
||||
});
|
||||
}
|
||||
|
||||
if (mainchain_diff.check_pow(resultHash)) {
|
||||
const std::string& s = client->m_customUser;
|
||||
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (!s.empty() ? " user " : "") << s << " found a mainchain block, submitting it");
|
||||
m_pool->submit_block_async(template_id, nonce, extra_nonce);
|
||||
block.update_tx_keys();
|
||||
}
|
||||
|
||||
SubmittedShare* share;
|
||||
|
||||
{
|
||||
@@ -334,6 +383,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
share->m_req.data = share;
|
||||
share->m_server = this;
|
||||
share->m_client = client;
|
||||
share->m_clientAddr = client->m_addr;
|
||||
share->m_clientResetCounter = client->m_resetCounter.load();
|
||||
share->m_rpcId = client->m_rpcId;
|
||||
share->m_id = id;
|
||||
@@ -342,13 +392,23 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
share->m_extraNonce = extra_nonce;
|
||||
share->m_target = target;
|
||||
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;
|
||||
@@ -375,7 +435,7 @@ uint64_t StratumServer::get_random64()
|
||||
|
||||
void StratumServer::print_status()
|
||||
{
|
||||
update_hashrate_data(0);
|
||||
update_hashrate_data(0, time(nullptr));
|
||||
print_stratum_status();
|
||||
}
|
||||
|
||||
@@ -485,7 +545,7 @@ void StratumServer::on_blobs_ready()
|
||||
|
||||
job_id = client->m_perConnectionJobId++;
|
||||
|
||||
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(client->m_jobs)];
|
||||
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
|
||||
saved_job.job_id = job_id;
|
||||
saved_job.extra_nonce = extra_nonce;
|
||||
saved_job.template_id = data->m_templateId;
|
||||
@@ -528,12 +588,9 @@ void StratumServer::on_blobs_ready()
|
||||
LOGINFO(3, "sent new job to " << extra_nonce << '/' << numClientsProcessed << " clients");
|
||||
}
|
||||
|
||||
void StratumServer::update_hashrate_data(uint64_t target)
|
||||
void StratumServer::update_hashrate_data(uint64_t hashes, time_t timestamp)
|
||||
{
|
||||
const time_t timestamp = time(nullptr);
|
||||
|
||||
uint64_t rem;
|
||||
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
|
||||
constexpr size_t N = array_size(&StratumServer::m_hashrateData);
|
||||
|
||||
WriteLock lock(m_hashrateDataLock);
|
||||
|
||||
@@ -545,20 +602,20 @@ void StratumServer::update_hashrate_data(uint64_t target)
|
||||
head.m_cumulativeHashes = m_cumulativeHashes;
|
||||
}
|
||||
else {
|
||||
m_hashrateDataHead = (m_hashrateDataHead + 1) % array_size(m_hashrateData);
|
||||
m_hashrateDataHead = (m_hashrateDataHead + 1) % N;
|
||||
data[m_hashrateDataHead] = { timestamp, m_cumulativeHashes };
|
||||
}
|
||||
|
||||
while (data[m_hashrateDataTail_15m].m_timestamp + 15 * 60 < timestamp) {
|
||||
m_hashrateDataTail_15m = (m_hashrateDataTail_15m + 1) % array_size(m_hashrateData);
|
||||
m_hashrateDataTail_15m = (m_hashrateDataTail_15m + 1) % N;
|
||||
}
|
||||
|
||||
while (data[m_hashrateDataTail_1h].m_timestamp + 60 * 60 < timestamp) {
|
||||
m_hashrateDataTail_1h = (m_hashrateDataTail_1h + 1) % array_size(m_hashrateData);
|
||||
m_hashrateDataTail_1h = (m_hashrateDataTail_1h + 1) % N;
|
||||
}
|
||||
|
||||
while (data[m_hashrateDataTail_24h].m_timestamp + 60 * 60 * 24 < timestamp) {
|
||||
m_hashrateDataTail_24h = (m_hashrateDataTail_24h + 1) % array_size(m_hashrateData);
|
||||
m_hashrateDataTail_24h = (m_hashrateDataTail_24h + 1) % N;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,42 +627,41 @@ void StratumServer::on_share_found(uv_work_t* req)
|
||||
StratumClient* client = share->m_client;
|
||||
StratumServer* server = share->m_server;
|
||||
p2pool* pool = server->m_pool;
|
||||
BlockTemplate& block = pool->block_template();
|
||||
|
||||
uint64_t target = share->m_target;
|
||||
if (target >= TARGET_4_BYTES_LIMIT) {
|
||||
target = (target >> 32) << 32;
|
||||
}
|
||||
|
||||
uint64_t rem;
|
||||
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
|
||||
|
||||
if (pool->stopped()) {
|
||||
LOGWARN(0, "p2pool is shutting down, but a share was found. Trying to process it anyway!");
|
||||
}
|
||||
|
||||
uint8_t blob[128];
|
||||
uint64_t height;
|
||||
difficulty_type difficulty;
|
||||
difficulty_type sidechain_difficulty;
|
||||
hash seed_hash;
|
||||
size_t nonce_offset;
|
||||
if (share->m_sidechainDifficulty.check_pow(share->m_resultHash)) {
|
||||
uint8_t blob[128];
|
||||
uint64_t height;
|
||||
difficulty_type difficulty;
|
||||
difficulty_type sidechain_difficulty;
|
||||
hash seed_hash;
|
||||
size_t nonce_offset;
|
||||
|
||||
const uint32_t blob_size = block.get_hashing_blob(share->m_templateId, share->m_extraNonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset);
|
||||
if (!blob_size) {
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
|
||||
share->m_result = SubmittedShare::Result::STALE;
|
||||
return;
|
||||
}
|
||||
const uint32_t blob_size = pool->block_template().get_hashing_blob(share->m_templateId, share->m_extraNonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset);
|
||||
if (!blob_size) {
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
|
||||
share->m_result = SubmittedShare::Result::STALE;
|
||||
return;
|
||||
}
|
||||
|
||||
const bool mainchain_solution = difficulty.check_pow(share->m_resultHash);
|
||||
const bool sidechain_solution = sidechain_difficulty.check_pow(share->m_resultHash);
|
||||
|
||||
if (mainchain_solution || sidechain_solution) {
|
||||
for (uint32_t i = 0, nonce = share->m_nonce; i < sizeof(share->m_nonce); ++i) {
|
||||
blob[nonce_offset + i] = nonce & 255;
|
||||
nonce >>= 8;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -617,9 +673,6 @@ void StratumServer::on_share_found(uv_work_t* req)
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t rem;
|
||||
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
|
||||
|
||||
const uint64_t n = server->m_cumulativeHashes + hashes;
|
||||
const double diff = sidechain_difficulty.to_double();
|
||||
const double effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
|
||||
@@ -628,23 +681,18 @@ 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 << '%');
|
||||
|
||||
if (mainchain_solution) {
|
||||
pool->submit_block_async(share->m_templateId, share->m_nonce, share->m_extraNonce);
|
||||
block.update_tx_keys();
|
||||
}
|
||||
|
||||
if (sidechain_solution) {
|
||||
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
// Send the response to miner
|
||||
const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t));
|
||||
|
||||
if (LIKELY(value < target)) {
|
||||
server->update_hashrate_data(target);
|
||||
const time_t timestamp = time(nullptr);
|
||||
server->update_hashrate_data(hashes, timestamp);
|
||||
server->api_update_local_stats(timestamp);
|
||||
share->m_result = SubmittedShare::Result::OK;
|
||||
}
|
||||
else {
|
||||
@@ -670,6 +718,8 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
StratumClient* client = share->m_client;
|
||||
StratumServer* server = share->m_server;
|
||||
|
||||
const bool bad_share = (share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW);
|
||||
|
||||
if ((client->m_resetCounter.load() == share->m_clientResetCounter) && (client->m_rpcId == share->m_rpcId)) {
|
||||
const bool result = server->send(client,
|
||||
[share](void* buf)
|
||||
@@ -695,7 +745,7 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
return s.m_pos;
|
||||
});
|
||||
|
||||
if ((share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW)) {
|
||||
if (bad_share) {
|
||||
client->ban(DEFAULT_BAN_TIME);
|
||||
client->close();
|
||||
}
|
||||
@@ -703,6 +753,9 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
client->close();
|
||||
}
|
||||
}
|
||||
else if (bad_share) {
|
||||
server->ban(share->m_clientAddr, DEFAULT_BAN_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
StratumServer::StratumClient::StratumClient()
|
||||
@@ -726,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)
|
||||
@@ -914,4 +968,76 @@ bool StratumServer::StratumClient::process_submit(rapidjson::Document& doc, uint
|
||||
return static_cast<StratumServer*>(m_owner)->on_submit(this, id, job_id.GetString(), nonce.GetString(), result.GetString());
|
||||
}
|
||||
|
||||
void StratumServer::api_update_local_stats(time_t timestamp)
|
||||
{
|
||||
if (!m_pool->api() || !m_pool->params().m_localStats) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rate limit to no more than once in 60 seconds.
|
||||
if (timestamp < m_apiLastUpdateTime + 60) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_apiLastUpdateTime = timestamp;
|
||||
|
||||
uint64_t hashes_15m, hashes_1h, hashes_24h, total_hashes;
|
||||
int64_t dt_15m, dt_1h, dt_24h;
|
||||
|
||||
uint64_t hashes_since_last_share;
|
||||
|
||||
{
|
||||
ReadLock lock(m_hashrateDataLock);
|
||||
|
||||
total_hashes = m_cumulativeHashes;
|
||||
hashes_since_last_share = m_cumulativeHashes - m_cumulativeHashesAtLastShare;
|
||||
|
||||
const HashrateData* data = m_hashrateData;
|
||||
const HashrateData& head = data[m_hashrateDataHead];
|
||||
const HashrateData& tail_15m = data[m_hashrateDataTail_15m];
|
||||
const HashrateData& tail_1h = data[m_hashrateDataTail_1h];
|
||||
const HashrateData& tail_24h = data[m_hashrateDataTail_24h];
|
||||
|
||||
hashes_15m = head.m_cumulativeHashes - tail_15m.m_cumulativeHashes;
|
||||
dt_15m = static_cast<int64_t>(head.m_timestamp - tail_15m.m_timestamp);
|
||||
|
||||
hashes_1h = head.m_cumulativeHashes - tail_1h.m_cumulativeHashes;
|
||||
dt_1h = static_cast<int64_t>(head.m_timestamp - tail_1h.m_timestamp);
|
||||
|
||||
hashes_24h = head.m_cumulativeHashes - tail_24h.m_cumulativeHashes;
|
||||
dt_24h = static_cast<int64_t>(head.m_timestamp - tail_24h.m_timestamp);
|
||||
}
|
||||
|
||||
const uint64_t hashrate_15m = (dt_15m > 0) ? (hashes_15m / dt_15m) : 0;
|
||||
const uint64_t hashrate_1h = (dt_1h > 0) ? (hashes_1h / dt_1h ) : 0;
|
||||
const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0;
|
||||
|
||||
double average_effort = 0.0;
|
||||
if (m_cumulativeFoundSharesDiff > 0.0) {
|
||||
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / m_cumulativeFoundSharesDiff;
|
||||
}
|
||||
|
||||
int shares_found = m_totalFoundShares;
|
||||
|
||||
double current_effort = static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double();
|
||||
|
||||
int connections = m_numConnections;
|
||||
int incoming_connections = m_numIncomingConnections;
|
||||
|
||||
m_pool->api()->set(p2pool_api::Category::LOCAL, "stats",
|
||||
[hashrate_15m, hashrate_1h, hashrate_24h, total_hashes, shares_found, average_effort, current_effort, connections, incoming_connections](log::Stream& s)
|
||||
{
|
||||
s << "{\"hashrate_15m\":" << hashrate_15m
|
||||
<< ",\"hashrate_1h\":" << hashrate_1h
|
||||
<< ",\"hashrate_24h\":" << hashrate_24h
|
||||
<< ",\"total_hashes\":" << total_hashes
|
||||
<< ",\"shares_found\":" << shares_found
|
||||
<< ",\"average_effort\":" << average_effort
|
||||
<< ",\"current_effort\":" << current_effort
|
||||
<< ",\"connections\":" << connections
|
||||
<< ",\"incoming_connections\":" << incoming_connections
|
||||
<< "}";
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
@@ -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);
|
||||
@@ -112,6 +114,7 @@ private:
|
||||
uv_work_t m_req;
|
||||
StratumServer* m_server;
|
||||
StratumClient* m_client;
|
||||
raw_ip m_clientAddr;
|
||||
uint32_t m_clientResetCounter;
|
||||
uint32_t m_rpcId;
|
||||
uint32_t m_id;
|
||||
@@ -120,6 +123,7 @@ private:
|
||||
uint32_t m_extraNonce;
|
||||
uint64_t m_target;
|
||||
hash m_resultHash;
|
||||
difficulty_type m_sidechainDifficulty;
|
||||
|
||||
enum class Result {
|
||||
STALE,
|
||||
@@ -152,7 +156,10 @@ private:
|
||||
double m_cumulativeFoundSharesDiff;
|
||||
uint32_t m_totalFoundShares;
|
||||
|
||||
void update_hashrate_data(uint64_t target);
|
||||
time_t m_apiLastUpdateTime;
|
||||
|
||||
void update_hashrate_data(uint64_t hashes, time_t timestamp);
|
||||
void api_update_local_stats(time_t timestamp);
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+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); }
|
||||
|
||||
+37
-32
@@ -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) {
|
||||
@@ -101,7 +107,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::parse_address_list(const std::str
|
||||
}
|
||||
|
||||
const int port = atoi(address.substr(k2 + 1).c_str());
|
||||
if ((port > 0) && (port < 655356)) {
|
||||
if ((port > 0) && (port < 65536)) {
|
||||
callback(is_v6, address, ip, port);
|
||||
}
|
||||
else {
|
||||
@@ -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.1 (built"
|
||||
const char* VERSION = "v1.6 (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)
|
||||
{
|
||||
|
||||
+48
-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;
|
||||
@@ -95,6 +110,7 @@ FORCEINLINE void writeVarint(T value, std::vector<uint8_t>& out)
|
||||
}
|
||||
|
||||
template<typename T, size_t N> FORCEINLINE constexpr size_t array_size(T(&)[N]) { return N; }
|
||||
template<typename T, typename U, size_t N> FORCEINLINE constexpr size_t array_size(T(U::*)[N]) { return N; }
|
||||
|
||||
[[noreturn]] void panic();
|
||||
|
||||
@@ -117,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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -143,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,7 +84,9 @@ set(SOURCES
|
||||
../src/json_rpc_request.cpp
|
||||
../src/keccak.cpp
|
||||
../src/log.cpp
|
||||
../src/memory_leak_debug.cpp
|
||||
../src/mempool.cpp
|
||||
../src/miner.cpp
|
||||
../src/p2p_server.cpp
|
||||
../src/p2pool.cpp
|
||||
../src/p2pool_api.cpp
|
||||
@@ -107,6 +109,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 +153,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