Compare commits

..
68 Commits
Author SHA1 Message Date
SChernykh d8bba4dc82 Updated version mismatch error message 2021-12-07 16:25:23 +01:00
SChernykh 3a8a24a5ee Updated dependencies 2021-12-07 16:09:21 +01:00
SChernykh eaf29872cb Update version 2021-12-01 18:24:36 +01:00
SChernykh 74f553274f Update c-cpp.yml
Use macos-10.15 instead of "latest".
2021-11-23 14:08:38 +01:00
SChernykhandGitHub d0f1e6eb2e Merge pull request #78 from SChernykh/calcpow
Added "calc_pow" RPC support
2021-11-21 09:20:01 +01:00
SChernykh f9def6cb73 Added "calc_pow" RPC support 2021-11-21 09:12:21 +01:00
SChernykh 2f659c8ecf Update c-cpp.yml 2021-11-11 22:59:04 +01:00
SChernykhandGitHub 2d559ca764 Merge pull request #71 from bladedoyle/docker-compose
docker-compose project for Monero P2Pool and all requirements
2021-11-11 22:00:29 +01:00
Blade Doyle 1328ea6aef docker-compose project for Monero P2Pool and all requirements 2021-11-11 12:52:03 -08:00
SChernykh 07cfb54bdc JSONRPCRequest: fixed undefined behavior in the error handler 2021-11-10 16:26:01 +01:00
SChernykh 5dd8d11b72 TCPServer: removed unused field 2021-11-09 15:48:59 +01:00
SChernykh dbab191bfc C/C++ CI: use GCC 11 for the ubuntu-latest build 2021-11-08 16:03:43 +01:00
SChernykh e44baad73c Update version 2021-11-05 15:39:20 +01:00
SChernykh bb7a5b4d6d Cleanup 2021-11-02 17:18:02 +01:00
SChernykh 64116643c2 Fixed block broadcasts 2021-11-01 19:36:11 +01:00
SChernykh 1e4c6ef5f4 Added block verification test 2021-11-01 18:53:34 +01:00
SChernykh 61edc6ed70 Reduce cache flush frequency to once per minute 2021-11-01 11:29:59 +01:00
SChernykh d283f91eb0 Update README.md 2021-11-01 11:07:00 +01:00
SChernykh dff2413cae TCPServer: make write buffers global
Reduced memory usage per connection.
2021-10-31 20:29:35 +01:00
SChernykh 998c2ba72f Fixed msys2 build crash 2021-10-31 20:29:35 +01:00
SChernykh b56d027dd5 Include P2P peer list into miner count 2021-10-31 20:29:35 +01:00
SChernykh eaed654012 Check for missing data from monerod on each new block
It helps in these cases:
- monerod was down/unavailable for a while and then restarted
- monerod was out of sync and then jumped several block heights ahead
2021-10-31 12:20:29 +01:00
SChernykh 981b161cc9 SideChain: faster processing of repeated broadcasts 2021-10-31 11:06:00 +01:00
SChernykh f59e9171e8 SideChain: use a separate lock for m_seenBlocks 2021-10-31 10:26:13 +01:00
SChernykh b45540ca08 TCPServer: clean up old IP bans 2021-10-29 14:54:36 +02:00
SChernykh 806e1ca0a3 SideChain: faster check for low-diff spam blocks 2021-10-29 11:39:15 +02:00
SChernykh 113e39817d Cleanup old Monero blocks data 2021-10-29 11:14:28 +02:00
SChernykh 0869326f39 SideChain: count miners in the last 48 hours 2021-10-29 10:37:12 +02:00
SChernykh 75d62c0303 SideChain: cleanup m_seenBlocks when pruning old blocks 2021-10-29 10:34:53 +02:00
SChernykh da4686b88f ZMQReader: start after initial RPC calls are finished 2021-10-28 20:05:40 +02:00
SChernykh a008eac8c6 ZMQReader: more reliable connect() 2021-10-28 19:28:33 +02:00
SChernykh 3d60ae8c32 Fix: don't start until monerod is fully synchronized 2021-10-28 18:47:28 +02:00
SChernykh 88f346414a Fixed race condition when using --no-color 2021-10-28 10:36:50 +02:00
SChernykh 2071a24a9f Update version 2021-10-27 17:27:48 +02:00
SChernykh 1c0da4513f Added --no-color command line option
`--no-color` disables colors in console output
2021-10-27 16:48:17 +02:00
SChernykh 6a002cda46 Create msvc-analysis.yml 2021-10-27 16:06:36 +02:00
SChernykh c21c9615a3 Fixed optimization flags for gcc/clang 2021-10-26 20:13:51 +02:00
SChernykh c0c210664a Show uptime in status 2021-10-26 17:55:47 +02:00
SChernykh 6859d11445 Disable console quick-edit mode on Windows
Prevent user from accidentally selecting text in console and freezing all output
2021-10-26 17:42:00 +02:00
SChernykh 111324b6e0 Added console command to show connected peers 2021-10-24 16:04:30 +02:00
SChernykh 148b9dd294 Switch to faster unordered_map/set 2021-10-22 18:34:20 +02:00
SChernykh 04d18cdf1d P2PServer: fixed m_timer leak 2021-10-21 19:19:22 +02:00
SChernykh 352ad81a0a StratumServer: faster processing of regular shares 2021-10-21 19:09:36 +02:00
SChernykh d26902468d P2PServer: peer list tweaks
- Increase outgoing connections to 10
- Stricter handling of peer list responses
2021-10-21 15:55:00 +02:00
SChernykh 0382027d70 Fixed wrong error message 2021-10-21 10:12:57 +02:00
SChernykh 966b499e5c ZMQReader: keep trying to connect on startup 2021-10-21 00:22:53 +02:00
SChernykh 9885f82e9e Limit user name length to 64 characters
Also avoid creating temporary strings (`" user " + client->m_customUser`) when logging user name
2021-10-19 21:09:08 +02:00
SChernykhandGitHub 32a58d1534 Merge pull request #58 from WeebDataHoarder/master
Display stratum user on SHARE FOUND and "client found mainchain block" log entries
2021-10-19 20:10:53 +02:00
WeebDataHoarder bc341918f6 Decode custom user from stratum client, display stratum client+user on SHARE FOUND and client mainchain found message 2021-10-19 19:33:15 +02:00
SChernykh f17c23528a Update version 2021-10-17 11:00:51 +02:00
SChernykh a06f4dcdcd P2PServer: fixed ever growing memory usage 2021-10-17 10:57:33 +02:00
SChernykh 3bc03e4801 CI: added Visual Studio 2022 build 2021-10-16 15:01:29 +02:00
SChernykh 816a29c5ab Added --no-cache command line parameter 2021-10-16 13:45:28 +02:00
SChernykh 285560e120 Fixed possible sidechain split during RandomX epoch change 2021-10-16 09:57:13 +02:00
SChernykh 39ba5c4131 P2PServer: check for messages that shouldn't be sent 2021-10-15 17:09:51 +02:00
SChernykh 9b86f8e81f Peer list refactoring
- Remove peers that weren't seen for > 1 hour
- Better peer selection algorithm for PEER_LIST_RESPONSE
2021-10-15 11:32:01 +02:00
SChernykh 9e90e988fa Refactored array_size to make it always constexpr 2021-10-14 16:46:49 +02:00
SChernykh 39b2167c98 Show correct max log level in usage 2021-10-14 13:59:11 +02:00
SChernykh f3024d3556 StratumServer: submit Monero block as soon as possible
1-3 ms saved when a block is found
2021-10-13 18:57:21 +02:00
SChernykhandGitHub 61ac90f1d5 Fixed typo 2021-10-13 14:56:27 +02:00
SChernykh 60a8538e0c P2PServer: show an error when there are no ZMQ messages 2021-10-09 11:01:26 +02:00
SChernykh 77a3a5857c P2PServer: show an error when there are no connections 2021-10-08 18:21:31 +02:00
SChernykh af2a8eeb08 Tweaked warning level for lagging nodes 2021-10-06 15:22:03 +02:00
SChernykh 54d0ab3658 Move most block broadcasts to log level 6 2021-10-06 09:47:41 +02:00
SChernykh c9faa92098 StratumServer: fixed "bad share" ban logic 2021-10-04 15:51:28 +02:00
SChernykh 52e5008bf4 Refactoring
- Skip calling time(nullptr) twice
- Use getter method for m_api instead of exposing it
- Moved api_udate_*() back to private since they're not used anywhere else
2021-10-04 10:28:56 +02:00
SChernykhandGitHub a0a31b0f3b Merge pull request #51 from Tonux599/stratumServerJson-staging
API for stratum server statistics.
2021-10-04 11:16:37 +03:00
Thomas Clarke 7742d163f7 API for stratum server statistics.
Users can pass `--stratum-api` to command line to enable a file called `local/stats` in `--data-api` directory. `local/stats` contains statistics that `StratumServer status` provides but in JSON format. It is currently setup that `local/stats` is updated each time a new job is sent to a worker.
2021-10-03 22:32:13 +01:00
59 changed files with 1559 additions and 387 deletions
+24 -16
View File
@@ -9,15 +9,20 @@ on:
jobs: jobs:
build-ubuntu: build-ubuntu:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.config.os }}
strategy: strategy:
matrix: 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: steps:
- name: Install dependencies - 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 - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -28,7 +33,7 @@ jobs:
run: | run: |
mkdir build mkdir build
cd build cd build
cmake .. cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
make -j2 make -j2
- name: Build tests - name: Build tests
@@ -36,7 +41,7 @@ jobs:
cd tests cd tests
mkdir build mkdir build
cd build cd build
cmake .. cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
make -j2 make -j2
- name: Run tests - name: Run tests
@@ -45,7 +50,7 @@ jobs:
- name: Archive binary - name: Archive binary
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: p2pool-${{ matrix.os }} name: p2pool-${{ matrix.config.os }}
path: build/p2pool path: build/p2pool
build-windows-msys2: build-windows-msys2:
@@ -96,7 +101,13 @@ jobs:
build-windows-msbuild: 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: steps:
- name: Checkout repository - name: Checkout repository
@@ -107,23 +118,20 @@ jobs:
- name: Setup cmake - name: Setup cmake
uses: lukka/get-cmake@latest uses: lukka/get-cmake@latest
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.3
- name: Build p2pool - name: Build p2pool
run: | run: |
mkdir build mkdir build
cd build cd build
cmake .. -G "Visual Studio 16 2019" cmake .. -G "${{ matrix.config.vs }}"
msbuild /m /p:Configuration=Release p2pool.vcxproj & "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool.vcxproj
- name: Build tests - name: Build tests
run: | run: |
cd tests cd tests
mkdir build mkdir build
cd build cd build
cmake .. -G "Visual Studio 16 2019" cmake .. -G "${{ matrix.config.vs }}"
msbuild /m /p:Configuration=Release p2pool_tests.vcxproj & "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool_tests.vcxproj
- name: Run tests - name: Run tests
run: | run: |
@@ -133,7 +141,7 @@ jobs:
- name: Archive binary - name: Archive binary
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: p2pool-msbuild.exe name: p2pool-msbuild-${{ matrix.config.os }}.exe
path: build/Release/p2pool.exe path: build/Release/p2pool.exe
build-macos: build-macos:
@@ -142,7 +150,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [macos-latest, macos-11] os: [macos-10.15, macos-11]
steps: steps:
- name: Checkout repository - name: Checkout repository
+61
View File
@@ -0,0 +1,61 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
#
# Find more information at:
# https://github.com/microsoft/msvc-code-analysis-action
name: Microsoft C++ Code Analysis
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
schedule:
- cron: '40 10 * * 0'
env:
# Path to the CMake build directory.
build: '${{ github.workspace }}/build'
jobs:
analyze:
name: Analyze
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Configure CMake
run: cmake -B ${{ env.build }}
# Build is not required unless generated source files are used
# - name: Build CMake
# run: cmake --build ${{ env.build }}
- name: Initialize MSVC Code Analysis
uses: microsoft/msvc-code-analysis-action@04825f6d9e00f87422d6bf04e1a38b1f3ed60d99
# Provide a unique ID to access the sarif output path
id: run-analysis
with:
cmakeBuildDirectory: ${{ env.build }}
# Ruleset file that will determine what checks will be run
ruleset: NativeRecommendedRules.ruleset
# Upload SARIF file to GitHub Code Scanning Alerts
- name: Upload SARIF to GitHub
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: ${{ steps.run-analysis.outputs.sarif }}
# Upload SARIF file as an Artifact to download and view
- name: Upload SARIF as an Artifact
uses: actions/upload-artifact@v2
with:
name: sarif-file
path: ${{ steps.run-analysis.outputs.sarif }}
+3
View File
@@ -19,3 +19,6 @@
[submodule "external/src/libzmq"] [submodule "external/src/libzmq"]
path = external/src/libzmq path = external/src/libzmq
url = https://github.com/SChernykh/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
+1
View File
@@ -82,6 +82,7 @@ include_directories(external/src/libzmq/include)
include_directories(external/src/llhttp) include_directories(external/src/llhttp)
include_directories(external/src/RandomX/src) include_directories(external/src/RandomX/src)
include_directories(external/src/rapidjson/include) include_directories(external/src/rapidjson/include)
include_directories(external/src/robin-hood-hashing/src/include)
if (WIN32) if (WIN32)
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi) set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
+50
View File
@@ -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"]
+12 -11
View File
@@ -2,7 +2,7 @@
Decentralized pool for Monero mining. 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 ### Build status
@@ -68,9 +68,9 @@ make -j$(nproc)
monerod binary compatible with p2pool: 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 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 git clone --recursive https://github.com/monero-project/monero/
cd monero cd monero
git checkout p2pool-api-v0.17 git checkout release-v0.17
git submodule sync && git submodule update --init --force --recursive git submodule sync && git submodule update --init --force --recursive
make release-static -j$(nproc) make release-static -j$(nproc)
``` ```
@@ -113,9 +113,9 @@ make -j$(sysctl -n hw.logicalcpu)
monerod binary compatible with p2pool: monerod binary compatible with p2pool:
``` ```
git clone --recursive https://github.com/SChernykh/monero git clone --recursive https://github.com/monero-project/monero/
cd monero cd monero
git checkout p2pool-api-v0.17 git checkout release-v0.17
git submodule sync && git submodule update --init --force --recursive git submodule sync && git submodule update --init --force --recursive
brew update && brew bundle --file=contrib/brew/Brewfile brew update && brew bundle --file=contrib/brew/Brewfile
make -j$(sysctl -n hw.logicalcpu) make -j$(sysctl -n hw.logicalcpu)
@@ -138,9 +138,9 @@ Alternatively, you can select "Clone a repository" within the GUI, then select "
monerod binary compatible with p2pool: monerod binary compatible with p2pool:
``` ```
git clone --recursive https://github.com/SChernykh/monero git clone --recursive https://github.com/monero-project/monero/
cd monero cd monero
git checkout p2pool-api-v0.17 git checkout release-v0.17
git submodule sync && git submodule update --init --force --recursive git submodule sync && git submodule update --init --force --recursive
``` ```
then follow the instructions from https://github.com/monero-project/monero/#on-windows then follow the instructions from https://github.com/monero-project/monero/#on-windows
@@ -171,10 +171,11 @@ Step-by-step guide:
- Prepare enough huge pages (each of monerod/p2pool/xmrig needs them): `sudo sysctl vm.nr_hugepages=3072` - 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 - 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 - 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 bundled with p2pool (official binaries don't have p2pool support yet)
- 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 `./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. - Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-fce167d7c)** on startup. Wait until it's synchronized.
- Run `./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS` - Run `./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
- p2pool has _very_ verbose logging by default, it will spam a lot, no I mean A LOT in both console and in p2pool.log. Logs help testing immensely! - p2pool has verbose logging by default, you can reduce it by using "loglevel N" command where N is between 0 and 6. Default loglevel is 3.
- You can use `logrotate` with a config like this to control logfile growth: - You can use `logrotate` with a config like this to control logfile growth:
``` ```
<path-to-logfile> <path-to-logfile>
@@ -197,7 +198,7 @@ Step-by-step guide:
*NOTE: Windows SmartScreen may block incoming connections by files that are "Downloaded from the Internet". You can allow 'p2pool.exe' and 'monerod.exe' by double-clicking them, clicking "More Info", then click "Run Anyway" and then closing them immediately so you can run them from the command line. Advanced users can use the PowerShell cmdlet `Unblock-File` to remove this flag.* *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 p2pool binaries from https://github.com/SChernykh/p2pool/releases/latest
- Download xmrig binary from https://github.com/xmrig/xmrig/releases/latest *(xmrig-6.15.0-gcc-win64.zip is the current version)* - Download xmrig binary from https://github.com/xmrig/xmrig/releases/latest *(xmrig-6.15.2-gcc-win64.zip is the current version)*
- Expand the p2pool binaries into an appropriate location (`%USERPROFILE%/bin` or `C:/bin/` are good options) - Expand 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) - Expand xmrig binary into appropriate location (same folder as p2pool is fine)
- Prepare huge pages (each of monerod/p2pool/xmrig needs them): - Prepare huge pages (each of monerod/p2pool/xmrig needs them):
@@ -206,7 +207,7 @@ Step-by-step guide:
- Open a command prompt and navigate to the folder where you extracted p2pool. - 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"* - *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 `.\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. - Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-fce167d7c)** on startup. Wait until it's synchronized.
- Run `.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS` - 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. - 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!** - Run `.\xmrig.exe -o 127.0.0.1:3333`. Note that you don't need to specify wallet address for xmrig. **Wallet address set in xmrig config will be ignored!**
+4 -4
View File
@@ -9,10 +9,10 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(WARNING_FLAGS "-Wall -Wextra -Werror") set(WARNING_FLAGS "-Wall -Wextra -Werror")
set(OPTIMIZATION_FLAGS "-Ofast -s") 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_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}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
if (WIN32) if (WIN32)
@@ -42,9 +42,9 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal -Werror") set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal -Werror")
set(OPTIMIZATION_FLAGS "-Ofast -funroll-loops -fmerge-all-constants") 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_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}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
endif() endif()
+1
View File
@@ -7,3 +7,4 @@
../external/src/llhttp/ ../external/src/llhttp/
../external/src/RandomX/src/ ../external/src/RandomX/src/
../external/src/rapidjson/include ../external/src/rapidjson/include
../external/src/robin-hood-hashing/src/include
+1 -1
View File
@@ -1,5 +1,5 @@
#!/bin/bash #!/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 grep -v 'external' errors_full.txt | grep -v 'unmatchedSuppression' > errors_filtered.txt
if [ -s errors_filtered.txt ]; then if [ -s errors_filtered.txt ]; then
cat errors_filtered.txt cat errors_filtered.txt
+38
View File
@@ -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
MONERO_GIT_TAG=release-v0.17
#
# 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"
+38
View File
@@ -0,0 +1,38 @@
# 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
```
<b>WALLET_ADDRESS</b> 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
* You can run everythng in the background by adding the "-d" argument to the "docker-compose up" command: ```docker-compose up -d```
* 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```
* 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)
+84
View File
@@ -0,0 +1,84 @@
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
cap_add:
- ALL
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
+79
View File
@@ -0,0 +1,79 @@
FROM ubuntu:latest
ARG MONERO_GIT_TAG="master"
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 && \
git checkout $MONERO_GIT_TAG && \
git submodule sync && git submodule update --init --force --recursive && \
make release-static -j$(nproc)
# ---
FROM ubuntu:latest
ARG MONERO_GIT_TAG="master"
COPY --from=0 /usr/src/monero/build/Linux/$MONERO_GIT_TAG/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"]
+37
View File
@@ -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"]
+26 -7
View File
@@ -66,7 +66,7 @@ BlockTemplate::BlockTemplate(p2pool* pool)
m_mempoolTxsOrder.reserve(1024); m_mempoolTxsOrder.reserve(1024);
m_shares.reserve(m_pool->side_chain().chain_window_size() * 2); 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); m_oldTemplates[i] = new BlockTemplate(*this);
} }
@@ -79,7 +79,7 @@ BlockTemplate::BlockTemplate(p2pool* pool)
BlockTemplate::~BlockTemplate() 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]; delete m_oldTemplates[i];
} }
@@ -185,7 +185,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
WriteLock lock(m_lock); WriteLock lock(m_lock);
if (m_templateId > 0) { 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; ++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 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); 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); 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)) { 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); 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); ReadLock lock(m_lock);
if (template_id != m_templateId) { 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)) { if (old && (template_id == old->m_templateId)) {
return old->get_block_template_blob(template_id, nonce_offset, extra_nonce_offset); return old->get_block_template_blob(template_id, nonce_offset, extra_nonce_offset);
} }
@@ -999,7 +1018,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
} }
hash pow_hash; 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! "); LOGERR(1, "PoW check failed for the sidechain block. Fix it! ");
} }
else if (!check.m_difficulty.check_pow(pow_hash)) { 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; 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)) { if (old && (template_id == old->m_templateId)) {
old->submit_sidechain_block(template_id, nonce, extra_nonce); old->submit_sidechain_block(template_id, nonce, extra_nonce);
+1
View File
@@ -40,6 +40,7 @@ public:
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet); 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(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; 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;
+33
View File
@@ -279,6 +279,39 @@ enum class NetworkType {
Stagenet, 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 } // namespace p2pool
#include "util.h" #include "util.h"
+10 -1
View File
@@ -69,7 +69,7 @@ typedef struct cmd {
cmdfunc *func; cmdfunc *func;
} cmd; } 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_exit;
static cmd cmds[] = { static cmd cmds[] = {
{ STRCONST("help"), "", "display list of commands", do_help }, { STRCONST("help"), "", "display list of commands", do_help },
@@ -77,6 +77,7 @@ static cmd cmds[] = {
{ STRCONST("loglevel"), "<level>", "set log level", do_loglevel }, { STRCONST("loglevel"), "<level>", "set log level", do_loglevel },
{ STRCONST("addpeers"), "<peeraddr>", "add peer", do_addpeers }, { STRCONST("addpeers"), "<peeraddr>", "add peer", do_addpeers },
{ STRCONST("droppeers"), "", "disconnect all peers", do_droppeers }, { STRCONST("droppeers"), "", "disconnect all peers", do_droppeers },
{ STRCONST("peers"), "", "show all peers", do_showpeers },
{ STRCONST("exit"), "", "terminate p2pool", do_exit }, { STRCONST("exit"), "", "terminate p2pool", do_exit },
{ STRCNULL, NULL, NULL, NULL } { STRCNULL, NULL, NULL, NULL }
}; };
@@ -129,6 +130,14 @@ static int do_droppeers(p2pool *m_pool, const char * /* args */)
return 0; 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_exit(p2pool *m_pool, const char * /* args */) static int do_exit(p2pool *m_pool, const char * /* args */)
{ {
bkg_jobs_tracker.wait(); bkg_jobs_tracker.wait();
+2 -3
View File
@@ -20,7 +20,6 @@
#include "keccak.h" #include "keccak.h"
#include "uv_util.h" #include "uv_util.h"
#include <random> #include <random>
#include <unordered_map>
extern "C" { extern "C" {
#include "crypto-ops.h" #include "crypto-ops.h"
@@ -237,8 +236,8 @@ public:
private: private:
uv_mutex_t m; uv_mutex_t m;
std::unordered_map<std::array<uint8_t, HASH_SIZE * 2>, hash> derivations; 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 + sizeof(size_t)>, hash> public_keys;
}; };
static Cache* cache = nullptr; static Cache* cache = nullptr;
+2 -2
View File
@@ -25,7 +25,7 @@ static constexpr char log_category_prefix[] = "JSONRPCRequest ";
namespace p2pool { 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_socket{}
, m_connect{} , m_connect{}
, m_write{} , m_write{}
@@ -38,7 +38,7 @@ JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, C
{ {
m_readBuf[0] = '\0'; 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); uv_tcp_nodelay(&m_socket, 1);
sockaddr_storage addr; sockaddr_storage addr;
+6 -5
View File
@@ -26,20 +26,21 @@ public:
static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb) 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 // 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) { if (!r->m_valid) {
delete r; delete r;
} }
} }
template<typename T, typename U> 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 // 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) { if (!r->m_valid) {
constexpr char err[] = "internal error"; constexpr char err[] = "internal error";
close_cb(err, sizeof(err) - 1); (*close_callback)(err, sizeof(err) - 1);
delete r; delete r;
} }
} }
@@ -62,7 +63,7 @@ private:
T m_cb; 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(); ~JSONRPCRequest();
static void on_connect(uv_connect_t* req, int status); static void on_connect(uv_connect_t* req, int status);
+16 -2
View File
@@ -28,6 +28,7 @@ namespace p2pool {
namespace log { namespace log {
int GLOBAL_LOG_LEVEL = 3; int GLOBAL_LOG_LEVEL = 3;
bool CONSOLE_COLORS = true;
#ifndef P2POOL_LOG_DISABLE #ifndef P2POOL_LOG_DISABLE
@@ -35,6 +36,7 @@ static volatile bool stopped = false;
static volatile bool worker_started = false; static volatile bool worker_started = false;
#ifdef _WIN32 #ifdef _WIN32
static const HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
static const HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); static const HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
static const HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE); static const HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE);
#endif #endif
@@ -52,7 +54,7 @@ public:
: m_writePos(0) : m_writePos(0)
, m_readPos(0) , m_readPos(0)
{ {
is_main_thread = true; set_main_thread();
m_logFile.open(log_file_name, std::ios::app | std::ios::binary); m_logFile.open(log_file_name, std::ios::app | std::ios::binary);
@@ -69,6 +71,9 @@ public:
#ifdef _WIN32 #ifdef _WIN32
DWORD dwConsoleMode; DWORD dwConsoleMode;
if (GetConsoleMode(hStdIn, &dwConsoleMode)) {
SetConsoleMode(hStdIn, dwConsoleMode & ~ENABLE_QUICK_EDIT_MODE);
}
if (GetConsoleMode(hStdOut, &dwConsoleMode)) { if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
SetConsoleMode(hStdOut, dwConsoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); SetConsoleMode(hStdOut, dwConsoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
} }
@@ -155,6 +160,13 @@ private:
p += 3; p += 3;
size -= 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 #ifdef _WIN32
DWORD k; DWORD k;
WriteConsole((severity == 1) ? hStdOut : hStdErr, p, size, &k, nullptr); WriteConsole((severity == 1) ? hStdOut : hStdErr, p, size, &k, nullptr);
@@ -172,7 +184,9 @@ private:
} }
if (m_logFile.is_open()) { if (m_logFile.is_open()) {
strip_colors(p, size); if (c) {
strip_colors(p, size);
}
if (severity == 1) { if (severity == 1) {
m_logFile.write("NOTICE ", 8); m_logFile.write("NOTICE ", 8);
+2
View File
@@ -22,6 +22,7 @@ namespace p2pool {
namespace log { namespace log {
extern int GLOBAL_LOG_LEVEL; extern int GLOBAL_LOG_LEVEL;
extern bool CONSOLE_COLORS;
constexpr int MAX_GLOBAL_LOG_LEVEL = 6; constexpr int MAX_GLOBAL_LOG_LEVEL = 6;
enum class Severity { enum class Severity {
@@ -371,6 +372,7 @@ template<> struct log::Stream::Entry<XMRAmount>
template<> struct log::Stream::Entry<NetworkType> template<> struct log::Stream::Entry<NetworkType>
{ {
// cppcheck-suppress constParameter
static NOINLINE void put(const NetworkType& value, Stream* wrapper) static NOINLINE void put(const NetworkType& value, Stream* wrapper)
{ {
switch (value) { switch (value) {
+6 -1
View File
@@ -33,13 +33,18 @@ static void usage()
"--p2p Comma-separated list of IP:port for p2p server to listen on\n" "--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" "--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" "--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" "--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" "--data-api Path to the p2pool JSON data (use it in tandem with an external web-server)\n"
"--stratum-api Enable /local/ path in api path for Stratum Server statistics\n"
"--no-cache Disable p2pool.cache\n"
"--no-color Disable colors in console output\n"
"--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes\n"
"--help Show this help message\n\n" "--help Show this help message\n\n"
"Example command line:\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", "%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::VERSION,
p2pool::log::MAX_GLOBAL_LOG_LEVEL,
#ifdef _WIN32 #ifdef _WIN32
"p2pool.exe" "p2pool.exe"
#else #else
+40 -10
View File
@@ -120,9 +120,9 @@ FORCEINLINE static void remove_allocation(void* p)
__debugbreak(); __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) { if (p) {
add_alocation(p, n); add_alocation(p, n);
} }
@@ -131,25 +131,46 @@ FORCEINLINE static void* allocate_noexcept(size_t n) noexcept
FORCEINLINE static void* allocate(size_t n) FORCEINLINE static void* allocate(size_t n)
{ {
void* p = allocate_noexcept(n); void* p = malloc_hook(n);
if (!p) { if (!p) {
throw std::bad_alloc(); throw std::bad_alloc();
} }
return p; return p;
} }
FORCEINLINE static void deallocate(void* p) void free_hook(void* p) noexcept
{ {
remove_allocation(p); remove_allocation(p);
free(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 } // p2pool
void memory_tracking_start() void memory_tracking_start()
{ {
using namespace p2pool; using namespace p2pool;
uv_replace_allocator(malloc_hook, realloc_hook, calloc_hook, free_hook);
uv_mutex_init_checked(&allocation_lock); uv_mutex_init_checked(&allocation_lock);
track_memory = true; 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) { 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::malloc_hook(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::malloc_hook(n); }
NOINLINE void operator delete(void* p) noexcept { p2pool::deallocate(p); } NOINLINE void operator delete(void* p) noexcept { p2pool::free_hook(p); }
NOINLINE void operator delete[](void* p) noexcept { p2pool::deallocate(p); } NOINLINE void operator delete[](void* p) noexcept { p2pool::free_hook(p); }
NOINLINE void operator delete(void* p, size_t) noexcept { p2pool::deallocate(p); } NOINLINE void operator delete(void* p, size_t) noexcept { p2pool::free_hook(p); }
NOINLINE void operator delete[](void* p, size_t) noexcept { p2pool::deallocate(p); } NOINLINE void operator delete[](void* p, size_t) noexcept { p2pool::free_hook(p); }
#else #else
void memory_tracking_start() {} void memory_tracking_start() {}
void memory_tracking_stop() {} 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 #endif
+1 -2
View File
@@ -18,7 +18,6 @@
#pragma once #pragma once
#include "uv_util.h" #include "uv_util.h"
#include <unordered_map>
namespace p2pool { namespace p2pool {
@@ -35,7 +34,7 @@ public:
public: public:
mutable uv_rwlock_t m_lock; mutable uv_rwlock_t m_lock;
std::unordered_map<hash, TxMempoolData> m_transactions; unordered_map<hash, TxMempoolData> m_transactions;
}; };
} // namespace p2pool } // namespace p2pool
+175 -53
View File
@@ -42,13 +42,14 @@ namespace p2pool {
P2PServer::P2PServer(p2pool* pool) P2PServer::P2PServer(p2pool* pool)
: TCPServer(P2PClient::allocate) : TCPServer(P2PClient::allocate)
, m_pool(pool) , m_pool(pool)
, m_cache(new BlockCache()) , m_cache(pool->params().m_blockCache ? new BlockCache() : nullptr)
, m_cacheLoaded(false) , m_cacheLoaded(false)
, m_initialPeerList(pool->params().m_p2pPeerList) , m_initialPeerList(pool->params().m_p2pPeerList)
, m_rd{} , m_rd{}
, m_rng(m_rd()) , m_rng(m_rd())
, m_block(new PoolBlock()) , m_block(new PoolBlock())
, m_timer{} , m_timer{}
, m_timerCounter(0)
, m_peerId(m_rng()) , m_peerId(m_rng())
, m_peerListLastSaved(0) , m_peerListLastSaved(0)
{ {
@@ -93,6 +94,7 @@ P2PServer::P2PServer(p2pool* pool)
P2PServer::~P2PServer() P2PServer::~P2PServer()
{ {
uv_timer_stop(&m_timer); 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); uv_close(reinterpret_cast<uv_handle_t*>(&m_broadcastAsync), nullptr);
shutdown_tcp(); shutdown_tcp();
@@ -166,12 +168,6 @@ void P2PServer::on_connect_failed(bool is_v6, const raw_ip& ip, int port)
void P2PServer::update_peer_connections() 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 cur_time = time(nullptr);
const time_t last_updated = m_pool->side_chain().last_updated(); const time_t last_updated = m_pool->side_chain().last_updated();
@@ -180,13 +176,14 @@ void P2PServer::update_peer_connections()
MutexLock lock(m_clientsListLock); MutexLock lock(m_clientsListLock);
connected_clients.reserve(m_numConnections); connected_clients.reserve(m_numConnections);
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) { for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
connected_clients.emplace_back(client->m_addr); bool disconnected = false;
const int timeout = client->m_handshakeComplete ? 300 : 10; const int timeout = client->m_handshakeComplete ? 300 : 10;
if (cur_time >= client->m_lastAlive + timeout) { if (cur_time >= client->m_lastAlive + timeout) {
const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive); const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive);
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting"); LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting");
client->close(); client->close();
disconnected = true;
} }
if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) { if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) {
@@ -200,13 +197,37 @@ void P2PServer::update_peer_connections()
client->ban(DEFAULT_BAN_TIME); client->ban(DEFAULT_BAN_TIME);
remove_peer_from_list(client); remove_peer_from_list(client);
client->close(); client->close();
disconnected = true;
} }
} }
if (!disconnected) {
connected_clients.emplace_back(client->m_addr);
}
} }
} }
// Try to have at least 8 outgoing connections std::vector<Peer> peer_list;
for (uint32_t i = m_numConnections - m_numIncomingConnections; (i < 8) && !peer_list.empty();) { {
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 10 outgoing connections
for (uint32_t i = m_numConnections - m_numIncomingConnections; (i < 10) && !peer_list.empty();) {
const uint64_t k = get_random64() % peer_list.size(); const uint64_t k = get_random64() % peer_list.size();
const Peer& peer = peer_list[k]; const Peer& peer = peer_list[k];
@@ -227,6 +248,10 @@ void P2PServer::update_peer_connections()
} }
peer_list.pop_back(); 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() void P2PServer::update_peer_list()
@@ -244,13 +269,18 @@ void P2PServer::update_peer_list()
// Send peer list requests at random intervals (60-120 seconds) // Send peer list requests at random intervals (60-120 seconds)
client->m_nextPeerListRequest = cur_time + 60 + (get_random64() % 61); client->m_nextPeerListRequest = cur_time + 60 + (get_random64() % 61);
send(client, const bool result = send(client,
[](void* buf) [](void* buf)
{ {
LOGINFO(5, "sending PEER_LIST_REQUEST"); LOGINFO(5, "sending PEER_LIST_REQUEST");
*reinterpret_cast<uint8_t*>(buf) = static_cast<uint8_t>(MessageId::PEER_LIST_REQUEST); *reinterpret_cast<uint8_t*>(buf) = static_cast<uint8_t>(MessageId::PEER_LIST_REQUEST);
return 1; return 1;
}); });
if (result) {
client->m_lastPeerListRequestTime = std::chrono::system_clock::now();
++client->m_peerListPendingRequests;
}
} }
} }
} }
@@ -286,7 +316,7 @@ void P2PServer::save_peer_list_async()
}); });
if (err) { 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; delete work;
} }
} }
@@ -424,8 +454,6 @@ void P2PServer::load_peer_list()
} }
p.m_isV6 = true; p.m_isV6 = true;
memcpy(p.m_addr.data, &addr6.sin6_addr, sizeof(in6_addr)); memcpy(p.m_addr.data, &addr6.sin6_addr, sizeof(in6_addr));
p.m_port = port;
p.m_numFailedConnections = 0;
} }
else { else {
sockaddr_in addr4; sockaddr_in addr4;
@@ -439,8 +467,6 @@ void P2PServer::load_peer_list()
p.m_addr.data[10] = 0xFF; p.m_addr.data[10] = 0xFF;
p.m_addr.data[11] = 0xFF; p.m_addr.data[11] = 0xFF;
memcpy(p.m_addr.data + 12, &addr4.sin_addr, sizeof(in_addr)); memcpy(p.m_addr.data + 12, &addr4.sin_addr, sizeof(in_addr));
p.m_port = port;
p.m_numFailedConnections = 0;
} }
bool already_added = false; bool already_added = false;
@@ -451,6 +477,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)) { if (!already_added && !is_banned(p.m_addr)) {
m_peerList.push_back(p); m_peerList.push_back(p);
} }
@@ -461,18 +491,21 @@ void P2PServer::load_peer_list()
void P2PServer::update_peer_in_list(bool is_v6, const raw_ip& ip, int port) 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); MutexLock lock(m_peerListLock);
for (Peer& p : m_peerList) { for (Peer& p : m_peerList) {
if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) { if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) {
p.m_port = port; p.m_port = port;
p.m_numFailedConnections = 0; p.m_numFailedConnections = 0;
p.m_lastSeen = cur_time;
return; return;
} }
} }
if (!is_banned(ip)) { 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 +642,19 @@ void P2PServer::on_broadcast()
uint8_t* p = p0; uint8_t* p = p0;
bool send_pruned = true; bool send_pruned = true;
{
ReadLock lock(client->m_broadcastedHashesLock); const hash* a = client->m_broadcastedHashes;
for (const hash& id : data->ancestor_hashes) { const hash* b = client->m_broadcastedHashes + array_size(&P2PClient::m_broadcastedHashes);
if (client->m_broadcastedHashes.find(id) == client->m_broadcastedHashes.end()) {
send_pruned = false; for (const hash& id : data->ancestor_hashes) {
break; if (std::find(a, b, id) == b) {
} send_pruned = false;
break;
} }
} }
if (send_pruned) { 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); *(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
*reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(data->pruned_blob.size()); *reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(data->pruned_blob.size());
@@ -654,14 +688,43 @@ uint64_t P2PServer::get_random64()
void P2PServer::print_status() 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" << LOGINFO(0, "status" <<
"\nConnections = " << m_numConnections << " (" << m_numIncomingConnections << " incoming)" << "\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() void P2PServer::on_timer()
{ {
++m_timerCounter;
if (!m_initialPeerList.empty()) { if (!m_initialPeerList.empty()) {
connect_to_peers(m_initialPeerList); connect_to_peers(m_initialPeerList);
m_initialPeerList.clear(); m_initialPeerList.clear();
@@ -672,11 +735,12 @@ void P2PServer::on_timer()
update_peer_list(); update_peer_list();
save_peer_list_async(); save_peer_list_async();
update_peer_connections(); update_peer_connections();
check_zmq();
} }
void P2PServer::flush_cache() void P2PServer::flush_cache()
{ {
if (!m_cache) { if (!m_cache || ((m_timerCounter % 30) != 2)) {
return; return;
} }
@@ -772,6 +836,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() P2PServer::P2PClient::P2PClient()
: m_peerId(0) : m_peerId(0)
, m_expectedMessage(MessageId::HANDSHAKE_CHALLENGE) , m_expectedMessage(MessageId::HANDSHAKE_CHALLENGE)
@@ -781,16 +860,18 @@ P2PServer::P2PClient::P2PClient()
, m_handshakeInvalid(false) , m_handshakeInvalid(false)
, m_listenPort(-1) , m_listenPort(-1)
, m_nextPeerListRequest(0) , m_nextPeerListRequest(0)
, m_lastPeerListRequestTime{}
, m_peerListPendingRequests(0)
, m_pingTime(0)
, m_lastAlive(0) , m_lastAlive(0)
, m_lastBroadcastTimestamp(0) , m_lastBroadcastTimestamp(0)
, m_lastBlockrequestTimestamp(0) , m_lastBlockrequestTimestamp(0)
, m_broadcastedHashes{}
{ {
uv_rwlock_init_checked(&m_broadcastedHashesLock);
} }
P2PServer::P2PClient::~P2PClient() P2PServer::P2PClient::~P2PClient()
{ {
uv_rwlock_destroy(&m_broadcastedHashesLock);
} }
void P2PServer::P2PClient::reset() void P2PServer::P2PClient::reset()
@@ -805,12 +886,17 @@ void P2PServer::P2PClient::reset()
m_handshakeInvalid = false; m_handshakeInvalid = false;
m_listenPort = -1; m_listenPort = -1;
m_nextPeerListRequest = 0; m_nextPeerListRequest = 0;
m_lastPeerListRequestTime = {};
m_peerListPendingRequests = 0;
m_pingTime = 0;
m_lastAlive = 0; m_lastAlive = 0;
m_lastBroadcastTimestamp = 0; m_lastBroadcastTimestamp = 0;
m_lastBlockrequestTimestamp = 0; m_lastBlockrequestTimestamp = 0;
WriteLock lock(m_broadcastedHashesLock); for (hash& h : m_broadcastedHashes) {
m_broadcastedHashes.clear(); h = {};
}
m_broadcastedHashesIndex = 0;
} }
bool P2PServer::P2PClient::on_connect() bool P2PServer::P2PClient::on_connect()
@@ -866,6 +952,13 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
switch (id) switch (id)
{ {
case MessageId::HANDSHAKE_CHALLENGE: 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"); LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent HANDSHAKE_CHALLENGE");
if (bytes_left >= 1 + CHALLENGE_SIZE + sizeof(uint64_t)) { if (bytes_left >= 1 + CHALLENGE_SIZE + sizeof(uint64_t)) {
@@ -880,6 +973,13 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
break; break;
case MessageId::HANDSHAKE_SOLUTION: 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"); LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent HANDSHAKE_SOLUTION");
if (bytes_left >= 1 + HASH_SIZE + CHALLENGE_SIZE) { if (bytes_left >= 1 + HASH_SIZE + CHALLENGE_SIZE) {
@@ -935,7 +1035,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
break; break;
case MessageId::BLOCK_BROADCAST: 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)) { if (bytes_left >= 1 + sizeof(uint32_t)) {
const uint32_t block_size = *reinterpret_cast<uint32_t*>(buf + 1); const uint32_t block_size = *reinterpret_cast<uint32_t*>(buf + 1);
@@ -964,10 +1064,17 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
break; break;
case MessageId::PEER_LIST_RESPONSE: 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"); LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent PEER_LIST_RESPONSE");
if (bytes_left >= 2) { 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) { 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 << ')'); LOGWARN(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent too long peer list (" << num_peers << ')');
ban(DEFAULT_BAN_TIME); ban(DEFAULT_BAN_TIME);
@@ -975,8 +1082,13 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
return false; return false;
} }
if (bytes_left >= 2u + num_peers * 19) { if (bytes_left >= 2u + num_peers * 19u) {
bytes_read = 2u + num_peers * 19; 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)) { if (!on_peer_list_response(buf + 1)) {
ban(DEFAULT_BAN_TIME); ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this); server->remove_peer_from_list(this);
@@ -1413,10 +1525,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
return false; return false;
} }
{ m_broadcastedHashes[m_broadcastedHashesIndex.fetch_add(1) % array_size(&P2PClient::m_broadcastedHashes)] = server->m_block->m_sidechainId;
WriteLock lock2(m_broadcastedHashesLock);
m_broadcastedHashes.insert(server->m_block->m_sidechainId);
}
const MinerData& miner_data = server->m_pool->miner_data(); const MinerData& miner_data = server->m_pool->miner_data();
@@ -1444,8 +1553,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
} }
else if (peer_height > our_height) { else if (peer_height > our_height) {
if (peer_height >= our_height + 2) { if (peer_height >= our_height + 2) {
const int level = (peer_height >= our_height + 3) ? 3 : 4; 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?");
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?");
} }
} }
else { else {
@@ -1470,23 +1578,28 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
MutexLock lock(server->m_clientsListLock); MutexLock lock(server->m_clientsListLock);
// Send every 4th peer on average, selected at random // 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, server->m_numConnections / 4));
const uint32_t peers_to_send_target = std::min<uint32_t>(PEER_LIST_RESPONSE_MAX_PEERS, std::max<uint32_t>(1, n / 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)) { 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; continue;
} }
uint64_t hi; const Peer p{ client->m_isV6, client->m_addr, client->m_listenPort, 0, 0 };
umul128(server->get_random64(), n, &hi); ++n;
if ((hi < peers_to_send_target) && (client->m_addr != m_addr)) { // Use https://en.wikipedia.org/wiki/Reservoir_sampling algorithm
peers[num_selected_peers++] = { client->m_isV6, client->m_addr, client->m_listenPort, 0 }; if (num_selected_peers < peers_to_send_target) {
peers[num_selected_peers++] = p;
continue;
}
if (num_selected_peers >= PEER_LIST_RESPONSE_MAX_PEERS) { uint64_t k;
break; umul128(server->get_random64(), n, &k);
}
if (k < peers_to_send_target) {
peers[k] = p;
} }
} }
} }
@@ -1520,6 +1633,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
{ {
P2PServer* server = static_cast<P2PServer*>(m_owner); P2PServer* server = static_cast<P2PServer*>(m_owner);
const time_t cur_time = time(nullptr);
MutexLock lock(server->m_peerListLock); MutexLock lock(server->m_peerListLock);
@@ -1531,20 +1645,28 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
memcpy(ip.data, buf, sizeof(ip.data)); memcpy(ip.data, buf, sizeof(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; int port = 0;
memcpy(&port, buf, 2); memcpy(&port, buf, 2);
buf += 2; buf += 2;
bool already_added = false; 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)) { if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) {
already_added = true; already_added = true;
p.m_lastSeen = cur_time;
break; break;
} }
} }
if (!already_added && !server->is_banned(ip)) { 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 });
} }
} }
+14 -5
View File
@@ -19,7 +19,6 @@
#include "tcp_server.h" #include "tcp_server.h"
#include <random> #include <random>
#include <unordered_map>
namespace p2pool { namespace p2pool {
@@ -109,13 +108,18 @@ public:
bool m_handshakeComplete; bool m_handshakeComplete;
bool m_handshakeInvalid; bool m_handshakeInvalid;
int m_listenPort; int m_listenPort;
time_t m_nextPeerListRequest; 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_lastAlive;
time_t m_lastBroadcastTimestamp; time_t m_lastBroadcastTimestamp;
time_t m_lastBlockrequestTimestamp; time_t m_lastBlockrequestTimestamp;
uv_rwlock_t m_broadcastedHashesLock; hash m_broadcastedHashes[8];
std::set<hash> m_broadcastedHashes; std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 };
}; };
void broadcast(const PoolBlock& block); void broadcast(const PoolBlock& block);
@@ -123,6 +127,8 @@ public:
uint64_t get_peerId() const { return m_peerId; } uint64_t get_peerId() const { return m_peerId; }
void print_status() override; void print_status() override;
void show_peers();
size_t peer_list_size() const { return m_peerList.size(); }
private: private:
p2pool* m_pool; p2pool* m_pool;
@@ -131,7 +137,7 @@ private:
std::string m_initialPeerList; std::string m_initialPeerList;
uv_rwlock_t m_cachedBlocksLock; uv_rwlock_t m_cachedBlocksLock;
std::unordered_map<hash, PoolBlock*> m_cachedBlocks; unordered_map<hash, PoolBlock*> m_cachedBlocks;
private: private:
static void on_timer(uv_timer_t* timer) { reinterpret_cast<P2PServer*>(timer->data)->on_timer(); } static void on_timer(uv_timer_t* timer) { reinterpret_cast<P2PServer*>(timer->data)->on_timer(); }
@@ -139,6 +145,7 @@ private:
void flush_cache(); void flush_cache();
void download_missing_blocks(); void download_missing_blocks();
void check_zmq();
void update_peer_connections(); void update_peer_connections();
void update_peer_list(); void update_peer_list();
void save_peer_list_async(); void save_peer_list_async();
@@ -156,6 +163,7 @@ private:
PoolBlock* m_block; PoolBlock* m_block;
uv_timer_t m_timer; uv_timer_t m_timer;
uint32_t m_timerCounter;
uint64_t m_peerId; uint64_t m_peerId;
@@ -167,6 +175,7 @@ private:
raw_ip m_addr; raw_ip m_addr;
int m_port; int m_port;
uint32_t m_numFailedConnections; uint32_t m_numFailedConnections;
time_t m_lastSeen;
}; };
std::vector<Peer> m_peerList; std::vector<Peer> m_peerList;
@@ -184,7 +193,7 @@ private:
std::vector<Broadcast*> m_broadcastQueue; std::vector<Broadcast*> m_broadcastQueue;
uv_mutex_t m_missingBlockRequestsLock; 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(); } static void on_broadcast(uv_async_t* handle) { reinterpret_cast<P2PServer*>(handle->data)->on_broadcast(); }
void on_broadcast(); void on_broadcast();
+100 -14
View File
@@ -49,6 +49,8 @@ p2pool::p2pool(int argc, char* argv[])
, m_params(new Params(argc, argv)) , m_params(new Params(argc, argv))
, m_updateSeed(true) , m_updateSeed(true)
, m_submitBlockData{} , m_submitBlockData{}
, m_zmqLastActive(0)
, m_startTime(time(nullptr))
{ {
LOGINFO(1, log::LightCyan() << VERSION); LOGINFO(1, log::LightCyan() << VERSION);
@@ -105,10 +107,17 @@ p2pool::p2pool(int argc, char* argv[])
uv_mutex_init_checked(&m_foundBlocksLock); uv_mutex_init_checked(&m_foundBlocksLock);
uv_mutex_init_checked(&m_submitBlockDataLock); 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_sideChain = new SideChain(this, type);
m_hasher = new RandomX_Hasher(this);
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_blockTemplate = new BlockTemplate(this);
m_mempool = new Mempool(); m_mempool = new Mempool();
m_consoleCommands = new ConsoleCommands(this); m_consoleCommands = new ConsoleCommands(this);
@@ -129,9 +138,9 @@ p2pool::~p2pool()
delete m_consoleCommands; 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) uint64_t p2pool::get_seed_height(uint64_t height)
@@ -173,6 +182,8 @@ void p2pool::handle_tx(TxMempoolData& tx)
#if TEST_MEMPOOL_PICKING_ALGORITHM #if TEST_MEMPOOL_PICKING_ALGORITHM
m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet); m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet);
#endif #endif
m_zmqLastActive = time(nullptr);
} }
void p2pool::handle_miner_data(MinerData& data) void p2pool::handle_miner_data(MinerData& data)
@@ -199,6 +210,8 @@ void p2pool::handle_miner_data(MinerData& data)
c.reward = 0; c.reward = 0;
m_mainchainByHash[c.id] = c; m_mainchainByHash[c.id] = c;
cleanup_mainchain_data(data.height);
} }
data.tx_backlog.clear(); data.tx_backlog.clear();
@@ -220,12 +233,49 @@ void p2pool::handle_miner_data(MinerData& data)
"\n---------------------------------------------------------------------------------------------------------------" "\n---------------------------------------------------------------------------------------------------------------"
); );
if (!is_main_thread) { if (!is_main_thread()) {
update_block_template_async(); update_block_template_async();
} }
else { else {
update_block_template(); 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\ const char* BLOCK_FOUND = "\n\
@@ -288,6 +338,8 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
} }
api_update_network_stats(); api_update_network_stats();
m_zmqLastActive = time(nullptr);
} }
void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce) void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
@@ -525,6 +577,7 @@ void p2pool::download_block_headers(uint64_t current_height)
if (parse_block_headers_range(data, size) == BLOCK_HEADERS_REQUIRED) { if (parse_block_headers_range(data, size) == BLOCK_HEADERS_REQUIRED) {
update_median_timestamp(); update_median_timestamp();
if (m_serversStarted.exchange(1) == 0) { 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_stratumServer = new StratumServer(this);
m_p2pServer = new P2PServer(this); m_p2pServer = new P2PServer(this);
api_update_network_stats(); api_update_network_stats();
@@ -680,18 +733,22 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
const auto& result = doc["result"]; const auto& result = doc["result"];
struct { struct {
bool busy_syncing, mainnet, testnet, stagenet; bool busy_syncing, synchronized, mainnet, testnet, stagenet;
} info; } 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"); LOGWARN(1, "get_info RPC response is invalid, trying again in 1 second");
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_info(); get_info();
return; return;
} }
if (info.busy_syncing) { if (info.busy_syncing || !info.synchronized) {
LOGINFO(1, "monerod is busy syncing, trying again in 1 second"); 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)); std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_info(); get_info();
return; return;
@@ -761,10 +818,14 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
return; 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_hi = version >> 16;
const uint64_t version_lo = version & 65535; 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(); panic();
} }
@@ -963,7 +1024,7 @@ void p2pool::api_update_pool_stats()
uint64_t t; uint64_t t;
const difficulty_type& diff = m_sideChain->difficulty(); const difficulty_type& diff = m_sideChain->difficulty();
const uint64_t hashrate = udiv128(diff.hi, diff.lo, m_sideChain->block_time(), &t); 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(); const difficulty_type total_hashes = m_sideChain->total_hashes();
time_t last_block_found_time = 0; time_t last_block_found_time = 0;
@@ -1026,7 +1087,7 @@ void p2pool::api_update_stats_mod()
s << last_block_found_hash << '\0'; s << last_block_found_hash << '\0';
memcpy(last_block_found_buf + 4, "...", 4); 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; uint64_t t;
const difficulty_type& diff = m_sideChain->difficulty(); const difficulty_type& diff = m_sideChain->difficulty();
@@ -1055,6 +1116,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) void p2pool::api_update_block_found(const ChainMain* data)
{ {
clear_crypto_cache(); clear_crypto_cache();
@@ -1210,11 +1296,11 @@ int p2pool::run()
} }
try { try {
ZMQReader z(m_params->m_host.c_str(), m_params->m_zmqPort, this);
get_info(); get_info();
load_found_blocks(); load_found_blocks();
const int rc = uv_run(uv_default_loop_checked(), UV_RUN_DEFAULT); const int rc = uv_run(uv_default_loop_checked(), UV_RUN_DEFAULT);
LOGINFO(1, "uv_run exited, result = " << rc); LOGINFO(1, "uv_run exited, result = " << rc);
delete m_ZMQReader;
} }
catch (const std::exception& e) { catch (const std::exception& e) {
const char* s = e.what(); const char* s = e.what();
+21 -9
View File
@@ -19,12 +19,11 @@
#include "uv_util.h" #include "uv_util.h"
#include <map> #include <map>
#include <unordered_map>
namespace p2pool { namespace p2pool {
struct Params; struct Params;
class RandomX_Hasher; class RandomX_Hasher_Base;
class BlockTemplate; class BlockTemplate;
class Mempool; class Mempool;
class SideChain; class SideChain;
@@ -32,6 +31,7 @@ class StratumServer;
class P2PServer; class P2PServer;
class ConsoleCommands; class ConsoleCommands;
class p2pool_api; class p2pool_api;
class ZMQReader;
class p2pool : public MinerCallbackHandler class p2pool : public MinerCallbackHandler
{ {
@@ -49,8 +49,10 @@ public:
SideChain& side_chain() { return *m_sideChain; } SideChain& side_chain() { return *m_sideChain; }
const MinerData& miner_data() const { return m_minerData; } const MinerData& miner_data() const { return m_minerData; }
RandomX_Hasher* hasher() const { return m_hasher; } p2pool_api* api() const { return m_api; }
bool calculate_hash(const void* data, size_t size, const hash& seed, hash& result);
RandomX_Hasher_Base* hasher() const { return m_hasher; }
bool calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result);
static uint64_t get_seed_height(uint64_t height); static uint64_t get_seed_height(uint64_t height);
bool get_seed(uint64_t height, hash& seed) const; bool get_seed(uint64_t height, hash& seed) const;
@@ -76,6 +78,9 @@ public:
bool get_difficulty_at_height(uint64_t height, difficulty_type& diff); bool get_difficulty_at_height(uint64_t height, difficulty_type& diff);
time_t zmq_last_active() const { return m_zmqLastActive; }
time_t start_time() const { return m_startTime; }
private: private:
p2pool(const p2pool&) = delete; p2pool(const p2pool&) = delete;
p2pool(p2pool&&) = delete; p2pool(p2pool&&) = delete;
@@ -92,7 +97,7 @@ private:
p2pool_api* m_api; p2pool_api* m_api;
SideChain* m_sideChain; SideChain* m_sideChain;
RandomX_Hasher* m_hasher; RandomX_Hasher_Base* m_hasher;
BlockTemplate* m_blockTemplate; BlockTemplate* m_blockTemplate;
MinerData m_minerData; MinerData m_minerData;
bool m_updateSeed; bool m_updateSeed;
@@ -100,7 +105,7 @@ private:
mutable uv_rwlock_t m_mainchainLock; mutable uv_rwlock_t m_mainchainLock;
std::map<uint64_t, ChainMain> m_mainchainByHeight; std::map<uint64_t, ChainMain> m_mainchainByHeight;
std::unordered_map<hash, ChainMain> m_mainchainByHash; unordered_map<hash, ChainMain> m_mainchainByHash;
enum { TIMESTAMP_WINDOW = 60 }; enum { TIMESTAMP_WINDOW = 60 };
bool get_timestamps(uint64_t (&timestamps)[TIMESTAMP_WINDOW]) const; bool get_timestamps(uint64_t (&timestamps)[TIMESTAMP_WINDOW]) const;
@@ -125,6 +130,8 @@ private:
void api_update_pool_stats(); void api_update_pool_stats();
void api_update_stats_mod(); void api_update_stats_mod();
void cleanup_mainchain_data(uint64_t height);
struct FoundBlock struct FoundBlock
{ {
FORCEINLINE FoundBlock(time_t _t, uint64_t _h, const hash& _id, const difficulty_type& _block_diff, const difficulty_type& _total_hashes) FORCEINLINE FoundBlock(time_t _t, uint64_t _h, const hash& _id, const difficulty_type& _block_diff, const difficulty_type& _total_hashes)
@@ -153,9 +160,9 @@ private:
struct SubmitBlockData struct SubmitBlockData
{ {
uint32_t template_id; uint32_t template_id = 0;
uint32_t nonce; uint32_t nonce = 0;
uint32_t extra_nonce; uint32_t extra_nonce = 0;
std::vector<uint8_t> blob; std::vector<uint8_t> blob;
}; };
@@ -165,6 +172,11 @@ private:
uv_async_t m_submitBlockAsync; uv_async_t m_submitBlockAsync;
uv_async_t m_blockTemplateAsync; uv_async_t m_blockTemplateAsync;
uv_async_t m_stopAsync; uv_async_t m_stopAsync;
time_t m_zmqLastActive;
time_t m_startTime;
ZMQReader* m_ZMQReader = nullptr;
}; };
} // namespace p2pool } // namespace p2pool
+8 -2
View File
@@ -28,7 +28,7 @@ static constexpr char log_category_prefix[] = "P2Pool API ";
namespace p2pool { 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()) { if (m_apiPath.empty()) {
LOGERR(1, "api path is 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_networkPath = m_apiPath + "network/";
m_poolPath = m_apiPath + "pool/"; m_poolPath = m_apiPath + "pool/";
m_localPath = m_apiPath + "local/";
create_dir(m_networkPath); create_dir(m_networkPath);
create_dir(m_poolPath); create_dir(m_poolPath);
if (local_stats) {
create_dir(m_localPath);
}
} }
p2pool_api::~p2pool_api() 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::GLOBAL: path = m_apiPath + filename; break;
case Category::NETWORK: path = m_networkPath + filename; break; case Category::NETWORK: path = m_networkPath + filename; break;
case Category::POOL: path = m_poolPath + 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() 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); MutexLock lock(m_dumpDataLock);
data = std::move(m_dumpData); data = std::move(m_dumpData);
+4 -3
View File
@@ -18,20 +18,20 @@
#pragma once #pragma once
#include "uv_util.h" #include "uv_util.h"
#include <unordered_map>
namespace p2pool { namespace p2pool {
class p2pool_api class p2pool_api
{ {
public: public:
explicit p2pool_api(const std::string& api_path); p2pool_api(const std::string& api_path, const bool local_stats);
~p2pool_api(); ~p2pool_api();
enum class Category { enum class Category {
GLOBAL, GLOBAL,
NETWORK, NETWORK,
POOL, POOL,
LOCAL,
}; };
void on_stop(); void on_stop();
@@ -80,9 +80,10 @@ private:
std::string m_apiPath; std::string m_apiPath;
std::string m_networkPath; std::string m_networkPath;
std::string m_poolPath; std::string m_poolPath;
std::string m_localPath;
uv_mutex_t m_dumpDataLock; 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; uv_async_t m_dumpToFileAsync;
}; };
+16
View File
@@ -69,6 +69,22 @@ Params::Params(int argc, char* argv[])
if ((strcmp(argv[i], "--data-api") == 0) && (i + 1 < argc)) { if ((strcmp(argv[i], "--data-api") == 0) && (i + 1 < argc)) {
m_apiPath = argv[++i]; m_apiPath = argv[++i];
} }
if (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 (m_stratumAddresses.empty()) { if (m_stratumAddresses.empty()) {
+3
View File
@@ -37,6 +37,9 @@ struct Params
std::string m_p2pPeerList; std::string m_p2pPeerList;
std::string m_config; std::string m_config;
std::string m_apiPath; std::string m_apiPath;
bool m_localStats = false;
bool m_blockCache = true;
bool m_disableRandomX = false;
}; };
} // namespace p2pool } // namespace p2pool
+7 -9
View File
@@ -60,7 +60,6 @@ PoolBlock::PoolBlock()
m_mainChainData.reserve(48 * 1024); m_mainChainData.reserve(48 * 1024);
m_outputs.reserve(2048); m_outputs.reserve(2048);
m_transactions.reserve(256); m_transactions.reserve(256);
m_tmpInts.reserve(m_transactions.capacity() * HASH_SIZE);
m_sideChainData.reserve(512); m_sideChainData.reserve(512);
m_uncles.reserve(8); m_uncles.reserve(8);
m_tmpTxExtra.reserve(80); m_tmpTxExtra.reserve(80);
@@ -110,7 +109,6 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_cumulativeDifficulty = b.m_cumulativeDifficulty; m_cumulativeDifficulty = b.m_cumulativeDifficulty;
m_sidechainId = b.m_sidechainId; m_sidechainId = b.m_sidechainId;
m_tmpTxExtra = b.m_tmpTxExtra; m_tmpTxExtra = b.m_tmpTxExtra;
m_tmpInts = b.m_tmpInts;
m_depth = b.m_depth; m_depth = b.m_depth;
m_verified = b.m_verified; m_verified = b.m_verified;
m_invalid = b.m_invalid; m_invalid = b.m_invalid;
@@ -227,7 +225,7 @@ void PoolBlock::serialize_sidechain_data()
writeVarint(m_cumulativeDifficulty.hi, m_sideChainData); 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]; 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; cnt >>= 1;
m_tmpInts.resize(cnt * HASH_SIZE); std::vector<uint8_t> tmp_ints(cnt * HASH_SIZE);
memcpy(m_tmpInts.data(), h, (cnt * 2 - count) * 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) { 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) { while (cnt > 2) {
cnt >>= 1; cnt >>= 1;
for (i = 0, j = 0; j < cnt; i += 2, ++j) { 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; blob_size += HASH_SIZE;
writeVarint(count, [&blob, &blob_size](uint8_t b) { blob[blob_size++] = b; }); 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 } // namespace p2pool
+2 -3
View File
@@ -28,7 +28,7 @@
namespace p2pool { namespace p2pool {
class RandomX_Hasher; class RandomX_Hasher_Base;
class SideChain; class SideChain;
/* /*
@@ -121,7 +121,6 @@ struct PoolBlock
// Just temporary stuff, not a part of the block // Just temporary stuff, not a part of the block
std::vector<uint8_t> m_tmpTxExtra; std::vector<uint8_t> m_tmpTxExtra;
std::vector<uint8_t> m_tmpInts;
uint64_t m_depth; uint64_t m_depth;
@@ -137,7 +136,7 @@ struct PoolBlock
void serialize_sidechain_data(); void serialize_sidechain_data();
int deserialize(const uint8_t* data, size_t size, SideChain& sidechain); int deserialize(const uint8_t* data, size_t size, 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 } // namespace p2pool
-1
View File
@@ -341,7 +341,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
// Defaults for off-chain variables // Defaults for off-chain variables
m_tmpTxExtra.clear(); m_tmpTxExtra.clear();
m_tmpInts.clear();
m_depth = 0; m_depth = 0;
+117 -5
View File
@@ -22,6 +22,9 @@
#include "randomx.h" #include "randomx.h"
#include "configuration.h" #include "configuration.h"
#include "virtual_machine.hpp" #include "virtual_machine.hpp"
#include "json_rpc_request.h"
#include "json_parsers.h"
#include <rapidjson/document.h>
#include <thread> #include <thread>
static constexpr char log_category_prefix[] = "RandomX_Hasher "; static constexpr char log_category_prefix[] = "RandomX_Hasher ";
@@ -54,7 +57,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
const randomx_flags flags = randomx_get_flags(); 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); m_cache[i] = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
if (!m_cache[i]) { if (!m_cache[i]) {
LOGWARN(1, "couldn't allocate RandomX cache using large pages"); 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_datasetLock);
uv_rwlock_init_checked(&m_cacheLock); 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); uv_mutex_init_checked(&m_vm[i].mutex);
m_vm[i].vm = nullptr; m_vm[i].vm = nullptr;
} }
@@ -91,7 +94,7 @@ RandomX_Hasher::~RandomX_Hasher()
uv_rwlock_destroy(&m_datasetLock); uv_rwlock_destroy(&m_datasetLock);
uv_rwlock_destroy(&m_cacheLock); 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); MutexLock lock(m_vm[i].mutex);
if (m_vm[i].vm) { if (m_vm[i].vm) {
@@ -105,7 +108,7 @@ RandomX_Hasher::~RandomX_Hasher()
randomx_release_dataset(m_dataset); 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]) { if (m_cache[i]) {
randomx_release_cache(m_cache[i]); randomx_release_cache(m_cache[i]);
} }
@@ -302,7 +305,7 @@ void RandomX_Hasher::set_old_seed(const hash& seed)
LOGINFO(1, log::LightCyan() << "old cache updated"); LOGINFO(1, log::LightCyan() << "old cache updated");
} }
bool RandomX_Hasher::calculate(const void* data, size_t size, const hash& seed, hash& result) bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*/, const hash& seed, hash& result)
{ {
// First try to use the dataset if it's ready // First try to use the dataset if it's ready
if (uv_rwlock_tryrdlock(&m_datasetLock) == 0) { if (uv_rwlock_tryrdlock(&m_datasetLock) == 0) {
@@ -348,4 +351,113 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, const hash& seed,
return false; return false;
} }
RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
: m_pool(pool)
, m_loopStopped(false)
, m_loopThread{}
{
int err = uv_loop_init(&m_loop);
if (err) {
LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
panic();
}
uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
uv_async_init(&m_loop, &m_kickTheLoopAsync, nullptr);
m_shutdownAsync.data = this;
uv_mutex_init_checked(&m_requestMutex);
uv_mutex_init_checked(&m_condMutex);
err = uv_cond_init(&m_cond);
if (err) {
LOGERR(1, "failed to create cond, error " << uv_err_name(err));
panic();
}
err = uv_thread_create(&m_loopThread, loop, this);
if (err) {
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
panic();
}
}
RandomX_Hasher_RPC::~RandomX_Hasher_RPC()
{
uv_async_send(&m_shutdownAsync);
using namespace std::chrono;
while (!m_loopStopped) {
std::this_thread::sleep_for(milliseconds(1));
}
uv_mutex_destroy(&m_requestMutex);
uv_mutex_destroy(&m_condMutex);
uv_cond_destroy(&m_cond);
LOGINFO(1, "stopped");
}
void RandomX_Hasher_RPC::loop(void* data)
{
LOGINFO(1, "event loop started");
RandomX_Hasher_RPC* hasher = static_cast<RandomX_Hasher_RPC*>(data);
uv_run(&hasher->m_loop, UV_RUN_DEFAULT);
uv_loop_close(&hasher->m_loop);
LOGINFO(1, "event loop stopped");
hasher->m_loopStopped = true;
}
bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t height, const hash& /*seed*/, hash& h)
{
MutexLock lock(m_requestMutex);
const uint8_t* data = reinterpret_cast<const uint8_t*>(data_ptr);
const uint8_t major_version = data[0];
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf);
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"calc_pow\",\"params\":{\"major_version\":" << major_version <<
",\"height\":" << height <<
",\"block_blob\":\"" << log::hex_buf(data, size) << '"' <<
",\"seed_hash\":\"\"}}";
volatile int result = 0;
volatile bool done = false;
JSONRPCRequest::call(m_pool->params().m_host.c_str(), m_pool->params().m_rpcPort, buf,
[&result, &h](const char* data, size_t size)
{
rapidjson::Document doc;
if (doc.Parse(data, size).HasParseError() || !parseValue(doc, "result", h)) {
LOGWARN(3, "RPC calc_pow: invalid JSON response (parse error)");
result = -1;
return;
}
result = 1;
},
[this, &result, &done](const char* data, size_t size)
{
if (size > 0) {
LOGWARN(3, "RPC calc_pow: server returned error " << log::const_buf(data, size));
result = -1;
}
MutexLock lock2(m_condMutex);
done = true;
uv_cond_signal(&m_cond);
}, &m_loop);
uv_async_send(&m_kickTheLoopAsync);
{
MutexLock lock2(m_condMutex);
while (!done) {
uv_cond_wait(&m_cond, &m_condMutex);
}
}
return result > 0;
}
} // namespace p2pool } // namespace p2pool
+50 -7
View File
@@ -27,25 +27,36 @@ namespace p2pool {
class p2pool; class p2pool;
class RandomX_Hasher class RandomX_Hasher_Base
{
public:
virtual ~RandomX_Hasher_Base() {}
virtual void set_seed_async(const hash&) {}
virtual void set_old_seed(const hash&) {}
virtual bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) = 0;
};
class RandomX_Hasher : public RandomX_Hasher_Base
{ {
public: public:
explicit RandomX_Hasher(p2pool* pool); explicit RandomX_Hasher(p2pool* pool);
~RandomX_Hasher(); ~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_seed(const hash& seed);
void set_old_seed(const hash& seed); void set_old_seed(const hash& seed) override;
bool calculate(const void* data, size_t size, const hash& seed, hash& result); bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) override;
private: private:
struct ThreadSafeVM struct ThreadSafeVM
{ {
uv_mutex_t mutex; uv_mutex_t mutex{};
randomx_vm* vm; randomx_vm* vm = nullptr;
}; };
p2pool* m_pool; p2pool* m_pool;
@@ -62,7 +73,7 @@ private:
// 1: light VM for the previous seed // 1: light VM for the previous seed
// 2: full dataset VM for the current seed // 2: full dataset VM for the current seed
enum { FULL_DATASET_VM = 2 }; enum { FULL_DATASET_VM = 2 };
ThreadSafeVM m_vm[3]; ThreadSafeVM m_vm[3]{};
hash m_seed[2]; hash m_seed[2];
uint32_t m_index; uint32_t m_index;
@@ -70,4 +81,36 @@ private:
std::atomic<uint32_t> m_setSeedCounter; std::atomic<uint32_t> m_setSeedCounter;
}; };
class RandomX_Hasher_RPC : public RandomX_Hasher_Base
{
public:
explicit RandomX_Hasher_RPC(p2pool* pool);
~RandomX_Hasher_RPC();
bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) override;
private:
static void loop(void* data);
p2pool* m_pool;
uv_mutex_t m_requestMutex;
uv_loop_t m_loop;
volatile bool m_loopStopped;
uv_thread_t m_loopThread;
uv_mutex_t m_condMutex;
uv_cond_t m_cond;
uv_async_t m_shutdownAsync;
uv_async_t m_kickTheLoopAsync;
static void on_shutdown(uv_async_t* async)
{
RandomX_Hasher_RPC* server = reinterpret_cast<RandomX_Hasher_RPC*>(async->data);
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_shutdownAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_kickTheLoopAsync), nullptr);
}
};
} // namespace p2pool } // namespace p2pool
+80 -43
View File
@@ -77,8 +77,11 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
} }
uv_mutex_init_checked(&m_sidechainLock); uv_mutex_init_checked(&m_sidechainLock);
uv_mutex_init_checked(&m_seenBlocksLock);
m_difficultyData.reserve(m_chainWindowSize); m_difficultyData.reserve(m_chainWindowSize);
m_tmpShares.reserve(m_chainWindowSize * 2);
m_tmpRewards.reserve(m_chainWindowSize * 2);
LOGINFO(1, "generating consensus ID"); LOGINFO(1, "generating consensus ID");
@@ -147,6 +150,7 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
SideChain::~SideChain() SideChain::~SideChain()
{ {
uv_mutex_destroy(&m_sidechainLock); uv_mutex_destroy(&m_sidechainLock);
uv_mutex_destroy(&m_seenBlocksLock);
for (auto& it : m_blocksById) { for (auto& it : m_blocksById) {
delete it.second; delete it.second;
} }
@@ -259,6 +263,11 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
get_shares(&block, shares); 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 bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) const
{ {
shares.clear(); shares.clear();
@@ -343,18 +352,24 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
bool SideChain::block_seen(const PoolBlock& block) bool SideChain::block_seen(const PoolBlock& block)
{ {
MutexLock lock(m_sidechainLock);
// Check if it's some old block // Check if it's some old block
if (m_chainTip && m_chainTip->m_sidechainHeight > block.m_sidechainHeight + m_chainWindowSize * 2 && const PoolBlock* tip = m_chainTip;
block.m_cumulativeDifficulty < m_chainTip->m_cumulativeDifficulty) { if (tip && tip->m_sidechainHeight > block.m_sidechainHeight + m_chainWindowSize * 2 &&
block.m_cumulativeDifficulty < tip->m_cumulativeDifficulty) {
return true; return true;
} }
// Check if it was received before // Check if it was received before
MutexLock lock(m_seenBlocksLock);
return !m_seenBlocks.insert(block.m_sidechainId).second; 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; extern const char* BLOCK_FOUND;
bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks) bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks)
@@ -364,7 +379,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
return false; return false;
} }
difficulty_type min_accepted_diff; bool too_low_diff = (block.m_difficulty < m_curDifficulty);
{ {
MutexLock lock(m_sidechainLock); MutexLock lock(m_sidechainLock);
if (m_blocksById.find(block.m_sidechainId) != m_blocksById.end()) { if (m_blocksById.find(block.m_sidechainId) != m_blocksById.end()) {
@@ -372,23 +387,25 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
return true; return true;
} }
// Find the minimum difficulty in the current PPLNS window // This is mainly an anti-spam measure, not an actual verification step
min_accepted_diff = m_curDifficulty; if (too_low_diff) {
for (PoolBlock* tmp = m_chainTip; tmp && (tmp->m_sidechainHeight + m_chainWindowSize > m_chainTip->m_sidechainHeight); tmp = get_parent(tmp)) { // Reduce required diff by 50% (by doubling this block's diff) to account for alternative chains
if (tmp->m_difficulty < min_accepted_diff) { difficulty_type diff2 = block.m_difficulty;
min_accepted_diff = tmp->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); 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 if (too_low_diff) {
min_accepted_diff.lo = (min_accepted_diff.lo >> 1) | (min_accepted_diff.hi << 63); LOGWARN(4, "add_external_block: block has too low difficulty " << block.m_difficulty << ", expected >= ~" << m_curDifficulty << ". Ignoring it.");
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.");
return true; return true;
} }
@@ -407,13 +424,15 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
hash seed; hash seed;
if (!m_pool->get_seed(block.m_txinGenHeight, 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); LOGWARN(3, "add_external_block: couldn't get seed hash for mainchain height " << block.m_txinGenHeight);
unsee_block(block);
return false; return false;
} }
hash pow_hash; hash pow_hash;
if (!block.get_pow_hash(m_pool->hasher(), seed, pow_hash)) { if (!block.get_pow_hash(m_pool->hasher(), block.m_txinGenHeight, seed, pow_hash)) {
LOGWARN(3, "add_external_block: couldn't get PoW hash for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight); LOGWARN(3, "add_external_block: couldn't get PoW hash for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight << ". Ignoring it.");
return false; unsee_block(block);
return true;
} }
// Check if it has the correct parent and difficulty to go right to monerod for checking // Check if it has the correct parent and difficulty to go right to monerod for checking
@@ -478,8 +497,8 @@ void SideChain::add_block(const PoolBlock& block)
); );
// Save it for faster syncing on the next p2pool start // Save it for faster syncing on the next p2pool start
if (m_pool->p2p_server()) { if (p2pServer()) {
m_pool->p2p_server()->store_in_cache(block); p2pServer()->store_in_cache(block);
} }
PoolBlock* new_block = new PoolBlock(block); PoolBlock* new_block = new PoolBlock(block);
@@ -556,21 +575,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) bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob)
{ {
std::vector<MinerShare> shares; blob.clear();
std::vector<uint64_t> rewards;
shares.reserve(m_chainWindowSize * 2);
rewards.reserve(m_chainWindowSize * 2);
MutexLock lock(m_sidechainLock); 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; return false;
} }
const size_t n = shares.size(); const size_t n = m_tmpShares.size();
blob.clear();
blob.reserve(n * 38 + 64); blob.reserve(n * 38 + 64);
writeVarint(n, blob); writeVarint(n, blob);
@@ -580,16 +612,16 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
hash eph_public_key; hash eph_public_key;
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
writeVarint(rewards[i], blob); writeVarint(m_tmpRewards[i], blob);
blob.emplace_back(TXOUT_TO_KEY); 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); LOGWARN(6, "get_eph_public_key failed at index " << i);
} }
blob.insert(blob.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE); 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; return true;
@@ -730,9 +762,9 @@ uint64_t SideChain::miner_count()
MutexLock lock(m_sidechainLock); 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();) { 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); it = m_seenWallets.erase(it);
} }
else { else {
@@ -946,14 +978,14 @@ void SideChain::verify_loop(PoolBlock* block)
// If it came through a broadcast, send it to our peers // If it came through a broadcast, send it to our peers
if (block->m_wantBroadcast && !block->m_broadcasted) { if (block->m_wantBroadcast && !block->m_broadcasted) {
block->m_broadcasted = true; block->m_broadcasted = true;
if (m_pool->p2p_server() && (block->m_depth < UNCLE_BLOCK_DEPTH)) { if (p2pServer() && (block->m_depth < UNCLE_BLOCK_DEPTH)) {
m_pool->p2p_server()->broadcast(*block); p2pServer()->broadcast(*block);
} }
} }
// Save it for faster syncing on the next p2pool start // Save it for faster syncing on the next p2pool start
if (m_pool->p2p_server()) { if (p2pServer()) {
m_pool->p2p_server()->store_in_cache(*block); p2pServer()->store_in_cache(*block);
} }
// Try to verify blocks on top of this one // Try to verify blocks on top of this one
@@ -1289,7 +1321,9 @@ void SideChain::update_chain_tip(PoolBlock* block)
", main chain height = " << log::Gray() << m_chainTip->m_txinGenHeight); ", main chain height = " << log::Gray() << m_chainTip->m_txinGenHeight);
block->m_wantBroadcast = true; block->m_wantBroadcast = true;
m_pool->update_block_template_async(); if (m_pool) {
m_pool->update_block_template_async();
}
prune_old_blocks(); prune_old_blocks();
} }
} }
@@ -1305,7 +1339,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
m_pool->update_block_template_async(); 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; block->m_broadcasted = true;
#ifdef DEBUG_BROADCAST_DELAY_MS #ifdef DEBUG_BROADCAST_DELAY_MS
struct Work struct Work
@@ -1316,7 +1350,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
}; };
Work* work = new Work{}; Work* work = new Work{};
work->req.data = work; work->req.data = work;
work->server = m_pool->p2p_server(); work->server = p2pServer();
work->block = block; work->block = block;
const int err = uv_queue_work(uv_default_loop(), &work->req, const int err = uv_queue_work(uv_default_loop(), &work->req,
[](uv_work_t*) [](uv_work_t*)
@@ -1335,7 +1369,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
LOGERR(1, "update_chain_tip: uv_queue_work failed, error " << uv_err_name(err)); LOGERR(1, "update_chain_tip: uv_queue_work failed, error " << uv_err_name(err));
} }
#else #else
m_pool->p2p_server()->broadcast(*block); p2pServer()->broadcast(*block);
#endif #endif
} }
} }
@@ -1531,6 +1565,7 @@ void SideChain::prune_old_blocks()
auto it2 = m_blocksById.find(block->m_sidechainId); auto it2 = m_blocksById.find(block->m_sidechainId);
if (it2 != m_blocksById.end()) { if (it2 != m_blocksById.end()) {
m_blocksById.erase(it2); m_blocksById.erase(it2);
unsee_block(*block);
delete block; delete block;
++num_blocks_pruned; ++num_blocks_pruned;
} }
@@ -1555,7 +1590,9 @@ void SideChain::prune_old_blocks()
// If side-chain started pruning blocks it means the initial sync is complete // If side-chain started pruning blocks it means the initial sync is complete
// It's now safe to delete cached blocks // It's now safe to delete cached blocks
m_pool->p2p_server()->clear_cached_blocks(); if (p2pServer()) {
p2pServer()->clear_cached_blocks();
}
} }
} }
+12 -5
View File
@@ -19,12 +19,11 @@
#include "uv_util.h" #include "uv_util.h"
#include <map> #include <map>
#include <unordered_map>
#include <unordered_set>
namespace p2pool { namespace p2pool {
class p2pool; class p2pool;
class P2PServer;
struct DifficultyData; struct DifficultyData;
struct PoolBlock; struct PoolBlock;
class Wallet; class Wallet;
@@ -47,6 +46,7 @@ public:
void fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares); void fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares);
bool block_seen(const PoolBlock& block); bool block_seen(const PoolBlock& block);
void unsee_block(const PoolBlock& block);
bool add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks); bool add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks);
void add_block(const PoolBlock& block); void add_block(const PoolBlock& block);
void get_missing_blocks(std::vector<hash>& missing_blocks); void get_missing_blocks(std::vector<hash>& missing_blocks);
@@ -72,10 +72,13 @@ public:
time_t last_updated() const; time_t last_updated() const;
bool is_default() const; bool is_default() 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); static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards);
private: private:
p2pool* m_pool; p2pool* m_pool;
P2PServer* p2pServer() const;
NetworkType m_networkType; NetworkType m_networkType;
private: private:
@@ -97,9 +100,13 @@ private:
mutable uv_mutex_t m_sidechainLock; mutable uv_mutex_t m_sidechainLock;
PoolBlock* m_chainTip; PoolBlock* m_chainTip;
std::map<uint64_t, std::vector<PoolBlock*>> m_blocksByHeight; std::map<uint64_t, std::vector<PoolBlock*>> m_blocksByHeight;
std::unordered_map<hash, PoolBlock*> m_blocksById; unordered_map<hash, PoolBlock*> m_blocksById;
std::unordered_set<hash> m_seenBlocks; unordered_map<hash, time_t> m_seenWallets;
std::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; std::vector<DifficultyData> m_difficultyData;
+175 -49
View File
@@ -21,6 +21,7 @@
#include "p2pool.h" #include "p2pool.h"
#include "side_chain.h" #include "side_chain.h"
#include "params.h" #include "params.h"
#include "p2pool_api.h"
static constexpr char log_category_prefix[] = "StratumServer "; static constexpr char log_category_prefix[] = "StratumServer ";
@@ -48,6 +49,7 @@ StratumServer::StratumServer(p2pool* pool)
, m_hashrateDataTail_24h(0) , m_hashrateDataTail_24h(0)
, m_cumulativeFoundSharesDiff(0.0) , m_cumulativeFoundSharesDiff(0.0)
, m_totalFoundShares(0) , m_totalFoundShares(0)
, m_apiLastUpdateTime(0)
{ {
m_hashrateData[0] = { time(nullptr), 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) bool StratumServer::get_custom_diff(const char* s, difficulty_type& diff)
{ {
const char* diff_str = nullptr; 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()); 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; uint32_t job_id;
{ {
MutexLock lock(client->m_jobsLock); MutexLock lock(client->m_jobsLock);
job_id = client->m_perConnectionJobId++; job_id = client->m_perConnectionJobId++;
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(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.job_id = job_id;
saved_job.extra_nonce = extra_nonce; saved_job.extra_nonce = extra_nonce;
saved_job.template_id = template_id; 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); 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) { if (saved_job.job_id == job_id) {
template_id = saved_job.template_id; template_id = saved_job.template_id;
extra_nonce = saved_job.extra_nonce; extra_nonce = saved_job.extra_nonce;
@@ -317,6 +345,27 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
} }
if (found) { 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; 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_req.data = share;
share->m_server = this; share->m_server = this;
share->m_client = client; share->m_client = client;
share->m_clientAddr = client->m_addr;
share->m_clientResetCounter = client->m_resetCounter.load(); share->m_clientResetCounter = client->m_resetCounter.load();
share->m_rpcId = client->m_rpcId; share->m_rpcId = client->m_rpcId;
share->m_id = id; 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_extraNonce = extra_nonce;
share->m_target = target; share->m_target = target;
share->m_resultHash = resultHash; 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); const int err = uv_queue_work(&m_loop, &share->m_req, on_share_found, on_after_share_found);
if (err) { if (err) {
LOGERR(1, "uv_queue_work failed, error " << uv_err_name(err)); LOGERR(1, "uv_queue_work failed, error " << uv_err_name(err));
MutexLock lock(m_submittedSharesPoolLock);
m_submittedSharesPool.push_back(share); // If uv_queue_work failed, process this share here anyway
return false; on_share_found(&share->m_req);
on_after_share_found(&share->m_req, 0);
} }
return true; return true;
@@ -375,7 +435,7 @@ uint64_t StratumServer::get_random64()
void StratumServer::print_status() void StratumServer::print_status()
{ {
update_hashrate_data(0); update_hashrate_data(0, time(nullptr));
print_stratum_status(); print_stratum_status();
} }
@@ -485,7 +545,7 @@ void StratumServer::on_blobs_ready()
job_id = client->m_perConnectionJobId++; 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.job_id = job_id;
saved_job.extra_nonce = extra_nonce; saved_job.extra_nonce = extra_nonce;
saved_job.template_id = data->m_templateId; 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"); 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); constexpr size_t N = array_size(&StratumServer::m_hashrateData);
uint64_t rem;
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
WriteLock lock(m_hashrateDataLock); WriteLock lock(m_hashrateDataLock);
@@ -545,20 +602,20 @@ void StratumServer::update_hashrate_data(uint64_t target)
head.m_cumulativeHashes = m_cumulativeHashes; head.m_cumulativeHashes = m_cumulativeHashes;
} }
else { else {
m_hashrateDataHead = (m_hashrateDataHead + 1) % array_size(m_hashrateData); m_hashrateDataHead = (m_hashrateDataHead + 1) % N;
data[m_hashrateDataHead] = { timestamp, m_cumulativeHashes }; data[m_hashrateDataHead] = { timestamp, m_cumulativeHashes };
} }
while (data[m_hashrateDataTail_15m].m_timestamp + 15 * 60 < timestamp) { 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) { 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) { 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; StratumClient* client = share->m_client;
StratumServer* server = share->m_server; StratumServer* server = share->m_server;
p2pool* pool = server->m_pool; p2pool* pool = server->m_pool;
BlockTemplate& block = pool->block_template();
uint64_t target = share->m_target; uint64_t target = share->m_target;
if (target >= TARGET_4_BYTES_LIMIT) { if (target >= TARGET_4_BYTES_LIMIT) {
target = (target >> 32) << 32; target = (target >> 32) << 32;
} }
uint64_t rem;
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
if (pool->stopped()) { if (pool->stopped()) {
LOGWARN(0, "p2pool is shutting down, but a share was found. Trying to process it anyway!"); LOGWARN(0, "p2pool is shutting down, but a share was found. Trying to process it anyway!");
} }
uint8_t blob[128]; if (share->m_sidechainDifficulty.check_pow(share->m_resultHash)) {
uint64_t height; uint8_t blob[128];
difficulty_type difficulty; uint64_t height;
difficulty_type sidechain_difficulty; difficulty_type difficulty;
hash seed_hash; difficulty_type sidechain_difficulty;
size_t nonce_offset; 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); 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) { if (!blob_size) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share"); LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
share->m_result = SubmittedShare::Result::STALE; share->m_result = SubmittedShare::Result::STALE;
return; 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) { for (uint32_t i = 0, nonce = share->m_nonce; i < sizeof(share->m_nonce); ++i) {
blob[nonce_offset + i] = nonce & 255; blob[nonce_offset + i] = nonce & 255;
nonce >>= 8; nonce >>= 8;
} }
hash pow_hash; 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"); LOGWARN(3, "client " << static_cast<char*>(client->m_addrString) << " couldn't check share PoW");
share->m_result = SubmittedShare::Result::COULDNT_CHECK_POW; share->m_result = SubmittedShare::Result::COULDNT_CHECK_POW;
return; return;
@@ -617,9 +673,6 @@ void StratumServer::on_share_found(uv_work_t* req)
return; 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 uint64_t n = server->m_cumulativeHashes + hashes;
const double diff = sidechain_difficulty.to_double(); const double diff = sidechain_difficulty.to_double();
const double effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff; const double effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
@@ -628,23 +681,18 @@ void StratumServer::on_share_found(uv_work_t* req)
server->m_cumulativeFoundSharesDiff += diff; server->m_cumulativeFoundSharesDiff += diff;
++server->m_totalFoundShares; ++server->m_totalFoundShares;
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", effort " << effort << '%'); const std::string& s = client->m_customUser;
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", client " << static_cast<char*>(client->m_addrString) << (!s.empty() ? " user " : "") << s << ", effort " << effort << '%');
if (mainchain_solution) { pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
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);
}
} }
// Send the response to miner // Send the response to miner
const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t)); const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t));
if (LIKELY(value < target)) { 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; share->m_result = SubmittedShare::Result::OK;
} }
else { else {
@@ -670,6 +718,8 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
StratumClient* client = share->m_client; StratumClient* client = share->m_client;
StratumServer* server = share->m_server; 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)) { if ((client->m_resetCounter.load() == share->m_clientResetCounter) && (client->m_rpcId == share->m_rpcId)) {
const bool result = server->send(client, const bool result = server->send(client,
[share](void* buf) [share](void* buf)
@@ -695,7 +745,7 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
return s.m_pos; 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->ban(DEFAULT_BAN_TIME);
client->close(); client->close();
} }
@@ -703,6 +753,9 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
client->close(); client->close();
} }
} }
else if (bad_share) {
server->ban(share->m_clientAddr, DEFAULT_BAN_TIME);
}
} }
StratumServer::StratumClient::StratumClient() StratumServer::StratumClient::StratumClient()
@@ -726,6 +779,7 @@ void StratumServer::StratumClient::reset()
memset(m_jobs, 0, sizeof(m_jobs)); memset(m_jobs, 0, sizeof(m_jobs));
m_perConnectionJobId = 0; m_perConnectionJobId = 0;
m_customDiff = {}; m_customDiff = {};
m_customUser.clear();
} }
bool StratumServer::StratumClient::on_read(char* data, uint32_t size) 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()); 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 } // namespace p2pool
+8 -1
View File
@@ -65,6 +65,7 @@ public:
uint32_t m_perConnectionJobId; uint32_t m_perConnectionJobId;
difficulty_type m_customDiff; difficulty_type m_customDiff;
std::string m_customUser;
}; };
bool on_login(StratumClient* client, uint32_t id, const char* login); bool on_login(StratumClient* client, uint32_t id, const char* login);
@@ -77,6 +78,7 @@ private:
void print_stratum_status() const; void print_stratum_status() const;
static bool get_custom_diff(const char* s, difficulty_type& diff); 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_share_found(uv_work_t* req);
static void on_after_share_found(uv_work_t* req, int status); static void on_after_share_found(uv_work_t* req, int status);
@@ -112,6 +114,7 @@ private:
uv_work_t m_req; uv_work_t m_req;
StratumServer* m_server; StratumServer* m_server;
StratumClient* m_client; StratumClient* m_client;
raw_ip m_clientAddr;
uint32_t m_clientResetCounter; uint32_t m_clientResetCounter;
uint32_t m_rpcId; uint32_t m_rpcId;
uint32_t m_id; uint32_t m_id;
@@ -120,6 +123,7 @@ private:
uint32_t m_extraNonce; uint32_t m_extraNonce;
uint64_t m_target; uint64_t m_target;
hash m_resultHash; hash m_resultHash;
difficulty_type m_sidechainDifficulty;
enum class Result { enum class Result {
STALE, STALE,
@@ -152,7 +156,10 @@ private:
double m_cumulativeFoundSharesDiff; double m_cumulativeFoundSharesDiff;
uint32_t m_totalFoundShares; 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 } // namespace p2pool
+12 -45
View File
@@ -18,8 +18,6 @@
#pragma once #pragma once
#include "uv_util.h" #include "uv_util.h"
#include <map>
#include <set>
namespace p2pool { namespace p2pool {
@@ -46,36 +44,6 @@ public:
int listen_port() const { return m_listenPort; } 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); 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); virtual void on_connect_failed(bool is_v6, const raw_ip& ip, int port);
@@ -108,7 +76,6 @@ public:
Client* m_next; Client* m_next;
uv_tcp_t m_socket; uv_tcp_t m_socket;
uv_write_t m_write;
uv_connect_t m_connectRequest; uv_connect_t m_connectRequest;
bool m_isV6; bool m_isV6;
@@ -121,21 +88,21 @@ public:
char m_readBuf[READ_BUF_SIZE]; char m_readBuf[READ_BUF_SIZE];
uint32_t m_numRead; 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 }; std::atomic<uint32_t> m_resetCounter{ 0 };
uv_mutex_t m_sendLock; 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 struct SendCallbackBase
{ {
virtual ~SendCallbackBase() {} virtual ~SendCallbackBase() {}
@@ -193,12 +160,12 @@ protected:
uint32_t m_numIncomingConnections; uint32_t m_numIncomingConnections;
uv_mutex_t m_bansLock; 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); bool is_banned(const raw_ip& ip);
uv_mutex_t m_pendingConnectionsLock; uv_mutex_t m_pendingConnectionsLock;
std::set<raw_ip> m_pendingConnections; unordered_set<raw_ip> m_pendingConnections;
uv_async_t m_dropConnectionsAsync; uv_async_t m_dropConnectionsAsync;
static void on_drop_connections(uv_async_t* async) { reinterpret_cast<TCPServer*>(async->data)->close_sockets(false); } static void on_drop_connections(uv_async_t* async) { reinterpret_cast<TCPServer*>(async->data)->close_sockets(false); }
+37 -32
View File
@@ -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_clientsListLock);
uv_mutex_init_checked(&m_bansLock); uv_mutex_init_checked(&m_bansLock);
uv_mutex_init_checked(&m_pendingConnectionsLock); 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); m_preallocatedClients.reserve(DEFAULT_BACKLOG);
for (int i = 0; i < DEFAULT_BACKLOG; ++i) { 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()); 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); callback(is_v6, address, ip, port);
} }
else { 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)); return connect_to_peer_nolock(client, is_v6, reinterpret_cast<sockaddr*>(&addr));
} }
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE> template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connect_failed(bool, const raw_ip&, int) 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); MutexLock lock(m_bansLock);
auto it = m_bans.find(ip); auto it = m_bans.find(ip);
if ((it != m_bans.end()) && (time(nullptr) < it->second)) { if (it != m_bans.end()) {
return true; const bool banned = (time(nullptr) < it->second);
if (!banned) {
m_bans.erase(it);
}
return banned;
} }
return false; 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_bansLock);
uv_mutex_destroy(&m_pendingConnectionsLock); uv_mutex_destroy(&m_pendingConnectionsLock);
{
MutexLock lock(m_writeBuffersLock);
for (WriteBuf* buf : m_writeBuffers) {
delete buf;
}
}
uv_mutex_destroy(&m_writeBuffersLock);
LOGINFO(1, "stopped"); 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); MutexLock lock0(client->m_sendLock);
typename Client::WriteBuf* buf = nullptr; WriteBuf* buf = nullptr;
{ {
MutexLock lock(client->m_writeBuffersLock); MutexLock lock(m_writeBuffersLock);
if (!client->m_writeBuffers.empty()) { if (!m_writeBuffers.empty()) {
buf = client->m_writeBuffers.back(); buf = m_writeBuffers.back();
client->m_writeBuffers.pop_back(); m_writeBuffers.pop_back();
} }
} }
if (!buf) { if (!buf) {
buf = new typename Client::WriteBuf(); buf = new WriteBuf();
} }
const size_t bytes_written = callback(buf->m_data); 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) { if (bytes_written == 0) {
LOGWARN(1, "send callback wrote 0 bytes, nothing to do"); LOGWARN(1, "send callback wrote 0 bytes, nothing to do");
{ {
MutexLock lock(client->m_writeBuffersLock); MutexLock lock(m_writeBuffersLock);
client->m_writeBuffers.push_back(buf); m_writeBuffers.push_back(buf);
} }
return true; 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); const int err = uv_write(&buf->m_write, reinterpret_cast<uv_stream_t*>(&client->m_socket), bufs, 1, Client::on_write);
if (err) { if (err) {
{ {
MutexLock lock(client->m_writeBuffersLock); MutexLock lock(m_writeBuffersLock);
client->m_writeBuffers.push_back(buf); 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)); LOGWARN(1, "failed to start writing data to client connection " << static_cast<const char*>(client->m_addrString) << ", error " << uv_err_name(err));
return false; return false;
@@ -547,6 +564,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::loop(void* data)
TCPServer* server = static_cast<TCPServer*>(data); TCPServer* server = static_cast<TCPServer*>(data);
uv_run(&server->m_loop, UV_RUN_DEFAULT); uv_run(&server->m_loop, UV_RUN_DEFAULT);
uv_loop_close(&server->m_loop); uv_loop_close(&server->m_loop);
LOGINFO(1, "event loop stopped");
server->m_loopStopped = true; server->m_loopStopped = true;
} }
@@ -761,27 +779,14 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::Client()
{ {
Client::reset(); Client::reset();
uv_mutex_init_checked(&m_writeBuffersLock);
uv_mutex_init_checked(&m_sendLock); uv_mutex_init_checked(&m_sendLock);
m_readBuf[0] = '\0'; 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> template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::~Client() 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); uv_mutex_destroy(&m_sendLock);
} }
@@ -794,7 +799,6 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::reset()
m_prev = nullptr; m_prev = nullptr;
m_next = nullptr; m_next = nullptr;
memset(&m_socket, 0, sizeof(m_socket)); memset(&m_socket, 0, sizeof(m_socket));
memset(&m_write, 0, sizeof(m_write));
memset(&m_connectRequest, 0, sizeof(m_connectRequest)); memset(&m_connectRequest, 0, sizeof(m_connectRequest));
m_isV6 = false; m_isV6 = false;
m_isIncoming = 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> 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) 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; Client* client = buf->m_client;
TCPServer* server = client->m_owner;
{ if (server) {
MutexLock lock(client->m_writeBuffersLock); MutexLock lock(server->m_writeBuffersLock);
client->m_writeBuffers.push_back(buf); server->m_writeBuffers.push_back(buf);
} }
if (status != 0) { if (status != 0) {
+6 -3
View File
@@ -32,7 +32,7 @@ namespace p2pool {
#define STR2(X) STR(X) #define STR2(X) STR(X)
#define STR(X) #X #define STR(X) #X
const char* VERSION = "v1.1 (built" const char* VERSION = "v1.4.0 (built"
#if defined(__clang__) #if defined(__clang__)
" with clang/" __clang_version__ " with clang/" __clang_version__
#elif defined(__GNUC__) #elif defined(__GNUC__)
@@ -225,7 +225,7 @@ void uv_rwlock_init_checked(uv_rwlock_t* lock)
uv_loop_t* uv_default_loop_checked() 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!"); LOGERR(1, "uv_default_loop() can only be used by the main thread. Fix the code!");
#ifdef _WIN32 #ifdef _WIN32
if (IsDebuggerPresent()) { if (IsDebuggerPresent()) {
@@ -339,7 +339,10 @@ void BackgroundJobTracker::print_status()
} }
BackgroundJobTracker bkg_jobs_tracker; 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) bool resolve_host(std::string& host, bool& is_v6)
{ {
+48 -13
View File
@@ -17,6 +17,21 @@
#pragma once #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 { namespace p2pool {
extern const char* VERSION; 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, 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(); [[noreturn]] void panic();
@@ -117,24 +133,28 @@ private:
}; };
extern BackgroundJobTracker bkg_jobs_tracker; 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); 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 p2pool
namespace std { namespace robin_hood {
template<> template<>
struct hash<p2pool::hash> struct hash<p2pool::hash>
{ {
FORCEINLINE size_t operator()(const p2pool::hash& value) const noexcept FORCEINLINE size_t operator()(const p2pool::hash& value) const noexcept
{ {
uint64_t result = 0xcbf29ce484222325ull; return hash_bytes(value.h, p2pool::HASH_SIZE);
for (size_t i = 0; i < p2pool::HASH_SIZE; ++i) {
result = (result ^ value.h[i]) * 0x100000001b3ull;
}
return static_cast<size_t>(result);
} }
}; };
@@ -143,12 +163,27 @@ struct hash<std::array<uint8_t, N>>
{ {
FORCEINLINE size_t operator()(const std::array<uint8_t, N>& value) const noexcept FORCEINLINE size_t operator()(const std::array<uint8_t, N>& value) const noexcept
{ {
uint64_t result = 0xcbf29ce484222325ull; return hash_bytes(value.data(), N);
for (size_t i = 0; i < N; ++i) {
result = (result ^ value[i]) * 0x100000001b3ull;
}
return static_cast<size_t>(result);
} }
}; };
} // 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
+3
View File
@@ -19,6 +19,9 @@
#include <uv.h> #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 { namespace p2pool {
struct MutexLock : public nocopy_nomove struct MutexLock : public nocopy_nomove
+31 -11
View File
@@ -19,6 +19,7 @@
#include "zmq_reader.h" #include "zmq_reader.h"
#include "json_parsers.h" #include "json_parsers.h"
#include <rapidjson/document.h> #include <rapidjson/document.h>
#include <random>
static constexpr char log_category_prefix[] = "ZMQReader "; 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() void ZMQReader::run()
{ {
try { try {
char addr[32]; char addr[32];
snprintf(addr, sizeof(addr), "tcp://%s:%u", m_address, m_zmqPort); snprintf(addr, sizeof(addr), "tcp://%s:%u", m_address, m_zmqPort);
if (!connect(addr, m_zmqPort)) { if (!connect(addr)) {
throw zmq::error_t(); return;
} }
snprintf(addr, sizeof(addr), "tcp://127.0.0.1:%u", m_publisherPort); snprintf(addr, sizeof(addr), "tcp://127.0.0.1:%u", m_publisherPort);
if (!connect(addr, m_publisherPort)) { if (!connect(addr)) {
throw zmq::error_t(); return;
} }
m_subscriber.set(zmq::sockopt::subscribe, "json-full-chain_main"); m_subscriber.set(zmq::sockopt::subscribe, "json-full-chain_main");
@@ -122,10 +129,9 @@ void ZMQReader::run()
LOGERR(1, "exception " << e.what() << ", aborting"); LOGERR(1, "exception " << e.what() << ", aborting");
panic(); 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 struct ConnectMonitor : public zmq::monitor_t
{ {
@@ -138,19 +144,33 @@ bool ZMQReader::connect(const char* address, uint32_t id)
bool connected = false; bool connected = false;
} monitor; } monitor;
char buf[32]; static uint64_t id = 0;
snprintf(buf, sizeof(buf), "inproc://connect-mon-%u", id);
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); monitor.init(m_subscriber, buf);
m_subscriber.connect(address); m_subscriber.connect(address);
using namespace std::chrono; 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)) { 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) { if (elapsed_time >= 3000) {
LOGERR(1, "failed to connect to " << address); LOGERR(1, "failed to connect to " << address);
return false; if (m_finished.load()) {
return false;
}
start_time = cur_time;
} }
} }
+2 -2
View File
@@ -28,9 +28,9 @@ public:
~ZMQReader(); ~ZMQReader();
private: private:
static void run_wrapper(void* arg) { reinterpret_cast<ZMQReader*>(arg)->run(); } static void run_wrapper(void* arg);
void run(); void run();
bool connect(const char* address, uint32_t id); bool connect(const char* address);
void parse(char* data, size_t size); void parse(char* data, size_t size);
+3
View File
@@ -84,6 +84,7 @@ set(SOURCES
../src/json_rpc_request.cpp ../src/json_rpc_request.cpp
../src/keccak.cpp ../src/keccak.cpp
../src/log.cpp ../src/log.cpp
../src/memory_leak_debug.cpp
../src/mempool.cpp ../src/mempool.cpp
../src/p2p_server.cpp ../src/p2p_server.cpp
../src/p2pool.cpp ../src/p2pool.cpp
@@ -107,6 +108,7 @@ include_directories(../external/src/libzmq/include)
include_directories(../external/src/llhttp) include_directories(../external/src/llhttp)
include_directories(../external/src/RandomX/src) include_directories(../external/src/RandomX/src)
include_directories(../external/src/rapidjson/include) include_directories(../external/src/rapidjson/include)
include_directories(../external/src/robin-hood-hashing/src/include)
include_directories(src) include_directories(src)
include_directories(googletest/googletest/include) include_directories(googletest/googletest/include)
@@ -150,3 +152,4 @@ add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} ${LIBS}) 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/crypto_tests.txt" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/mainnet_test2_block.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>) add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/mainnet_test2_block.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/sidechain_dump.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
+41 -2
View File
@@ -16,6 +16,7 @@
*/ */
#include "common.h" #include "common.h"
#include "crypto.h"
#include "pool_block.h" #include "pool_block.h"
#include "pow_hash.h" #include "pow_hash.h"
#include "side_chain.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.lo, 12544665764606ull);
ASSERT_EQ(b.m_cumulativeDifficulty.hi, 0); ASSERT_EQ(b.m_cumulativeDifficulty.hi, 0);
ASSERT_EQ(b.m_tmpTxExtra.size(), 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_depth, 0);
ASSERT_EQ(b.m_verified, false); ASSERT_EQ(b.m_verified, false);
ASSERT_EQ(b.m_invalid, false); ASSERT_EQ(b.m_invalid, false);
@@ -91,7 +91,7 @@ TEST(pool_block, deserialize)
hasher.set_seed(seed); hasher.set_seed(seed);
hash pow_hash; 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; std::stringstream s;
s << pow_hash; s << pow_hash;
@@ -100,4 +100,43 @@ TEST(pool_block, deserialize)
ASSERT_EQ(b.m_difficulty.check_pow(pow_hash), true); 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.