Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6cf1b13500 | |||
| 4a8d4ab8d9 | |||
| 65a9574e52 | |||
| 6eacb677a4 | |||
| d081c8ea74 | |||
| 0342e7ffb5 | |||
| c029e5c39d | |||
| a917a9493c | |||
| 02405bb8ff | |||
| d8bb85a1d8 | |||
| fd6b2360aa | |||
| 3e655961e9 | |||
| 077837054b | |||
| 45674ef554 | |||
| eabf856dbd | |||
| 32ea0f2cf6 | |||
| 47f40c0be3 | |||
| 6a66615f37 | |||
| 314a1ba39b | |||
| 2daa7f9fea | |||
| f0d576e1b0 | |||
| be76c58264 | |||
| 8245f232d4 | |||
| 7468af0779 | |||
| 38a6d3e549 | |||
| 3022588e35 | |||
| b81f1c4975 | |||
| ed78e9df6e | |||
| f044149004 | |||
| f126eb7611 | |||
| afa9cf371e | |||
| f813cf6d36 | |||
| 3dcd3dbb7f | |||
| d80a06bc8e | |||
| 947178f10a | |||
| e92cc7d50c | |||
| 989a28a8ca | |||
| e4dd46b498 | |||
| c49e8d4770 | |||
| 86156f3fec | |||
| fec826b10e | |||
| 229b07391b | |||
| e157477b6d | |||
| d40bb61da8 | |||
| 4733d42932 | |||
| e6030dcc3f | |||
| 6615fa67f5 | |||
| dc4b88d08e | |||
| cc2abe3c30 | |||
| 70640b65ea | |||
| 4599d81a59 | |||
| 3ea5b575b7 | |||
| b9eb66e2b3 | |||
| bde5b19c77 | |||
| cc60ab3d63 | |||
| 42f7e6f486 | |||
| 5f68b5923d | |||
| 85abbdd699 | |||
| 3802886722 | |||
| d4e27a27cb | |||
| e882b91b06 | |||
| 4c8dbee79d | |||
| 1438175477 | |||
| 30861bbf91 | |||
| ea6a19a260 | |||
| fc81fa8745 | |||
| 948436e57e | |||
| 3dde71f1ff | |||
| be0e220fe4 | |||
| 2e9a209a78 | |||
| 3ce4f06b6c | |||
| 555414cd51 | |||
| c33033b2f4 | |||
| 3c697c2d7e | |||
| 0e7c1aa481 | |||
| 7d5ff13f75 | |||
| 02a8a512dc | |||
| 4733f46a28 | |||
| 945de348f3 | |||
| 20dd9d7adc | |||
| c5bd184bbc | |||
| ae161fac49 | |||
| 8eb780a8b5 | |||
| 1c33cc9f3d | |||
| 9d692d5194 | |||
| a7da7f932c | |||
| 2e4f7adee4 | |||
| dcb822f812 | |||
| 5da286fed4 | |||
| 4ec0fe8d96 | |||
| 340a3e85c8 | |||
| 64baf660bb | |||
| 4f24f18b27 |
@@ -1 +1,2 @@
|
||||
**/build
|
||||
**/docker-compose
|
||||
|
||||
+33
-26
@@ -10,8 +10,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {os: ubuntu-latest, c: gcc-11, cpp: g++-11}
|
||||
- {os: ubuntu-18.04, c: gcc, cpp: g++}
|
||||
- {os: ubuntu-20.04, c: gcc-11, cpp: g++-11}
|
||||
- {os: ubuntu-22.04, c: gcc-12, cpp: g++-12}
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
sudo apt install -y git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev libcurl4-openssl-dev libidn2-0-dev ${{ matrix.config.c }} ${{ matrix.config.cpp }}
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
@@ -44,25 +44,25 @@ jobs:
|
||||
run: cd tests/build && ./p2pool_tests
|
||||
|
||||
- name: Archive binary
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: p2pool-${{ matrix.config.os }}
|
||||
path: build/p2pool
|
||||
|
||||
build-ubuntu-static-libs:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
sudo apt update
|
||||
sudo apt install -y git build-essential cmake autoconf libgss-dev gcc-11 g++-11
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100 --slave /usr/bin/g++ g++ /usr/bin/g++-11 --slave /usr/bin/gcov gcov /usr/bin/gcov-11
|
||||
sudo apt install -y git build-essential cmake autoconf libgss-dev gcc-12 g++-12
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100 --slave /usr/bin/g++ g++ /usr/bin/g++-12 --slave /usr/bin/gcov gcov /usr/bin/gcov-12
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
run: |
|
||||
cd external/src/curl
|
||||
autoreconf -fi
|
||||
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
|
||||
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Build libuv
|
||||
@@ -108,14 +108,21 @@ jobs:
|
||||
run: cd tests/build && ./p2pool_tests
|
||||
|
||||
- name: Archive binary
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: p2pool-ubuntu-static-libs
|
||||
path: build/p2pool
|
||||
|
||||
build-ubuntu-aarch64:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {os: ubuntu-18.04}
|
||||
- {os: ubuntu-20.04}
|
||||
- {os: ubuntu-22.04}
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
@@ -124,7 +131,7 @@ jobs:
|
||||
sudo apt install -y git build-essential cmake autoconf gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
@@ -132,7 +139,7 @@ jobs:
|
||||
run: |
|
||||
cd external/src/curl
|
||||
autoreconf -fi
|
||||
./configure --host=aarch64-linux-gnu --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
|
||||
./configure --host=aarch64-linux-gnu --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Build libuv
|
||||
@@ -159,9 +166,9 @@ jobs:
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Archive binary
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: p2pool-ubuntu-latest-aarch64
|
||||
name: p2pool-${{ matrix.config.os }}-aarch64
|
||||
path: build/p2pool
|
||||
|
||||
build-windows-msys2:
|
||||
@@ -174,7 +181,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -188,7 +195,7 @@ jobs:
|
||||
run: |
|
||||
cd external/src/curl
|
||||
autoreconf -fi
|
||||
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
|
||||
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Build libuv
|
||||
@@ -228,7 +235,7 @@ jobs:
|
||||
./p2pool_tests.exe
|
||||
|
||||
- name: Archive binary
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: p2pool-msys2.exe
|
||||
path: build/p2pool.exe
|
||||
@@ -246,7 +253,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -266,15 +273,15 @@ jobs:
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "${{ matrix.config.vs }}"
|
||||
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool_tests.vcxproj
|
||||
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Debug p2pool_tests.vcxproj
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd tests/build/Release
|
||||
cd tests/build/Debug
|
||||
./p2pool_tests.exe
|
||||
|
||||
- name: Archive binary
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}.exe
|
||||
path: build/Release/p2pool.exe
|
||||
@@ -285,11 +292,11 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-10.15, macos-11]
|
||||
os: [macos-11, macos-12]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -300,7 +307,7 @@ jobs:
|
||||
run: |
|
||||
cd external/src/curl
|
||||
autoreconf -fi
|
||||
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
|
||||
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
|
||||
make -j3
|
||||
|
||||
- name: Build libuv
|
||||
@@ -340,7 +347,7 @@ jobs:
|
||||
./p2pool_tests
|
||||
|
||||
- name: Archive binary
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: p2pool-${{ matrix.os }}
|
||||
path: build/p2pool
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev libcurl4-openssl-dev libidn2-0-dev
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
# run: cmake --build ${{ env.build }}
|
||||
|
||||
- name: Initialize MSVC Code Analysis
|
||||
uses: microsoft/msvc-code-analysis-action@47ecec99bf7ce0399ed85cbb5012b6f24eab5959
|
||||
uses: microsoft/msvc-code-analysis-action@24c285ab36952c9e9182f4b78dfafbac38a7e5ee
|
||||
# Provide a unique ID to access the sarif output path
|
||||
id: run-analysis
|
||||
with:
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
|
||||
# Upload SARIF file as an Artifact to download and view
|
||||
- name: Upload SARIF as an Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: sarif-file
|
||||
path: ${{ steps.run-analysis.outputs.sarif }}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
name: Sync test
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
sync-test-ubuntu:
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
sudo apt update
|
||||
sudo apt install -y git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev libcurl4-openssl-dev libidn2-0-dev gcc-12 g++-12
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Build p2pool
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DDEV_TEST_SYNC=ON -DCMAKE_C_COMPILER=gcc-12 -DCMAKE_CXX_COMPILER=g++-12
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Run p2pool
|
||||
timeout-minutes: 15
|
||||
run: |
|
||||
cd build
|
||||
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 6
|
||||
|
||||
- name: Archive p2pool.log
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: p2pool_ubuntu.log
|
||||
path: build/p2pool.log
|
||||
|
||||
sync-test-macos:
|
||||
|
||||
runs-on: macos-12
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake autoconf libtool automake libuv zmq libpgm curl
|
||||
|
||||
- name: Build p2pool
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DDEV_TEST_SYNC=ON
|
||||
make -j3
|
||||
|
||||
- name: Run p2pool
|
||||
timeout-minutes: 15
|
||||
run: |
|
||||
cd build
|
||||
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 6
|
||||
|
||||
- name: Archive p2pool.log
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: p2pool_macos.log
|
||||
path: build/p2pool.log
|
||||
|
||||
sync-test-windows:
|
||||
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup cmake
|
||||
uses: lukka/get-cmake@latest
|
||||
|
||||
- name: Build p2pool
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Visual Studio 17 2022" -DDEV_TEST_SYNC=ON
|
||||
& "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\msbuild" /m /p:Configuration=Debug p2pool.vcxproj
|
||||
|
||||
- name: Run p2pool
|
||||
timeout-minutes: 15
|
||||
run: |
|
||||
cd build/Debug
|
||||
./p2pool.exe --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 6
|
||||
|
||||
- name: Archive p2pool.log
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: p2pool_windows.log
|
||||
path: build/Debug/p2pool.log
|
||||
+32
-11
@@ -1,10 +1,12 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
project(p2pool)
|
||||
|
||||
option(STATIC_BINARY "Build static binary" OFF)
|
||||
option(STATIC_LIBS "Link libuv and libzmq statically" OFF)
|
||||
option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned off, p2pool will rely on monerod for verifying RandomX hashes" ON)
|
||||
|
||||
option(DEV_TEST_SYNC "[Developer only] Sync test, stop p2pool after sync is complete" OFF)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
|
||||
@@ -15,6 +17,10 @@ if (WITH_RANDOMX)
|
||||
set(LIBS randomx)
|
||||
endif()
|
||||
|
||||
if (DEV_TEST_SYNC)
|
||||
add_definitions(-DDEV_TEST_SYNC)
|
||||
endif()
|
||||
|
||||
include(cmake/flags.cmake)
|
||||
|
||||
set(HEADERS
|
||||
@@ -77,10 +83,19 @@ if (WITH_RANDOMX)
|
||||
set(SOURCES ${SOURCES} src/miner.cpp)
|
||||
endif()
|
||||
|
||||
if (NOT STATIC_BINARY AND NOT STATIC_LIBS)
|
||||
include(FindCURL)
|
||||
endif()
|
||||
|
||||
if (CURL_INCLUDE_DIRS)
|
||||
include_directories(${CURL_INCLUDE_DIRS})
|
||||
else()
|
||||
include_directories(external/src/curl/include)
|
||||
endif()
|
||||
|
||||
include_directories(src)
|
||||
include_directories(external/src)
|
||||
include_directories(external/src/cryptonote)
|
||||
include_directories(external/src/curl/include)
|
||||
include_directories(external/src/libuv/include)
|
||||
include_directories(external/src/cppzmq)
|
||||
include_directories(external/src/libzmq/include)
|
||||
@@ -92,6 +107,9 @@ include_directories(external/src/robin-hood-hashing/src/include)
|
||||
|
||||
if (WIN32)
|
||||
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||
set(LIBS ${LIBS} bcrypt)
|
||||
endif()
|
||||
add_definitions(-DCURL_STATICLIB)
|
||||
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
|
||||
set(LIBS ${LIBS} pthread)
|
||||
@@ -106,6 +124,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
||||
find_library(UV_LIBRARY NAMES uv_a PATHS "external/lib/libuv/Release")
|
||||
find_library(CURL_LIBRARY_DEBUG NAMES libcurld PATHS "external/lib/libcurl/Debug")
|
||||
find_library(CURL_LIBRARY NAMES libcurl PATHS "external/lib/libcurl/Release")
|
||||
add_definitions(-D_DISABLE_VECTOR_ANNOTATION)
|
||||
add_definitions(-D_DISABLE_STRING_ANNOTATION)
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
|
||||
find_library(ZMQ_LIBRARY_DEBUG NAMES zmq libzmq.a)
|
||||
find_library(ZMQ_LIBRARY NAMES zmq libzmq.a)
|
||||
@@ -115,8 +135,13 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang
|
||||
find_library(CURL_LIBRARY_DEBUG NAMES libcurl.a PATHS "external/src/curl/lib/.libs" NO_DEFAULT_PATH)
|
||||
find_library(CURL_LIBRARY NAMES libcurl.a PATHS "external/src/curl/lib/.libs" NO_DEFAULT_PATH)
|
||||
else()
|
||||
find_library(CURL_LIBRARY_DEBUG NAMES curl)
|
||||
find_library(CURL_LIBRARY NAMES curl)
|
||||
if (CURL_LIBRARIES)
|
||||
set(CURL_LIBRARY_DEBUG ${CURL_LIBRARIES})
|
||||
set(CURL_LIBRARY ${CURL_LIBRARIES})
|
||||
else()
|
||||
find_library(CURL_LIBRARY_DEBUG NAMES curl)
|
||||
find_library(CURL_LIBRARY NAMES curl)
|
||||
endif()
|
||||
endif()
|
||||
find_library(SODIUM_LIBRARY sodium)
|
||||
endif()
|
||||
@@ -145,14 +170,7 @@ endif()
|
||||
|
||||
add_definitions(/DZMQ_STATIC)
|
||||
|
||||
include(CheckSymbolExists)
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS "${GENERAL_FLAGS}")
|
||||
check_symbol_exists(pthread_cancel pthread.h HAVE_PTHREAD_CANCEL)
|
||||
|
||||
if (HAVE_PTHREAD_CANCEL)
|
||||
add_definitions(/DHAVE_PTHREAD_CANCEL)
|
||||
endif()
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
@@ -182,6 +200,9 @@ if (STATIC_BINARY OR STATIC_LIBS)
|
||||
|
||||
if (WIN32)
|
||||
set(STATIC_LIBS ${STATIC_LIBS} ws2_32 iphlpapi userenv psapi wldap32)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||
set(STATIC_LIBS ${STATIC_LIBS} bcrypt)
|
||||
endif()
|
||||
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
|
||||
set(STATIC_LIBS ${STATIC_LIBS} pthread)
|
||||
elseif (APPLE)
|
||||
|
||||
@@ -66,34 +66,37 @@ First you need to find a pool share. This share will stay in PPLNS window for 21
|
||||
|
||||
## Monero version support
|
||||
|
||||
Monero will undergo a network upgrade on July 16th, 2022 (block 2,668,888). In order to continue mining after that date, you must update both Monero and P2Pool software to the latest available versions as soon as they are released.
|
||||
Monero network upgrade happened on August 13th, 2022 (block 2,688,888). In order to continue mining, you must update both Monero and P2Pool software to the latest available versions as soon as they are released.
|
||||
|
||||
|Monero protocol version|Required Monero software version|Required P2Pool version
|
||||
|-|-|-|
|
||||
|v14 (active until July 16th, 2022)|v0.17.3.0 or newer|v1.0 or newer
|
||||
|v15, v16 (active after July 16th, 2022)|v0.18.0.0 or newer|v2.0 or newer
|
||||
|v15, v16 (active after August 13th, 2022)|v0.18.0.0 or newer|v2.2 or newer
|
||||
|
||||
## How to mine on P2Pool
|
||||
|
||||
### General Considerations
|
||||
|
||||
- In order to mine on P2Pool, a synced Monero node using monerod v0.17.3.0 or newer is required. If you do not currently have one configured, you can find instructions to do so [here](https://sethforprivacy.com/guides/run-a-monero-node-advanced/).
|
||||
- It is highly recommended that you create a separate restricted user account for mining. While P2Pool has been battle-tested for a long time now, any software may have unknown bugs/vulnerabilities.
|
||||
- In order to mine on P2Pool, a synced Monero node using monerod v0.18.0.0 or newer is required. If you don't currently have one, you can download the official Monero binaries, start `monerod` on your PC and wait until it's fully synced. Advanced Monero node setup instructions are [here](https://sethforprivacy.com/guides/run-a-monero-node-advanced/).
|
||||
- It is highly recommended that you create a separate restricted user account (in your OS) for mining. While P2Pool has been battle-tested for a long time now, any software may have unknown bugs/vulnerabilities.
|
||||
- You have to use a primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
|
||||
- Starting from P2Pool v1.7, you can add the `--mini` parameter to your P2Pool command to connect to the **p2pool-mini** sidechain. Note that it will also change the default p2p port from 37889 to 37888.
|
||||
- You can add the `--mini` parameter to your P2Pool command to connect to the **p2pool-mini** sidechain. Note that it will also change the default p2p port from 37889 to 37888.
|
||||
- Check that ports 18080 (Monero p2p port) and 37889/37888 (P2Pool/P2Pool mini p2p port) are open in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine.
|
||||
- You can connect multiple miners to the same P2Pool node. The more the better!
|
||||
- The below steps assume that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup.
|
||||
- The steps below assume that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup.
|
||||
- It is highly recommended to create a new mainnet wallet for P2Pool mining because **wallet addresses are public on P2Pool**.
|
||||
|
||||
**Wallet software compatible with P2Pool payouts**
|
||||
- [Official Monero CLI and GUI v0.17.2.3 and newer](https://www.getmonero.org/downloads/)
|
||||
- [Monerujo v2.1.0 "Vertant" and newer](https://www.monerujo.io/)
|
||||
- [Cake Wallet v4.2.7 and newer](https://cakewallet.com/)
|
||||
- [Official Monero CLI and GUI v0.18.0.0 and newer](https://www.getmonero.org/downloads/)
|
||||
- [Monerujo v3.0.2 "Fluorine Fermi" and newer](https://www.monerujo.io/)
|
||||
- [Cake Wallet v4.4.5 and newer](https://cakewallet.com/)
|
||||
- [Monero.com by Cake Wallet](https://monero.com/)
|
||||
- [Feather Wallet v1.0.0 and newer](https://featherwallet.org/)
|
||||
- [Feather Wallet v2.1.0 and newer](https://featherwallet.org/)
|
||||
- [MyMonero](https://mymonero.com/)
|
||||
|
||||
### GUI for P2Pool
|
||||
|
||||
- [Gupax](https://github.com/hinto-janaiyo/gupax) project (currently in development) aims to provide an easy to use GUI to configure and run monerod/p2pool/xmrig
|
||||
|
||||
### GNU/Linux
|
||||
|
||||
1. Download the latest P2Pool binaries [here](https://github.com/SChernykh/p2pool/releases/latest).
|
||||
@@ -157,21 +160,23 @@ nocreate
|
||||
5. Prepare huge pages to work properly (each instance of monerod/P2Pool/XMRig needs them):
|
||||
- On Windows 10 or above, run XMRig at least once as Administrator (right-click Run As Administrator)
|
||||
- On earlier versions of Windows, you'll need to run XMRig as Administrator at least once per login.
|
||||
6. Open a command prompt and navigate to the folder where you extracted P2Pool.
|
||||
6. Create "Monero" folder inside extracted P2Pool folder and copy Monero binaries there.
|
||||
7. Open a command prompt and navigate to the folder where you extracted P2Pool.
|
||||
|
||||
**Note:** *When running the below commands, Windows Firewall may prompt to allow connections, click "Allow" if prompted.*
|
||||
|
||||
7. Start `monerod` with the following command/options:
|
||||
8. Start `monerod` with the following command/options:
|
||||
```
|
||||
.\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
|
||||
```
|
||||
**Note:** The `--zmq-pub` option is required for P2Pool to work properly.
|
||||
8. Start P2Pool with the following command/options:
|
||||
|
||||
9. Start P2Pool with the following command/options:
|
||||
```
|
||||
.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS
|
||||
.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS --mini
|
||||
```
|
||||
9. Wait until the initial P2Pool sync is finished (shouldn't take more than 5-10 minutes).
|
||||
10. Start XMRig with the following command/options:
|
||||
10. Wait until the initial P2Pool sync is finished (shouldn't take more than 5-10 minutes).
|
||||
11. Start XMRig with the following command/options:
|
||||
```
|
||||
.\xmrig.exe -o 127.0.0.1:3333
|
||||
```
|
||||
@@ -180,17 +185,17 @@ nocreate
|
||||
```
|
||||
xmrig.exe -u x+10000 -o 127.0.0.1:3333
|
||||
```
|
||||
11. XMRig should connect and start mining!
|
||||
12. *(Optional but highly recommended)* You can create a Quickstart by creating a batch (.bat) file with the following contents and placing it in your P2Pool directory along with `xmrig.exe`.
|
||||
12. XMRig should connect and start mining!
|
||||
13. *(Optional but highly recommended)* You can create a Quickstart by creating a batch (.bat) file with the following contents and placing it in your P2Pool directory along with `xmrig.exe`.
|
||||
```
|
||||
@ECHO OFF
|
||||
start cmd /k %~dp0\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
|
||||
ECHO Wait until the Monero daemon shows fully synced before continuing. This can take some time. Type 'status' in other window to check progress.
|
||||
PAUSE
|
||||
start cmd /k %~dp0\p2pool.exe --wallet YOUR_WALLET_ADDRESS
|
||||
start cmd /k %~dp0\p2pool.exe --wallet YOUR_WALLET_ADDRESS --mini
|
||||
ECHO Wait until the daemon shows fully synced before continuing. This can take some time.
|
||||
PAUSE
|
||||
%~dp0\xmrig.exe -u x+30000 -o 127.0.0.1
|
||||
%~dp0\xmrig.exe -o 127.0.0.1
|
||||
```
|
||||
|
||||
## Build instructions
|
||||
@@ -226,12 +231,12 @@ nix shell github:nixos/nix/master
|
||||
|
||||
Run the binary:
|
||||
```
|
||||
nix run git+https://github.com/SChernykh/p2pool?ref=master
|
||||
nix run git+https://github.com/SChernykh/p2pool?ref=master&submodules=1
|
||||
```
|
||||
|
||||
Run the binary with arguments:
|
||||
```
|
||||
nix run git+https://github.com/SChernykh/p2pool?ref=master -- --help
|
||||
nix run git+https://github.com/SChernykh/p2pool?ref=master&submodules=1 -- --help
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -253,7 +258,7 @@ Alternatively, you can select "Clone a repository" within the GUI, then select "
|
||||
|
||||
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on your Mac:
|
||||
```
|
||||
brew update && brew install git cmake libuv zmq libpgm
|
||||
brew update && brew install git cmake libuv zmq libpgm curl
|
||||
git clone --recursive https://github.com/SChernykh/p2pool
|
||||
cd p2pool
|
||||
mkdir build && cd build
|
||||
@@ -265,7 +270,7 @@ make -j$(sysctl -n hw.logicalcpu)
|
||||
|
||||
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on FreeBSD:
|
||||
```
|
||||
pkg install git cmake libuv libzmq4
|
||||
pkg install git cmake libuv libzmq4 curl
|
||||
git clone --recursive https://github.com/SChernykh/p2pool
|
||||
cd p2pool
|
||||
mkdir build && cd build
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||
endif()
|
||||
endif()
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
||||
set(GENERAL_FLAGS "")
|
||||
set(GENERAL_FLAGS "/MP")
|
||||
set(WARNING_FLAGS "/Wall /WX /sdl")
|
||||
set(SECURITY_FLAGS "/GS /guard:cf")
|
||||
set(OPTIMIZATION_FLAGS "/O2 /Oi /Ob2 /Ot /DNDEBUG /GL")
|
||||
|
||||
@@ -24,7 +24,7 @@ Make sure to set your own monero **Wallet Address**. The default is to donate m
|
||||
|
||||
#### Build the docker containers
|
||||
```
|
||||
docker compose build
|
||||
docker compose build --no-cache
|
||||
```
|
||||
|
||||
#### Run the node, pool, and CPU miner (or updated configuration)
|
||||
@@ -34,21 +34,24 @@ docker compose up
|
||||
|
||||
#### Optional
|
||||
* Open ports 18080 (Monero p2p port) and 37889 (P2Pool p2p port) or 37888 (P2Pool-mini p2p port) in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine
|
||||
* An XMRig CPU miner is included by default, but you can connect additional miners to this same p2pool node using port 3333
|
||||
* An XMRig CPU miner is included by default, but you can connect additional miners to this same p2pool node using port 3333 (or alternate if configured) when you set it as "exposed" in the configuration
|
||||
* Configure your kernel for maximum mining performance: [XMRig RandomX Optimization Guide](https://xmrig.com/docs/miner/randomx-optimization-guide)
|
||||
* Small miners can mine on p2pool-mini by editing the .env file
|
||||
* Many optional configurations and customizations are available by running './configure'
|
||||
|
||||
|
||||
#### Other usefull commands
|
||||
* You can **run everything in the background** by adding the "-d" argument to the "docker compose up" command: ```docker compose up -d```
|
||||
* You can **stop everything** with CTRL-C or ```docker compose down```
|
||||
* You can see logs when running in the background for with the "docker logs" command: ```docker logs -f p2pool-xmrig``` or ```docker logs -f p2pool-p2pool``` or ```docker logs -f p2pool-monero```
|
||||
* You can **update** by building new images with the ```--no-cache``` option. Example: ```docker compose build --no-cache``` or just update Monero with: ```docker compose build --no-cache monero``` followed by ```docker compose up```
|
||||
* You can see logs when running in the background for with the "docker compose logs" command: ```docker compose logs -f```
|
||||
* You can pause mining with: ```docker compose pause xmrig``` and resume mining with: ```docker compose unpause xmrig```
|
||||
* You can disable mining with: ```docker compose stop xmrig``` and re-enable mining with: ```docker compose start xmrig```
|
||||
* You can view your Server Statistics using a web browser if you enabled that feature in the configuration at: http://localhost:3380 (or alternate port as configured)
|
||||
|
||||
|
||||
#### Uninstall
|
||||
Change to p2pool/docker-compose directory <br />
|
||||
Stop and remove all containers: ```docker compose down``` <br />
|
||||
Remove the p2pool data: ```docker volume rm p2pool``` <br />
|
||||
Remove the p2pool-mini data: ```docker volume rm p2pool-mini``` <br />
|
||||
Remove the monero data: ```docker volume rm monero```
|
||||
|
||||
+609
-255
File diff suppressed because it is too large
Load Diff
@@ -3,14 +3,26 @@
|
||||
"configure_monero": {{ configure_monero | tojson(indent=2) }},
|
||||
"configure_xmrig": {{ configure_xmrig | tojson(indent=2) }},
|
||||
"sidechain": {{ sidechain | tojson(indent=2) }},
|
||||
"enable_statistics": {{ enable_statistics | tojson(indent=2) }},
|
||||
"statistics_port": {{ statistics_port | tojson(indent=2) }},
|
||||
"expose_stratum_port": {{ expose_stratum_port | tojson(indent=2) }},
|
||||
"stratum_port": {{ stratum_port | tojson(indent=2) }},
|
||||
"p2pool_log_level": {{ p2pool_log_level | int | tojson(indent=2) }},
|
||||
"enable_autodiff": {{ enable_autodiff | tojson(indent=2) }},
|
||||
"light_mode": {{ light_mode | tojson(indent=2) }},
|
||||
"no_cache": {{ no_cache | tojson(indent=2) }},
|
||||
"p2pool_options": {{ p2pool_options | tojson(indent=2) }},
|
||||
"monero_version": {{ monero_version | tojson(indent=2) }},
|
||||
"prune_blockchain": {{ prune_blockchain | tojson(indent=2) }},
|
||||
"monero_log_level": {{ monero_log_level | int | tojson(indent=2) }},
|
||||
"expose_rpc_port": {{ expose_rpc_port | tojson(indent=2) }},
|
||||
"rpc_port": {{ rpc_port | tojson(indent=2) }},
|
||||
"rpc_login": {{ rpc_login | tojson(indent=2) }},
|
||||
"limit_data_rates": {{ limit_data_rates | tojson(indent=2) }},
|
||||
"rate_limit_up": {{ rate_limit_up | tojson(indent=2) }},
|
||||
"rate_limit_down": {{ rate_limit_down | tojson(indent=2) }},
|
||||
"sync_pruned_blocks": {{ sync_pruned_blocks | tojson(indent=2) }},
|
||||
"fast_sync": {{ fast_sync | tojson(indent=2) }},
|
||||
"monero_options": {{ monero_options | tojson(indent=2) }},
|
||||
"public_monero_node": {{ public_monero_node | tojson(indent=2) }},
|
||||
"monero_node_login": {{ monero_node_login | tojson(indent=2) }},
|
||||
@@ -18,5 +30,6 @@
|
||||
"use_fixed_difficulty": {{ use_fixed_difficulty | tojson(indent=2) }},
|
||||
"fixed_difficulty": {{ fixed_difficulty | int | tojson(indent=2) }},
|
||||
"cpu_percent": {{ cpu_percent | int | tojson(indent=2) }},
|
||||
"cpu_priority": {{ cpu_priority | int | tojson(indent=2) }},
|
||||
"xmrig_options": {{ xmrig_options | tojson(indent=2) }}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,26 @@
|
||||
"configure_monero": true,
|
||||
"configure_xmrig": true,
|
||||
"sidechain": [0],
|
||||
"enable_statistics": true,
|
||||
"statistics_port": "3380",
|
||||
"expose_stratum_port": false,
|
||||
"stratum_port": "3333",
|
||||
"p2pool_log_level": 3,
|
||||
"enable_autodiff": true,
|
||||
"light_mode": false,
|
||||
"no_cache": false,
|
||||
"p2pool_options": "",
|
||||
"monero_version": "latest",
|
||||
"prune_blockchain": true,
|
||||
"monero_log_level": 0,
|
||||
"expose_rpc_port": false,
|
||||
"rpc_port": "18081",
|
||||
"rpc_login": "",
|
||||
"limit_data_rates": false,
|
||||
"rate_limit_up": "2048",
|
||||
"rate_limit_down": "8192",
|
||||
"sync_pruned_blocks": false,
|
||||
"fast_sync": false,
|
||||
"monero_options": "",
|
||||
"public_monero_node": "",
|
||||
"monero_node_login": "",
|
||||
@@ -18,5 +30,6 @@
|
||||
"use_fixed_difficulty": true,
|
||||
"fixed_difficulty": 500000,
|
||||
"cpu_percent": 100,
|
||||
"cpu_priority": 2,
|
||||
"xmrig_options": ""
|
||||
}
|
||||
|
||||
@@ -7,7 +7,12 @@ networks:
|
||||
|
||||
volumes:
|
||||
p2pool:
|
||||
{% if sidechain[0] == 0 %}
|
||||
name: p2pool
|
||||
{% else %}
|
||||
name: p2pool-mini
|
||||
{% endif %}
|
||||
|
||||
{% if configure_monero == True %}
|
||||
monero:
|
||||
name: monero
|
||||
@@ -20,6 +25,7 @@ services:
|
||||
container_name: p2pool-p2pool
|
||||
networks:
|
||||
- p2pool
|
||||
privileged: true
|
||||
ports:
|
||||
{% if sidechain[0] == 0 %}
|
||||
- 37888:37888/tcp
|
||||
@@ -27,7 +33,7 @@ services:
|
||||
- 37889:37889/tcp
|
||||
{% endif %}
|
||||
{% if expose_stratum_port == True %}
|
||||
- {{ stratum_port | int }}:3333/tcp
|
||||
- {{ stratum_port }}:3333/tcp
|
||||
{% endif %}
|
||||
volumes:
|
||||
- p2pool:/home/p2pool/.p2pool:rw
|
||||
@@ -53,11 +59,42 @@ services:
|
||||
{% if enable_autodiff == False %}
|
||||
--no-autodiff
|
||||
{% endif %}
|
||||
{% if enable_statistics == True %}
|
||||
--local-api
|
||||
--data-api /home/p2pool/.p2pool
|
||||
{% endif %}
|
||||
{% if light_mode == True %}
|
||||
--light-mode
|
||||
{% endif %}
|
||||
{% if no_cache == True %}
|
||||
--no-cache
|
||||
{% endif %}
|
||||
{% if rpc_login != "" %}
|
||||
--rpc-login {{ rpc_login }}
|
||||
{% endif %}
|
||||
{% if p2pool_options != "" %}
|
||||
{{ p2pool_options }}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if enable_statistics == True %}
|
||||
statistics:
|
||||
image: statistics:latest
|
||||
build:
|
||||
context: statistics
|
||||
container_name: p2pool-statistics
|
||||
networks:
|
||||
- p2pool
|
||||
ports:
|
||||
- {{ statistics_port }}:80/tcp
|
||||
volumes:
|
||||
- p2pool:/data:ro
|
||||
depends_on:
|
||||
- p2pool
|
||||
restart: unless-stopped
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if configure_monero == True %}
|
||||
monero:
|
||||
image: monero:latest
|
||||
@@ -70,6 +107,9 @@ services:
|
||||
- p2pool
|
||||
ports:
|
||||
- 18080:18080/tcp
|
||||
{% if expose_rpc_port == True %}
|
||||
- {{ rpc_port }}:18081/tcp
|
||||
{% endif %}
|
||||
volumes:
|
||||
- monero:/home/monero/.bitmonero:rw
|
||||
- /dev/null:/home/monero/.bitmonero/bitmonero.log:rw
|
||||
@@ -84,10 +124,26 @@ services:
|
||||
--p2p-bind-port=18080
|
||||
--rpc-bind-ip=0.0.0.0
|
||||
--rpc-bind-port=18081
|
||||
--restricted-rpc
|
||||
--confirm-external-bind
|
||||
--log-level={{ monero_log_level | int }}
|
||||
{% if prune_blockchain == True %}
|
||||
--prune-blockchain
|
||||
{% if sync_pruned_blocks == True %}
|
||||
--sync-pruned-blocks
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if rpc_login != "" %}
|
||||
--rpc-login {{ rpc_login }}
|
||||
{% endif %}
|
||||
{% if limit_data_rates == True %}
|
||||
--limit-rate-up {{ rate_limit_up }}
|
||||
--limit-rate-down {{ rate_limit_down }}
|
||||
{% endif %}
|
||||
{% if fast_sync == True %}
|
||||
--fast-block-sync=1
|
||||
{% else %}
|
||||
--fast-block-sync=0
|
||||
{% endif %}
|
||||
{% if monero_options != "" %}
|
||||
{{ monero_options }}
|
||||
@@ -119,6 +175,7 @@ services:
|
||||
-u {{ xmrig_username }}
|
||||
{% endif %}
|
||||
--cpu-max-threads-hint={{ cpu_percent | int }}
|
||||
--cpu-priority={{ cpu_priority | int }}
|
||||
{% if xmrig_options != "" %}
|
||||
{{ xmrig_options }}
|
||||
{% endif %}
|
||||
|
||||
Vendored
+39
@@ -1,6 +1,45 @@
|
||||
#!/bin/bash
|
||||
echo ""
|
||||
echo ""
|
||||
echo "Verifying Requirements:"
|
||||
DOCKER_VER=$(docker version -f "{{.Server.Version}}" 2> /dev/null)
|
||||
if [ -z "$DOCKER_VER" ]; then
|
||||
echo "Docker not found; install it: https://docs.docker.com/engine/install/"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(echo "$DOCKER_VER"| cut -d'.' -f 1)" -ge 19 ] && \
|
||||
[ "$(echo "$DOCKER_VER"| cut -d'.' -f 2)" -ge 0 ] && \
|
||||
[ "$(echo "$DOCKER_VER"| cut -d'.' -f 3)" -ge 3 ]; then
|
||||
echo "Docker Found; OK"
|
||||
else
|
||||
echo "Docker version less than 19.0.3; upgrade it: https://docs.docker.com/engine/install/"
|
||||
exit 1
|
||||
fi
|
||||
docker compose version 2>&1 > /dev/null
|
||||
COMPOSE_PLUGIN_RC=$?
|
||||
docker-compose --version 2>&1 > /dev/null
|
||||
COMPOSE_CLI_RC=$?
|
||||
if [ "$COMPOSE_PLUGIN_RC" -eq 0 ] || [ "$COMPOSE_CLI_RC" -eq 0 ]; then
|
||||
echo "Docker Compose found; OK"
|
||||
if [ "$COMPOSE_PLUGIN_RC" -eq 0 ]; then
|
||||
COMPOSE_COMMAND="docker compose"
|
||||
else
|
||||
COMPOSE_COMMAND="docker-compose"
|
||||
fi
|
||||
else
|
||||
echo "Docker Compose not found; install it: https://docs.docker.com/compose/install/compose-plugin/"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
echo ""
|
||||
echo "Building and Running P2Pool docker-compose Configuration"
|
||||
docker build -t p2pool_config:latest cfg
|
||||
docker run -it --rm -v $PWD:/docker-compose --user $(id -u):$(id -g) p2pool_config:latest
|
||||
CONFIGURE_RC=$?
|
||||
echo ""
|
||||
echo ""
|
||||
if [ "$CONFIGURE_RC" -eq 0 ]; then
|
||||
echo "P2Pool is configured. Start the project with: $COMPOSE_COMMAND up --build -d"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,14 +5,26 @@
|
||||
"sidechain": [
|
||||
0
|
||||
],
|
||||
"enable_statistics": true,
|
||||
"statistics_port": "3380",
|
||||
"expose_stratum_port": false,
|
||||
"stratum_port": "3333",
|
||||
"p2pool_log_level": 3,
|
||||
"enable_autodiff": true,
|
||||
"light_mode": false,
|
||||
"no_cache": false,
|
||||
"p2pool_options": "",
|
||||
"monero_version": "latest",
|
||||
"prune_blockchain": true,
|
||||
"monero_log_level": 0,
|
||||
"expose_rpc_port": false,
|
||||
"rpc_port": "18081",
|
||||
"rpc_login": "",
|
||||
"limit_data_rates": false,
|
||||
"rate_limit_up": "2048",
|
||||
"rate_limit_down": "8192",
|
||||
"sync_pruned_blocks": false,
|
||||
"fast_sync": false,
|
||||
"monero_options": "",
|
||||
"public_monero_node": "",
|
||||
"monero_node_login": "",
|
||||
@@ -20,5 +32,6 @@
|
||||
"use_fixed_difficulty": true,
|
||||
"fixed_difficulty": 500000,
|
||||
"cpu_percent": 100,
|
||||
"cpu_priority": 2,
|
||||
"xmrig_options": ""
|
||||
}
|
||||
@@ -7,8 +7,11 @@ networks:
|
||||
|
||||
volumes:
|
||||
p2pool:
|
||||
|
||||
name: p2pool
|
||||
|
||||
|
||||
|
||||
monero:
|
||||
name: monero
|
||||
|
||||
@@ -20,6 +23,7 @@ services:
|
||||
container_name: p2pool-p2pool
|
||||
networks:
|
||||
- p2pool
|
||||
privileged: true
|
||||
ports:
|
||||
|
||||
- 37888:37888/tcp
|
||||
@@ -43,6 +47,31 @@ services:
|
||||
|
||||
|
||||
|
||||
--local-api
|
||||
--data-api /home/p2pool/.p2pool
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
statistics:
|
||||
image: statistics:latest
|
||||
build:
|
||||
context: statistics
|
||||
container_name: p2pool-statistics
|
||||
networks:
|
||||
- p2pool
|
||||
ports:
|
||||
- 3380:80/tcp
|
||||
volumes:
|
||||
- p2pool:/data:ro
|
||||
depends_on:
|
||||
- p2pool
|
||||
restart: unless-stopped
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -57,6 +86,7 @@ services:
|
||||
- p2pool
|
||||
ports:
|
||||
- 18080:18080/tcp
|
||||
|
||||
volumes:
|
||||
- monero:/home/monero/.bitmonero:rw
|
||||
- /dev/null:/home/monero/.bitmonero/bitmonero.log:rw
|
||||
@@ -71,6 +101,7 @@ services:
|
||||
--p2p-bind-port=18080
|
||||
--rpc-bind-ip=0.0.0.0
|
||||
--rpc-bind-port=18081
|
||||
--restricted-rpc
|
||||
--confirm-external-bind
|
||||
--log-level=0
|
||||
|
||||
@@ -80,6 +111,12 @@ services:
|
||||
|
||||
|
||||
|
||||
--fast-block-sync=0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
xmrig:
|
||||
image: xmrig:latest
|
||||
@@ -102,4 +139,5 @@ services:
|
||||
-u p2pool
|
||||
|
||||
--cpu-max-threads-hint=100
|
||||
--cpu-priority=2
|
||||
|
||||
|
||||
@@ -1,80 +1,59 @@
|
||||
FROM ubuntu:20.04
|
||||
# Multistage docker build, requires docker 17.05
|
||||
|
||||
# builder stage
|
||||
FROM ubuntu:20.04 as builder
|
||||
ARG MONERO_GIT_TAG="latest"
|
||||
|
||||
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 \
|
||||
automake \
|
||||
autotools-dev \
|
||||
bsdmainutils \
|
||||
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 \
|
||||
ca-certificates \
|
||||
ccache \
|
||||
doxygen \
|
||||
graphviz
|
||||
cmake \
|
||||
curl \
|
||||
git \
|
||||
libtool \
|
||||
pkg-config \
|
||||
gperf
|
||||
|
||||
WORKDIR /usr/src
|
||||
|
||||
WORKDIR /src
|
||||
RUN git clone --recursive https://github.com/monero-project/monero && \
|
||||
cd monero && \
|
||||
if [ "x$MONERO_GIT_TAG" = "xlatest" ]; then MONERO_GIT_TAG=$(git describe --tags $(git rev-list --tags --max-count=1)); fi && \
|
||||
git checkout $MONERO_GIT_TAG && \
|
||||
git submodule sync && git submodule update --init --force --recursive && \
|
||||
make release-static -j$(nproc)
|
||||
make -j$(nproc) depends target=$(contrib/depends/config.guess)
|
||||
|
||||
# ---
|
||||
|
||||
# runtime stage
|
||||
FROM ubuntu:20.04
|
||||
|
||||
COPY --from=0 /usr/src/monero/build/Linux/*/release/bin/* /
|
||||
COPY --from=0 /src/monero/build/*/release/bin /usr/local/bin/
|
||||
|
||||
RUN set -e && \
|
||||
apt-get update -q -y --no-install-recommends && \
|
||||
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
|
||||
libgssapi-krb5-2 \
|
||||
&& \
|
||||
ca-certificates \
|
||||
netcat \
|
||||
&& \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt
|
||||
|
||||
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
|
||||
adduser --uid 1000 --gid 1000 --system --disabled-password monero && \
|
||||
mkdir -p /home/monero/.bitmonero && \
|
||||
chown -R monero:monero /home/monero/.bitmonero
|
||||
|
||||
USER monero
|
||||
WORKDIR /home/monero
|
||||
|
||||
EXPOSE 18080 18081 18083
|
||||
|
||||
VOLUME /home/monero/.bitmonero
|
||||
|
||||
WORKDIR /home/monero
|
||||
ENTRYPOINT ["/monerod"]
|
||||
ENTRYPOINT ["/usr/local/bin/monerod"]
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
FROM python:slim
|
||||
|
||||
COPY app /app
|
||||
|
||||
WORKDIR /app
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
CMD ["/app/p2pool_statistics.py"]
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
from datetime import datetime
|
||||
from prefixed import Float
|
||||
import humanfriendly
|
||||
from flask import Flask, render_template
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
##
|
||||
# Add some custom jinja filters
|
||||
def timeago(value):
|
||||
"""Format a date time to human friendly time ago"""
|
||||
if value is None:
|
||||
return ""
|
||||
if type(value) is int:
|
||||
dt = datetime.fromtimestamp(value).replace(microsecond=0)
|
||||
now = datetime.now().replace(microsecond=0)
|
||||
return humanfriendly.format_timespan(now - dt)
|
||||
|
||||
|
||||
app.jinja_env.filters["timeago"] = timeago
|
||||
|
||||
|
||||
def human_numbers(value):
|
||||
"""Format a number in human readable format"""
|
||||
if value is None:
|
||||
return ""
|
||||
return "{:!.3h}".format(Float(value))
|
||||
|
||||
|
||||
app.jinja_env.filters["humanize"] = human_numbers
|
||||
|
||||
|
||||
##
|
||||
# Get Pool Instance Birth Date
|
||||
def birthdate():
|
||||
try:
|
||||
with open("/data/p2pool.blocks") as reader:
|
||||
first_block = reader.readline().rstrip()
|
||||
bday_ts = int(first_block.split(" ")[0])
|
||||
bday = timeago(bday_ts)
|
||||
return bday
|
||||
except Exception as e:
|
||||
return "unknown time"
|
||||
|
||||
|
||||
##
|
||||
# The App Routes
|
||||
@app.route("/")
|
||||
def render():
|
||||
try:
|
||||
my_bday = birthdate()
|
||||
with open("/data/stats_mod", "r") as reader:
|
||||
stats_mod = json.loads(reader.read())
|
||||
with open("/data/pool/stats", "r") as reader:
|
||||
pool_stats = json.loads(reader.read())
|
||||
with open("/data/network/stats", "r") as reader:
|
||||
network_stats = json.loads(reader.read())
|
||||
with open("/data/local/stats", "r") as reader:
|
||||
local_stats = json.loads(reader.read())
|
||||
return render_template(
|
||||
"index.html",
|
||||
my_bday=my_bday,
|
||||
stats_mod=stats_mod,
|
||||
pool_stats=pool_stats,
|
||||
network_stats=network_stats,
|
||||
local_stats=local_stats,
|
||||
)
|
||||
except Exception as e:
|
||||
return render_template("oops.html", error=str(e))
|
||||
|
||||
|
||||
##
|
||||
# main()
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=False, host="0.0.0.0", port=80)
|
||||
@@ -0,0 +1,3 @@
|
||||
flask
|
||||
prefixed
|
||||
humanfriendly
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
File diff suppressed because one or more lines are too long
@@ -0,0 +1,158 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="{{url_for('static', filename='bootstrap.min.css')}}">
|
||||
<title>Monero P2Pool Server Statistics</title>
|
||||
</head>
|
||||
<body style="font-size:85%;">
|
||||
<script src="{{url_for('static', filename='jquery-3.2.1.slim.min.js')}}"></script>
|
||||
<script src="{{url_for('static', filename='popper.min.js')}}"></script>
|
||||
<script src="{{url_for('static', filename='bootstrap.min.js')}}"></script>
|
||||
<div style="text-align:center;">
|
||||
<img src="{{url_for('static', filename='monero-symbol-480.png')}}" width="75px" height="75px" alt="Monero"/>
|
||||
<h1 style="color: #FF6600;">P2Pool Server Statistics</h1>
|
||||
</div>
|
||||
|
||||
<div class="card-group">
|
||||
<div class="col-sm-4 grid-margin stretch-card">
|
||||
<div class="card">
|
||||
<div class="card-header text-black mb-1 pb-1" style="text-align: center; background-color: #FF6600;"><h2>Local Pool</h2></div>
|
||||
<div class="card-body mb-0 pb-0">
|
||||
<h6 class="card-subtitle text-muted" style="font-size:80%;">(note: stats reset on restart)</h6>
|
||||
<div class="table-responsive table-hover table-condensed table-striped">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Hashrate 15 Minutes</td>
|
||||
<td>{{ local_stats["hashrate_15m"]|humanize }}H/s</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hashrate 1 Hour</td>
|
||||
<td>{{ local_stats["hashrate_1h"]|humanize }}H/s</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hashrate 24 Hours</td>
|
||||
<td>{{ local_stats["hashrate_24h"]|humanize }}H/s</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shares Found</td>
|
||||
<td>{{ local_stats["shares_found"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Current Effort</td>
|
||||
<td>{{ local_stats["current_effort"] }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Average Effort</td>
|
||||
<td>{{ local_stats["average_effort"] }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total Hashes</td>
|
||||
<td>{{ local_stats["total_hashes"]|humanize }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Miner Connections</td>
|
||||
<td>{{ local_stats["incoming_connections"] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 grid-margin stretch-card">
|
||||
<div class="card">
|
||||
<div class="card-header text-black mb-1 pb-1" style="text-align: center; background-color: #FF6600;"><h2>Global Pool</h2></div>
|
||||
<div class="card-body mb-0 pb-0">
|
||||
<h6 class="card-subtitle text-muted" style="font-size:80%;"> </h6>
|
||||
<div class="table-responsive table-hover table-condensed table-striped">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Hash Rate</td>
|
||||
<td>{{ pool_stats["pool_statistics"]["hashRate"]|humanize }}H/s</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Round Hashes</td>
|
||||
<td>{{ stats_mod["pool"]["roundHashes"]|humanize }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Last Block Found</td>
|
||||
<td>{{ pool_stats["pool_statistics"]["lastBlockFound"] }} <br>
|
||||
{{ pool_stats["pool_statistics"]["lastBlockFoundTime"]|timeago }} ago</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Payout Method</td>
|
||||
<td>{{ pool_stats["pool_list"]|join(',') }}<br>2160 block window (~6 hours)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h6 class="card-subtitle text-muted" style="font-size:80%;">(since instance birth: {{ my_bday }} ago)</h6>
|
||||
<div class="table-responsive table-hover table-condensed table-striped">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Total Hashes</td>
|
||||
<td>{{ pool_stats["pool_statistics"]["totalHashes"]|humanize }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Blocks Found</td>
|
||||
<td>{{ pool_stats["pool_statistics"]["totalBlocksFound"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Known Miners</td>
|
||||
<td>{{ pool_stats["pool_statistics"]["miners"] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 grid-margin stretch-card">
|
||||
<div class="card">
|
||||
<div class="card-header text-black mb-1 pb-1" style="text-align: center; background-color: #FF6600;"><h2>Monero Network</h2></div>
|
||||
<div class="card-body mb-0 pb-0">
|
||||
<h6 class="card-subtitle text-muted" style="font-size:80%;"> </h6>
|
||||
<div class="table-responsive table-hover table-condensed table-striped">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Height</td>
|
||||
<td>{{ network_stats["height"] }}<br>
|
||||
{{ network_stats["timestamp"]|timeago }} ago</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Difficulty</td>
|
||||
<td>{{ network_stats["difficulty"]|humanize }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Reward</td>
|
||||
<td>0.{{ network_stats["reward"] }} Monero</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Head Hash</td>
|
||||
<td>{{ network_stats["hash"] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.setInterval('refresh()', 30000); // Call refresh function every 30000 milliseconds (30 seconds).
|
||||
function refresh() {
|
||||
window .location.reload();
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Oops</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Ooops, something went wrong:</h2>
|
||||
{{ error }}
|
||||
(Maybe you need to wait a few minutes for p2pool to start and sync for the first time?)
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,33 @@
|
||||
### P2Pool command line options
|
||||
|
||||
```
|
||||
--wallet Wallet address to mine to. Subaddresses and integrated addresses are not supported!
|
||||
--host IP address of your Monero node, default is 127.0.0.1
|
||||
--rpc-port monerod RPC API port number, default is 18081
|
||||
--zmq-port monerod ZMQ pub port number, default is 18083 (same port as in monerod's "--zmq-pub" command line parameter)
|
||||
--stratum Comma-separated list of IP:port for stratum server to listen on
|
||||
--p2p Comma-separated list of IP:port for p2p server to listen on
|
||||
--addpeers Comma-separated list of IP:port of other p2pool nodes to connect to
|
||||
--light-mode Don't allocate RandomX dataset, saves 2GB of RAM
|
||||
--loglevel Verbosity of the log, integer number between 0 and 6
|
||||
--config Name of the p2pool config file
|
||||
--data-api Path to the p2pool JSON data (use it in tandem with an external web-server)
|
||||
--local-api Enable /local/ path in api path for Stratum Server and built-in miner statistics
|
||||
--stratum-api An alias for --local-api
|
||||
--no-cache Disable p2pool.cache
|
||||
--no-color Disable colors in console output
|
||||
--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes
|
||||
--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 450)
|
||||
--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 450)
|
||||
--start-mining N Start built-in miner using N threads (any value between 1 and 64)
|
||||
--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from 37889 to 37888
|
||||
--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum
|
||||
--rpc-login Specify username[:password] required for Monero RPC server
|
||||
--socks5 Specify IP:port of a SOCKS5 proxy to use for outgoing connections
|
||||
--no-dns disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too)
|
||||
--p2p-external-port port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections
|
||||
```
|
||||
|
||||
### Example command line
|
||||
|
||||
p2pool.exe --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:3333 --p2p 0.0.0.0:37889
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Vendored
+1
-1
Submodule external/src/RandomX updated: 256d27c5c5...d10edcbc93
Vendored
+1
-1
Submodule external/src/cppzmq updated: 7742eb3693...381f699d37
Vendored
+1
-1
Submodule external/src/curl updated: e5926fe5f9...93d092867f
Vendored
+1
-1
Submodule external/src/libuv updated: 1a91b51976...0c1fa696aa
Vendored
+1
-1
Submodule external/src/libzmq updated: 37224c93de...ec6f3b1dba
Vendored
+1
-1
Submodule external/src/rapidjson updated: 6e3ab6dd56...914b772dfd
@@ -23,7 +23,7 @@
|
||||
};
|
||||
|
||||
buildInputs = builtins.attrValues {
|
||||
inherit (pkgs) libuv zeromq libsodium gss;
|
||||
inherit (pkgs) libuv zeromq libsodium gss curl;
|
||||
};
|
||||
|
||||
installPhase = ''
|
||||
|
||||
+8
-5
@@ -165,8 +165,11 @@ BlockCache::~BlockCache()
|
||||
|
||||
void BlockCache::store(const PoolBlock& block)
|
||||
{
|
||||
const size_t n1 = block.m_mainChainData.size();
|
||||
const size_t n2 = block.m_sideChainData.size();
|
||||
const std::vector<uint8_t> mainchain_data = block.serialize_mainchain_data();
|
||||
const std::vector<uint8_t> sidechain_data = block.serialize_sidechain_data();
|
||||
|
||||
const size_t n1 = mainchain_data.size();
|
||||
const size_t n2 = sidechain_data.size();
|
||||
|
||||
if (!m_impl->m_data || (sizeof(uint32_t) + n1 + n2 > BLOCK_SIZE)) {
|
||||
return;
|
||||
@@ -175,8 +178,8 @@ void BlockCache::store(const PoolBlock& block)
|
||||
uint8_t* data = m_impl->m_data + (static_cast<size_t>((m_storeIndex++) % NUM_BLOCKS) * BLOCK_SIZE);
|
||||
|
||||
*reinterpret_cast<uint32_t*>(data) = static_cast<uint32_t>(n1 + n2);
|
||||
memcpy(data + sizeof(uint32_t), block.m_mainChainData.data(), n1);
|
||||
memcpy(data + sizeof(uint32_t) + n1, block.m_sideChainData.data(), n2);
|
||||
memcpy(data + sizeof(uint32_t), mainchain_data.data(), n1);
|
||||
memcpy(data + sizeof(uint32_t) + n1, sidechain_data.data(), n2);
|
||||
}
|
||||
|
||||
void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
|
||||
@@ -198,7 +201,7 @@ void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (block.deserialize(data + sizeof(uint32_t), n, side_chain) == 0) {
|
||||
if (block.deserialize(data + sizeof(uint32_t), n, side_chain, uv_default_loop_checked()) == 0) {
|
||||
server.add_cached_block(block);
|
||||
++blocks_loaded;
|
||||
}
|
||||
|
||||
+49
-27
@@ -54,7 +54,11 @@ BlockTemplate::BlockTemplate(p2pool* pool)
|
||||
, m_txkeySec{}
|
||||
, m_poolBlockTemplate(new PoolBlock())
|
||||
, m_finalReward(0)
|
||||
, m_rng(RandomDeviceSeed::instance)
|
||||
{
|
||||
// Diffuse the initial state in case it has low quality
|
||||
m_rng.discard(10000);
|
||||
|
||||
uv_rwlock_init_checked(&m_lock);
|
||||
|
||||
m_blockHeader.reserve(64);
|
||||
@@ -133,6 +137,8 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
|
||||
m_mempoolTxsOrder.clear();
|
||||
m_shares.clear();
|
||||
|
||||
m_rng = b.m_rng;
|
||||
|
||||
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
||||
m_knapsack.clear();
|
||||
#endif
|
||||
@@ -171,6 +177,14 @@ static FORCEINLINE uint64_t get_block_reward(uint64_t base_reward, uint64_t medi
|
||||
return reward + fees;
|
||||
}
|
||||
|
||||
void BlockTemplate::shuffle_tx_order()
|
||||
{
|
||||
const int64_t n = static_cast<int64_t>(m_mempoolTxsOrder.size());
|
||||
for (int64_t i = n - 1; i > 0; --i) {
|
||||
std::swap(m_mempoolTxsOrder[i], m_mempoolTxsOrder[m_rng() % (i + 1)]);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet)
|
||||
{
|
||||
if (data.major_version > HARDFORK_SUPPORTED_VERSION) {
|
||||
@@ -315,16 +329,16 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
|
||||
// if a block doesn't get into the penalty zone, just pick all transactions
|
||||
if (total_tx_weight + miner_tx_weight <= data.median_weight) {
|
||||
m_numTransactionHashes = 0;
|
||||
|
||||
final_fees = 0;
|
||||
final_weight = miner_tx_weight;
|
||||
|
||||
m_transactionHashes.assign(HASH_SIZE, 0);
|
||||
for (const TxMempoolData& tx : m_mempoolTxs) {
|
||||
m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
|
||||
++m_numTransactionHashes;
|
||||
shuffle_tx_order();
|
||||
|
||||
m_numTransactionHashes = m_mempoolTxsOrder.size();
|
||||
m_transactionHashes.assign(HASH_SIZE, 0);
|
||||
for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
|
||||
const TxMempoolData& tx = m_mempoolTxs[m_mempoolTxsOrder[i]];
|
||||
m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
|
||||
final_fees += tx.fee;
|
||||
final_weight += tx.weight;
|
||||
}
|
||||
@@ -398,6 +412,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
final_fees = 0;
|
||||
final_weight = miner_tx_weight;
|
||||
|
||||
shuffle_tx_order();
|
||||
|
||||
m_numTransactionHashes = m_mempoolTxsOrder.size();
|
||||
m_transactionHashes.assign(HASH_SIZE, 0);
|
||||
for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
|
||||
@@ -528,15 +544,16 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
|
||||
memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
|
||||
|
||||
m_poolBlockTemplate->serialize_mainchain_data(0, 0, m_poolBlockTemplate->m_sidechainId);
|
||||
|
||||
#if POOL_BLOCK_DEBUG
|
||||
if (m_poolBlockTemplate->m_mainChainData != m_blockTemplateBlob) {
|
||||
const std::vector<uint8_t> mainchain_data = m_poolBlockTemplate->serialize_mainchain_data();
|
||||
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
|
||||
|
||||
if (mainchain_data != m_blockTemplateBlob) {
|
||||
LOGERR(1, "serialize_mainchain_data() has a bug, fix it! ");
|
||||
LOGERR(1, "m_poolBlockTemplate->m_mainChainData.size() = " << m_poolBlockTemplate->m_mainChainData.size());
|
||||
LOGERR(1, "m_poolBlockTemplate->m_mainChainData.size() = " << mainchain_data.size());
|
||||
LOGERR(1, "m_blockTemplateBlob.size() = " << m_blockTemplateBlob.size());
|
||||
for (size_t i = 0, n = std::min(m_poolBlockTemplate->m_mainChainData.size(), m_blockTemplateBlob.size()); i < n; ++i) {
|
||||
if (m_poolBlockTemplate->m_mainChainData[i] != m_blockTemplateBlob[i]) {
|
||||
for (size_t i = 0, n = std::min(mainchain_data.size(), m_blockTemplateBlob.size()); i < n; ++i) {
|
||||
if (mainchain_data[i] != m_blockTemplateBlob[i]) {
|
||||
LOGERR(1, "m_poolBlockTemplate->m_mainChainData is different at offset " << i);
|
||||
break;
|
||||
}
|
||||
@@ -545,10 +562,10 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
|
||||
{
|
||||
std::vector<uint8_t> buf = m_blockTemplateBlob;
|
||||
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end());
|
||||
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
|
||||
|
||||
PoolBlock check;
|
||||
const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain());
|
||||
const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain(), nullptr);
|
||||
if (result != 0) {
|
||||
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
|
||||
}
|
||||
@@ -712,7 +729,7 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
|
||||
LOGERR(1, "get_eph_public_key failed at index " << i);
|
||||
}
|
||||
m_minerTx.insert(m_minerTx.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
|
||||
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key, tx_type, view_tag);
|
||||
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key, view_tag);
|
||||
}
|
||||
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
@@ -745,7 +762,7 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
|
||||
const uint64_t corrected_extra_nonce_size = EXTRA_NONCE_SIZE + max_reward_amounts_weight - reward_amounts_weight;
|
||||
if (corrected_extra_nonce_size > EXTRA_NONCE_SIZE) {
|
||||
if (corrected_extra_nonce_size > EXTRA_NONCE_MAX_SIZE) {
|
||||
LOGWARN(4, "create_miner_tx: corrected_extra_nonce_size (" << corrected_extra_nonce_size << ") is too large");
|
||||
LOGWARN(5, "create_miner_tx: corrected_extra_nonce_size (" << corrected_extra_nonce_size << ") is too large");
|
||||
return -3;
|
||||
}
|
||||
LOGINFO(4, "increased EXTRA_NONCE from " << EXTRA_NONCE_SIZE << " to " << corrected_extra_nonce_size << " bytes to maintain miner tx weight");
|
||||
@@ -784,8 +801,9 @@ hash BlockTemplate::calc_sidechain_hash() const
|
||||
const int blob_size = static_cast<int>(m_blockTemplateBlob.size());
|
||||
|
||||
const std::vector<uint8_t>& consensus_id = m_pool->side_chain().consensus_id();
|
||||
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
|
||||
|
||||
keccak_custom([this, sidechain_hash_offset, blob_size, consensus_id](int offset) -> uint8_t {
|
||||
keccak_custom([this, sidechain_hash_offset, blob_size, consensus_id, &sidechain_data](int offset) -> uint8_t {
|
||||
uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(m_nonceOffset));
|
||||
if (k < NONCE_SIZE) {
|
||||
return 0;
|
||||
@@ -806,15 +824,15 @@ hash BlockTemplate::calc_sidechain_hash() const
|
||||
}
|
||||
|
||||
const int side_chain_data_offsset = offset - blob_size;
|
||||
const int side_chain_data_size = static_cast<int>(m_poolBlockTemplate->m_sideChainData.size());
|
||||
const int side_chain_data_size = static_cast<int>(sidechain_data.size());
|
||||
if (side_chain_data_offsset < side_chain_data_size) {
|
||||
return m_poolBlockTemplate->m_sideChainData[side_chain_data_offsset];
|
||||
return sidechain_data[side_chain_data_offsset];
|
||||
}
|
||||
|
||||
const int consensus_id_offset = side_chain_data_offsset - side_chain_data_size;
|
||||
return consensus_id[consensus_id_offset];
|
||||
},
|
||||
static_cast<int>(m_blockTemplateBlob.size() + m_poolBlockTemplate->m_sideChainData.size() + consensus_id.size()), sidechain_hash.h, HASH_SIZE);
|
||||
static_cast<int>(m_blockTemplateBlob.size() + sidechain_data.size() + consensus_id.size()), sidechain_hash.h, HASH_SIZE);
|
||||
|
||||
return sidechain_hash;
|
||||
}
|
||||
@@ -910,11 +928,12 @@ 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
|
||||
bool BlockTemplate::get_difficulties(const uint32_t template_id, uint64_t& height, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const
|
||||
{
|
||||
ReadLock lock(m_lock);
|
||||
|
||||
if (template_id == m_templateId) {
|
||||
height = m_height;
|
||||
mainchain_difficulty = m_difficulty;
|
||||
sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
|
||||
return true;
|
||||
@@ -923,7 +942,7 @@ bool BlockTemplate::get_difficulties(const uint32_t template_id, difficulty_type
|
||||
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 old->get_difficulties(template_id, height, mainchain_difficulty, sidechain_difficulty);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1066,18 +1085,21 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
|
||||
if (template_id == m_templateId) {
|
||||
m_poolBlockTemplate->m_nonce = nonce;
|
||||
m_poolBlockTemplate->m_extraNonce = extra_nonce;
|
||||
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_nonceOffset, &nonce, NONCE_SIZE);
|
||||
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_extraNonceOffsetInTemplate, &extra_nonce, EXTRA_NONCE_SIZE);
|
||||
|
||||
SideChain& side_chain = m_pool->side_chain();
|
||||
|
||||
#if POOL_BLOCK_DEBUG
|
||||
{
|
||||
std::vector<uint8_t> buf = m_poolBlockTemplate->m_mainChainData;
|
||||
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end());
|
||||
std::vector<uint8_t> buf = m_poolBlockTemplate->serialize_mainchain_data();
|
||||
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
|
||||
|
||||
memcpy(buf.data() + m_nonceOffset, &nonce, NONCE_SIZE);
|
||||
memcpy(buf.data() + m_extraNonceOffsetInTemplate, &extra_nonce, EXTRA_NONCE_SIZE);
|
||||
|
||||
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
|
||||
|
||||
PoolBlock check;
|
||||
const int result = check.deserialize(buf.data(), buf.size(), side_chain);
|
||||
const int result = check.deserialize(buf.data(), buf.size(), side_chain, nullptr);
|
||||
if (result != 0) {
|
||||
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
|
||||
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet);
|
||||
|
||||
bool get_difficulties(const uint32_t template_id, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
|
||||
bool get_difficulties(const uint32_t template_id, uint64_t& height, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
|
||||
uint32_t get_hashing_blob(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const;
|
||||
|
||||
uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
|
||||
@@ -106,6 +106,10 @@ private:
|
||||
std::vector<int> m_mempoolTxsOrder;
|
||||
std::vector<MinerShare> m_shares;
|
||||
|
||||
std::mt19937_64 m_rng;
|
||||
|
||||
void shuffle_tx_order();
|
||||
|
||||
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
||||
void fill_optimal_knapsack(const MinerData& data, uint64_t base_reward, uint64_t miner_tx_weight, uint64_t& best_reward, uint64_t& final_fees, uint64_t& final_weight);
|
||||
|
||||
|
||||
@@ -81,6 +81,14 @@
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef __has_feature
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#if defined(_DEBUG) || defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) || defined(__SANITIZE_THREAD__) || __has_feature(thread_sanitizer)
|
||||
#define P2POOL_DEBUGGING 1
|
||||
#endif
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
constexpr size_t HASH_SIZE = 32;
|
||||
|
||||
+123
-76
@@ -27,35 +27,61 @@
|
||||
#include "side_chain.h"
|
||||
#include <iostream>
|
||||
|
||||
#ifdef HAVE_PTHREAD_CANCEL
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
static constexpr char log_category_prefix[] = "ConsoleCommands ";
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
bool ConsoleCommands::stopped = false;
|
||||
|
||||
ConsoleCommands::ConsoleCommands(p2pool* pool)
|
||||
: m_pool(pool)
|
||||
, m_loop{}
|
||||
, m_shutdownAsync{}
|
||||
, m_tty{}
|
||||
, m_loopThread{}
|
||||
, m_readBuf{}
|
||||
, m_readBufInUse(false)
|
||||
{
|
||||
m_worker = new std::thread(&ConsoleCommands::run, this);
|
||||
if (uv_guess_handle(0) != UV_TTY) {
|
||||
LOGERR(1, "tty is not available");
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
int err = uv_loop_init(&m_loop);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
err = uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
|
||||
if (err) {
|
||||
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
||||
throw std::exception();
|
||||
}
|
||||
m_shutdownAsync.data = this;
|
||||
|
||||
err = uv_tty_init(&m_loop, &m_tty, 0, 1);
|
||||
if (err) {
|
||||
LOGERR(1, "uv_tty_init failed, error " << uv_err_name(err));
|
||||
throw std::exception();
|
||||
}
|
||||
m_tty.data = this;
|
||||
|
||||
err = uv_read_start(reinterpret_cast<uv_stream_t*>(&m_tty), allocCallback, stdinReadCallback);
|
||||
if (err) {
|
||||
LOGERR(1, "uv_read_start failed, error " << uv_err_name(err));
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
err = uv_thread_create(&m_loopThread, loop, this);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleCommands::~ConsoleCommands()
|
||||
{
|
||||
stopped = true;
|
||||
|
||||
#ifdef _WIN32
|
||||
TerminateThread(reinterpret_cast<HANDLE>(m_worker->native_handle()), 0);
|
||||
#elif defined HAVE_PTHREAD_CANCEL
|
||||
pthread_cancel(m_worker->native_handle());
|
||||
#endif
|
||||
|
||||
m_worker->join();
|
||||
delete m_worker;
|
||||
|
||||
uv_async_send(&m_shutdownAsync);
|
||||
uv_thread_join(&m_loopThread);
|
||||
LOGINFO(1, "stopped");
|
||||
}
|
||||
|
||||
@@ -67,7 +93,7 @@ typedef struct strconst {
|
||||
#define STRCONST(x) {x, sizeof(x)-1}
|
||||
#define STRCNULL {NULL, 0}
|
||||
|
||||
typedef int (cmdfunc)(p2pool *pool, const char *args);
|
||||
typedef void (cmdfunc)(p2pool *pool, const char *args);
|
||||
|
||||
typedef struct cmd {
|
||||
strconst name;
|
||||
@@ -101,16 +127,15 @@ static cmd cmds[] = {
|
||||
{ STRCNULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static int do_help(p2pool * /* m_pool */, const char * /* args */)
|
||||
static void do_help(p2pool * /* m_pool */, const char * /* args */)
|
||||
{
|
||||
LOGINFO(0, "List of commands");
|
||||
for (int i = 0; cmds[i].name.len; ++i) {
|
||||
LOGINFO(0, cmds[i].name.str << " " << cmds[i].arg << "\t" << cmds[i].descr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_status(p2pool *m_pool, const char * /* args */)
|
||||
static void do_status(p2pool *m_pool, const char * /* args */)
|
||||
{
|
||||
m_pool->side_chain().print_status();
|
||||
if (m_pool->stratum_server()) {
|
||||
@@ -123,51 +148,45 @@ static int do_status(p2pool *m_pool, const char * /* args */)
|
||||
m_pool->print_miner_status();
|
||||
#endif
|
||||
bkg_jobs_tracker.print_status();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_loglevel(p2pool * /* m_pool */, const char *args)
|
||||
static void do_loglevel(p2pool * /* m_pool */, const char *args)
|
||||
{
|
||||
int level = strtol(args, nullptr, 10);
|
||||
level = std::min(std::max(level, 0), log::MAX_GLOBAL_LOG_LEVEL);
|
||||
log::GLOBAL_LOG_LEVEL = level;
|
||||
LOGINFO(0, "log level set to " << level);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_addpeers(p2pool *m_pool, const char *args)
|
||||
static void do_addpeers(p2pool *m_pool, const char *args)
|
||||
{
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->connect_to_peers(args);
|
||||
m_pool->p2p_server()->connect_to_peers_async(args);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_droppeers(p2pool *m_pool, const char * /* args */)
|
||||
static void do_droppeers(p2pool *m_pool, const char * /* args */)
|
||||
{
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->drop_connections();
|
||||
m_pool->p2p_server()->drop_connections_async();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_showpeers(p2pool* m_pool, const char* /* args */)
|
||||
static void do_showpeers(p2pool* m_pool, const char* /* args */)
|
||||
{
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->show_peers();
|
||||
m_pool->p2p_server()->show_peers_async();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_showworkers(p2pool* m_pool, const char* /* args */)
|
||||
static void do_showworkers(p2pool* m_pool, const char* /* args */)
|
||||
{
|
||||
if (m_pool->stratum_server()) {
|
||||
m_pool->stratum_server()->show_workers();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_showbans(p2pool* m_pool, const char* /* args */)
|
||||
static void do_showbans(p2pool* m_pool, const char* /* args */)
|
||||
{
|
||||
if (m_pool->stratum_server()) {
|
||||
m_pool->stratum_server()->print_bans();
|
||||
@@ -175,87 +194,115 @@ static int do_showbans(p2pool* m_pool, const char* /* args */)
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->print_bans();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_outpeers(p2pool* m_pool, const char* args)
|
||||
static void do_outpeers(p2pool* m_pool, const char* args)
|
||||
{
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->set_max_outgoing_peers(strtoul(args, nullptr, 10));
|
||||
LOGINFO(0, "max outgoing peers set to " << m_pool->p2p_server()->max_outgoing_peers());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_inpeers(p2pool* m_pool, const char* args)
|
||||
static void do_inpeers(p2pool* m_pool, const char* args)
|
||||
{
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->set_max_incoming_peers(strtoul(args, nullptr, 10));
|
||||
LOGINFO(0, "max incoming peers set to " << m_pool->p2p_server()->max_incoming_peers());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
static int do_start_mining(p2pool* m_pool, const char* args)
|
||||
static void do_start_mining(p2pool* m_pool, const char* args)
|
||||
{
|
||||
uint32_t threads = strtoul(args, nullptr, 10);
|
||||
threads = std::min(std::max(threads, 1u), 64u);
|
||||
m_pool->start_mining(threads);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_stop_mining(p2pool* m_pool, const char* /*args*/)
|
||||
static void do_stop_mining(p2pool* m_pool, const char* /*args*/)
|
||||
{
|
||||
m_pool->stop_mining();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int do_exit(p2pool *m_pool, const char * /* args */)
|
||||
static void do_exit(p2pool *m_pool, const char * /* args */)
|
||||
{
|
||||
bkg_jobs_tracker.wait();
|
||||
m_pool->stop();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ConsoleCommands::run()
|
||||
void ConsoleCommands::allocCallback(uv_handle_t* handle, size_t /*suggested_size*/, uv_buf_t* buf)
|
||||
{
|
||||
LOGINFO(1, "started");
|
||||
ConsoleCommands* pThis = static_cast<ConsoleCommands*>(handle->data);
|
||||
|
||||
std::string command;
|
||||
command.reserve(1024);
|
||||
if (pThis->m_readBufInUse) {
|
||||
buf->len = 0;
|
||||
buf->base = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
std::getline(std::cin, command);
|
||||
buf->len = sizeof(pThis->m_readBuf);
|
||||
buf->base = pThis->m_readBuf;
|
||||
pThis->m_readBufInUse = true;
|
||||
}
|
||||
|
||||
if (std::cin.eof()) {
|
||||
LOGINFO(1, "EOF, stopping");
|
||||
return;
|
||||
}
|
||||
void ConsoleCommands::stdinReadCallback(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
|
||||
{
|
||||
ConsoleCommands* pThis = static_cast<ConsoleCommands*>(stream->data);
|
||||
|
||||
if (stopped) {
|
||||
LOGINFO(1, "stopping");
|
||||
return;
|
||||
}
|
||||
if (nread > 0) {
|
||||
std::string& command = pThis->m_command;
|
||||
command.append(buf->base, nread);
|
||||
|
||||
cmd* c = cmds;
|
||||
for (; c->name.len; ++c) {
|
||||
if (!strncmp(command.c_str(), c->name.str, c->name.len)) {
|
||||
const char *args = command.c_str() + c->name.len + 1;
|
||||
if (c->func(m_pool, args)) {
|
||||
LOGINFO(1, "exit requested, stopping");
|
||||
return;
|
||||
}
|
||||
do {
|
||||
size_t k = command.find_first_of("\r\n");
|
||||
if (k == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
command[k] = '\0';
|
||||
|
||||
if (!c->name.len) {
|
||||
LOGWARN(0, "Unknown command " << command);
|
||||
}
|
||||
} while (true);
|
||||
cmd* c = cmds;
|
||||
for (; c->name.len; ++c) {
|
||||
if (!strncmp(command.c_str(), c->name.str, c->name.len)) {
|
||||
const char* args = (c->name.len + 1 <= k) ? (command.c_str() + c->name.len + 1) : "";
|
||||
c->func(pThis->m_pool, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!c->name.len) {
|
||||
LOGWARN(0, "Unknown command " << command.c_str());
|
||||
}
|
||||
|
||||
k = command.find_first_not_of("\r\n", k + 1);
|
||||
command.erase(0, k);
|
||||
} while (true);
|
||||
}
|
||||
else if (nread < 0) {
|
||||
LOGWARN(4, "read error " << uv_err_name(static_cast<int>(nread)));
|
||||
}
|
||||
|
||||
pThis->m_readBufInUse = false;
|
||||
}
|
||||
|
||||
void ConsoleCommands::loop(void* data)
|
||||
{
|
||||
LOGINFO(1, "event loop started");
|
||||
|
||||
ConsoleCommands* pThis = static_cast<ConsoleCommands*>(data);
|
||||
|
||||
int err = uv_run(&pThis->m_loop, UV_RUN_DEFAULT);
|
||||
if (err) {
|
||||
LOGWARN(1, "uv_run returned " << err);
|
||||
}
|
||||
|
||||
err = uv_loop_close(&pThis->m_loop);
|
||||
if (err) {
|
||||
LOGWARN(1, "uv_loop_close returned error " << uv_err_name(err));
|
||||
}
|
||||
|
||||
LOGINFO(1, "event loop stopped");
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+22
-4
@@ -17,7 +17,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include "uv_util.h"
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
@@ -31,10 +31,28 @@ public:
|
||||
|
||||
private:
|
||||
p2pool* m_pool;
|
||||
std::thread* m_worker;
|
||||
|
||||
static bool stopped;
|
||||
void run();
|
||||
uv_loop_t m_loop;
|
||||
uv_async_t m_shutdownAsync;
|
||||
uv_tty_t m_tty;
|
||||
uv_thread_t m_loopThread;
|
||||
|
||||
char m_readBuf[64];
|
||||
bool m_readBufInUse;
|
||||
|
||||
std::string m_command;
|
||||
|
||||
static void loop(void* data);
|
||||
|
||||
static void on_shutdown(uv_async_t* async)
|
||||
{
|
||||
ConsoleCommands* pThis = reinterpret_cast<ConsoleCommands*>(async->data);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&pThis->m_shutdownAsync), nullptr);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&pThis->m_tty), nullptr);
|
||||
}
|
||||
|
||||
static void allocCallback(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
|
||||
static void stdinReadCallback(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf);
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+96
-46
@@ -154,54 +154,75 @@ static FORCEINLINE void derivation_to_scalar(const hash& derivation, size_t outp
|
||||
hash_to_scalar(data, static_cast<int>(p - data), res);
|
||||
}
|
||||
|
||||
class Cache
|
||||
class Cache : public nocopy_nomove
|
||||
{
|
||||
public:
|
||||
Cache()
|
||||
: derivations(new DerivationsMap())
|
||||
, public_keys(new PublicKeysMap())
|
||||
, tx_keys(new TxKeysMap())
|
||||
{
|
||||
uv_mutex_init_checked(&m);
|
||||
uv_rwlock_init_checked(&derivations_lock);
|
||||
uv_rwlock_init_checked(&public_keys_lock);
|
||||
uv_rwlock_init_checked(&tx_keys_lock);
|
||||
}
|
||||
|
||||
~Cache()
|
||||
{
|
||||
uv_mutex_destroy(&m);
|
||||
delete derivations;
|
||||
delete public_keys;
|
||||
delete tx_keys;
|
||||
|
||||
uv_rwlock_destroy(&derivations_lock);
|
||||
uv_rwlock_destroy(&public_keys_lock);
|
||||
uv_rwlock_destroy(&tx_keys_lock);
|
||||
}
|
||||
|
||||
bool get_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag)
|
||||
{
|
||||
std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)> index;
|
||||
std::array<uint8_t, HASH_SIZE * 2> index;
|
||||
memcpy(index.data(), key1.h, HASH_SIZE);
|
||||
memcpy(index.data() + HASH_SIZE, key2.h, HASH_SIZE);
|
||||
memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t));
|
||||
|
||||
derivation = {};
|
||||
{
|
||||
MutexLock lock(m);
|
||||
auto it = derivations.find(index);
|
||||
if (it != derivations.end()) {
|
||||
derivation = it->second.derivation;
|
||||
view_tag = it->second.view_tag;
|
||||
return true;
|
||||
ReadLock lock(derivations_lock);
|
||||
auto it = derivations->find(index);
|
||||
if (it != derivations->end()) {
|
||||
const DerivationEntry& entry = it->second;
|
||||
derivation = entry.m_derivation;
|
||||
if (entry.find_view_tag(output_index, view_tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ge_p3 point;
|
||||
ge_p2 point2;
|
||||
ge_p1p1 point3;
|
||||
if (derivation.empty()) {
|
||||
ge_p3 point;
|
||||
ge_p2 point2;
|
||||
ge_p1p1 point3;
|
||||
|
||||
if (ge_frombytes_vartime(&point, key1.h) != 0) {
|
||||
return false;
|
||||
if (ge_frombytes_vartime(&point, key1.h) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ge_scalarmult(&point2, key2.h, &point);
|
||||
ge_mul8(&point3, &point2);
|
||||
ge_p1p1_to_p2(&point2, &point3);
|
||||
ge_tobytes(reinterpret_cast<uint8_t*>(&derivation), &point2);
|
||||
}
|
||||
|
||||
ge_scalarmult(&point2, key2.h, &point);
|
||||
ge_mul8(&point3, &point2);
|
||||
ge_p1p1_to_p2(&point2, &point3);
|
||||
ge_tobytes(reinterpret_cast<uint8_t*>(&derivation), &point2);
|
||||
|
||||
derive_view_tag(derivation, output_index, view_tag);
|
||||
|
||||
{
|
||||
MutexLock lock(m);
|
||||
derivations.emplace(index, DerivationEntry{ derivation, view_tag } );
|
||||
WriteLock lock(derivations_lock);
|
||||
|
||||
DerivationEntry& entry = derivations->emplace(index, DerivationEntry{ derivation, {} }).first->second;
|
||||
|
||||
const uint32_t k = static_cast<uint32_t>(output_index << 8) | view_tag;
|
||||
if (std::find(entry.m_viewTags.begin(), entry.m_viewTags.end(), k) == entry.m_viewTags.end()) {
|
||||
entry.m_viewTags.emplace_back(k);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -215,9 +236,9 @@ public:
|
||||
memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t));
|
||||
|
||||
{
|
||||
MutexLock lock(m);
|
||||
auto it = public_keys.find(index);
|
||||
if (it != public_keys.end()) {
|
||||
ReadLock lock(public_keys_lock);
|
||||
auto it = public_keys->find(index);
|
||||
if (it != public_keys->end()) {
|
||||
derived_key = it->second;
|
||||
return true;
|
||||
}
|
||||
@@ -242,8 +263,8 @@ public:
|
||||
ge_tobytes(derived_key.h, &point5);
|
||||
|
||||
{
|
||||
MutexLock lock(m);
|
||||
public_keys.emplace(index, derived_key);
|
||||
WriteLock lock(public_keys_lock);
|
||||
public_keys->emplace(index, derived_key);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -256,9 +277,9 @@ public:
|
||||
memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE);
|
||||
|
||||
{
|
||||
MutexLock lock(m);
|
||||
auto it = tx_keys.find(index);
|
||||
if (it != tx_keys.end()) {
|
||||
ReadLock lock(tx_keys_lock);
|
||||
auto it = tx_keys->find(index);
|
||||
if (it != tx_keys->end()) {
|
||||
pub = it->second.first;
|
||||
sec = it->second.second;
|
||||
return;
|
||||
@@ -276,32 +297,59 @@ public:
|
||||
generate_keys_deterministic(pub, sec, entropy, sizeof(entropy));
|
||||
|
||||
{
|
||||
MutexLock lock(m);
|
||||
tx_keys.emplace(index, std::pair<hash, hash>(pub, sec));
|
||||
WriteLock lock(tx_keys_lock);
|
||||
tx_keys->emplace(index, std::pair<hash, hash>(pub, sec));
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
MutexLock lock(m);
|
||||
|
||||
derivations.clear();
|
||||
public_keys.clear();
|
||||
tx_keys.clear();
|
||||
{
|
||||
WriteLock lock(derivations_lock);
|
||||
delete derivations;
|
||||
derivations = new DerivationsMap();
|
||||
}
|
||||
{
|
||||
WriteLock lock(public_keys_lock);
|
||||
delete public_keys;
|
||||
public_keys = new PublicKeysMap();
|
||||
}
|
||||
{
|
||||
WriteLock lock(tx_keys_lock);
|
||||
delete tx_keys;
|
||||
tx_keys = new TxKeysMap();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct DerivationEntry
|
||||
{
|
||||
hash derivation;
|
||||
// cppcheck-suppress unusedStructMember
|
||||
uint8_t view_tag;
|
||||
hash m_derivation;
|
||||
std::vector<uint32_t> m_viewTags;
|
||||
|
||||
bool find_view_tag(size_t output_index, uint8_t& view_tag) const {
|
||||
for (uint32_t k : m_viewTags) {
|
||||
if ((k >> 8) == output_index) {
|
||||
view_tag = static_cast<uint8_t>(k);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
uv_mutex_t m;
|
||||
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, DerivationEntry> derivations;
|
||||
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
|
||||
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> tx_keys;
|
||||
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, DerivationEntry> DerivationsMap;
|
||||
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> PublicKeysMap;
|
||||
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> TxKeysMap;
|
||||
|
||||
uv_rwlock_t derivations_lock;
|
||||
DerivationsMap* derivations;
|
||||
|
||||
uv_rwlock_t public_keys_lock;
|
||||
PublicKeysMap* public_keys;
|
||||
|
||||
uv_rwlock_t tx_keys_lock;
|
||||
TxKeysMap* tx_keys;
|
||||
};
|
||||
|
||||
static Cache* cache = nullptr;
|
||||
@@ -354,7 +402,9 @@ void destroy_crypto_cache()
|
||||
|
||||
void clear_crypto_cache()
|
||||
{
|
||||
cache->clear();
|
||||
if (cache) {
|
||||
cache->clear();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+161
-44
@@ -27,7 +27,7 @@ namespace JSONRPCRequest {
|
||||
|
||||
struct CurlContext
|
||||
{
|
||||
CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
|
||||
CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
|
||||
~CurlContext();
|
||||
|
||||
static int socket_func(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp)
|
||||
@@ -58,8 +58,9 @@ struct CurlContext
|
||||
|
||||
static void on_close(uv_handle_t* h);
|
||||
|
||||
uv_poll_t m_pollHandle;
|
||||
curl_socket_t m_socket;
|
||||
void shutdown();
|
||||
|
||||
std::vector<std::pair<curl_socket_t, uv_poll_t*>> m_pollHandles;
|
||||
|
||||
CallbackBase* m_callback;
|
||||
CallbackBase* m_closeCallback;
|
||||
@@ -72,16 +73,15 @@ struct CurlContext
|
||||
|
||||
std::string m_url;
|
||||
std::string m_req;
|
||||
std::string m_auth;
|
||||
|
||||
std::vector<char> m_response;
|
||||
std::string m_error;
|
||||
|
||||
curl_slist* m_headers;
|
||||
};
|
||||
|
||||
CurlContext::CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
|
||||
: m_pollHandle{}
|
||||
, m_socket{}
|
||||
, m_callback(cb)
|
||||
CurlContext::CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
|
||||
: m_callback(cb)
|
||||
, m_closeCallback(close_cb)
|
||||
, m_loop(loop)
|
||||
, m_timer{}
|
||||
@@ -89,8 +89,10 @@ CurlContext::CurlContext(const std::string& address, int port, const std::string
|
||||
, m_multiHandle(nullptr)
|
||||
, m_handle(nullptr)
|
||||
, m_req(req)
|
||||
, m_auth(auth)
|
||||
, m_headers(nullptr)
|
||||
{
|
||||
m_pollHandles.reserve(2);
|
||||
|
||||
{
|
||||
char buf[log::Stream::BUF_SIZE + 1];
|
||||
buf[0] = '\0';
|
||||
@@ -172,14 +174,31 @@ CurlContext::CurlContext(const std::string& address, int port, const std::string
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_WRITEFUNCTION, write_func);
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_WRITEDATA, this);
|
||||
|
||||
const int timeout = proxy.empty() ? 1 : 5;
|
||||
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_URL, m_url.c_str());
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_POSTFIELDS, m_req.c_str());
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_CONNECTTIMEOUT, 1);
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_TIMEOUT, 10);
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_CONNECTTIMEOUT, timeout);
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_TIMEOUT, timeout * 10);
|
||||
|
||||
if (!m_auth.empty()) {
|
||||
m_headers = curl_slist_append(m_headers, "Content-Type: application/json");
|
||||
if (m_headers) {
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_HTTPHEADER, m_headers);
|
||||
}
|
||||
|
||||
if (!auth.empty()) {
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_ONLY);
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_USERPWD, m_auth.c_str());
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_USERPWD, auth.c_str());
|
||||
}
|
||||
|
||||
if (!proxy.empty()) {
|
||||
if (is_localhost(address)) {
|
||||
LOGINFO(5, "not using proxy to connect to localhost address " << log::Gray() << address);
|
||||
}
|
||||
else {
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_PROXY, proxy.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
CURLMcode curl_err = curl_multi_add_handle(m_multiHandle, m_handle);
|
||||
@@ -200,45 +219,89 @@ CurlContext::~CurlContext()
|
||||
}
|
||||
delete m_callback;
|
||||
|
||||
if (m_response.empty()) {
|
||||
if (m_error.empty()) {
|
||||
m_error = "empty response";
|
||||
}
|
||||
else {
|
||||
m_error += " (empty response)";
|
||||
}
|
||||
}
|
||||
|
||||
(*m_closeCallback)(m_error.c_str(), m_error.length());
|
||||
delete m_closeCallback;
|
||||
|
||||
curl_slist_free_all(m_headers);
|
||||
}
|
||||
|
||||
int CurlContext::on_socket(CURL* /*easy*/, curl_socket_t s, int action)
|
||||
{
|
||||
auto it = std::find_if(m_pollHandles.begin(), m_pollHandles.end(), [s](const auto& value) { return value.first == s; });
|
||||
|
||||
switch (action) {
|
||||
case CURL_POLL_IN:
|
||||
case CURL_POLL_OUT:
|
||||
case CURL_POLL_INOUT:
|
||||
{
|
||||
if (!m_socket) {
|
||||
m_socket = s;
|
||||
curl_multi_assign(m_multiHandle, s, this);
|
||||
uv_poll_t* h = nullptr;
|
||||
|
||||
if (it != m_pollHandles.end()) {
|
||||
h = it->second;
|
||||
}
|
||||
else if (m_socket != s) {
|
||||
LOGERR(1, "This code can't work with multiple parallel requests. Fix the code!");
|
||||
else {
|
||||
h = new uv_poll_t{};
|
||||
|
||||
// cppcheck-suppress nullPointer
|
||||
h->data = this;
|
||||
|
||||
const int result = uv_poll_init_socket(m_loop, h, s);
|
||||
if (result < 0) {
|
||||
LOGERR(1, "uv_poll_init_socket failed: " << uv_err_name(result));
|
||||
delete h;
|
||||
h = nullptr;
|
||||
}
|
||||
else {
|
||||
m_pollHandles.emplace_back(s, h);
|
||||
}
|
||||
}
|
||||
|
||||
int events = 0;
|
||||
if (action != CURL_POLL_IN) events |= UV_WRITABLE;
|
||||
if (action != CURL_POLL_OUT) events |= UV_READABLE;
|
||||
if (h) {
|
||||
const CURLMcode err = curl_multi_assign(m_multiHandle, s, this);
|
||||
if (err != CURLM_OK) {
|
||||
LOGERR(1, "curl_multi_assign(action = " << action << ") failed: " << curl_multi_strerror(err));
|
||||
}
|
||||
|
||||
if (!m_pollHandle.data) {
|
||||
uv_poll_init_socket(m_loop, &m_pollHandle, s);
|
||||
m_pollHandle.data = this;
|
||||
int events = 0;
|
||||
if (action != CURL_POLL_IN) events |= UV_WRITABLE;
|
||||
if (action != CURL_POLL_OUT) events |= UV_READABLE;
|
||||
|
||||
const int result = uv_poll_start(h, events, curl_perform);
|
||||
if (result < 0) {
|
||||
LOGERR(1, "uv_poll_start failed with error " << uv_err_name(result));
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGERR(1, "failed to start polling on socket " << static_cast<int>(s));
|
||||
}
|
||||
|
||||
uv_poll_start(&m_pollHandle, events, curl_perform);
|
||||
}
|
||||
break;
|
||||
|
||||
case CURL_POLL_REMOVE:
|
||||
default:
|
||||
curl_multi_assign(m_multiHandle, s, nullptr);
|
||||
uv_poll_stop(&m_pollHandle);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), on_close);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), on_close);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_pollHandle), on_close);
|
||||
{
|
||||
if (it != m_pollHandles.end()) {
|
||||
uv_poll_t* h = it->second;
|
||||
m_pollHandles.erase(it);
|
||||
|
||||
uv_poll_stop(h);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(h), [](uv_handle_t* h) { delete reinterpret_cast<uv_poll_t*>(h); });
|
||||
}
|
||||
|
||||
const CURLMcode err = curl_multi_assign(m_multiHandle, s, nullptr);
|
||||
if (err != CURLM_OK) {
|
||||
LOGERR(1, "curl_multi_assign(action = " << action << ") failed: " << curl_multi_strerror(err));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -254,11 +317,24 @@ int CurlContext::on_timer(CURLM* /*multi*/, long timeout_ms)
|
||||
|
||||
if (timeout_ms == 0) {
|
||||
// 0 ms timeout, but we can't just call on_timeout() here - we have to kick the UV loop
|
||||
uv_async_send(&m_async);
|
||||
return 0;
|
||||
const int result = uv_async_send(&m_async);
|
||||
if (result < 0) {
|
||||
LOGERR(1, "uv_async_send failed with error " << uv_err_name(result));
|
||||
|
||||
// if async call didn't work, try to use the timer with 1 ms timeout
|
||||
timeout_ms = 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const int result = uv_timer_start(&m_timer, reinterpret_cast<uv_timer_cb>(on_timeout), timeout_ms, 0);
|
||||
if (result < 0) {
|
||||
LOGERR(1, "uv_timer_start failed with error " << uv_err_name(result));
|
||||
return -1;
|
||||
}
|
||||
|
||||
uv_timer_start(&m_timer, reinterpret_cast<uv_timer_cb>(on_timeout), timeout_ms, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -266,16 +342,25 @@ void CurlContext::on_timeout(uv_handle_t* req)
|
||||
{
|
||||
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
|
||||
|
||||
int running_handles;
|
||||
curl_multi_socket_action(ctx->m_multiHandle, CURL_SOCKET_TIMEOUT, 0, &running_handles);
|
||||
int running_handles = 0;
|
||||
const CURLMcode err = curl_multi_socket_action(ctx->m_multiHandle, CURL_SOCKET_TIMEOUT, 0, &running_handles);
|
||||
if (err != CURLM_OK) {
|
||||
LOGERR(1, "curl_multi_socket_action failed, error " << curl_multi_strerror(err));
|
||||
}
|
||||
|
||||
ctx->check_multi_info();
|
||||
|
||||
if (running_handles == 0) {
|
||||
ctx->shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
size_t CurlContext::on_write(const void* buffer, size_t size, size_t count)
|
||||
{
|
||||
const size_t realsize = size * count;
|
||||
const char* p = reinterpret_cast<const char*>(buffer);
|
||||
m_response.insert(m_response.end(), p, p + size * count);
|
||||
return count;
|
||||
m_response.insert(m_response.end(), p, p + realsize);
|
||||
return realsize;
|
||||
}
|
||||
|
||||
void CurlContext::curl_perform(uv_poll_t* req, int status, int events)
|
||||
@@ -292,9 +377,20 @@ void CurlContext::curl_perform(uv_poll_t* req, int status, int events)
|
||||
|
||||
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
|
||||
|
||||
int running_handles;
|
||||
curl_multi_socket_action(ctx->m_multiHandle, ctx->m_socket, flags, &running_handles);
|
||||
int running_handles = 0;
|
||||
auto it = std::find_if(ctx->m_pollHandles.begin(), ctx->m_pollHandles.end(), [req](const auto& value) { return value.second == req; });
|
||||
if (it != ctx->m_pollHandles.end()) {
|
||||
const CURLMcode err = curl_multi_socket_action(ctx->m_multiHandle, it->first, flags, &running_handles);
|
||||
if (err != CURLM_OK) {
|
||||
LOGERR(1, "curl_multi_socket_action failed, error " << curl_multi_strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
ctx->check_multi_info();
|
||||
|
||||
if (running_handles == 0) {
|
||||
ctx->shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void CurlContext::check_multi_info()
|
||||
@@ -333,30 +429,51 @@ void CurlContext::on_close(uv_handle_t* h)
|
||||
CurlContext* ctx = reinterpret_cast<CurlContext*>(h->data);
|
||||
h->data = nullptr;
|
||||
|
||||
if (ctx->m_timer.data || ctx->m_async.data || ctx->m_pollHandle.data) {
|
||||
if (ctx->m_timer.data || ctx->m_async.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
|
||||
void CurlContext::shutdown()
|
||||
{
|
||||
for (const auto& p : m_pollHandles) {
|
||||
uv_poll_stop(p.second);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(p.second), [](uv_handle_t* h) { delete reinterpret_cast<uv_poll_t*>(h); });
|
||||
}
|
||||
m_pollHandles.clear();
|
||||
|
||||
if (m_async.data && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_async))) {
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), on_close);
|
||||
}
|
||||
|
||||
if (m_timer.data && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_timer))) {
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), on_close);
|
||||
}
|
||||
}
|
||||
|
||||
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
|
||||
{
|
||||
if (!loop) {
|
||||
loop = uv_default_loop();
|
||||
}
|
||||
|
||||
CallOnLoop(loop,
|
||||
const bool result = CallOnLoop(loop,
|
||||
[=]()
|
||||
{
|
||||
try {
|
||||
new CurlContext(address, port, req, auth, cb, close_cb, loop);
|
||||
new CurlContext(address, port, req, auth, proxy, cb, close_cb, loop);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
const char* msg = e.what();
|
||||
(*close_cb)(msg, strlen(msg));
|
||||
}
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
LOGERR(1, "JSON RPC \"" << req << "\" failed");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace JSONRPCRequest
|
||||
|
||||
@@ -37,12 +37,12 @@ private:
|
||||
T m_cb;
|
||||
};
|
||||
|
||||
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
|
||||
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
|
||||
|
||||
template<typename T, typename U>
|
||||
FORCEINLINE void call(const std::string& address, int port, const std::string& req, const std::string& auth, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr)
|
||||
FORCEINLINE void call(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr)
|
||||
{
|
||||
Call(address, port, req, auth, new Callback<T>(std::move(cb)), new Callback<U>(std::move(close_cb)), loop);
|
||||
Call(address, port, req, auth, proxy, new Callback<T>(std::move(cb)), new Callback<U>(std::move(close_cb)), loop);
|
||||
}
|
||||
|
||||
} // namespace JSONRPCRequest
|
||||
|
||||
+5
-2
@@ -42,12 +42,15 @@ void p2pool_usage()
|
||||
"--no-cache Disable p2pool.cache\n"
|
||||
"--no-color Disable colors in console output\n"
|
||||
"--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes\n"
|
||||
"--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 1000)\n"
|
||||
"--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 1000)\n"
|
||||
"--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 450)\n"
|
||||
"--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 450)\n"
|
||||
"--start-mining N Start built-in miner using N threads (any value between 1 and 64)\n"
|
||||
"--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d\n"
|
||||
"--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum\n"
|
||||
"--rpc-login Specify username[:password] required for Monero RPC server\n"
|
||||
"--socks5 Specify IP:port of a SOCKS5 proxy to use for outgoing connections\n"
|
||||
"--no-dns disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too)\n"
|
||||
"--p2p-external-port port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections\n"
|
||||
"--help Show this help message\n\n"
|
||||
"Example command line:\n\n"
|
||||
"%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",
|
||||
|
||||
+95
-34
@@ -32,15 +32,48 @@ namespace p2pool {
|
||||
|
||||
static bool track_memory = false;
|
||||
|
||||
constexpr size_t N = 1048576;
|
||||
constexpr size_t MAX_FRAMES = 30;
|
||||
constexpr size_t N = 1 << 22;
|
||||
constexpr size_t MAX_FRAMES = 29;
|
||||
|
||||
struct TrackedAllocation
|
||||
{
|
||||
void* p;
|
||||
void* stack_trace[MAX_FRAMES];
|
||||
uint64_t allocated_size;
|
||||
uint32_t thread_id;
|
||||
uint32_t allocated_size;
|
||||
|
||||
FORCEINLINE bool operator<(const TrackedAllocation& rhs) { return memcmp(stack_trace, rhs.stack_trace, sizeof(stack_trace)) < 0; }
|
||||
FORCEINLINE bool operator==(const TrackedAllocation& rhs) { return memcmp(stack_trace, rhs.stack_trace, sizeof(stack_trace)) == 0; }
|
||||
|
||||
void print(HANDLE h) const
|
||||
{
|
||||
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = {};
|
||||
PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
|
||||
|
||||
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
pSymbol->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
IMAGEHLP_LINE64 line{};
|
||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
|
||||
for (size_t j = 0; j < MAX_FRAMES; ++j) {
|
||||
const DWORD64 address = reinterpret_cast<DWORD64>(stack_trace[j]);
|
||||
DWORD64 t1 = 0;
|
||||
DWORD t2 = 0;
|
||||
if (SymFromAddr(h, address, &t1, pSymbol) && SymGetLineFromAddr64(h, address, &t2, &line)) {
|
||||
const char* s = line.FileName;
|
||||
const char* file_name = nullptr;
|
||||
while (*s) {
|
||||
if ((*s == '\\') || (*s == '/')) {
|
||||
file_name = s + 1;
|
||||
}
|
||||
++s;
|
||||
}
|
||||
printf("%-25s %s (line %lu)\n", file_name ? file_name : line.FileName, pSymbol->Name, line.LineNumber);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(TrackedAllocation) == 256, "");
|
||||
@@ -51,15 +84,62 @@ uint32_t first[N];
|
||||
uint32_t next[N];
|
||||
TrackedAllocation allocations[N];
|
||||
uint32_t num_allocations = 0;
|
||||
uint64_t total_allocated = 0;
|
||||
uint32_t cur_allocation_index = 1;
|
||||
|
||||
void show_top_10()
|
||||
{
|
||||
TrackedAllocation* buf = reinterpret_cast<TrackedAllocation*>(VirtualAlloc(nullptr, sizeof(TrackedAllocation) * N, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HANDLE h = GetCurrentProcess();
|
||||
|
||||
{
|
||||
p2pool::MutexLock lock(allocation_lock);
|
||||
|
||||
TrackedAllocation* end = buf;
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (allocations[i].allocated_size) {
|
||||
*(end++) = allocations[i];
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(buf, end);
|
||||
|
||||
TrackedAllocation* prev = buf;
|
||||
for (TrackedAllocation* p = buf + 1; p < end; ++p) {
|
||||
if (*p == *prev) {
|
||||
prev->allocated_size += p->allocated_size;
|
||||
}
|
||||
else {
|
||||
++prev;
|
||||
*prev = *p;
|
||||
}
|
||||
}
|
||||
end = prev + 1;
|
||||
|
||||
std::sort(buf, end, [](const auto& a, const auto& b) { return a.allocated_size > b.allocated_size; });
|
||||
|
||||
printf("%I64u total bytes allocated\n", total_allocated);
|
||||
|
||||
for (TrackedAllocation* p = buf; (p < buf + 10) && (p < end); ++p) {
|
||||
printf("%I64u bytes allocated at:\n", p->allocated_size);
|
||||
p->print(h);
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFree(buf, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
FORCEINLINE static void add_alocation(void* p, size_t size)
|
||||
{
|
||||
if (!track_memory) {
|
||||
return;
|
||||
}
|
||||
|
||||
void* stack_trace[MAX_FRAMES];
|
||||
void* stack_trace[MAX_FRAMES] = {};
|
||||
DWORD hash;
|
||||
CaptureStackBackTrace(1, MAX_FRAMES, stack_trace, &hash);
|
||||
|
||||
@@ -74,6 +154,7 @@ FORCEINLINE static void add_alocation(void* p, size_t size)
|
||||
// Make N two times bigger if this triggers
|
||||
__debugbreak();
|
||||
}
|
||||
total_allocated += size;
|
||||
|
||||
for (uint64_t i = cur_allocation_index;; i = (i + 1) & (N - 1)) {
|
||||
if (i && !allocations[i].allocated_size) {
|
||||
@@ -105,6 +186,7 @@ FORCEINLINE static void remove_allocation(void* p)
|
||||
|
||||
for (uint32_t prev = 0, k = first[index]; k != 0; prev = k, k = next[k]) {
|
||||
if (allocations[k].p == p) {
|
||||
total_allocated -= allocations[k].allocated_size;
|
||||
allocations[k].allocated_size = 0;
|
||||
if (prev) {
|
||||
next[prev] = next[k];
|
||||
@@ -181,6 +263,8 @@ void* calloc_hook(size_t count, size_t size) noexcept
|
||||
|
||||
void memory_tracking_start()
|
||||
{
|
||||
SymInitialize(GetCurrentProcess(), NULL, TRUE);
|
||||
|
||||
using namespace p2pool;
|
||||
|
||||
uv_replace_allocator(malloc_hook, realloc_hook, calloc_hook, free_hook);
|
||||
@@ -196,47 +280,24 @@ void memory_tracking_stop()
|
||||
uv_mutex_destroy(&allocation_lock);
|
||||
|
||||
const HANDLE h = GetCurrentProcess();
|
||||
SymInitialize(h, NULL, TRUE);
|
||||
|
||||
uint64_t total_leaks = 0;
|
||||
|
||||
for (uint32_t i = 0; i < N; ++i) {
|
||||
if (allocations[i].allocated_size) {
|
||||
total_leaks += allocations[i].allocated_size;
|
||||
const TrackedAllocation& t = allocations[i];
|
||||
if (t.allocated_size) {
|
||||
total_leaks += t.allocated_size;
|
||||
|
||||
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = {};
|
||||
PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
|
||||
|
||||
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
pSymbol->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
IMAGEHLP_LINE64 line{};
|
||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
|
||||
printf("Memory leak detected, %u bytes allocated by thread %u at:\n", allocations[i].allocated_size, allocations[i].thread_id);
|
||||
for (size_t j = 0; j < MAX_FRAMES; ++j) {
|
||||
const DWORD64 address = reinterpret_cast<DWORD64>(allocations[i].stack_trace[j]);
|
||||
DWORD64 t1 = 0;
|
||||
DWORD t2 = 0;
|
||||
if (SymFromAddr(h, address, &t1, pSymbol) && SymGetLineFromAddr64(h, address, &t2, &line)) {
|
||||
const char* s = line.FileName;
|
||||
const char* file_name = nullptr;
|
||||
while (*s) {
|
||||
if ((*s == '\\') || (*s == '/')) {
|
||||
file_name = s + 1;
|
||||
}
|
||||
++s;
|
||||
}
|
||||
printf("%-25s %s (line %lu)\n", file_name ? file_name : line.FileName, pSymbol->Name, line.LineNumber);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
printf("Memory leak detected, %I64u bytes allocated at %p by thread %u:\n", t.allocated_size, t.p, t.thread_id);
|
||||
t.print(h);
|
||||
}
|
||||
}
|
||||
|
||||
if (total_leaks > 0) {
|
||||
printf("%I64u bytes leaked\n\n", total_leaks);
|
||||
}
|
||||
|
||||
SymCleanup(h);
|
||||
}
|
||||
|
||||
NOINLINE void* operator new(size_t n) { return p2pool::allocate(n); }
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@
|
||||
#include "params.h"
|
||||
#include "p2pool_api.h"
|
||||
#include "side_chain.h"
|
||||
#include "p2p_server.h"
|
||||
#include <thread>
|
||||
|
||||
static constexpr char log_category_prefix[] = "Miner ";
|
||||
@@ -40,7 +41,7 @@ Miner::Miner(p2pool* pool, uint32_t threads)
|
||||
, m_startTimestamp(high_resolution_clock::now())
|
||||
, m_nonce(0)
|
||||
, m_nonceTimestamp(m_startTimestamp)
|
||||
, m_extraNonce(0xF19E3779U)
|
||||
, m_extraNonce(static_cast<uint32_t>(pool->p2p_server()->get_random64()))
|
||||
, m_totalHashes(0)
|
||||
, m_sharesFound(0)
|
||||
, m_job{}
|
||||
|
||||
+249
-154
@@ -52,11 +52,16 @@ P2PServer::P2PServer(p2pool* pool)
|
||||
, m_cachedBlocks(nullptr)
|
||||
, m_rng(RandomDeviceSeed::instance)
|
||||
, m_block(new PoolBlock())
|
||||
, m_blockDeserializeResult(0)
|
||||
, m_timer{}
|
||||
, m_timerCounter(0)
|
||||
, m_timerInterval(2)
|
||||
, m_peerListLastSaved(0)
|
||||
, m_lookForMissingBlocks(true)
|
||||
, m_fastestPeer(nullptr)
|
||||
{
|
||||
m_blockDeserializeBuf.reserve(131072);
|
||||
|
||||
// Diffuse the initial state in case it has low quality
|
||||
m_rng.discard(10000);
|
||||
|
||||
@@ -64,6 +69,19 @@ P2PServer::P2PServer(p2pool* pool)
|
||||
|
||||
const Params& params = pool->params();
|
||||
|
||||
if (!params.m_socks5Proxy.empty()) {
|
||||
parse_address_list(params.m_socks5Proxy,
|
||||
[this](bool is_v6, const std::string& /*address*/, const std::string& ip, int port)
|
||||
{
|
||||
if (!str_to_ip(is_v6, ip.c_str(), m_socks5ProxyIP)) {
|
||||
panic();
|
||||
}
|
||||
m_socks5ProxyV6 = is_v6;
|
||||
m_socks5ProxyPort = port;
|
||||
});
|
||||
m_socks5Proxy = params.m_socks5Proxy;
|
||||
}
|
||||
|
||||
set_max_outgoing_peers(params.m_maxOutgoingPeers);
|
||||
set_max_incoming_peers(params.m_maxIncomingPeers);
|
||||
|
||||
@@ -71,8 +89,8 @@ P2PServer::P2PServer(p2pool* pool)
|
||||
uv_mutex_init_checked(&m_blockLock);
|
||||
uv_mutex_init_checked(&m_peerListLock);
|
||||
uv_mutex_init_checked(&m_broadcastLock);
|
||||
uv_mutex_init_checked(&m_missingBlockRequestsLock);
|
||||
uv_rwlock_init_checked(&m_cachedBlocksLock);
|
||||
uv_mutex_init_checked(&m_connectToPeersLock);
|
||||
|
||||
int err = uv_async_init(&m_loop, &m_broadcastAsync, on_broadcast);
|
||||
if (err) {
|
||||
@@ -82,6 +100,20 @@ P2PServer::P2PServer(p2pool* pool)
|
||||
m_broadcastAsync.data = this;
|
||||
m_broadcastQueue.reserve(2);
|
||||
|
||||
err = uv_async_init(&m_loop, &m_connectToPeersAsync, on_connect_to_peers);
|
||||
if (err) {
|
||||
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
m_connectToPeersAsync.data = this;
|
||||
|
||||
err = uv_async_init(&m_loop, &m_showPeersAsync, on_show_peers);
|
||||
if (err) {
|
||||
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
m_showPeersAsync.data = this;
|
||||
|
||||
err = uv_timer_init(&m_loop, &m_timer);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to create timer, error " << uv_err_name(err));
|
||||
@@ -107,21 +139,18 @@ P2PServer::P2PServer(p2pool* pool)
|
||||
|
||||
P2PServer::~P2PServer()
|
||||
{
|
||||
uv_timer_stop(&m_timer);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_broadcastAsync), nullptr);
|
||||
|
||||
shutdown_tcp();
|
||||
|
||||
uv_mutex_destroy(&m_rngLock);
|
||||
uv_mutex_destroy(&m_blockLock);
|
||||
uv_mutex_destroy(&m_peerListLock);
|
||||
uv_mutex_destroy(&m_broadcastLock);
|
||||
uv_mutex_destroy(&m_missingBlockRequestsLock);
|
||||
|
||||
clear_cached_blocks();
|
||||
uv_rwlock_destroy(&m_cachedBlocksLock);
|
||||
|
||||
uv_mutex_destroy(&m_connectToPeersLock);
|
||||
|
||||
delete m_block;
|
||||
delete m_cache;
|
||||
|
||||
@@ -175,12 +204,42 @@ void P2PServer::store_in_cache(const PoolBlock& block)
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::connect_to_peers_async(const char* peer_list)
|
||||
{
|
||||
{
|
||||
MutexLock lock(m_connectToPeersLock);
|
||||
if (!m_connectToPeersData.empty()) {
|
||||
m_connectToPeersData.append(1, ',');
|
||||
}
|
||||
m_connectToPeersData.append(peer_list);
|
||||
}
|
||||
|
||||
if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_connectToPeersAsync))) {
|
||||
uv_async_send(&m_connectToPeersAsync);
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::on_connect_to_peers(uv_async_t* handle)
|
||||
{
|
||||
P2PServer* server = reinterpret_cast<P2PServer*>(handle->data);
|
||||
|
||||
std::string peer_list;
|
||||
{
|
||||
MutexLock lock(server->m_connectToPeersLock);
|
||||
peer_list = std::move(server->m_connectToPeersData);
|
||||
}
|
||||
|
||||
if (!peer_list.empty()) {
|
||||
server->connect_to_peers(peer_list);
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::connect_to_peers(const std::string& peer_list)
|
||||
{
|
||||
parse_address_list(peer_list,
|
||||
[this](bool is_v6, const std::string& /*address*/, std::string ip, int port)
|
||||
{
|
||||
if (resolve_host(ip, is_v6)) {
|
||||
if (!m_pool->params().m_dns || resolve_host(ip, is_v6)) {
|
||||
connect_to_peer(is_v6, ip.c_str(), port);
|
||||
}
|
||||
});
|
||||
@@ -207,20 +266,20 @@ void P2PServer::update_peer_connections()
|
||||
const uint64_t last_updated = m_pool->side_chain().last_updated();
|
||||
|
||||
bool has_good_peers = false;
|
||||
m_fastestPeer = nullptr;
|
||||
|
||||
unordered_set<raw_ip> connected_clients;
|
||||
{
|
||||
MutexLock lock(m_clientsListLock);
|
||||
|
||||
connected_clients.reserve(m_numConnections);
|
||||
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
bool disconnected = false;
|
||||
|
||||
const int timeout = client->m_handshakeComplete ? 300 : 10;
|
||||
if (cur_time >= client->m_lastAlive + timeout) {
|
||||
if ((cur_time >= client->m_lastAlive + timeout) && (client->m_socks5ProxyState == Client::Socks5ProxyState::Default)) {
|
||||
const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive);
|
||||
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting");
|
||||
client->close();
|
||||
disconnected = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) {
|
||||
@@ -234,14 +293,15 @@ void P2PServer::update_peer_connections()
|
||||
client->ban(DEFAULT_BAN_TIME);
|
||||
remove_peer_from_list(client);
|
||||
client->close();
|
||||
disconnected = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!disconnected) {
|
||||
connected_clients.insert(client->m_addr);
|
||||
if (client->m_handshakeComplete && !client->m_handshakeInvalid && (client->m_listenPort >= 0)) {
|
||||
has_good_peers = true;
|
||||
connected_clients.insert(client->m_addr);
|
||||
if (client->is_good()) {
|
||||
has_good_peers = true;
|
||||
if ((client->m_pingTime >= 0) && (!m_fastestPeer || (m_fastestPeer->m_pingTime > client->m_pingTime))) {
|
||||
m_fastestPeer = client;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -293,7 +353,7 @@ void P2PServer::update_peer_connections()
|
||||
peer_list.pop_back();
|
||||
}
|
||||
|
||||
if (!has_good_peers && ((m_timerCounter % 30) == 0)) {
|
||||
if (!has_good_peers && ((m_timerCounter % 10) == 0)) {
|
||||
LOGERR(1, "no connections to other p2pool nodes, check your monerod/p2pool/network/firewall setup!!!");
|
||||
load_peer_list();
|
||||
if (m_peerListMonero.empty()) {
|
||||
@@ -305,40 +365,40 @@ void P2PServer::update_peer_connections()
|
||||
void P2PServer::update_peer_list()
|
||||
{
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
{
|
||||
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_handshakeComplete || !client->m_handshakeSolutionSent) {
|
||||
continue;
|
||||
}
|
||||
MutexLock lock(m_clientsListLock);
|
||||
|
||||
if (cur_time >= client->m_nextOutgoingPeerListRequest) {
|
||||
// Send peer list requests at random intervals (60-120 seconds)
|
||||
client->m_nextOutgoingPeerListRequest = cur_time + (60 + (get_random64() % 61));
|
||||
|
||||
const bool result = send(client,
|
||||
[](void* buf, size_t buf_size)
|
||||
{
|
||||
LOGINFO(5, "sending PEER_LIST_REQUEST");
|
||||
|
||||
if (buf_size < SEND_BUF_MIN_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*reinterpret_cast<uint8_t*>(buf) = static_cast<uint8_t>(MessageId::PEER_LIST_REQUEST);
|
||||
return 1;
|
||||
});
|
||||
|
||||
if (result) {
|
||||
client->m_lastPeerListRequestTime = std::chrono::high_resolution_clock::now();
|
||||
++client->m_peerListPendingRequests;
|
||||
}
|
||||
}
|
||||
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
if (client->is_good() && (cur_time >= client->m_nextOutgoingPeerListRequest)) {
|
||||
send_peer_list_request(client, cur_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::send_peer_list_request(P2PClient* client, uint64_t cur_time)
|
||||
{
|
||||
// Send peer list requests at random intervals (60-120 seconds)
|
||||
client->m_nextOutgoingPeerListRequest = cur_time + (60 + (get_random64() % 61));
|
||||
|
||||
const bool result = send(client,
|
||||
[client](void* buf, size_t buf_size)
|
||||
{
|
||||
LOGINFO(5, "sending PEER_LIST_REQUEST to " << static_cast<char*>(client->m_addrString));
|
||||
|
||||
if (buf_size < SEND_BUF_MIN_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*reinterpret_cast<uint8_t*>(buf) = static_cast<uint8_t>(MessageId::PEER_LIST_REQUEST);
|
||||
return 1;
|
||||
});
|
||||
|
||||
if (result) {
|
||||
client->m_lastPeerListRequestTime = std::chrono::high_resolution_clock::now();
|
||||
++client->m_peerListPendingRequests;
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::save_peer_list_async()
|
||||
{
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
@@ -411,6 +471,7 @@ void P2PServer::save_peer_list()
|
||||
}
|
||||
}
|
||||
|
||||
f.flush();
|
||||
f.close();
|
||||
|
||||
LOGINFO(5, "peer list saved (" << peer_list.size() << " peers)");
|
||||
@@ -478,18 +539,20 @@ void P2PServer::load_peer_list()
|
||||
}
|
||||
};
|
||||
|
||||
if (m_pool->side_chain().is_default()) {
|
||||
load_from_seed_nodes(seed_nodes, DEFAULT_P2P_PORT);
|
||||
}
|
||||
else if (m_pool->side_chain().is_mini()) {
|
||||
load_from_seed_nodes(seed_nodes_mini, DEFAULT_P2P_PORT_MINI);
|
||||
if (m_pool->params().m_dns) {
|
||||
if (m_pool->side_chain().is_default()) {
|
||||
load_from_seed_nodes(seed_nodes, DEFAULT_P2P_PORT);
|
||||
}
|
||||
else if (m_pool->side_chain().is_mini()) {
|
||||
load_from_seed_nodes(seed_nodes_mini, DEFAULT_P2P_PORT_MINI);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally load peers from p2pool_peers.txt
|
||||
std::ifstream f(saved_peer_list_file_name);
|
||||
if (f.is_open()) {
|
||||
std::string address;
|
||||
while (!f.eof()) {
|
||||
while (f.good()) {
|
||||
std::getline(f, address);
|
||||
if (!address.empty()) {
|
||||
if (!saved_list.empty()) {
|
||||
@@ -511,29 +574,10 @@ void P2PServer::load_peer_list()
|
||||
[this](bool is_v6, const std::string& /*address*/, const std::string& ip, int port)
|
||||
{
|
||||
Peer p;
|
||||
if (is_v6) {
|
||||
sockaddr_in6 addr6;
|
||||
const int err = uv_ip6_addr(ip.c_str(), port, &addr6);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to parse IPv6 address " << ip << ", error " << uv_err_name(err));
|
||||
return;
|
||||
}
|
||||
p.m_isV6 = true;
|
||||
memcpy(p.m_addr.data, &addr6.sin6_addr, sizeof(in6_addr));
|
||||
}
|
||||
else {
|
||||
sockaddr_in addr4;
|
||||
const int err = uv_ip4_addr(ip.c_str(), port, &addr4);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to parse IPv4 address " << ip << ", error " << uv_err_name(err));
|
||||
return;
|
||||
}
|
||||
p.m_isV6 = false;
|
||||
p.m_addr = {};
|
||||
p.m_addr.data[10] = 0xFF;
|
||||
p.m_addr.data[11] = 0xFF;
|
||||
memcpy(p.m_addr.data + 12, &addr4.sin_addr, sizeof(in_addr));
|
||||
if (!str_to_ip(is_v6, ip.c_str(), p.m_addr)) {
|
||||
return;
|
||||
}
|
||||
p.m_isV6 = is_v6;
|
||||
|
||||
bool already_added = false;
|
||||
for (const Peer& peer : m_peerList) {
|
||||
@@ -559,7 +603,7 @@ void P2PServer::load_monerod_peer_list()
|
||||
{
|
||||
const Params& params = m_pool->params();
|
||||
|
||||
JSONRPCRequest::call(params.m_host, params.m_rpcPort, "/get_peer_list", params.m_rpcLogin,
|
||||
JSONRPCRequest::call(params.m_host, params.m_rpcPort, "/get_peer_list", params.m_rpcLogin, m_socks5Proxy,
|
||||
[this](const char* data, size_t size)
|
||||
{
|
||||
#define ERR_STR "/get_peer_list RPC request returned invalid JSON "
|
||||
@@ -608,27 +652,10 @@ void P2PServer::load_monerod_peer_list()
|
||||
|
||||
Peer p;
|
||||
p.m_lastSeen = last_seen;
|
||||
p.m_isV6 = (strchr(ip, ':') != 0);
|
||||
|
||||
if (strchr(ip, ':')) {
|
||||
sockaddr_in6 addr6;
|
||||
const int err = uv_ip6_addr(ip, port, &addr6);
|
||||
if (err) {
|
||||
continue;
|
||||
}
|
||||
p.m_isV6 = true;
|
||||
memcpy(p.m_addr.data, &addr6.sin6_addr, sizeof(in6_addr));
|
||||
}
|
||||
else {
|
||||
sockaddr_in addr4;
|
||||
const int err = uv_ip4_addr(ip, port, &addr4);
|
||||
if (err) {
|
||||
continue;
|
||||
}
|
||||
p.m_isV6 = false;
|
||||
p.m_addr = {};
|
||||
p.m_addr.data[10] = 0xFF;
|
||||
p.m_addr.data[11] = 0xFF;
|
||||
memcpy(p.m_addr.data + 12, &addr4.sin_addr, sizeof(in_addr));
|
||||
if (!str_to_ip(p.m_isV6, ip, p.m_addr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
p.m_port = port;
|
||||
@@ -639,8 +666,8 @@ void P2PServer::load_monerod_peer_list()
|
||||
}
|
||||
}
|
||||
|
||||
// Put recently active peers first in the list
|
||||
std::sort(m_peerListMonero.begin(), m_peerListMonero.end(), [](const Peer& a, const Peer& b) { return a.m_lastSeen > b.m_lastSeen; });
|
||||
// Put recently active peers last in the list (it will be scanned backwards)
|
||||
std::sort(m_peerListMonero.begin(), m_peerListMonero.end(), [](const Peer& a, const Peer& b) { return a.m_lastSeen < b.m_lastSeen; });
|
||||
|
||||
LOGINFO(4, "monerod peer list loaded (" << m_peerListMonero.size() << " peers)");
|
||||
},
|
||||
@@ -714,12 +741,16 @@ void P2PServer::broadcast(const PoolBlock& block)
|
||||
|
||||
Broadcast* data = new Broadcast();
|
||||
|
||||
data->blob.reserve(block.m_mainChainData.size() + block.m_sideChainData.size());
|
||||
data->blob = block.m_mainChainData;
|
||||
data->blob.insert(data->blob.end(), block.m_sideChainData.begin(), block.m_sideChainData.end());
|
||||
int outputs_offset, outputs_blob_size;
|
||||
const std::vector<uint8_t> mainchain_data = block.serialize_mainchain_data(nullptr, nullptr, &outputs_offset, &outputs_blob_size);
|
||||
const std::vector<uint8_t> sidechain_data = block.serialize_sidechain_data();
|
||||
|
||||
data->pruned_blob.reserve(block.m_mainChainData.size() + block.m_sideChainData.size() + 16 - block.m_mainChainOutputsBlobSize);
|
||||
data->pruned_blob.assign(block.m_mainChainData.begin(), block.m_mainChainData.begin() + block.m_mainChainOutputsOffset);
|
||||
data->blob.reserve(mainchain_data.size() + sidechain_data.size());
|
||||
data->blob = mainchain_data;
|
||||
data->blob.insert(data->blob.end(), sidechain_data.begin(), sidechain_data.end());
|
||||
|
||||
data->pruned_blob.reserve(mainchain_data.size() + sidechain_data.size() + 16 - outputs_blob_size);
|
||||
data->pruned_blob.assign(mainchain_data.begin(), mainchain_data.begin() + outputs_offset);
|
||||
|
||||
// 0 outputs in the pruned blob
|
||||
data->pruned_blob.push_back(0);
|
||||
@@ -731,10 +762,10 @@ void P2PServer::broadcast(const PoolBlock& block)
|
||||
});
|
||||
|
||||
writeVarint(total_reward, data->pruned_blob);
|
||||
writeVarint(block.m_mainChainOutputsBlobSize, data->pruned_blob);
|
||||
writeVarint(outputs_blob_size, data->pruned_blob);
|
||||
|
||||
data->pruned_blob.insert(data->pruned_blob.end(), block.m_mainChainData.begin() + block.m_mainChainOutputsOffset + block.m_mainChainOutputsBlobSize, block.m_mainChainData.end());
|
||||
data->pruned_blob.insert(data->pruned_blob.end(), block.m_sideChainData.begin(), block.m_sideChainData.end());
|
||||
data->pruned_blob.insert(data->pruned_blob.end(), mainchain_data.begin() + outputs_offset + outputs_blob_size, mainchain_data.end());
|
||||
data->pruned_blob.insert(data->pruned_blob.end(), sidechain_data.begin(), sidechain_data.end());
|
||||
|
||||
data->ancestor_hashes.reserve(block.m_uncles.size() + 1);
|
||||
data->ancestor_hashes = block.m_uncles;
|
||||
@@ -797,7 +828,7 @@ void P2PServer::on_broadcast()
|
||||
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_handshakeComplete || !client->m_handshakeSolutionSent) {
|
||||
if (!client->is_good()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -879,6 +910,13 @@ void P2PServer::print_status()
|
||||
);
|
||||
}
|
||||
|
||||
void P2PServer::show_peers_async()
|
||||
{
|
||||
if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_showPeersAsync))) {
|
||||
uv_async_send(&m_showPeersAsync);
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::show_peers()
|
||||
{
|
||||
MutexLock lock(m_clientsListLock);
|
||||
@@ -895,6 +933,30 @@ void P2PServer::show_peers()
|
||||
LOGINFO(0, "Total: " << n << " peers");
|
||||
}
|
||||
|
||||
int P2PServer::listen_port() const
|
||||
{
|
||||
const Params& params = m_pool->params();
|
||||
return params.m_p2pExternalPort ? params.m_p2pExternalPort : m_listenPort;
|
||||
}
|
||||
|
||||
int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((m_blockDeserializeBuf.size() == size) && (memcmp(m_blockDeserializeBuf.data(), buf, size) == 0)) {
|
||||
m_block->reset_offchain_data();
|
||||
result = m_blockDeserializeResult;
|
||||
}
|
||||
else {
|
||||
result = m_block->deserialize(buf, size, m_pool->side_chain(), &m_loop);
|
||||
m_blockDeserializeBuf.assign(buf, buf + size);
|
||||
m_blockDeserializeResult = result;
|
||||
m_lookForMissingBlocks = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void P2PServer::on_timer()
|
||||
{
|
||||
++m_timerCounter;
|
||||
@@ -948,11 +1010,15 @@ void P2PServer::flush_cache()
|
||||
|
||||
void P2PServer::download_missing_blocks()
|
||||
{
|
||||
if (!m_lookForMissingBlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<hash> missing_blocks;
|
||||
m_pool->side_chain().get_missing_blocks(missing_blocks);
|
||||
|
||||
if (missing_blocks.empty()) {
|
||||
MutexLock lock(m_missingBlockRequestsLock);
|
||||
m_lookForMissingBlocks = false;
|
||||
m_missingBlockRequests.clear();
|
||||
return;
|
||||
}
|
||||
@@ -967,11 +1033,9 @@ void P2PServer::download_missing_blocks()
|
||||
clients.reserve(m_numConnections);
|
||||
|
||||
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
if (!client->m_handshakeComplete || !client->m_handshakeSolutionSent) {
|
||||
continue;
|
||||
if (client->is_good()) {
|
||||
clients.emplace_back(client);
|
||||
}
|
||||
|
||||
clients.emplace_back(client);
|
||||
}
|
||||
|
||||
if (clients.empty()) {
|
||||
@@ -984,15 +1048,11 @@ void P2PServer::download_missing_blocks()
|
||||
for (const hash& id : missing_blocks) {
|
||||
P2PClient* client = clients[get_random64() % clients.size()];
|
||||
|
||||
{
|
||||
MutexLock lock3(m_missingBlockRequestsLock);
|
||||
|
||||
const uint64_t truncated_block_id = *reinterpret_cast<const uint64_t*>(id.h);
|
||||
if (!m_missingBlockRequests.insert({ client->m_peerId, truncated_block_id }).second) {
|
||||
// We already asked this peer about this block
|
||||
// Don't try to ask another peer, leave it for another timer tick
|
||||
continue;
|
||||
}
|
||||
const uint64_t truncated_block_id = *reinterpret_cast<const uint64_t*>(id.h);
|
||||
if (!m_missingBlockRequests.insert({ client->m_peerId, truncated_block_id }).second) {
|
||||
// We already asked this peer about this block
|
||||
// Don't try to ask another peer, leave it for another timer tick
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_cachedBlocks) {
|
||||
@@ -1005,9 +1065,9 @@ void P2PServer::download_missing_blocks()
|
||||
}
|
||||
|
||||
const bool result = send(client,
|
||||
[&id](void* buf, size_t buf_size) -> size_t
|
||||
[&id, client](void* buf, size_t buf_size) -> size_t
|
||||
{
|
||||
LOGINFO(5, "sending BLOCK_REQUEST for id = " << id);
|
||||
LOGINFO(5, "sending BLOCK_REQUEST for id = " << id << " to " << static_cast<char*>(client->m_addrString));
|
||||
|
||||
if (buf_size < SEND_BUF_MIN_SIZE) {
|
||||
return 0;
|
||||
@@ -1032,7 +1092,7 @@ void P2PServer::download_missing_blocks()
|
||||
|
||||
void P2PServer::check_zmq()
|
||||
{
|
||||
if ((m_timerCounter % 30) != 0) {
|
||||
if ((m_timerCounter % 30) != 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1059,7 +1119,7 @@ P2PServer::P2PClient::P2PClient()
|
||||
, m_nextOutgoingPeerListRequest(0)
|
||||
, m_lastPeerListRequestTime{}
|
||||
, m_peerListPendingRequests(0)
|
||||
, m_pingTime(0)
|
||||
, m_pingTime(-1)
|
||||
, m_blockPendingRequests(0)
|
||||
, m_chainTipBlockRequest(false)
|
||||
, m_lastAlive(0)
|
||||
@@ -1069,12 +1129,27 @@ P2PServer::P2PClient::P2PClient()
|
||||
{
|
||||
}
|
||||
|
||||
void P2PServer::on_shutdown()
|
||||
{
|
||||
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_connectToPeersAsync), nullptr);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_showPeersAsync), nullptr);
|
||||
}
|
||||
|
||||
P2PServer::P2PClient::~P2PClient()
|
||||
{
|
||||
}
|
||||
|
||||
void P2PServer::P2PClient::reset()
|
||||
{
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
|
||||
if (server && (server->m_fastestPeer == this)) {
|
||||
server->m_fastestPeer = nullptr;
|
||||
}
|
||||
|
||||
Client::reset();
|
||||
|
||||
m_peerId = 0;
|
||||
@@ -1089,7 +1164,7 @@ void P2PServer::P2PClient::reset()
|
||||
m_nextOutgoingPeerListRequest = 0;
|
||||
m_lastPeerListRequestTime = {};
|
||||
m_peerListPendingRequests = 0;
|
||||
m_pingTime = 0;
|
||||
m_pingTime = -1;
|
||||
m_blockPendingRequests = 0;
|
||||
m_chainTipBlockRequest = false;
|
||||
m_lastAlive = 0;
|
||||
@@ -1115,12 +1190,14 @@ bool P2PServer::P2PClient::on_connect()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't allow multiple connections to/from the same IP
|
||||
// Don't allow multiple connections to/from the same IP (except localhost)
|
||||
// server->m_clientsListLock is already locked here
|
||||
for (P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
if ((client != this) && (client->m_addr == m_addr)) {
|
||||
LOGINFO(5, "peer " << static_cast<char*>(m_addrString) << " is already connected as " << static_cast<char*>(client->m_addrString));
|
||||
return false;
|
||||
if (!m_addr.is_localhost()) {
|
||||
for (P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
if ((client != this) && (client->m_addr == m_addr)) {
|
||||
LOGINFO(5, "peer " << static_cast<char*>(m_addrString) << " is already connected as " << static_cast<char*>(client->m_addrString));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1324,7 +1401,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
bytes_read = 2u + num_peers * 19u;
|
||||
|
||||
using namespace std::chrono;
|
||||
m_pingTime = duration_cast<milliseconds>(high_resolution_clock::now() - m_lastPeerListRequestTime).count();
|
||||
m_pingTime = std::max<int64_t>(duration_cast<milliseconds>(high_resolution_clock::now() - m_lastPeerListRequestTime).count(), 0);
|
||||
|
||||
--m_peerListPendingRequests;
|
||||
if (!on_peer_list_response(buf + 1)) {
|
||||
@@ -1362,11 +1439,18 @@ void P2PServer::P2PClient::on_read_failed(int /*err*/)
|
||||
|
||||
void P2PServer::P2PClient::on_disconnected()
|
||||
{
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
|
||||
if (server && (server->m_fastestPeer == this)) {
|
||||
server->m_fastestPeer = nullptr;
|
||||
}
|
||||
|
||||
m_pingTime = -1;
|
||||
|
||||
if (!m_handshakeComplete) {
|
||||
LOGWARN(5, "peer " << static_cast<char*>(m_addrString) << " disconnected before finishing handshake");
|
||||
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
if (server) {
|
||||
server->remove_peer_from_list(this);
|
||||
}
|
||||
@@ -1381,7 +1465,7 @@ bool P2PServer::P2PClient::send_handshake_challenge()
|
||||
return owner->send(this,
|
||||
[this, owner](void* buf, size_t buf_size) -> size_t
|
||||
{
|
||||
LOGINFO(5, "sending HANDSHAKE_CHALLENGE");
|
||||
LOGINFO(5, "sending HANDSHAKE_CHALLENGE to " << static_cast<char*>(m_addrString));
|
||||
|
||||
if (buf_size < SEND_BUF_MIN_SIZE) {
|
||||
return 0;
|
||||
@@ -1503,7 +1587,7 @@ void P2PServer::P2PClient::send_handshake_solution(const uint8_t (&challenge)[CH
|
||||
const bool result = work->server->send(work->client,
|
||||
[work](void* buf, size_t buf_size) -> size_t
|
||||
{
|
||||
LOGINFO(5, "sending HANDSHAKE_SOLUTION");
|
||||
LOGINFO(5, "sending HANDSHAKE_SOLUTION to " << static_cast<char*>(work->client->m_addrString));
|
||||
|
||||
if (buf_size < SEND_BUF_MIN_SIZE) {
|
||||
return 0;
|
||||
@@ -1658,7 +1742,7 @@ bool P2PServer::P2PClient::on_handshake_solution(const uint8_t* buf)
|
||||
return m_owner->send(this,
|
||||
[this](void* buf, size_t buf_size) -> size_t
|
||||
{
|
||||
LOGINFO(5, "sending LISTEN_PORT and BLOCK_REQUEST for the chain tip");
|
||||
LOGINFO(5, "sending LISTEN_PORT and BLOCK_REQUEST for the chain tip to " << static_cast<char*>(m_addrString));
|
||||
|
||||
if (buf_size < SEND_BUF_MIN_SIZE) {
|
||||
return 0;
|
||||
@@ -1676,14 +1760,14 @@ bool P2PServer::P2PClient::on_handshake_solution(const uint8_t* buf)
|
||||
|
||||
void P2PServer::P2PClient::on_after_handshake(uint8_t* &p)
|
||||
{
|
||||
LOGINFO(5, "sending LISTEN_PORT");
|
||||
LOGINFO(5, "sending LISTEN_PORT to " << static_cast<char*>(m_addrString));
|
||||
*(p++) = static_cast<uint8_t>(MessageId::LISTEN_PORT);
|
||||
|
||||
const int32_t port = m_owner->listen_port();
|
||||
memcpy(p, &port, sizeof(port));
|
||||
p += sizeof(port);
|
||||
|
||||
LOGINFO(5, "sending BLOCK_REQUEST for the chain tip");
|
||||
LOGINFO(5, "sending BLOCK_REQUEST for the chain tip to " << static_cast<char*>(m_addrString));
|
||||
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_REQUEST);
|
||||
|
||||
hash empty;
|
||||
@@ -1726,9 +1810,9 @@ bool P2PServer::P2PClient::on_block_request(const uint8_t* buf)
|
||||
}
|
||||
|
||||
return server->send(this,
|
||||
[&blob](void* buf, size_t buf_size) -> size_t
|
||||
[this, &blob](void* buf, size_t buf_size) -> size_t
|
||||
{
|
||||
LOGINFO(5, "sending BLOCK_RESPONSE");
|
||||
LOGINFO(5, "sending BLOCK_RESPONSE to " << static_cast<char*>(m_addrString));
|
||||
|
||||
const uint32_t len = static_cast<uint32_t>(blob.size());
|
||||
|
||||
@@ -1764,7 +1848,7 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size)
|
||||
|
||||
MutexLock lock(server->m_blockLock);
|
||||
|
||||
const int result = server->m_block->deserialize(buf, size, server->m_pool->side_chain());
|
||||
const int result = server->deserialize_block(buf, size);
|
||||
if (result != 0) {
|
||||
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
|
||||
return false;
|
||||
@@ -1773,16 +1857,18 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size)
|
||||
if (m_chainTipBlockRequest) {
|
||||
m_chainTipBlockRequest = false;
|
||||
|
||||
const uint64_t peer_height = server->m_block->m_txinGenHeight;
|
||||
const uint64_t peer_height = server->get_block()->m_txinGenHeight;
|
||||
const uint64_t our_height = server->m_pool->miner_data().height;
|
||||
|
||||
if (peer_height + 2 < our_height) {
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " is mining on top of a stale block (mainchain height " << peer_height << ", expected >= " << our_height << ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
server->send_peer_list_request(this, seconds_since_epoch());
|
||||
}
|
||||
|
||||
return handle_incoming_block_async(server->m_block);
|
||||
return handle_incoming_block_async(server->get_block());
|
||||
}
|
||||
|
||||
bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
@@ -1796,19 +1882,21 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
|
||||
MutexLock lock(server->m_blockLock);
|
||||
|
||||
const int result = server->m_block->deserialize(buf, size, server->m_pool->side_chain());
|
||||
const int result = server->deserialize_block(buf, size);
|
||||
if (result != 0) {
|
||||
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_broadcastedHashes[m_broadcastedHashesIndex.fetch_add(1) % array_size(&P2PClient::m_broadcastedHashes)] = server->m_block->m_sidechainId;
|
||||
const PoolBlock* block = server->get_block();
|
||||
|
||||
m_broadcastedHashes[m_broadcastedHashesIndex.fetch_add(1) % array_size(&P2PClient::m_broadcastedHashes)] = block->m_sidechainId;
|
||||
|
||||
MinerData miner_data = server->m_pool->miner_data();
|
||||
|
||||
if (server->m_block->m_prevId != miner_data.prev_id) {
|
||||
if (block->m_prevId != miner_data.prev_id) {
|
||||
// This peer is mining on top of a different Monero block, investigate it
|
||||
const uint64_t peer_height = server->m_block->m_txinGenHeight;
|
||||
const uint64_t peer_height = block->m_txinGenHeight;
|
||||
const uint64_t our_height = miner_data.height;
|
||||
|
||||
if (peer_height < our_height) {
|
||||
@@ -1838,11 +1926,11 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
}
|
||||
}
|
||||
|
||||
server->m_block->m_wantBroadcast = true;
|
||||
block->m_wantBroadcast = true;
|
||||
|
||||
m_lastBroadcastTimestamp = seconds_since_epoch();
|
||||
|
||||
return handle_incoming_block_async(server->m_block);
|
||||
return handle_incoming_block_async(block);
|
||||
}
|
||||
|
||||
bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||
@@ -1871,7 +1959,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||
uint32_t n = 0;
|
||||
|
||||
for (P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
if ((client->m_listenPort < 0) || (client->m_addr == m_addr)) {
|
||||
if (!client->is_good() || (client->m_addr == m_addr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1894,9 +1982,9 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||
}
|
||||
|
||||
return server->send(this,
|
||||
[&peers, num_selected_peers](void* buf, size_t buf_size) -> size_t
|
||||
[this, &peers, num_selected_peers](void* buf, size_t buf_size) -> size_t
|
||||
{
|
||||
LOGINFO(5, "sending PEER_LIST_RESPONSE");
|
||||
LOGINFO(5, "sending PEER_LIST_RESPONSE to " << static_cast<char*>(m_addrString));
|
||||
|
||||
if (buf_size < SEND_BUF_MIN_SIZE + 2 + num_selected_peers * 19) {
|
||||
return 0;
|
||||
@@ -1967,7 +2055,7 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool P2PServer::P2PClient::handle_incoming_block_async(PoolBlock* block)
|
||||
bool P2PServer::P2PClient::handle_incoming_block_async(const PoolBlock* block)
|
||||
{
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
|
||||
@@ -2047,6 +2135,13 @@ void P2PServer::P2PClient::post_handle_incoming_block(const uint32_t reset_count
|
||||
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
|
||||
// If the initial sync is not finished yet, try to ask the fastest peer too
|
||||
P2PClient* c = server->m_fastestPeer;
|
||||
if (c && (c != this) && !server->m_pool->side_chain().precalcFinished()) {
|
||||
LOGINFO(5, "peer " << static_cast<char*>(c->m_addrString) << " is faster, sending BLOCK_REQUEST to it too");
|
||||
c->post_handle_incoming_block(c->m_resetCounter.load(), missing_blocks);
|
||||
}
|
||||
|
||||
ReadLock lock(server->m_cachedBlocksLock);
|
||||
|
||||
for (const hash& id : missing_blocks) {
|
||||
@@ -2059,10 +2154,10 @@ void P2PServer::P2PClient::post_handle_incoming_block(const uint32_t reset_count
|
||||
}
|
||||
}
|
||||
|
||||
const bool result = m_owner->send(this,
|
||||
[&id](void* buf, size_t buf_size) -> size_t
|
||||
const bool result = server->send(this,
|
||||
[this, &id](void* buf, size_t buf_size) -> size_t
|
||||
{
|
||||
LOGINFO(5, "sending BLOCK_REQUEST for id = " << id);
|
||||
LOGINFO(5, "sending BLOCK_REQUEST for id = " << id << " to " << static_cast<char*>(m_addrString));
|
||||
|
||||
if (buf_size < SEND_BUF_MIN_SIZE + 1 + HASH_SIZE) {
|
||||
return 0;
|
||||
|
||||
+31
-5
@@ -51,6 +51,7 @@ public:
|
||||
void clear_cached_blocks();
|
||||
void store_in_cache(const PoolBlock& block);
|
||||
|
||||
void connect_to_peers_async(const char* peer_list);
|
||||
void connect_to_peers(const std::string& peer_list);
|
||||
void on_connect_failed(bool is_v6, const raw_ip& ip, int port) override;
|
||||
|
||||
@@ -97,10 +98,12 @@ public:
|
||||
bool on_peer_list_request(const uint8_t* buf);
|
||||
bool on_peer_list_response(const uint8_t* buf) const;
|
||||
|
||||
bool handle_incoming_block_async(PoolBlock* block);
|
||||
bool handle_incoming_block_async(const PoolBlock* block);
|
||||
void handle_incoming_block(p2pool* pool, PoolBlock& block, const uint32_t reset_counter, const raw_ip& addr, std::vector<hash>& missing_blocks);
|
||||
void post_handle_incoming_block(const uint32_t reset_counter, std::vector<hash>& missing_blocks);
|
||||
|
||||
bool is_good() const { return m_handshakeComplete && !m_handshakeInvalid && (m_listenPort >= 0); }
|
||||
|
||||
uint64_t m_peerId;
|
||||
MessageId m_expectedMessage;
|
||||
uint64_t m_handshakeChallenge;
|
||||
@@ -132,14 +135,19 @@ public:
|
||||
uint64_t get_peerId() const { return m_peerId; }
|
||||
|
||||
void print_status() override;
|
||||
void show_peers();
|
||||
void show_peers_async();
|
||||
size_t peer_list_size() const { MutexLock lock(m_peerListLock); return m_peerList.size(); }
|
||||
|
||||
int listen_port() const override;
|
||||
|
||||
uint32_t max_outgoing_peers() const { return m_maxOutgoingPeers; }
|
||||
uint32_t max_incoming_peers() const { return m_maxIncomingPeers; }
|
||||
|
||||
void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 1000U); }
|
||||
void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 1000U); }
|
||||
void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 450U); }
|
||||
void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 450U); }
|
||||
|
||||
int deserialize_block(const uint8_t* buf, uint32_t size);
|
||||
const PoolBlock* get_block() const { return m_block; }
|
||||
|
||||
private:
|
||||
p2pool* m_pool;
|
||||
@@ -161,6 +169,7 @@ private:
|
||||
void check_zmq();
|
||||
void update_peer_connections();
|
||||
void update_peer_list();
|
||||
void send_peer_list_request(P2PClient* client, uint64_t cur_time);
|
||||
void save_peer_list_async();
|
||||
void save_peer_list();
|
||||
void load_peer_list();
|
||||
@@ -174,6 +183,8 @@ private:
|
||||
|
||||
uv_mutex_t m_blockLock;
|
||||
PoolBlock* m_block;
|
||||
std::vector<uint8_t> m_blockDeserializeBuf;
|
||||
int m_blockDeserializeResult;
|
||||
|
||||
uv_timer_t m_timer;
|
||||
uint64_t m_timerCounter;
|
||||
@@ -207,11 +218,26 @@ private:
|
||||
uv_async_t m_broadcastAsync;
|
||||
std::vector<Broadcast*> m_broadcastQueue;
|
||||
|
||||
uv_mutex_t m_missingBlockRequestsLock;
|
||||
bool m_lookForMissingBlocks;
|
||||
unordered_set<std::pair<uint64_t, uint64_t>> m_missingBlockRequests;
|
||||
|
||||
P2PClient* m_fastestPeer;
|
||||
|
||||
static void on_broadcast(uv_async_t* handle) { reinterpret_cast<P2PServer*>(handle->data)->on_broadcast(); }
|
||||
void on_broadcast();
|
||||
|
||||
uv_mutex_t m_connectToPeersLock;
|
||||
uv_async_t m_connectToPeersAsync;
|
||||
std::string m_connectToPeersData;
|
||||
|
||||
static void on_connect_to_peers(uv_async_t* handle);
|
||||
|
||||
uv_async_t m_showPeersAsync;
|
||||
|
||||
static void on_show_peers(uv_async_t* handle) { reinterpret_cast<P2PServer*>(handle->data)->show_peers(); }
|
||||
void show_peers();
|
||||
|
||||
void on_shutdown() override;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+107
-50
@@ -64,10 +64,18 @@ p2pool::p2pool(int argc, char* argv[])
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
bool is_v6;
|
||||
if (!resolve_host(m_params->m_host, is_v6)) {
|
||||
LOGERR(1, "resolve_host failed for " << m_params->m_host);
|
||||
throw std::exception();
|
||||
if (m_params->m_socks5Proxy.empty()) {
|
||||
if (m_params->m_dns) {
|
||||
bool is_v6;
|
||||
if (!resolve_host(m_params->m_host, is_v6)) {
|
||||
LOGERR(1, "resolve_host failed for " << m_params->m_host);
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
else if (m_params->m_host.find_first_not_of("0123456789.:") != std::string::npos) {
|
||||
LOGERR(1, "Can't resolve hostname " << m_params->m_host << " with DNS disabled");
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
|
||||
hash pub, sec, eph_public_key;
|
||||
@@ -156,7 +164,14 @@ p2pool::p2pool(int argc, char* argv[])
|
||||
|
||||
m_blockTemplate = new BlockTemplate(this);
|
||||
m_mempool = new Mempool();
|
||||
m_consoleCommands = new ConsoleCommands(this);
|
||||
|
||||
try {
|
||||
m_consoleCommands = new ConsoleCommands(this);
|
||||
}
|
||||
catch (...) {
|
||||
LOGERR(1, "Couldn't start console commands handler");
|
||||
m_consoleCommands = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
p2pool::~p2pool()
|
||||
@@ -175,7 +190,6 @@ p2pool::~p2pool()
|
||||
delete m_blockTemplate;
|
||||
delete m_mempool;
|
||||
delete m_params;
|
||||
delete m_consoleCommands;
|
||||
}
|
||||
|
||||
bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result)
|
||||
@@ -287,6 +301,11 @@ void p2pool::handle_miner_data(MinerData& data)
|
||||
"\n---------------------------------------------------------------------------------------------------------------"
|
||||
);
|
||||
|
||||
// Tx secret keys from all miners change every block, so cache can be cleared here
|
||||
if (m_sideChain->precalcFinished()) {
|
||||
clear_crypto_cache();
|
||||
}
|
||||
|
||||
if (!is_main_thread()) {
|
||||
update_block_template_async();
|
||||
}
|
||||
@@ -315,7 +334,7 @@ void p2pool::handle_miner_data(MinerData& data)
|
||||
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, m_params->m_rpcPort, buf, m_params->m_rpcLogin,
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin, m_params->m_socks5Proxy,
|
||||
[this, h](const char* data, size_t size)
|
||||
{
|
||||
ChainMain block;
|
||||
@@ -390,7 +409,7 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
|
||||
if (payout) {
|
||||
LOGINFO(0, log::LightCyan() << "You received a payout of " << log::LightGreen() << log::XMRAmount(payout) << log::LightCyan() << " in block " << log::LightGreen() << data.height);
|
||||
}
|
||||
api_update_block_found(&data);
|
||||
api_update_block_found(&data, block);
|
||||
}
|
||||
else {
|
||||
side_chain().watch_mainchain_block(data, sidechain_id);
|
||||
@@ -426,7 +445,7 @@ void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t e
|
||||
}
|
||||
}
|
||||
|
||||
void p2pool::submit_block_async(const std::vector<uint8_t>& blob)
|
||||
void p2pool::submit_block_async(std::vector<uint8_t>&& blob)
|
||||
{
|
||||
{
|
||||
MutexLock lock(m_submitBlockDataLock);
|
||||
@@ -434,7 +453,7 @@ void p2pool::submit_block_async(const std::vector<uint8_t>& blob)
|
||||
m_submitBlockData.template_id = 0;
|
||||
m_submitBlockData.nonce = 0;
|
||||
m_submitBlockData.extra_nonce = 0;
|
||||
m_submitBlockData.blob = blob;
|
||||
m_submitBlockData.blob = std::move(blob);
|
||||
}
|
||||
|
||||
// If p2pool is stopped, m_submitBlockAsync is most likely already closed
|
||||
@@ -450,10 +469,14 @@ void p2pool::submit_block_async(const std::vector<uint8_t>& blob)
|
||||
}
|
||||
}
|
||||
|
||||
bool init_signals(p2pool* pool, bool init);
|
||||
|
||||
void p2pool::on_stop(uv_async_t* async)
|
||||
{
|
||||
p2pool* pool = reinterpret_cast<p2pool*>(async->data);
|
||||
|
||||
delete pool->m_consoleCommands;
|
||||
|
||||
if (pool->m_api) {
|
||||
pool->m_api->on_stop();
|
||||
}
|
||||
@@ -463,9 +486,11 @@ void p2pool::on_stop(uv_async_t* async)
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_stopAsync), nullptr);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_restartZMQAsync), nullptr);
|
||||
|
||||
init_signals(pool, false);
|
||||
|
||||
uv_loop_t* loop = uv_default_loop_checked();
|
||||
delete GetLoopUserData(loop, false);
|
||||
uv_stop(loop);
|
||||
loop->data = nullptr;
|
||||
}
|
||||
|
||||
void p2pool::submit_block() const
|
||||
@@ -525,7 +550,7 @@ void p2pool::submit_block() const
|
||||
}
|
||||
request.append("\"]}");
|
||||
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, request, m_params->m_rpcLogin,
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, request, m_params->m_rpcLogin, m_params->m_socks5Proxy,
|
||||
[height, diff, template_id, nonce, extra_nonce, is_external](const char* data, size_t size)
|
||||
{
|
||||
rapidjson::Document doc;
|
||||
@@ -589,13 +614,15 @@ void p2pool::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32
|
||||
m_blockTemplate->submit_sidechain_block(template_id, nonce, extra_nonce);
|
||||
}
|
||||
|
||||
void p2pool::update_block_template_async()
|
||||
void p2pool::update_block_template_async(bool is_alternative_block)
|
||||
{
|
||||
// If p2pool is stopped, m_blockTemplateAsync is most likely already closed
|
||||
if (m_stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_isAlternativeBlock = is_alternative_block;
|
||||
|
||||
const int err = uv_async_send(&m_blockTemplateAsync);
|
||||
if (err) {
|
||||
LOGERR(1, "uv_async_send failed, error " << uv_err_name(err));
|
||||
@@ -613,6 +640,16 @@ void p2pool::update_block_template()
|
||||
m_blockTemplate->update(data, *m_mempool, &m_params->m_wallet);
|
||||
stratum_on_block();
|
||||
api_update_pool_stats();
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
if (m_isAlternativeBlock.exchange(false)) {
|
||||
MutexLock lock(m_minerLock);
|
||||
|
||||
if (m_miner) {
|
||||
m_miner->reset_share_counters();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void p2pool::download_block_headers(uint64_t current_height)
|
||||
@@ -629,7 +666,7 @@ void p2pool::download_block_headers(uint64_t current_height)
|
||||
s.m_pos = 0;
|
||||
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << height << "}}\0";
|
||||
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin,
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin, m_params->m_socks5Proxy,
|
||||
[this, prev_seed_height, height](const char* data, size_t size)
|
||||
{
|
||||
ChainMain block;
|
||||
@@ -658,14 +695,14 @@ void p2pool::download_block_headers(uint64_t current_height)
|
||||
s.m_pos = 0;
|
||||
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_headers_range\",\"params\":{\"start_height\":" << start_height << ",\"end_height\":" << current_height - 1 << "}}\0";
|
||||
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin,
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin, m_params->m_socks5Proxy,
|
||||
[this, start_height, current_height](const char* data, size_t size)
|
||||
{
|
||||
if (parse_block_headers_range(data, size) == current_height - start_height) {
|
||||
update_median_timestamp();
|
||||
if (m_serversStarted.exchange(1) == 0) {
|
||||
try {
|
||||
m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this);
|
||||
m_ZMQReader = new ZMQReader(m_params->m_host, m_params->m_zmqPort, m_params->m_socks5Proxy, this);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
LOGERR(1, "Couldn't start ZMQ reader: exception " << e.what());
|
||||
@@ -766,7 +803,7 @@ void p2pool::stratum_on_block()
|
||||
|
||||
void p2pool::get_info()
|
||||
{
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_info\"}", m_params->m_rpcLogin,
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_info\"}", m_params->m_rpcLogin, m_params->m_socks5Proxy,
|
||||
[this](const char* data, size_t size)
|
||||
{
|
||||
parse_get_info_rpc(data, size);
|
||||
@@ -775,8 +812,10 @@ void p2pool::get_info()
|
||||
{
|
||||
if (size > 0) {
|
||||
LOGWARN(1, "get_info RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
get_info();
|
||||
if (!m_stopped) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
get_info();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -792,34 +831,39 @@ void p2pool::load_found_blocks()
|
||||
return;
|
||||
}
|
||||
|
||||
while (!f.eof()) {
|
||||
while (f.good()) {
|
||||
time_t timestamp;
|
||||
f >> timestamp;
|
||||
if (f.eof()) break;
|
||||
if (!f.good()) break;
|
||||
|
||||
uint64_t height;
|
||||
f >> height;
|
||||
if (f.eof()) break;
|
||||
if (!f.good()) break;
|
||||
|
||||
hash id;
|
||||
f >> id;
|
||||
if (f.eof()) break;
|
||||
if (!f.good()) break;
|
||||
|
||||
difficulty_type block_difficulty;
|
||||
f >> block_difficulty;
|
||||
if (f.eof()) break;
|
||||
if (!f.good()) break;
|
||||
|
||||
difficulty_type cumulative_difficulty;
|
||||
f >> cumulative_difficulty;
|
||||
if (!f.good() && !f.eof()) break;
|
||||
|
||||
m_foundBlocks.emplace_back(timestamp, height, id, block_difficulty, cumulative_difficulty);
|
||||
}
|
||||
|
||||
api_update_block_found(nullptr);
|
||||
api_update_block_found(nullptr, nullptr);
|
||||
}
|
||||
|
||||
void p2pool::parse_get_info_rpc(const char* data, size_t size)
|
||||
{
|
||||
if (m_stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
rapidjson::Document doc;
|
||||
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
|
||||
|
||||
@@ -872,7 +916,7 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
|
||||
|
||||
void p2pool::get_version()
|
||||
{
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_version\"}", m_params->m_rpcLogin,
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_version\"}", m_params->m_rpcLogin, m_params->m_socks5Proxy,
|
||||
[this](const char* data, size_t size)
|
||||
{
|
||||
parse_get_version_rpc(data, size);
|
||||
@@ -881,14 +925,20 @@ void p2pool::get_version()
|
||||
{
|
||||
if (size > 0) {
|
||||
LOGWARN(1, "get_version RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
get_version();
|
||||
if (!m_stopped) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
get_version();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void p2pool::parse_get_version_rpc(const char* data, size_t size)
|
||||
{
|
||||
if (m_stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
rapidjson::Document doc;
|
||||
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
|
||||
|
||||
@@ -918,14 +968,16 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t required = m_params->m_disableRandomX ? 0x30009 : 0x30008;
|
||||
constexpr uint64_t major = 3;
|
||||
constexpr uint64_t minor = 10;
|
||||
constexpr uint64_t required = (major << 16) | minor;
|
||||
|
||||
if (version < required) {
|
||||
const uint64_t version_hi = version >> 16;
|
||||
const uint64_t version_lo = version & 65535;
|
||||
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)");
|
||||
LOGERR(1, "monerod RPC v" << version_hi << '.' << version_lo << " is incompatible, update to RPC >= v" << required_version_hi << '.' << required_version_lo << " (Monero v0.18.0.0 or newer)");
|
||||
panic();
|
||||
}
|
||||
|
||||
@@ -936,7 +988,7 @@ void p2pool::get_miner_data()
|
||||
{
|
||||
m_getMinerDataPending = true;
|
||||
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_miner_data\"}", m_params->m_rpcLogin,
|
||||
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_miner_data\"}", m_params->m_rpcLogin, m_params->m_socks5Proxy,
|
||||
[this](const char* data, size_t size)
|
||||
{
|
||||
parse_get_miner_data_rpc(data, size);
|
||||
@@ -945,8 +997,10 @@ void p2pool::get_miner_data()
|
||||
{
|
||||
if (size > 0) {
|
||||
LOGWARN(1, "get_miner_data RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
get_miner_data();
|
||||
if (!m_stopped) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
get_miner_data();
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_getMinerDataPending = false;
|
||||
@@ -956,6 +1010,10 @@ void p2pool::get_miner_data()
|
||||
|
||||
void p2pool::parse_get_miner_data_rpc(const char* data, size_t size)
|
||||
{
|
||||
if (m_stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
hash h;
|
||||
keccak(reinterpret_cast<const uint8_t*>(data), static_cast<int>(size), h.h, HASH_SIZE);
|
||||
if (h == m_getMinerDataHash) {
|
||||
@@ -1267,22 +1325,22 @@ void p2pool::cleanup_mainchain_data(uint64_t height)
|
||||
}
|
||||
}
|
||||
|
||||
void p2pool::api_update_block_found(const ChainMain* data)
|
||||
void p2pool::api_update_block_found(const ChainMain* data, const PoolBlock* block)
|
||||
{
|
||||
clear_crypto_cache();
|
||||
|
||||
if (!m_api) {
|
||||
return;
|
||||
}
|
||||
|
||||
const time_t cur_time = time(nullptr);
|
||||
const difficulty_type total_hashes = m_sideChain->total_hashes();
|
||||
const difficulty_type total_hashes = block ? block->m_cumulativeDifficulty : m_sideChain->total_hashes();
|
||||
difficulty_type diff;
|
||||
|
||||
if (data && get_difficulty_at_height(data->height, diff)) {
|
||||
std::ofstream f(FOUND_BLOCKS_FILE, std::ios::app);
|
||||
if (f.is_open()) {
|
||||
f << cur_time << ' ' << data->height << ' ' << data->id << ' ' << diff << ' ' << total_hashes << '\n';
|
||||
f.flush();
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1348,15 +1406,6 @@ void p2pool::stop_mining()
|
||||
m_miner = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void p2pool::reset_miner()
|
||||
{
|
||||
MutexLock lock(m_minerLock);
|
||||
|
||||
if (m_miner) {
|
||||
m_miner->reset_share_counters();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void on_signal(uv_signal_t* handle, int signum)
|
||||
@@ -1399,7 +1448,7 @@ static bool init_uv_threadpool()
|
||||
return (uv_queue_work(uv_default_loop_checked(), &dummy, [](uv_work_t*) {}, nullptr) == 0);
|
||||
}
|
||||
|
||||
static bool init_signals(p2pool* pool)
|
||||
bool init_signals(p2pool* pool, bool init)
|
||||
{
|
||||
#ifdef SIGPIPE
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
@@ -1419,6 +1468,14 @@ static bool init_signals(p2pool* pool)
|
||||
|
||||
static uv_signal_t signals[array_size(signal_names)];
|
||||
|
||||
if (!init) {
|
||||
for (size_t i = 0; i < array_size(signals); ++i) {
|
||||
uv_signal_stop(&signals[i]);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&signals[i]), nullptr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < array_size(signal_names); ++i) {
|
||||
uv_signal_init(uv_default_loop_checked(), &signals[i]);
|
||||
signals[i].data = pool;
|
||||
@@ -1460,7 +1517,7 @@ void p2pool::restart_zmq()
|
||||
m_ZMQReader = nullptr;
|
||||
|
||||
try {
|
||||
m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this);
|
||||
m_ZMQReader = new ZMQReader(m_params->m_host, m_params->m_zmqPort, m_params->m_socks5Proxy, this);
|
||||
m_zmqLastActive = seconds_since_epoch();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
@@ -1470,7 +1527,7 @@ void p2pool::restart_zmq()
|
||||
|
||||
int p2pool::run()
|
||||
{
|
||||
if (!m_params->ok()) {
|
||||
if (!m_params->valid()) {
|
||||
LOGERR(1, "Invalid or missing command line. Try \"p2pool --help\".");
|
||||
return 1;
|
||||
}
|
||||
@@ -1480,7 +1537,7 @@ int p2pool::run()
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!init_signals(this)) {
|
||||
if (!init_signals(this, true)) {
|
||||
LOGERR(1, "failed to initialize signal handlers");
|
||||
return 1;
|
||||
}
|
||||
|
||||
+7
-5
@@ -33,6 +33,7 @@ class Miner;
|
||||
class ConsoleCommands;
|
||||
class p2pool_api;
|
||||
class ZMQReader;
|
||||
struct PoolBlock;
|
||||
|
||||
class p2pool : public MinerCallbackHandler
|
||||
{
|
||||
@@ -74,24 +75,23 @@ public:
|
||||
virtual void handle_chain_main(ChainMain& data, const char* extra) override;
|
||||
|
||||
void submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
|
||||
void submit_block_async(const std::vector<uint8_t>& blob);
|
||||
void submit_block_async(std::vector<uint8_t>&& blob);
|
||||
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
|
||||
|
||||
void update_block_template_async();
|
||||
void update_block_template_async(bool is_alternative_block = false);
|
||||
void update_block_template();
|
||||
|
||||
void download_block_headers(uint64_t current_height);
|
||||
|
||||
bool chainmain_get_by_hash(const hash& id, ChainMain& data) const;
|
||||
|
||||
void api_update_block_found(const ChainMain* data);
|
||||
void api_update_block_found(const ChainMain* data, const PoolBlock* block);
|
||||
|
||||
bool get_difficulty_at_height(uint64_t height, difficulty_type& diff);
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
void start_mining(uint32_t threads);
|
||||
void stop_mining();
|
||||
void reset_miner();
|
||||
#endif
|
||||
|
||||
uint64_t zmq_last_active() const { return m_zmqLastActive; }
|
||||
@@ -143,7 +143,7 @@ private:
|
||||
void get_miner_data();
|
||||
void parse_get_miner_data_rpc(const char* data, size_t size);
|
||||
|
||||
bool parse_block_header(const char* data, size_t size, ChainMain& result);
|
||||
bool parse_block_header(const char* data, size_t size, ChainMain& c);
|
||||
uint32_t parse_block_headers_range(const char* data, size_t size);
|
||||
|
||||
void api_update_network_stats();
|
||||
@@ -198,6 +198,8 @@ private:
|
||||
uv_async_t m_blockTemplateAsync;
|
||||
uv_async_t m_stopAsync;
|
||||
|
||||
std::atomic<bool> m_isAlternativeBlock{ false };
|
||||
|
||||
std::atomic<uint64_t> m_zmqLastActive;
|
||||
uint64_t m_startTime;
|
||||
uv_async_t m_restartZMQAsync;
|
||||
|
||||
+41
-27
@@ -28,7 +28,9 @@ static constexpr char log_category_prefix[] = "P2Pool API ";
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats): m_apiPath(api_path)
|
||||
p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats)
|
||||
: m_apiPath(api_path)
|
||||
, m_counter(0)
|
||||
{
|
||||
if (m_apiPath.empty()) {
|
||||
LOGERR(1, "api path is empty");
|
||||
@@ -131,14 +133,12 @@ void p2pool_api::dump_to_file()
|
||||
{
|
||||
MutexLock lock(m_dumpDataLock);
|
||||
data = std::move(m_dumpData);
|
||||
m_dumpData.clear();
|
||||
}
|
||||
|
||||
for (auto& it : data) {
|
||||
DumpFileWork* work = new DumpFileWork{ {}, {}, {}, it.first, std::move(it.second) };
|
||||
work->open_req.data = work;
|
||||
work->write_req.data = work;
|
||||
work->close_req.data = work;
|
||||
DumpFileWork* work = new DumpFileWork{ {}, 0, it.first, it.first + std::to_string(m_counter), std::move(it.second) };
|
||||
work->req.data = work;
|
||||
++m_counter;
|
||||
|
||||
const int flags = O_WRONLY | O_CREAT | O_TRUNC
|
||||
#ifdef O_BINARY
|
||||
@@ -146,9 +146,9 @@ void p2pool_api::dump_to_file()
|
||||
#endif
|
||||
;
|
||||
|
||||
const int result = uv_fs_open(uv_default_loop_checked(), &work->open_req, it.first.c_str(), flags, 0644, on_fs_open);
|
||||
const int result = uv_fs_open(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), flags, 0644, on_fs_open);
|
||||
if (result < 0) {
|
||||
LOGWARN(4, "failed to open " << it.first << ", error " << uv_err_name(result));
|
||||
LOGWARN(4, "failed to open " << work->tmp_name << ", error " << uv_err_name(result));
|
||||
delete work;
|
||||
}
|
||||
}
|
||||
@@ -157,11 +157,11 @@ void p2pool_api::dump_to_file()
|
||||
void p2pool_api::on_fs_open(uv_fs_t* req)
|
||||
{
|
||||
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
|
||||
const int fd = static_cast<int>(req->result);
|
||||
work->fd = static_cast<int>(req->result);
|
||||
uv_fs_req_cleanup(req);
|
||||
|
||||
if (fd < 0) {
|
||||
LOGWARN(4, "failed to open " << work->name << ", error " << uv_err_name(fd));
|
||||
uv_fs_req_cleanup(req);
|
||||
if (work->fd < 0) {
|
||||
LOGWARN(4, "failed to open " << work->tmp_name << ", error " << uv_err_name(work->fd));
|
||||
delete work;
|
||||
return;
|
||||
}
|
||||
@@ -170,15 +170,12 @@ void p2pool_api::on_fs_open(uv_fs_t* req)
|
||||
buf[0].base = work->buf.data();
|
||||
buf[0].len = static_cast<uint32_t>(work->buf.size());
|
||||
|
||||
const int result = uv_fs_write(uv_default_loop_checked(), &work->write_req, static_cast<uv_file>(fd), buf, 1, 0, on_fs_write);
|
||||
const int result = uv_fs_write(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), buf, 1, 0, on_fs_write);
|
||||
if (result < 0) {
|
||||
LOGWARN(4, "failed to write to " << work->name << ", error " << uv_err_name(result));
|
||||
uv_fs_req_cleanup(req);
|
||||
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
|
||||
delete work;
|
||||
return;
|
||||
}
|
||||
|
||||
uv_fs_req_cleanup(req);
|
||||
}
|
||||
|
||||
void p2pool_api::on_fs_write(uv_fs_t* req)
|
||||
@@ -186,18 +183,17 @@ void p2pool_api::on_fs_write(uv_fs_t* req)
|
||||
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
|
||||
|
||||
if (req->result < 0) {
|
||||
LOGWARN(4, "failed to write to " << work->name << ", error " << uv_err_name(static_cast<int>(req->result)));
|
||||
}
|
||||
|
||||
const int result = uv_fs_close(uv_default_loop_checked(), &work->close_req, static_cast<uv_file>(work->open_req.result), on_fs_close);
|
||||
if (result < 0) {
|
||||
LOGWARN(4, "failed to close " << work->name << ", error " << uv_err_name(result));
|
||||
uv_fs_req_cleanup(req);
|
||||
delete work;
|
||||
return;
|
||||
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(static_cast<int>(req->result)));
|
||||
}
|
||||
|
||||
uv_fs_req_cleanup(req);
|
||||
|
||||
const int result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_close);
|
||||
if (result < 0) {
|
||||
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
|
||||
delete work;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void p2pool_api::on_fs_close(uv_fs_t* req)
|
||||
@@ -205,7 +201,25 @@ void p2pool_api::on_fs_close(uv_fs_t* req)
|
||||
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
|
||||
|
||||
if (req->result < 0) {
|
||||
LOGWARN(4, "failed to close " << work->name << ", error " << uv_err_name(static_cast<int>(req->result)));
|
||||
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(static_cast<int>(req->result)));
|
||||
}
|
||||
|
||||
uv_fs_req_cleanup(req);
|
||||
|
||||
const int result = uv_fs_rename(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), work->name.c_str(), on_fs_rename);
|
||||
if (result < 0) {
|
||||
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(result));
|
||||
delete work;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void p2pool_api::on_fs_rename(uv_fs_t* req)
|
||||
{
|
||||
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
|
||||
|
||||
if (req->result < 0) {
|
||||
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(static_cast<int>(req->result)));
|
||||
}
|
||||
|
||||
uv_fs_req_cleanup(req);
|
||||
|
||||
+6
-3
@@ -46,11 +46,11 @@ private:
|
||||
static void on_dump_to_file(uv_async_t* async) { reinterpret_cast<p2pool_api*>(async->data)->dump_to_file(); }
|
||||
|
||||
struct DumpFileWork {
|
||||
uv_fs_t open_req;
|
||||
uv_fs_t write_req;
|
||||
uv_fs_t close_req;
|
||||
uv_fs_t req;
|
||||
int fd;
|
||||
|
||||
std::string name;
|
||||
std::string tmp_name;
|
||||
std::vector<char> buf;
|
||||
};
|
||||
|
||||
@@ -77,6 +77,7 @@ private:
|
||||
static void on_fs_open(uv_fs_t* req);
|
||||
static void on_fs_write(uv_fs_t* req);
|
||||
static void on_fs_close(uv_fs_t* req);
|
||||
static void on_fs_rename(uv_fs_t* req);
|
||||
|
||||
std::string m_apiPath;
|
||||
std::string m_networkPath;
|
||||
@@ -87,6 +88,8 @@ private:
|
||||
unordered_map<std::string, std::vector<char>> m_dumpData;
|
||||
|
||||
uv_async_t m_dumpToFileAsync;
|
||||
|
||||
uint64_t m_counter;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+21
-5
@@ -35,12 +35,12 @@ Params::Params(int argc, char* argv[])
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--rpc-port") == 0) && (i + 1 < argc)) {
|
||||
m_rpcPort = strtoul(argv[++i], nullptr, 10);
|
||||
m_rpcPort = std::min(std::max(strtoul(argv[++i], nullptr, 10), 1UL), 65535UL);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--zmq-port") == 0) && (i + 1 < argc)) {
|
||||
m_zmqPort = strtoul(argv[++i], nullptr, 10);
|
||||
m_zmqPort = std::min(std::max(strtoul(argv[++i], nullptr, 10), 1UL), 65535UL);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
@@ -106,12 +106,12 @@ Params::Params(int argc, char* argv[])
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--out-peers") == 0) && (i + 1 < argc)) {
|
||||
m_maxOutgoingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 1000UL);
|
||||
m_maxOutgoingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 450UL);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--in-peers") == 0) && (i + 1 < argc)) {
|
||||
m_maxIncomingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 1000UL);
|
||||
m_maxIncomingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 450UL);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
@@ -135,6 +135,22 @@ Params::Params(int argc, char* argv[])
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--socks5") == 0) && (i + 1 < argc)) {
|
||||
m_socks5Proxy = argv[++i];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--no-dns") == 0) {
|
||||
m_dns = false;
|
||||
disable_resolve_host = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((strcmp(argv[i], "--p2p-external-port") == 0) && (i + 1 < argc)) {
|
||||
m_p2pExternalPort = std::min(std::max(strtoul(argv[++i], nullptr, 10), 1UL), 65535UL);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
|
||||
p2pool_usage();
|
||||
@@ -153,7 +169,7 @@ Params::Params(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
bool Params::ok() const
|
||||
bool Params::valid() const
|
||||
{
|
||||
return !m_host.empty() && m_rpcPort && m_zmqPort && m_wallet.valid();
|
||||
}
|
||||
|
||||
+5
-2
@@ -25,7 +25,7 @@ struct Params
|
||||
{
|
||||
Params(int argc, char* argv[]);
|
||||
|
||||
bool ok() const;
|
||||
bool valid() const;
|
||||
|
||||
std::string m_host = "127.0.0.1";
|
||||
uint32_t m_rpcPort = 18081;
|
||||
@@ -45,11 +45,14 @@ struct Params
|
||||
bool m_disableRandomX = true;
|
||||
#endif
|
||||
uint32_t m_maxOutgoingPeers = 10;
|
||||
uint32_t m_maxIncomingPeers = 1000;
|
||||
uint32_t m_maxIncomingPeers = 450;
|
||||
uint32_t m_minerThreads = 0;
|
||||
bool m_mini = false;
|
||||
bool m_autoDiff = true;
|
||||
std::string m_rpcLogin;
|
||||
std::string m_socks5Proxy;
|
||||
bool m_dns = true;
|
||||
uint32_t m_p2pExternalPort = 0;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+139
-85
@@ -29,11 +29,7 @@ static constexpr char log_category_prefix[] = "PoolBlock ";
|
||||
namespace p2pool {
|
||||
|
||||
PoolBlock::PoolBlock()
|
||||
: m_mainChainHeaderSize(0)
|
||||
, m_mainChainMinerTxSize(0)
|
||||
, m_mainChainOutputsOffset(0)
|
||||
, m_mainChainOutputsBlobSize(0)
|
||||
, m_majorVersion(0)
|
||||
: m_majorVersion(0)
|
||||
, m_minorVersion(0)
|
||||
, m_timestamp(0)
|
||||
, m_prevId{}
|
||||
@@ -53,16 +49,10 @@ PoolBlock::PoolBlock()
|
||||
, m_invalid(false)
|
||||
, m_broadcasted(false)
|
||||
, m_wantBroadcast(false)
|
||||
, m_precalculated(false)
|
||||
, m_localTimestamp(seconds_since_epoch())
|
||||
{
|
||||
uv_mutex_init_checked(&m_lock);
|
||||
|
||||
m_mainChainData.reserve(48 * 1024);
|
||||
m_outputs.reserve(2048);
|
||||
m_transactions.reserve(256);
|
||||
m_sideChainData.reserve(512);
|
||||
m_uncles.reserve(8);
|
||||
m_tmpTxExtra.reserve(80);
|
||||
}
|
||||
|
||||
PoolBlock::PoolBlock(const PoolBlock& b)
|
||||
@@ -83,11 +73,11 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
|
||||
LOGERR(1, "operator= uv_mutex_trylock failed. Fix the code!");
|
||||
}
|
||||
|
||||
m_mainChainData = b.m_mainChainData;
|
||||
m_mainChainHeaderSize = b.m_mainChainHeaderSize;
|
||||
m_mainChainMinerTxSize = b.m_mainChainMinerTxSize;
|
||||
m_mainChainOutputsOffset = b.m_mainChainOutputsOffset;
|
||||
m_mainChainOutputsBlobSize = b.m_mainChainOutputsBlobSize;
|
||||
#if POOL_BLOCK_DEBUG
|
||||
m_mainChainDataDebug = b.m_mainChainDataDebug;
|
||||
m_sideChainDataDebug = b.m_sideChainDataDebug;
|
||||
#endif
|
||||
|
||||
m_majorVersion = b.m_majorVersion;
|
||||
m_minorVersion = b.m_minorVersion;
|
||||
m_timestamp = b.m_timestamp;
|
||||
@@ -99,7 +89,6 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
|
||||
m_extraNonceSize = b.m_extraNonceSize;
|
||||
m_extraNonce = b.m_extraNonce;
|
||||
m_transactions = b.m_transactions;
|
||||
m_sideChainData = b.m_sideChainData;
|
||||
m_minerWallet = b.m_minerWallet;
|
||||
m_txkeySec = b.m_txkeySec;
|
||||
m_parent = b.m_parent;
|
||||
@@ -108,12 +97,12 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
|
||||
m_difficulty = b.m_difficulty;
|
||||
m_cumulativeDifficulty = b.m_cumulativeDifficulty;
|
||||
m_sidechainId = b.m_sidechainId;
|
||||
m_tmpTxExtra = b.m_tmpTxExtra;
|
||||
m_depth = b.m_depth;
|
||||
m_verified = b.m_verified;
|
||||
m_invalid = b.m_invalid;
|
||||
m_broadcasted = b.m_broadcasted;
|
||||
m_wantBroadcast = b.m_wantBroadcast;
|
||||
m_precalculated = b.m_precalculated;
|
||||
|
||||
m_localTimestamp = seconds_since_epoch();
|
||||
|
||||
@@ -129,104 +118,164 @@ PoolBlock::~PoolBlock()
|
||||
uv_mutex_destroy(&m_lock);
|
||||
}
|
||||
|
||||
void PoolBlock::serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash)
|
||||
std::vector<uint8_t> PoolBlock::serialize_mainchain_data(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const
|
||||
{
|
||||
MutexLock lock(m_lock);
|
||||
return serialize_mainchain_data_nolock(header_size, miner_tx_size, outputs_offset, outputs_blob_size);
|
||||
}
|
||||
|
||||
m_mainChainData.clear();
|
||||
std::vector<uint8_t> PoolBlock::serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
data.reserve(128 + m_outputs.size() * 39 + m_transactions.size() * HASH_SIZE);
|
||||
|
||||
// Header
|
||||
m_mainChainData.push_back(m_majorVersion);
|
||||
m_mainChainData.push_back(m_minorVersion);
|
||||
writeVarint(m_timestamp, m_mainChainData);
|
||||
m_mainChainData.insert(m_mainChainData.end(), m_prevId.h, m_prevId.h + HASH_SIZE);
|
||||
m_mainChainData.insert(m_mainChainData.end(), reinterpret_cast<uint8_t*>(&nonce), reinterpret_cast<uint8_t*>(&nonce) + NONCE_SIZE);
|
||||
data.push_back(m_majorVersion);
|
||||
data.push_back(m_minorVersion);
|
||||
writeVarint(m_timestamp, data);
|
||||
data.insert(data.end(), m_prevId.h, m_prevId.h + HASH_SIZE);
|
||||
data.insert(data.end(), reinterpret_cast<const uint8_t*>(&m_nonce), reinterpret_cast<const uint8_t*>(&m_nonce) + NONCE_SIZE);
|
||||
|
||||
m_mainChainHeaderSize = m_mainChainData.size();
|
||||
const size_t header_size0 = data.size();
|
||||
if (header_size) {
|
||||
*header_size = header_size0;
|
||||
}
|
||||
|
||||
// Miner tx
|
||||
m_mainChainData.push_back(TX_VERSION);
|
||||
writeVarint(m_txinGenHeight + MINER_REWARD_UNLOCK_TIME, m_mainChainData);
|
||||
m_mainChainData.push_back(1);
|
||||
m_mainChainData.push_back(TXIN_GEN);
|
||||
writeVarint(m_txinGenHeight, m_mainChainData);
|
||||
data.push_back(TX_VERSION);
|
||||
writeVarint(m_txinGenHeight + MINER_REWARD_UNLOCK_TIME, data);
|
||||
data.push_back(1);
|
||||
data.push_back(TXIN_GEN);
|
||||
writeVarint(m_txinGenHeight, data);
|
||||
|
||||
m_mainChainOutputsOffset = static_cast<int>(m_mainChainData.size());
|
||||
const int outputs_offset0 = static_cast<int>(data.size());
|
||||
if (outputs_offset) {
|
||||
*outputs_offset = outputs_offset0;
|
||||
}
|
||||
|
||||
writeVarint(m_outputs.size(), m_mainChainData);
|
||||
writeVarint(m_outputs.size(), data);
|
||||
|
||||
for (TxOutput& output : m_outputs) {
|
||||
writeVarint(output.m_reward, m_mainChainData);
|
||||
m_mainChainData.push_back(output.m_txType);
|
||||
m_mainChainData.insert(m_mainChainData.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
|
||||
const uint8_t tx_type = get_tx_type();
|
||||
|
||||
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
|
||||
m_mainChainData.push_back(output.m_viewTag);
|
||||
for (const TxOutput& output : m_outputs) {
|
||||
writeVarint(output.m_reward, data);
|
||||
data.push_back(tx_type);
|
||||
data.insert(data.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
|
||||
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
data.push_back(static_cast<uint8_t>(output.m_viewTag));
|
||||
}
|
||||
}
|
||||
|
||||
m_mainChainOutputsBlobSize = static_cast<int>(m_mainChainData.size()) - m_mainChainOutputsOffset;
|
||||
|
||||
m_tmpTxExtra.clear();
|
||||
|
||||
m_tmpTxExtra.push_back(TX_EXTRA_TAG_PUBKEY);
|
||||
m_tmpTxExtra.insert(m_tmpTxExtra.end(), m_txkeyPub.h, m_txkeyPub.h + HASH_SIZE);
|
||||
|
||||
m_tmpTxExtra.push_back(TX_EXTRA_NONCE);
|
||||
writeVarint(m_extraNonceSize, m_tmpTxExtra);
|
||||
|
||||
m_extraNonce = extra_nonce;
|
||||
m_tmpTxExtra.insert(m_tmpTxExtra.end(), reinterpret_cast<uint8_t*>(&m_extraNonce), reinterpret_cast<uint8_t*>(&m_extraNonce) + EXTRA_NONCE_SIZE);
|
||||
if (m_extraNonceSize > EXTRA_NONCE_SIZE) {
|
||||
m_tmpTxExtra.insert(m_tmpTxExtra.end(), m_extraNonceSize - EXTRA_NONCE_SIZE, 0);
|
||||
if (outputs_blob_size) {
|
||||
*outputs_blob_size = static_cast<int>(data.size()) - outputs_offset0;
|
||||
}
|
||||
|
||||
m_tmpTxExtra.push_back(TX_EXTRA_MERGE_MINING_TAG);
|
||||
writeVarint(HASH_SIZE, m_tmpTxExtra);
|
||||
m_tmpTxExtra.insert(m_tmpTxExtra.end(), sidechain_hash.h, sidechain_hash.h + HASH_SIZE);
|
||||
uint8_t tx_extra[128];
|
||||
uint8_t* p = tx_extra;
|
||||
|
||||
writeVarint(m_tmpTxExtra.size(), m_mainChainData);
|
||||
m_mainChainData.insert(m_mainChainData.end(), m_tmpTxExtra.begin(), m_tmpTxExtra.end());
|
||||
*(p++) = TX_EXTRA_TAG_PUBKEY;
|
||||
memcpy(p, m_txkeyPub.h, HASH_SIZE);
|
||||
p += HASH_SIZE;
|
||||
|
||||
m_tmpTxExtra.clear();
|
||||
uint64_t extra_nonce_size = m_extraNonceSize;
|
||||
if (extra_nonce_size > EXTRA_NONCE_MAX_SIZE) {
|
||||
LOGERR(1, "extra nonce size is too large (" << extra_nonce_size << "), fix the code!");
|
||||
extra_nonce_size = EXTRA_NONCE_MAX_SIZE;
|
||||
}
|
||||
|
||||
m_mainChainData.push_back(0);
|
||||
*(p++) = TX_EXTRA_NONCE;
|
||||
*(p++) = static_cast<uint8_t>(extra_nonce_size);
|
||||
|
||||
m_mainChainMinerTxSize = m_mainChainData.size() - m_mainChainHeaderSize;
|
||||
memcpy(p, &m_extraNonce, EXTRA_NONCE_SIZE);
|
||||
p += EXTRA_NONCE_SIZE;
|
||||
if (extra_nonce_size > EXTRA_NONCE_SIZE) {
|
||||
memset(p, 0, extra_nonce_size - EXTRA_NONCE_SIZE);
|
||||
p += extra_nonce_size - EXTRA_NONCE_SIZE;
|
||||
}
|
||||
|
||||
writeVarint(m_transactions.size() - 1, m_mainChainData);
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(m_transactions.data());
|
||||
m_mainChainData.insert(m_mainChainData.end(), data + HASH_SIZE, data + m_transactions.size() * HASH_SIZE);
|
||||
*(p++) = TX_EXTRA_MERGE_MINING_TAG;
|
||||
*(p++) = HASH_SIZE;
|
||||
memcpy(p, m_sidechainId.h, HASH_SIZE);
|
||||
p += HASH_SIZE;
|
||||
|
||||
writeVarint(static_cast<size_t>(p - tx_extra), data);
|
||||
data.insert(data.end(), tx_extra, p);
|
||||
|
||||
data.push_back(0);
|
||||
|
||||
if (miner_tx_size) {
|
||||
*miner_tx_size = data.size() - header_size0;
|
||||
}
|
||||
|
||||
writeVarint(m_transactions.size() - 1, data);
|
||||
const uint8_t* t = reinterpret_cast<const uint8_t*>(m_transactions.data());
|
||||
data.insert(data.end(), t + HASH_SIZE, t + m_transactions.size() * HASH_SIZE);
|
||||
|
||||
#if POOL_BLOCK_DEBUG
|
||||
if (!m_mainChainDataDebug.empty() && (data != m_mainChainDataDebug)) {
|
||||
LOGERR(1, "serialize_mainchain_data() has a bug, fix it!");
|
||||
panic();
|
||||
}
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void PoolBlock::serialize_sidechain_data()
|
||||
std::vector<uint8_t> PoolBlock::serialize_sidechain_data() const
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
MutexLock lock(m_lock);
|
||||
|
||||
m_sideChainData.clear();
|
||||
m_sideChainData.reserve((m_uncles.size() + 4) * HASH_SIZE + 11);
|
||||
data.reserve((m_uncles.size() + 4) * HASH_SIZE + 20);
|
||||
|
||||
const hash& spend = m_minerWallet.spend_public_key();
|
||||
const hash& view = m_minerWallet.view_public_key();
|
||||
|
||||
m_sideChainData.insert(m_sideChainData.end(), spend.h, spend.h + HASH_SIZE);
|
||||
m_sideChainData.insert(m_sideChainData.end(), view.h, view.h + HASH_SIZE);
|
||||
m_sideChainData.insert(m_sideChainData.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE);
|
||||
m_sideChainData.insert(m_sideChainData.end(), m_parent.h, m_parent.h + HASH_SIZE);
|
||||
data.insert(data.end(), spend.h, spend.h + HASH_SIZE);
|
||||
data.insert(data.end(), view.h, view.h + HASH_SIZE);
|
||||
data.insert(data.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE);
|
||||
data.insert(data.end(), m_parent.h, m_parent.h + HASH_SIZE);
|
||||
|
||||
writeVarint(m_uncles.size(), m_sideChainData);
|
||||
writeVarint(m_uncles.size(), data);
|
||||
|
||||
for (const hash& id : m_uncles) {
|
||||
m_sideChainData.insert(m_sideChainData.end(), id.h, id.h + HASH_SIZE);
|
||||
data.insert(data.end(), id.h, id.h + HASH_SIZE);
|
||||
}
|
||||
|
||||
writeVarint(m_sidechainHeight, m_sideChainData);
|
||||
writeVarint(m_sidechainHeight, data);
|
||||
|
||||
writeVarint(m_difficulty.lo, m_sideChainData);
|
||||
writeVarint(m_difficulty.hi, m_sideChainData);
|
||||
writeVarint(m_difficulty.lo, data);
|
||||
writeVarint(m_difficulty.hi, data);
|
||||
|
||||
writeVarint(m_cumulativeDifficulty.lo, m_sideChainData);
|
||||
writeVarint(m_cumulativeDifficulty.hi, m_sideChainData);
|
||||
writeVarint(m_cumulativeDifficulty.lo, data);
|
||||
writeVarint(m_cumulativeDifficulty.hi, data);
|
||||
|
||||
#if POOL_BLOCK_DEBUG
|
||||
if (!m_sideChainDataDebug.empty() && (data != m_sideChainDataDebug)) {
|
||||
LOGERR(1, "serialize_sidechain_data() has a bug, fix it!");
|
||||
panic();
|
||||
}
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void PoolBlock::reset_offchain_data()
|
||||
{
|
||||
// Defaults for off-chain variables
|
||||
m_depth = 0;
|
||||
|
||||
m_verified = false;
|
||||
m_invalid = false;
|
||||
|
||||
m_broadcasted = false;
|
||||
m_wantBroadcast = false;
|
||||
|
||||
m_precalculated = false;
|
||||
|
||||
m_localTimestamp = seconds_since_epoch();
|
||||
}
|
||||
|
||||
bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash)
|
||||
@@ -249,16 +298,19 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
|
||||
{
|
||||
MutexLock lock(m_lock);
|
||||
|
||||
if (!m_mainChainHeaderSize || !m_mainChainMinerTxSize || (m_mainChainData.size() < m_mainChainHeaderSize + m_mainChainMinerTxSize)) {
|
||||
size_t header_size, miner_tx_size;
|
||||
const std::vector<uint8_t> mainchain_data = serialize_mainchain_data_nolock(&header_size, &miner_tx_size, nullptr, nullptr);
|
||||
|
||||
if (!header_size || !miner_tx_size || (mainchain_data.size() < header_size + miner_tx_size)) {
|
||||
LOGERR(1, "tried to calculate PoW of uninitialized block");
|
||||
return false;
|
||||
}
|
||||
|
||||
blob_size = m_mainChainHeaderSize;
|
||||
memcpy(blob, m_mainChainData.data(), blob_size);
|
||||
blob_size = header_size;
|
||||
memcpy(blob, mainchain_data.data(), blob_size);
|
||||
|
||||
uint8_t* miner_tx = m_mainChainData.data() + m_mainChainHeaderSize;
|
||||
keccak(miner_tx, static_cast<int>(m_mainChainMinerTxSize) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE);
|
||||
const uint8_t* miner_tx = mainchain_data.data() + header_size;
|
||||
keccak(miner_tx, static_cast<int>(miner_tx_size) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE);
|
||||
|
||||
count = m_transactions.size();
|
||||
uint8_t* h = reinterpret_cast<uint8_t*>(m_transactions.data());
|
||||
@@ -304,11 +356,13 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
|
||||
|
||||
uint64_t PoolBlock::get_payout(const Wallet& w) const
|
||||
{
|
||||
const uint8_t tx_type = get_tx_type();
|
||||
|
||||
for (size_t i = 0, n = m_outputs.size(); i < n; ++i) {
|
||||
const TxOutput& out = m_outputs[i];
|
||||
hash eph_public_key;
|
||||
|
||||
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
if (w.get_eph_public_key_with_view_tag(m_txkeySec, i, eph_public_key, out.m_viewTag) && (eph_public_key == out.m_ephPublicKey)) {
|
||||
return out.m_reward;
|
||||
}
|
||||
|
||||
+21
-21
@@ -63,13 +63,12 @@ struct PoolBlock
|
||||
|
||||
mutable uv_mutex_t m_lock;
|
||||
|
||||
// Monero block template
|
||||
std::vector<uint8_t> m_mainChainData;
|
||||
size_t m_mainChainHeaderSize;
|
||||
size_t m_mainChainMinerTxSize;
|
||||
int m_mainChainOutputsOffset;
|
||||
int m_mainChainOutputsBlobSize;
|
||||
#if POOL_BLOCK_DEBUG
|
||||
std::vector<uint8_t> m_mainChainDataDebug;
|
||||
std::vector<uint8_t> m_sideChainDataDebug;
|
||||
#endif
|
||||
|
||||
// Monero block template
|
||||
uint8_t m_majorVersion;
|
||||
uint8_t m_minorVersion;
|
||||
uint64_t m_timestamp;
|
||||
@@ -81,15 +80,16 @@ struct PoolBlock
|
||||
|
||||
struct TxOutput
|
||||
{
|
||||
FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey(), m_txType(0), m_viewTag(0) {}
|
||||
FORCEINLINE TxOutput(uint64_t r, const hash& k, uint8_t tx_type, uint8_t view_tag) : m_reward(r), m_ephPublicKey(k), m_txType(tx_type), m_viewTag(view_tag) {}
|
||||
FORCEINLINE TxOutput() : m_ephPublicKey(), m_reward(0), m_viewTag(0) {}
|
||||
FORCEINLINE TxOutput(uint64_t r, const hash& k, uint8_t view_tag) : m_ephPublicKey(k), m_reward(r), m_viewTag(view_tag) {}
|
||||
|
||||
uint64_t m_reward;
|
||||
hash m_ephPublicKey;
|
||||
uint8_t m_txType;
|
||||
uint8_t m_viewTag;
|
||||
uint64_t m_reward : 56;
|
||||
uint64_t m_viewTag : 8;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TxOutput) == sizeof(hash) + sizeof(uint64_t), "TxOutput bit packing didn't work with this compiler, fix the code!");
|
||||
|
||||
std::vector<TxOutput> m_outputs;
|
||||
|
||||
hash m_txkeyPub;
|
||||
@@ -99,9 +99,6 @@ struct PoolBlock
|
||||
// All block transaction hashes including the miner transaction hash at index 0
|
||||
std::vector<hash> m_transactions;
|
||||
|
||||
// Side-chain data
|
||||
std::vector<uint8_t> m_sideChainData;
|
||||
|
||||
// Miner's wallet
|
||||
Wallet m_minerWallet{ nullptr };
|
||||
|
||||
@@ -122,22 +119,25 @@ struct PoolBlock
|
||||
hash m_sidechainId;
|
||||
|
||||
// Just temporary stuff, not a part of the block
|
||||
std::vector<uint8_t> m_tmpTxExtra;
|
||||
|
||||
uint64_t m_depth;
|
||||
|
||||
bool m_verified;
|
||||
bool m_invalid;
|
||||
|
||||
bool m_broadcasted;
|
||||
bool m_wantBroadcast;
|
||||
mutable bool m_broadcasted;
|
||||
mutable bool m_wantBroadcast;
|
||||
|
||||
bool m_precalculated;
|
||||
|
||||
uint64_t m_localTimestamp;
|
||||
|
||||
void serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash);
|
||||
void serialize_sidechain_data();
|
||||
std::vector<uint8_t> serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr) const;
|
||||
std::vector<uint8_t> serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const;
|
||||
std::vector<uint8_t> serialize_sidechain_data() const;
|
||||
|
||||
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop);
|
||||
void reset_offchain_data();
|
||||
|
||||
int deserialize(const uint8_t* data, size_t size, SideChain& sidechain);
|
||||
bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash);
|
||||
|
||||
uint64_t get_payout(const Wallet& w) const;
|
||||
|
||||
+38
-47
@@ -23,7 +23,7 @@ namespace p2pool {
|
||||
// Since data here can come from external and possibly malicious sources, check everything
|
||||
// Only the syntax (i.e. the serialized block binary format) and the keccak hash are checked here
|
||||
// Semantics must also be checked elsewhere before accepting the block (PoW, reward split between miners, difficulty calculation and so on)
|
||||
int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechain)
|
||||
int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop)
|
||||
{
|
||||
try {
|
||||
// Sanity check
|
||||
@@ -75,8 +75,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
const int nonce_offset = static_cast<int>(data - data_begin);
|
||||
READ_BUF(&m_nonce, NONCE_SIZE);
|
||||
|
||||
m_mainChainHeaderSize = data - data_begin;
|
||||
|
||||
EXPECT_BYTE(TX_VERSION);
|
||||
|
||||
uint64_t unlock_height;
|
||||
@@ -89,12 +87,13 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
if (unlock_height != m_txinGenHeight + MINER_REWARD_UNLOCK_TIME) return __LINE__;
|
||||
|
||||
std::vector<uint8_t> outputs_blob;
|
||||
m_mainChainOutputsOffset = static_cast<int>(data - data_begin);
|
||||
const int outputs_offset = static_cast<int>(data - data_begin);
|
||||
|
||||
uint64_t num_outputs;
|
||||
READ_VARINT(num_outputs);
|
||||
|
||||
uint64_t total_reward = 0;
|
||||
int outputs_blob_size;
|
||||
|
||||
if (num_outputs > 0) {
|
||||
// Outputs are in the buffer, just read them
|
||||
@@ -105,31 +104,32 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
if (num_outputs > std::numeric_limits<uint64_t>::max() / MIN_OUTPUT_SIZE) return __LINE__;
|
||||
if (static_cast<uint64_t>(data_end - data) < num_outputs * MIN_OUTPUT_SIZE) return __LINE__;
|
||||
|
||||
m_outputs.clear();
|
||||
m_outputs.reserve(num_outputs);
|
||||
m_outputs.resize(num_outputs);
|
||||
m_outputs.shrink_to_fit();
|
||||
|
||||
const uint8_t expected_tx_type = get_tx_type();
|
||||
|
||||
for (uint64_t i = 0; i < num_outputs; ++i) {
|
||||
TxOutput t;
|
||||
TxOutput& t = m_outputs[i];
|
||||
|
||||
READ_VARINT(t.m_reward);
|
||||
total_reward += t.m_reward;
|
||||
uint64_t reward;
|
||||
READ_VARINT(reward);
|
||||
t.m_reward = reward;
|
||||
total_reward += reward;
|
||||
|
||||
EXPECT_BYTE(expected_tx_type);
|
||||
t.m_txType = expected_tx_type;
|
||||
|
||||
READ_BUF(t.m_ephPublicKey.h, HASH_SIZE);
|
||||
|
||||
if (expected_tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
READ_BYTE(t.m_viewTag);
|
||||
uint8_t view_tag;
|
||||
READ_BYTE(view_tag);
|
||||
t.m_viewTag = view_tag;
|
||||
}
|
||||
|
||||
m_outputs.emplace_back(std::move(t));
|
||||
}
|
||||
|
||||
m_mainChainOutputsBlobSize = static_cast<int>(data - data_begin) - m_mainChainOutputsOffset;
|
||||
outputs_blob.assign(data_begin + m_mainChainOutputsOffset, data);
|
||||
outputs_blob_size = static_cast<int>(data - data_begin) - outputs_offset;
|
||||
outputs_blob.assign(data_begin + outputs_offset, data);
|
||||
}
|
||||
else {
|
||||
// Outputs are not in the buffer and must be calculated from sidechain data
|
||||
@@ -144,7 +144,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
m_mainChainOutputsBlobSize = static_cast<int>(tmp);
|
||||
outputs_blob_size = static_cast<int>(tmp);
|
||||
}
|
||||
|
||||
// Technically some p2pool node could keep stuffing block with transactions until reward is less than 0.6 XMR
|
||||
@@ -153,13 +153,12 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
const int outputs_actual_blob_size = static_cast<int>(data - data_begin) - m_mainChainOutputsOffset;
|
||||
|
||||
if (m_mainChainOutputsBlobSize < outputs_actual_blob_size) {
|
||||
const int outputs_actual_blob_size = static_cast<int>(data - data_begin) - outputs_offset;
|
||||
if (outputs_blob_size < outputs_actual_blob_size) {
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
const int outputs_blob_size_diff = m_mainChainOutputsBlobSize - outputs_actual_blob_size;
|
||||
const int outputs_blob_size_diff = outputs_blob_size - outputs_actual_blob_size;
|
||||
|
||||
uint64_t tx_extra_size;
|
||||
READ_VARINT(tx_extra_size);
|
||||
@@ -191,8 +190,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
|
||||
EXPECT_BYTE(0);
|
||||
|
||||
m_mainChainMinerTxSize = (data - data_begin) + outputs_blob_size_diff - m_mainChainHeaderSize;
|
||||
|
||||
uint64_t num_transactions;
|
||||
READ_VARINT(num_transactions);
|
||||
|
||||
@@ -208,12 +205,14 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
m_transactions.emplace_back(std::move(id));
|
||||
}
|
||||
|
||||
m_mainChainData.reserve((data - data_begin) + outputs_blob_size_diff);
|
||||
m_mainChainData.assign(data_begin, data_begin + m_mainChainOutputsOffset);
|
||||
m_mainChainData.insert(m_mainChainData.end(), m_mainChainOutputsBlobSize, 0);
|
||||
m_mainChainData.insert(m_mainChainData.end(), data_begin + m_mainChainOutputsOffset + outputs_actual_blob_size, data);
|
||||
#if POOL_BLOCK_DEBUG
|
||||
m_mainChainDataDebug.reserve((data - data_begin) + outputs_blob_size_diff);
|
||||
m_mainChainDataDebug.assign(data_begin, data_begin + outputs_offset);
|
||||
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), outputs_blob_size, 0);
|
||||
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), data_begin + outputs_offset + outputs_actual_blob_size, data);
|
||||
|
||||
const uint8_t* sidechain_data_begin = data;
|
||||
#endif
|
||||
|
||||
hash spend_pub_key;
|
||||
hash view_pub_key;
|
||||
@@ -272,20 +271,22 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
if ((num_outputs == 0) && !sidechain.get_outputs_blob(this, total_reward, outputs_blob)) {
|
||||
if ((num_outputs == 0) && !sidechain.get_outputs_blob(this, total_reward, outputs_blob, loop)) {
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
if (static_cast<int>(outputs_blob.size()) != m_mainChainOutputsBlobSize) {
|
||||
if (static_cast<int>(outputs_blob.size()) != outputs_blob_size) {
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
memcpy(m_mainChainData.data() + m_mainChainOutputsOffset, outputs_blob.data(), m_mainChainOutputsBlobSize);
|
||||
#if POOL_BLOCK_DEBUG
|
||||
memcpy(m_mainChainDataDebug.data() + outputs_offset, outputs_blob.data(), outputs_blob_size);
|
||||
#endif
|
||||
|
||||
hash check;
|
||||
const std::vector<uint8_t>& consensus_id = sidechain.consensus_id();
|
||||
keccak_custom(
|
||||
[this, nonce_offset, extra_nonce_offset, sidechain_hash_offset, data_begin, data_end, &consensus_id, &outputs_blob, outputs_blob_size_diff](int offset) -> uint8_t
|
||||
[nonce_offset, extra_nonce_offset, sidechain_hash_offset, data_begin, data_end, &consensus_id, &outputs_blob, outputs_blob_size_diff, outputs_offset, outputs_blob_size](int offset) -> uint8_t
|
||||
{
|
||||
uint32_t k = static_cast<uint32_t>(offset - nonce_offset);
|
||||
if (k < NONCE_SIZE) {
|
||||
@@ -304,11 +305,11 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
|
||||
const int data_size = static_cast<int>((data_end - data_begin) + outputs_blob_size_diff);
|
||||
if (offset < data_size) {
|
||||
if (offset < m_mainChainOutputsOffset) {
|
||||
if (offset < outputs_offset) {
|
||||
return data_begin[offset];
|
||||
}
|
||||
else if (offset < m_mainChainOutputsOffset + m_mainChainOutputsBlobSize) {
|
||||
const int tmp = offset - m_mainChainOutputsOffset;
|
||||
else if (offset < outputs_offset + outputs_blob_size) {
|
||||
const int tmp = offset - outputs_offset;
|
||||
return outputs_blob[tmp];
|
||||
}
|
||||
else {
|
||||
@@ -325,7 +326,9 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
m_sideChainData.assign(sidechain_data_begin, data_end);
|
||||
#if POOL_BLOCK_DEBUG
|
||||
m_sideChainDataDebug.assign(sidechain_data_begin, data_end);
|
||||
#endif
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
const char* msg = e.what();
|
||||
@@ -333,19 +336,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
// Defaults for off-chain variables
|
||||
m_tmpTxExtra.clear();
|
||||
|
||||
m_depth = 0;
|
||||
|
||||
m_verified = false;
|
||||
m_invalid = false;
|
||||
|
||||
m_broadcasted = false;
|
||||
m_wantBroadcast = false;
|
||||
|
||||
m_localTimestamp = seconds_since_epoch();
|
||||
|
||||
reset_offchain_data();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+14
-8
@@ -389,11 +389,7 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
|
||||
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();
|
||||
}
|
||||
uv_cond_init_checked(&m_cond);
|
||||
|
||||
err = uv_thread_create(&m_loopThread, loop, this);
|
||||
if (err) {
|
||||
@@ -417,9 +413,19 @@ RandomX_Hasher_RPC::~RandomX_Hasher_RPC()
|
||||
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);
|
||||
|
||||
int err = uv_run(&hasher->m_loop, UV_RUN_DEFAULT);
|
||||
if (err) {
|
||||
LOGWARN(1, "uv_run returned " << err);
|
||||
}
|
||||
|
||||
err = uv_loop_close(&hasher->m_loop);
|
||||
if (err) {
|
||||
LOGWARN(1, "uv_loop_close returned error " << uv_err_name(err));
|
||||
}
|
||||
|
||||
LOGINFO(1, "event loop stopped");
|
||||
}
|
||||
|
||||
@@ -442,7 +448,7 @@ bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t h
|
||||
|
||||
const Params& params = m_pool->params();
|
||||
|
||||
JSONRPCRequest::call(params.m_host, params.m_rpcPort, buf, params.m_rpcLogin,
|
||||
JSONRPCRequest::call(params.m_host, params.m_rpcPort, buf, params.m_rpcLogin, params.m_socks5Proxy,
|
||||
[&result, &h](const char* data, size_t size)
|
||||
{
|
||||
rapidjson::Document doc;
|
||||
|
||||
+1
-1
@@ -100,7 +100,7 @@ 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;
|
||||
bool calculate(const void* data_ptr, size_t size, uint64_t height, const hash& seed, hash& h) override;
|
||||
|
||||
private:
|
||||
static void loop(void* data);
|
||||
|
||||
+326
-62
@@ -32,19 +32,13 @@
|
||||
#include "stratum_server.h"
|
||||
#include "params.h"
|
||||
#include "json_parsers.h"
|
||||
#include "crypto.h"
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/istreamwrapper.h>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
|
||||
// Only uncomment it to debug issues with uncle/orphan blocks
|
||||
//#define DEBUG_BROADCAST_DELAY_MS 100
|
||||
|
||||
#ifdef DEBUG_BROADCAST_DELAY_MS
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
static constexpr char log_category_prefix[] = "SideChain ";
|
||||
|
||||
static constexpr uint64_t MIN_DIFFICULTY = 100000;
|
||||
@@ -70,6 +64,7 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
|
||||
, m_chainWindowSize(2160)
|
||||
, m_unclePenalty(20)
|
||||
, m_curDifficulty(m_minDifficulty)
|
||||
, m_precalcFinished(false)
|
||||
{
|
||||
LOGINFO(1, log::LightCyan() << "network type = " << m_networkType);
|
||||
|
||||
@@ -161,14 +156,45 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
|
||||
memset(buf + 8, '*', HASH_SIZE * 2 - 16);
|
||||
m_consensusIdDisplayStr.assign(buf);
|
||||
LOGINFO(1, "consensus ID = " << log::LightCyan() << m_consensusIdDisplayStr.c_str());
|
||||
|
||||
uv_cond_init_checked(&m_precalcJobsCond);
|
||||
uv_mutex_init_checked(&m_precalcJobsMutex);
|
||||
m_precalcJobs.reserve(16);
|
||||
|
||||
uint32_t numThreads = std::thread::hardware_concurrency();
|
||||
|
||||
// Leave 1 CPU core free from worker threads
|
||||
if (numThreads > 1) {
|
||||
--numThreads;
|
||||
}
|
||||
|
||||
// Use between 1 and 8 threads
|
||||
if (numThreads < 1) numThreads = 1;
|
||||
|
||||
// Don't limit thread count when debugging because debug builds are slow
|
||||
#ifndef P2POOL_DEBUGGING
|
||||
if (numThreads > 8) numThreads = 8;
|
||||
#endif
|
||||
|
||||
LOGINFO(4, "running " << numThreads << " pre-calculation workers");
|
||||
|
||||
m_precalcWorkers.reserve(numThreads);
|
||||
for (uint32_t i = 0; i < numThreads; ++i) {
|
||||
m_precalcWorkers.emplace_back(&SideChain::precalc_worker, this);
|
||||
}
|
||||
|
||||
m_uniquePrecalcInputs = new unordered_set<size_t>();
|
||||
}
|
||||
|
||||
SideChain::~SideChain()
|
||||
{
|
||||
finish_precalc();
|
||||
|
||||
uv_rwlock_destroy(&m_sidechainLock);
|
||||
uv_mutex_destroy(&m_seenWalletsLock);
|
||||
uv_mutex_destroy(&m_seenBlocksLock);
|
||||
uv_rwlock_destroy(&m_curDifficultyLock);
|
||||
|
||||
for (const auto& it : m_blocksById) {
|
||||
delete it.second;
|
||||
}
|
||||
@@ -289,7 +315,7 @@ 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(const PoolBlock* tip, std::vector<MinerShare>& shares) const
|
||||
{
|
||||
shares.clear();
|
||||
shares.reserve(m_chainWindowSize * 2);
|
||||
@@ -297,7 +323,7 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
|
||||
// Collect shares from each block in the PPLNS window, starting from the "tip"
|
||||
|
||||
uint64_t block_depth = 0;
|
||||
PoolBlock* cur = tip;
|
||||
const PoolBlock* cur = tip;
|
||||
do {
|
||||
MinerShare cur_share{ cur->m_difficulty.lo, &cur->m_minerWallet };
|
||||
|
||||
@@ -371,6 +397,50 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SideChain::get_wallets(const PoolBlock* tip, std::vector<const Wallet*>& wallets) const
|
||||
{
|
||||
// Collect wallets from each block in the PPLNS window, starting from the "tip"
|
||||
wallets.clear();
|
||||
wallets.reserve(m_chainWindowSize * 2);
|
||||
|
||||
uint64_t block_depth = 0;
|
||||
const PoolBlock* cur = tip;
|
||||
|
||||
do {
|
||||
wallets.push_back(&cur->m_minerWallet);
|
||||
|
||||
for (const hash& uncle_id : cur->m_uncles) {
|
||||
auto it = m_blocksById.find(uncle_id);
|
||||
if (it == m_blocksById.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip uncles which are already out of PPLNS window
|
||||
if (tip->m_sidechainHeight - it->second->m_sidechainHeight < m_chainWindowSize) {
|
||||
wallets.push_back(&it->second->m_minerWallet);
|
||||
}
|
||||
}
|
||||
|
||||
++block_depth;
|
||||
if ((block_depth >= m_chainWindowSize) || (cur->m_sidechainHeight == 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto it = m_blocksById.find(cur->m_parent);
|
||||
if (it == m_blocksById.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cur = it->second;
|
||||
} while (true);
|
||||
|
||||
// Remove duplicates
|
||||
std::sort(wallets.begin(), wallets.end(), [](const Wallet* a, const Wallet* b) { return *a < *b; });
|
||||
wallets.erase(std::unique(wallets.begin(), wallets.end(), [](const Wallet* a, const Wallet* b) { return *a == *b; }), wallets.end());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SideChain::block_seen(const PoolBlock& block)
|
||||
{
|
||||
// Check if it's some old block
|
||||
@@ -462,7 +532,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
MinerData miner_data = m_pool->miner_data();
|
||||
if ((block.m_prevId == miner_data.prev_id) && miner_data.difficulty.check_pow(pow_hash)) {
|
||||
LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero network, submitting it");
|
||||
m_pool->submit_block_async(block.m_mainChainData);
|
||||
m_pool->submit_block_async(block.serialize_mainchain_data());
|
||||
}
|
||||
else {
|
||||
difficulty_type diff;
|
||||
@@ -471,7 +541,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
}
|
||||
else if (diff.check_pow(pow_hash)) {
|
||||
LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero height " << block.m_txinGenHeight << ", submitting it");
|
||||
m_pool->submit_block_async(block.m_mainChainData);
|
||||
m_pool->submit_block_async(block.serialize_mainchain_data());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,7 +579,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
}
|
||||
|
||||
if (block_found) {
|
||||
m_pool->api_update_block_found(&data);
|
||||
m_pool->api_update_block_found(&data, &block);
|
||||
}
|
||||
|
||||
add_block(block);
|
||||
@@ -545,6 +615,9 @@ void SideChain::add_block(const PoolBlock& block)
|
||||
|
||||
m_blocksByHeight[new_block->m_sidechainHeight].push_back(new_block);
|
||||
|
||||
// Pre-calculate eph_public_keys during initial sync
|
||||
launch_precalc(new_block);
|
||||
|
||||
update_depths(new_block);
|
||||
|
||||
if (new_block->m_verified) {
|
||||
@@ -607,14 +680,14 @@ bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob) const
|
||||
return false;
|
||||
}
|
||||
|
||||
blob.reserve(block->m_mainChainData.size() + block->m_sideChainData.size());
|
||||
blob = block->serialize_mainchain_data();
|
||||
const std::vector<uint8_t> sidechain_data = block->serialize_sidechain_data();
|
||||
blob.insert(blob.end(), sidechain_data.begin(), sidechain_data.end());
|
||||
|
||||
blob = block->m_mainChainData;
|
||||
blob.insert(blob.end(), block->m_sideChainData.begin(), block->m_sideChainData.end());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob) const
|
||||
bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob, uv_loop_t* loop) const
|
||||
{
|
||||
blob.clear();
|
||||
|
||||
@@ -628,13 +701,15 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
|
||||
blob.reserve(n * 39 + 64);
|
||||
writeVarint(n, blob);
|
||||
|
||||
const uint8_t tx_type = b->get_tx_type();
|
||||
|
||||
for (const PoolBlock::TxOutput& output : b->m_outputs) {
|
||||
writeVarint(output.m_reward, blob);
|
||||
blob.emplace_back(output.m_txType);
|
||||
blob.emplace_back(tx_type);
|
||||
blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
|
||||
|
||||
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
|
||||
blob.emplace_back(output.m_viewTag);
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
blob.emplace_back(static_cast<uint8_t>(output.m_viewTag));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,6 +726,63 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
|
||||
|
||||
const size_t n = tmpShares.size();
|
||||
|
||||
// Helper jobs call get_eph_public_key with indices in descending order
|
||||
// Current thread will process indices in ascending order so when they meet, everything will be cached
|
||||
|
||||
std::atomic<int> counter{ 0 };
|
||||
std::atomic<int> num_helper_jobs_finished{ 0 };
|
||||
int num_helper_jobs_started = 0;
|
||||
|
||||
if (loop) {
|
||||
constexpr size_t HELPER_JOBS_COUNT = 4;
|
||||
|
||||
struct Work
|
||||
{
|
||||
uv_work_t req;
|
||||
const std::vector<MinerShare>& tmpShares;
|
||||
const hash& txkeySec;
|
||||
std::atomic<int>& counter;
|
||||
std::atomic<int>& num_helper_jobs_finished;
|
||||
|
||||
// Fix MSVC warnings
|
||||
Work() = delete;
|
||||
Work& operator=(Work&&) = delete;
|
||||
};
|
||||
|
||||
counter = static_cast<int>(n) - 1;
|
||||
num_helper_jobs_started = HELPER_JOBS_COUNT;
|
||||
|
||||
for (size_t i = 0; i < HELPER_JOBS_COUNT; ++i) {
|
||||
Work* w = new Work{ {}, tmpShares, block->m_txkeySec, counter, num_helper_jobs_finished };
|
||||
w->req.data = w;
|
||||
|
||||
const int err = uv_queue_work(loop, &w->req,
|
||||
[](uv_work_t* req)
|
||||
{
|
||||
Work* work = reinterpret_cast<Work*>(req->data);
|
||||
hash eph_public_key;
|
||||
|
||||
int index;
|
||||
while ((index = work->counter.fetch_sub(1)) >= 0) {
|
||||
uint8_t view_tag;
|
||||
work->tmpShares[index].m_wallet->get_eph_public_key(work->txkeySec, static_cast<size_t>(index), eph_public_key, view_tag);
|
||||
}
|
||||
|
||||
++work->num_helper_jobs_finished;
|
||||
},
|
||||
[](uv_work_t* req, int /*status*/)
|
||||
{
|
||||
delete reinterpret_cast<Work*>(req->data);
|
||||
});
|
||||
|
||||
if (err) {
|
||||
LOGERR(1, "get_outputs_blob: uv_queue_work failed, error " << uv_err_name(err));
|
||||
--num_helper_jobs_started;
|
||||
delete w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blob.reserve(n * 39 + 64);
|
||||
|
||||
writeVarint(n, blob);
|
||||
@@ -662,6 +794,13 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
|
||||
|
||||
hash eph_public_key;
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
// stop helper jobs when they meet with current thread
|
||||
const int c = counter.load();
|
||||
if ((c >= 0) && (static_cast<int>(i) >= c)) {
|
||||
// this will cause all helper jobs to finish immediately
|
||||
counter = -1;
|
||||
}
|
||||
|
||||
writeVarint(tmpRewards[i], blob);
|
||||
|
||||
blob.emplace_back(tx_type);
|
||||
@@ -676,20 +815,32 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
|
||||
blob.emplace_back(view_tag);
|
||||
}
|
||||
|
||||
block->m_outputs.emplace_back(tmpRewards[i], eph_public_key, tx_type, view_tag);
|
||||
block->m_outputs.emplace_back(tmpRewards[i], eph_public_key, view_tag);
|
||||
}
|
||||
|
||||
block->m_outputs.shrink_to_fit();
|
||||
|
||||
if (loop) {
|
||||
// this will cause all helper jobs to finish immediately
|
||||
counter = -1;
|
||||
|
||||
while (num_helper_jobs_finished < num_helper_jobs_started) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SideChain::print_status() const
|
||||
void SideChain::print_status(bool obtain_sidechain_lock) const
|
||||
{
|
||||
std::vector<hash> blocks_in_window;
|
||||
blocks_in_window.reserve(m_chainWindowSize * 9 / 8);
|
||||
|
||||
const difficulty_type diff = difficulty();
|
||||
|
||||
ReadLock lock(m_sidechainLock);
|
||||
if (obtain_sidechain_lock) uv_rwlock_rdlock(&m_sidechainLock);
|
||||
ON_SCOPE_LEAVE([this, obtain_sidechain_lock]() { if (obtain_sidechain_lock) uv_rwlock_rdunlock(&m_sidechainLock); });
|
||||
|
||||
uint64_t rem;
|
||||
uint64_t pool_hashrate = udiv128(diff.hi, diff.lo, m_targetBlockTime, &rem);
|
||||
@@ -769,12 +920,13 @@ void SideChain::print_status() const
|
||||
}
|
||||
|
||||
const Wallet& w = m_pool->params().m_wallet;
|
||||
const uint8_t tx_type = tip->get_tx_type();
|
||||
|
||||
hash eph_public_key;
|
||||
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
|
||||
const PoolBlock::TxOutput& out = tip->m_outputs[i];
|
||||
if (!your_reward) {
|
||||
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
|
||||
your_reward = out.m_reward;
|
||||
}
|
||||
@@ -841,11 +993,12 @@ double SideChain::get_reward_share(const Wallet& w) const
|
||||
|
||||
const PoolBlock* tip = m_chainTip;
|
||||
if (tip) {
|
||||
const uint8_t tx_type = tip->get_tx_type();
|
||||
hash eph_public_key;
|
||||
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
|
||||
const PoolBlock::TxOutput& out = tip->m_outputs[i];
|
||||
if (!reward) {
|
||||
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
|
||||
reward = out.m_reward;
|
||||
}
|
||||
@@ -946,11 +1099,11 @@ bool SideChain::split_reward(uint64_t reward, const std::vector<MinerShare>& sha
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SideChain::get_difficulty(PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const
|
||||
bool SideChain::get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const
|
||||
{
|
||||
difficultyData.clear();
|
||||
|
||||
PoolBlock* cur = tip;
|
||||
const PoolBlock* cur = tip;
|
||||
uint64_t oldest_timestamp = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
uint64_t block_depth = 0;
|
||||
@@ -1396,6 +1549,8 @@ void SideChain::verify(PoolBlock* block)
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t tx_type = block->get_tx_type();
|
||||
|
||||
for (size_t i = 0, n = rewards.size(); i < n; ++i) {
|
||||
const PoolBlock::TxOutput& out = block->m_outputs[i];
|
||||
|
||||
@@ -1419,7 +1574,7 @@ void SideChain::verify(PoolBlock* block)
|
||||
return;
|
||||
}
|
||||
|
||||
if ((out.m_txType == TXOUT_TO_TAGGED_KEY) && (out.m_viewTag != view_tag)) {
|
||||
if ((tx_type == TXOUT_TO_TAGGED_KEY) && (out.m_viewTag != view_tag)) {
|
||||
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
|
||||
", id = " << block->m_sidechainId <<
|
||||
", mainchain height = " << block->m_txinGenHeight <<
|
||||
@@ -1442,7 +1597,7 @@ void SideChain::verify(PoolBlock* block)
|
||||
block->m_invalid = false;
|
||||
}
|
||||
|
||||
void SideChain::update_chain_tip(PoolBlock* block)
|
||||
void SideChain::update_chain_tip(const PoolBlock* block)
|
||||
{
|
||||
if (!block->m_verified || block->m_invalid) {
|
||||
LOGERR(1, "trying to update chain tip to an unverified or invalid block, fix the code!");
|
||||
@@ -1460,7 +1615,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
|
||||
if (is_longer_chain(tip, block, is_alternative)) {
|
||||
difficulty_type diff;
|
||||
if (get_difficulty(block, m_difficultyData, diff)) {
|
||||
m_chainTip = block;
|
||||
m_chainTip = const_cast<PoolBlock*>(block);
|
||||
{
|
||||
WriteLock lock(m_curDifficultyLock);
|
||||
m_curDifficulty = diff;
|
||||
@@ -1472,7 +1627,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
|
||||
|
||||
block->m_wantBroadcast = true;
|
||||
if (m_pool) {
|
||||
m_pool->update_block_template_async();
|
||||
m_pool->update_block_template_async(is_alternative);
|
||||
|
||||
// Reset stratum share counters when switching to an alternative chain to avoid confusion
|
||||
if (is_alternative) {
|
||||
@@ -1480,9 +1635,8 @@ void SideChain::update_chain_tip(PoolBlock* block)
|
||||
if (s) {
|
||||
s->reset_share_counters();
|
||||
}
|
||||
#ifdef WITH_RANDOMX
|
||||
m_pool->reset_miner();
|
||||
#endif
|
||||
// Also clear cache because it has data from all old blocks now
|
||||
clear_crypto_cache();
|
||||
LOGINFO(0, log::LightCyan() << "SYNCHRONIZED");
|
||||
}
|
||||
}
|
||||
@@ -1503,36 +1657,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
|
||||
|
||||
if (p2pServer() && block->m_wantBroadcast && !block->m_broadcasted) {
|
||||
block->m_broadcasted = true;
|
||||
#ifdef DEBUG_BROADCAST_DELAY_MS
|
||||
struct Work
|
||||
{
|
||||
uv_work_t req;
|
||||
P2PServer* server;
|
||||
PoolBlock* block;
|
||||
};
|
||||
Work* work = new Work{};
|
||||
work->req.data = work;
|
||||
work->server = p2pServer();
|
||||
work->block = block;
|
||||
const int err = uv_queue_work(uv_default_loop(), &work->req,
|
||||
[](uv_work_t*)
|
||||
{
|
||||
num_running_jobs.fetch_add(1);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(DEBUG_BROADCAST_DELAY_MS));
|
||||
},
|
||||
[](uv_work_t* req, int)
|
||||
{
|
||||
Work* work = reinterpret_cast<Work*>(req->data);
|
||||
work->server->broadcast(*work->block);
|
||||
delete reinterpret_cast<Work*>(req->data);
|
||||
num_running_jobs.fetch_sub(1);
|
||||
});
|
||||
if (err) {
|
||||
LOGERR(1, "update_chain_tip: uv_queue_work failed, error " << uv_err_name(err));
|
||||
}
|
||||
#else
|
||||
p2pServer()->broadcast(*block);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1774,6 +1899,9 @@ void SideChain::prune_old_blocks()
|
||||
if (p2pServer()) {
|
||||
p2pServer()->clear_cached_blocks();
|
||||
}
|
||||
|
||||
// Pre-calc workers are not needed anymore
|
||||
finish_precalc();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1792,9 +1920,18 @@ void SideChain::get_missing_blocks(std::vector<hash>& missing_blocks) const
|
||||
missing_blocks.push_back(b.second->m_parent);
|
||||
}
|
||||
|
||||
int num_missing_uncles = 0;
|
||||
|
||||
for (const hash& h : b.second->m_uncles) {
|
||||
if (!h.empty() && (m_blocksById.find(h) == m_blocksById.end())) {
|
||||
missing_blocks.push_back(h);
|
||||
|
||||
// Get no more than 2 first missing uncles at a time from each block
|
||||
// Blocks with more than 2 uncles are very rare and they will be processed in several steps
|
||||
++num_missing_uncles;
|
||||
if (num_missing_uncles >= 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1891,4 +2028,131 @@ bool SideChain::check_config()
|
||||
return true;
|
||||
}
|
||||
|
||||
void SideChain::launch_precalc(const PoolBlock* block)
|
||||
{
|
||||
if (m_precalcFinished) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int h = UNCLE_BLOCK_DEPTH - 1; h >= 0; --h) {
|
||||
auto it = m_blocksByHeight.find(block->m_sidechainHeight + m_chainWindowSize + h - 1);
|
||||
if (it == m_blocksByHeight.end()) {
|
||||
continue;
|
||||
}
|
||||
for (PoolBlock* b : it->second) {
|
||||
if (b->m_precalculated) {
|
||||
continue;
|
||||
}
|
||||
std::vector<const Wallet*> wallets;
|
||||
if (get_wallets(b, wallets)) {
|
||||
b->m_precalculated = true;
|
||||
PrecalcJob* job = new PrecalcJob{ b, std::move(wallets) };
|
||||
{
|
||||
MutexLock lock2(m_precalcJobsMutex);
|
||||
m_precalcJobs.push_back(job);
|
||||
}
|
||||
uv_cond_signal(&m_precalcJobsCond);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SideChain::precalc_worker()
|
||||
{
|
||||
do {
|
||||
PrecalcJob* job;
|
||||
{
|
||||
MutexLock lock(m_precalcJobsMutex);
|
||||
|
||||
if (m_precalcFinished) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (m_precalcJobs.empty()) {
|
||||
uv_cond_wait(&m_precalcJobsCond, &m_precalcJobsMutex);
|
||||
|
||||
if (m_precalcFinished) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
job = m_precalcJobs.back();
|
||||
m_precalcJobs.pop_back();
|
||||
|
||||
// Filter out duplicate inputs for get_eph_public_key()
|
||||
uint8_t t[HASH_SIZE * 2 + sizeof(size_t)];
|
||||
memcpy(t, job->b->m_txkeySec.h, HASH_SIZE);
|
||||
|
||||
for (size_t i = 0, n = job->wallets.size(); i < n; ++i) {
|
||||
memcpy(t + HASH_SIZE, job->wallets[i]->view_public_key().h, HASH_SIZE);
|
||||
memcpy(t + HASH_SIZE * 2, &i, sizeof(i));
|
||||
if (!m_uniquePrecalcInputs->insert(robin_hood::hash_bytes(t, array_size(t))).second) {
|
||||
job->wallets[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0, n = job->wallets.size(); i < n; ++i) {
|
||||
if (job->wallets[i]) {
|
||||
hash eph_public_key;
|
||||
uint8_t view_tag;
|
||||
job->wallets[i]->get_eph_public_key(job->b->m_txkeySec, i, eph_public_key, view_tag);
|
||||
}
|
||||
}
|
||||
delete job;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
void SideChain::finish_precalc()
|
||||
{
|
||||
if (m_precalcFinished.exchange(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
{
|
||||
MutexLock lock(m_precalcJobsMutex);
|
||||
for (PrecalcJob* job : m_precalcJobs) {
|
||||
delete job;
|
||||
}
|
||||
m_precalcJobs.clear();
|
||||
m_precalcJobs.shrink_to_fit();
|
||||
uv_cond_broadcast(&m_precalcJobsCond);
|
||||
}
|
||||
|
||||
for (std::thread& t : m_precalcWorkers) {
|
||||
t.join();
|
||||
}
|
||||
m_precalcWorkers.clear();
|
||||
m_precalcWorkers.shrink_to_fit();
|
||||
|
||||
delete m_uniquePrecalcInputs;
|
||||
m_uniquePrecalcInputs = nullptr;
|
||||
|
||||
uv_mutex_destroy(&m_precalcJobsMutex);
|
||||
uv_cond_destroy(&m_precalcJobsCond);
|
||||
|
||||
LOGINFO(4, "pre-calculation workers stopped");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOGERR(1, "exception in finish_precalc(): " << e.what());
|
||||
}
|
||||
|
||||
// Also clear cache because it has data from all old blocks now
|
||||
clear_crypto_cache();
|
||||
|
||||
#ifdef DEV_TEST_SYNC
|
||||
if (m_pool) {
|
||||
LOGINFO(0, log::LightGreen() << "[DEV] Synchronization finished successfully, stopping P2Pool now");
|
||||
print_status(false);
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->print_status();
|
||||
}
|
||||
m_pool->stop();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+30
-8
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "uv_util.h"
|
||||
#include <map>
|
||||
#include <thread>
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
@@ -31,13 +32,13 @@ class Wallet;
|
||||
struct MinerShare
|
||||
{
|
||||
FORCEINLINE MinerShare() : m_weight(0), m_wallet(nullptr) {}
|
||||
FORCEINLINE MinerShare(uint64_t w, Wallet* x) : m_weight(w), m_wallet(x) {}
|
||||
FORCEINLINE MinerShare(uint64_t w, const Wallet* x) : m_weight(w), m_wallet(x) {}
|
||||
|
||||
uint64_t m_weight;
|
||||
Wallet* m_wallet;
|
||||
const Wallet* m_wallet;
|
||||
};
|
||||
|
||||
class SideChain
|
||||
class SideChain : public nocopy_nomove
|
||||
{
|
||||
public:
|
||||
SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr);
|
||||
@@ -55,9 +56,9 @@ public:
|
||||
void watch_mainchain_block(const ChainMain& data, const hash& possible_id);
|
||||
|
||||
bool get_block_blob(const hash& id, std::vector<uint8_t>& blob) const;
|
||||
bool get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob) const;
|
||||
bool get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob, uv_loop_t* loop) const;
|
||||
|
||||
void print_status() const;
|
||||
void print_status(bool obtain_sidechain_lock = true) const;
|
||||
double get_reward_share(const Wallet& w) const;
|
||||
|
||||
// Consensus ID can be used to spawn independent P2Pools with their own sidechains
|
||||
@@ -75,6 +76,7 @@ public:
|
||||
bool is_mini() const;
|
||||
|
||||
const PoolBlock* chainTip() const { return m_chainTip; }
|
||||
bool precalcFinished() const { return m_precalcFinished.load(); }
|
||||
|
||||
static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards);
|
||||
|
||||
@@ -84,11 +86,12 @@ private:
|
||||
NetworkType m_networkType;
|
||||
|
||||
private:
|
||||
bool get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) const;
|
||||
bool get_difficulty(PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const;
|
||||
bool get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares) const;
|
||||
bool get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const;
|
||||
bool get_wallets(const PoolBlock* tip, std::vector<const Wallet*>& wallets) const;
|
||||
void verify_loop(PoolBlock* block);
|
||||
void verify(PoolBlock* block);
|
||||
void update_chain_tip(PoolBlock* block);
|
||||
void update_chain_tip(const PoolBlock* block);
|
||||
PoolBlock* get_parent(const PoolBlock* block) const;
|
||||
|
||||
// Checks if "candidate" has longer (higher difficulty) chain than "block"
|
||||
@@ -128,6 +131,25 @@ private:
|
||||
|
||||
ChainMain m_watchBlock;
|
||||
hash m_watchBlockSidechainId;
|
||||
|
||||
struct PrecalcJob
|
||||
{
|
||||
const PoolBlock* b;
|
||||
std::vector<const Wallet*> wallets;
|
||||
};
|
||||
|
||||
uv_cond_t m_precalcJobsCond;
|
||||
uv_mutex_t m_precalcJobsMutex;
|
||||
|
||||
std::vector<PrecalcJob*> m_precalcJobs;
|
||||
std::vector<std::thread> m_precalcWorkers;
|
||||
unordered_set<size_t>* m_uniquePrecalcInputs;
|
||||
|
||||
std::atomic<bool> m_precalcFinished;
|
||||
|
||||
void launch_precalc(const PoolBlock* block);
|
||||
void precalc_worker();
|
||||
void finish_precalc();
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+43
-20
@@ -33,6 +33,10 @@ static constexpr uint64_t AUTO_DIFF_TARGET_TIME = 30;
|
||||
// Use short target format (4 bytes) for diff <= 4 million
|
||||
static constexpr uint64_t TARGET_4_BYTES_LIMIT = std::numeric_limits<uint64_t>::max() / 4000001;
|
||||
|
||||
static constexpr int32_t BAD_SHARE_POINTS = -5;
|
||||
static constexpr int32_t GOOD_SHARE_POINTS = 1;
|
||||
static constexpr int32_t BAN_THRESHOLD_POINTS = -15;
|
||||
|
||||
#include "tcp_server.inl"
|
||||
|
||||
namespace p2pool {
|
||||
@@ -41,7 +45,6 @@ StratumServer::StratumServer(p2pool* pool)
|
||||
: TCPServer(StratumClient::allocate)
|
||||
, m_pool(pool)
|
||||
, m_autoDiff(pool->params().m_autoDiff)
|
||||
, m_extraNonce(0)
|
||||
, m_rng(RandomDeviceSeed::instance)
|
||||
, m_cumulativeHashes(0)
|
||||
, m_cumulativeHashesAtLastShare(0)
|
||||
@@ -56,6 +59,8 @@ StratumServer::StratumServer(p2pool* pool)
|
||||
// Diffuse the initial state in case it has low quality
|
||||
m_rng.discard(10000);
|
||||
|
||||
m_extraNonce = static_cast<uint32_t>(m_rng());
|
||||
|
||||
m_hashrateData[0] = { seconds_since_epoch(), 0 };
|
||||
|
||||
uv_mutex_init_checked(&m_blobsQueueLock);
|
||||
@@ -80,8 +85,6 @@ StratumServer::StratumServer(p2pool* pool)
|
||||
|
||||
StratumServer::~StratumServer()
|
||||
{
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_blobsAsync), nullptr);
|
||||
|
||||
shutdown_tcp();
|
||||
|
||||
uv_mutex_destroy(&m_blobsQueueLock);
|
||||
@@ -102,9 +105,12 @@ void StratumServer::on_block(const BlockTemplate& block)
|
||||
LOGINFO(4, "no clients connected");
|
||||
return;
|
||||
}
|
||||
m_extraNonce.exchange(num_connections);
|
||||
|
||||
const uint32_t extra_nonce_start = static_cast<uint32_t>(get_random64());
|
||||
m_extraNonce.exchange(extra_nonce_start + num_connections);
|
||||
|
||||
BlobsData* blobs_data = new BlobsData{};
|
||||
blobs_data->m_extraNonceStart = extra_nonce_start;
|
||||
|
||||
difficulty_type difficulty;
|
||||
difficulty_type sidechain_difficulty;
|
||||
@@ -114,7 +120,7 @@ void StratumServer::on_block(const BlockTemplate& block)
|
||||
// Even if they do, they'll be added to the beginning of the list and will get their block template in on_login()
|
||||
// We'll iterate through the list backwards so when we get to the beginning and run out of extra_nonce values, it'll be only new clients left
|
||||
blobs_data->m_numClientsExpected = num_connections;
|
||||
blobs_data->m_blobSize = block.get_hashing_blobs(0, num_connections, blobs_data->m_blobs, blobs_data->m_height, difficulty, sidechain_difficulty, blobs_data->m_seedHash, nonce_offset, blobs_data->m_templateId);
|
||||
blobs_data->m_blobSize = block.get_hashing_blobs(extra_nonce_start, num_connections, blobs_data->m_blobs, blobs_data->m_height, difficulty, sidechain_difficulty, blobs_data->m_seedHash, nonce_offset, blobs_data->m_templateId);
|
||||
|
||||
// Integrity checks
|
||||
if (blobs_data->m_blobSize < 76) {
|
||||
@@ -357,9 +363,10 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
|
||||
if (found) {
|
||||
BlockTemplate& block = m_pool->block_template();
|
||||
uint64_t height;
|
||||
difficulty_type mainchain_diff, sidechain_diff;
|
||||
|
||||
if (!block.get_difficulties(template_id, mainchain_diff, sidechain_diff)) {
|
||||
if (!block.get_difficulties(template_id, height, mainchain_diff, sidechain_diff)) {
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
|
||||
return send(client,
|
||||
[id](void* buf, size_t buf_size)
|
||||
@@ -404,6 +411,8 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
share->m_target = target;
|
||||
share->m_resultHash = resultHash;
|
||||
share->m_sidechainDifficulty = sidechain_diff;
|
||||
share->m_mainchainHeight = height;
|
||||
share->m_effort = -1.0;
|
||||
share->m_timestamp = seconds_since_epoch();
|
||||
|
||||
uint64_t rem;
|
||||
@@ -432,7 +441,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
return true;
|
||||
}
|
||||
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a share with invalid job id");
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a share with invalid job id " << job_id << " (latest job sent has id " << client->m_perConnectionJobId << ')');
|
||||
|
||||
const bool result = send(client,
|
||||
[id](void* buf, size_t buf_size)
|
||||
@@ -614,14 +623,14 @@ void StratumServer::update_auto_diff(StratumClient* client, const uint64_t times
|
||||
|
||||
if (k >= N) {
|
||||
// Full window
|
||||
const uint64_t dt = t2 - t1;
|
||||
const uint16_t dt = t2 - t1;
|
||||
client->m_autoDiff.lo = std::max<uint64_t>((client->m_autoDiffWindowHashes * AUTO_DIFF_TARGET_TIME) / (dt ? dt : 1), MIN_DIFF);
|
||||
client->m_autoDiff.hi = 0;
|
||||
}
|
||||
else if (k >= 10) {
|
||||
// Partial window
|
||||
const uint64_t h0 = hash_uncompress(client->m_autoDiffData[0].m_hashes);
|
||||
const uint64_t dt = client->m_autoDiffData[k].m_timestamp - client->m_autoDiffData[0].m_timestamp;
|
||||
const uint16_t dt = client->m_autoDiffData[k].m_timestamp - client->m_autoDiffData[0].m_timestamp;
|
||||
|
||||
client->m_autoDiff.lo = std::max<uint64_t>(((client->m_autoDiffWindowHashes - h0) * AUTO_DIFF_TARGET_TIME) / (dt ? dt : 1), MIN_DIFF);
|
||||
client->m_autoDiff.hi = 0;
|
||||
@@ -657,9 +666,10 @@ void StratumServer::on_blobs_ready()
|
||||
|
||||
// Only send the latest blob
|
||||
BlobsData* data = blobs_queue.back();
|
||||
const uint32_t extra_nonce_start = data->m_extraNonceStart;
|
||||
|
||||
size_t numClientsProcessed = 0;
|
||||
uint32_t extra_nonce = 0;
|
||||
uint32_t num_sent = 0;
|
||||
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
{
|
||||
@@ -678,12 +688,12 @@ void StratumServer::on_blobs_ready()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (extra_nonce >= data->m_numClientsExpected) {
|
||||
if (num_sent >= data->m_numClientsExpected) {
|
||||
// We don't have any more extra_nonce values available
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t* hashing_blob = data->m_blobs.data() + extra_nonce * data->m_blobSize;
|
||||
uint8_t* hashing_blob = data->m_blobs.data() + num_sent * data->m_blobSize;
|
||||
|
||||
uint64_t target = data->m_target;
|
||||
if (client->m_customDiff.lo) {
|
||||
@@ -719,7 +729,7 @@ void StratumServer::on_blobs_ready()
|
||||
|
||||
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE];
|
||||
saved_job.job_id = job_id;
|
||||
saved_job.extra_nonce = extra_nonce;
|
||||
saved_job.extra_nonce = extra_nonce_start + num_sent;
|
||||
saved_job.template_id = data->m_templateId;
|
||||
saved_job.target = target;
|
||||
}
|
||||
@@ -745,7 +755,7 @@ void StratumServer::on_blobs_ready()
|
||||
});
|
||||
|
||||
if (result) {
|
||||
++extra_nonce;
|
||||
++num_sent;
|
||||
}
|
||||
else {
|
||||
client->close();
|
||||
@@ -758,7 +768,7 @@ void StratumServer::on_blobs_ready()
|
||||
}
|
||||
}
|
||||
|
||||
LOGINFO(3, "sent new job to " << extra_nonce << '/' << numClientsProcessed << " clients");
|
||||
LOGINFO(3, "sent new job to " << num_sent << '/' << numClientsProcessed << " clients");
|
||||
}
|
||||
|
||||
void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp)
|
||||
@@ -840,19 +850,20 @@ void StratumServer::on_share_found(uv_work_t* req)
|
||||
if (pow_hash != share->m_resultHash) {
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " submitted a share with invalid PoW");
|
||||
share->m_result = SubmittedShare::Result::INVALID_POW;
|
||||
client->m_score += BAD_SHARE_POINTS;
|
||||
return;
|
||||
}
|
||||
|
||||
client->m_score += GOOD_SHARE_POINTS;
|
||||
|
||||
const uint64_t n = server->m_cumulativeHashes + hashes;
|
||||
const double diff = sidechain_difficulty.to_double();
|
||||
const double effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
|
||||
share->m_effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
|
||||
server->m_cumulativeHashesAtLastShare = n;
|
||||
|
||||
server->m_cumulativeFoundSharesDiff += diff;
|
||||
++server->m_totalFoundShares;
|
||||
|
||||
const char* s = client->m_customUser;
|
||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << ", effort " << effort << '%');
|
||||
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
|
||||
}
|
||||
|
||||
@@ -868,19 +879,23 @@ void StratumServer::on_share_found(uv_work_t* req)
|
||||
else {
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a low diff share");
|
||||
share->m_result = SubmittedShare::Result::LOW_DIFF;
|
||||
client->m_score += BAD_SHARE_POINTS;
|
||||
}
|
||||
}
|
||||
|
||||
void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
{
|
||||
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
|
||||
StratumClient* client = share->m_client;
|
||||
|
||||
if (share->m_highEnoughDifficulty) {
|
||||
const char* s = client->m_customUser;
|
||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << share->m_mainchainHeight << ", diff " << share->m_sidechainDifficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << ", effort " << share->m_effort << '%');
|
||||
bkg_jobs_tracker.stop("StratumServer::on_share_found");
|
||||
}
|
||||
|
||||
ON_SCOPE_LEAVE([share]() { share->m_server->m_submittedSharesPool.push_back(share); });
|
||||
|
||||
StratumClient* client = share->m_client;
|
||||
StratumServer* server = share->m_server;
|
||||
|
||||
const bool bad_share = (share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW);
|
||||
@@ -910,7 +925,7 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
return s.m_pos;
|
||||
});
|
||||
|
||||
if (bad_share) {
|
||||
if (bad_share && (client->m_score <= BAN_THRESHOLD_POINTS)) {
|
||||
client->ban(DEFAULT_BAN_TIME);
|
||||
client->close();
|
||||
}
|
||||
@@ -923,6 +938,11 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
}
|
||||
}
|
||||
|
||||
void StratumServer::on_shutdown()
|
||||
{
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_blobsAsync), nullptr);
|
||||
}
|
||||
|
||||
StratumServer::StratumClient::StratumClient()
|
||||
: m_rpcId(0)
|
||||
, m_perConnectionJobId(0)
|
||||
@@ -934,6 +954,7 @@ StratumServer::StratumClient::StratumClient()
|
||||
, m_customDiff{}
|
||||
, m_autoDiff{}
|
||||
, m_customUser{}
|
||||
, m_score(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -953,6 +974,8 @@ void StratumServer::StratumClient::reset()
|
||||
m_customDiff = {};
|
||||
m_autoDiff = {};
|
||||
m_customUser[0] = '\0';
|
||||
|
||||
m_score = 0;
|
||||
}
|
||||
|
||||
bool StratumServer::StratumClient::on_connect()
|
||||
|
||||
@@ -78,6 +78,8 @@ public:
|
||||
difficulty_type m_customDiff;
|
||||
difficulty_type m_autoDiff;
|
||||
char m_customUser[32];
|
||||
|
||||
int32_t m_score;
|
||||
};
|
||||
|
||||
bool on_login(StratumClient* client, uint32_t id, const char* login);
|
||||
@@ -101,6 +103,7 @@ private:
|
||||
|
||||
struct BlobsData
|
||||
{
|
||||
uint32_t m_extraNonceStart;
|
||||
std::vector<uint8_t> m_blobs;
|
||||
size_t m_blobSize;
|
||||
uint64_t m_target;
|
||||
@@ -137,6 +140,8 @@ private:
|
||||
uint64_t m_target;
|
||||
hash m_resultHash;
|
||||
difficulty_type m_sidechainDifficulty;
|
||||
uint64_t m_mainchainHeight;
|
||||
double m_effort;
|
||||
uint64_t m_timestamp;
|
||||
uint64_t m_hashes;
|
||||
bool m_highEnoughDifficulty;
|
||||
@@ -175,6 +180,8 @@ private:
|
||||
|
||||
void update_hashrate_data(uint64_t hashes, uint64_t timestamp);
|
||||
void api_update_local_stats(uint64_t timestamp);
|
||||
|
||||
void on_shutdown() override;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+27
-19
@@ -36,16 +36,16 @@ public:
|
||||
|
||||
bool connect_to_peer(bool is_v6, const char* ip, int port);
|
||||
|
||||
void drop_connections() { uv_async_send(&m_dropConnectionsAsync); }
|
||||
void drop_connections_async() { if (m_finished.load() == 0) { uv_async_send(&m_dropConnectionsAsync); } }
|
||||
void shutdown_tcp();
|
||||
virtual void print_status();
|
||||
|
||||
uv_loop_t* get_loop() { return &m_loop; }
|
||||
|
||||
int listen_port() const { return m_listenPort; }
|
||||
virtual int listen_port() const { return m_listenPort; }
|
||||
|
||||
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*/) {}
|
||||
|
||||
void ban(const raw_ip& ip, uint64_t seconds);
|
||||
virtual void print_bans();
|
||||
@@ -58,6 +58,7 @@ public:
|
||||
virtual void reset();
|
||||
virtual bool on_connect() = 0;
|
||||
virtual bool on_read(char* data, uint32_t size) = 0;
|
||||
bool on_proxy_handshake(char* data, uint32_t size);
|
||||
virtual void on_read_failed(int /*err*/) {}
|
||||
virtual void on_disconnected() {}
|
||||
|
||||
@@ -68,7 +69,7 @@ public:
|
||||
void close();
|
||||
void ban(uint64_t seconds);
|
||||
|
||||
void init_addr_string(bool is_v6, const sockaddr_storage* peer_addr);
|
||||
void init_addr_string();
|
||||
|
||||
alignas(8) char m_readBuf[READ_BUF_SIZE];
|
||||
|
||||
@@ -83,11 +84,18 @@ public:
|
||||
bool m_isV6;
|
||||
bool m_isIncoming;
|
||||
bool m_readBufInUse;
|
||||
bool m_isClosing;
|
||||
uint32_t m_numRead;
|
||||
|
||||
raw_ip m_addr;
|
||||
int m_port;
|
||||
char m_addrString[64];
|
||||
char m_addrString[72];
|
||||
|
||||
enum class Socks5ProxyState {
|
||||
Default,
|
||||
MethodSelectionSent,
|
||||
ConnectRequestSent,
|
||||
} m_socks5ProxyState;
|
||||
|
||||
std::atomic<uint32_t> m_resetCounter;
|
||||
};
|
||||
@@ -99,7 +107,6 @@ public:
|
||||
std::vector<uint8_t> m_data;
|
||||
};
|
||||
|
||||
uv_mutex_t m_writeBuffersLock;
|
||||
std::vector<WriteBuf*> m_writeBuffers;
|
||||
|
||||
struct SendCallbackBase
|
||||
@@ -130,9 +137,9 @@ private:
|
||||
static void on_connection_error(uv_handle_t* handle);
|
||||
static void on_connect(uv_connect_t* req, int status);
|
||||
void on_new_client(uv_stream_t* server);
|
||||
void on_new_client_nolock(uv_stream_t* server, Client* client);
|
||||
void on_new_client(uv_stream_t* server, Client* client);
|
||||
|
||||
bool connect_to_peer_nolock(Client* client, bool is_v6, const sockaddr* addr);
|
||||
bool connect_to_peer(Client* client);
|
||||
|
||||
bool send_internal(Client* client, SendCallbackBase&& callback);
|
||||
|
||||
@@ -147,11 +154,15 @@ private:
|
||||
protected:
|
||||
void start_listening(const std::string& listen_addresses);
|
||||
|
||||
std::string m_socks5Proxy;
|
||||
bool m_socks5ProxyV6;
|
||||
raw_ip m_socks5ProxyIP;
|
||||
int m_socks5ProxyPort;
|
||||
|
||||
std::atomic<int> m_finished;
|
||||
int m_listenPort;
|
||||
|
||||
uv_loop_t m_loop;
|
||||
std::atomic<bool> m_loopStopped;
|
||||
|
||||
uv_mutex_t m_clientsListLock;
|
||||
std::vector<Client*> m_preallocatedClients;
|
||||
@@ -164,23 +175,20 @@ protected:
|
||||
|
||||
bool is_banned(const raw_ip& ip);
|
||||
|
||||
uv_mutex_t m_pendingConnectionsLock;
|
||||
unordered_set<raw_ip> m_pendingConnections;
|
||||
|
||||
uv_async_t m_dropConnectionsAsync;
|
||||
static void on_drop_connections(uv_async_t* async) { reinterpret_cast<TCPServer*>(async->data)->close_sockets(false); }
|
||||
|
||||
virtual void on_shutdown() = 0;
|
||||
|
||||
uv_async_t m_shutdownAsync;
|
||||
static void on_shutdown(uv_async_t* async)
|
||||
{
|
||||
TCPServer* server = reinterpret_cast<TCPServer*>(async->data);
|
||||
server->close_sockets(true);
|
||||
uv_prepare_t m_shutdownPrepare;
|
||||
uv_timer_t m_shutdownTimer;
|
||||
uint32_t m_shutdownCountdown;
|
||||
uint32_t m_numHandles;
|
||||
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_dropConnectionsAsync), nullptr);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_shutdownAsync), nullptr);
|
||||
|
||||
delete GetLoopUserData(&server->m_loop, false);
|
||||
}
|
||||
static void on_shutdown(uv_async_t* async);
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+400
-239
@@ -25,12 +25,18 @@ template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback allocate_new_client)
|
||||
: m_allocateNewClient(allocate_new_client)
|
||||
, m_loopThread{}
|
||||
, m_socks5ProxyV6(false)
|
||||
, m_socks5ProxyIP{}
|
||||
, m_socks5ProxyPort(-1)
|
||||
, m_finished(0)
|
||||
, m_listenPort(-1)
|
||||
, m_loop{}
|
||||
, m_loopStopped{false}
|
||||
, m_numConnections{ 0 }
|
||||
, m_numIncomingConnections{ 0 }
|
||||
, m_shutdownPrepare{}
|
||||
, m_shutdownTimer{}
|
||||
, m_shutdownCountdown(30)
|
||||
, m_numHandles(0)
|
||||
{
|
||||
int err = uv_loop_init(&m_loop);
|
||||
if (err) {
|
||||
@@ -57,18 +63,6 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
|
||||
|
||||
uv_mutex_init_checked(&m_clientsListLock);
|
||||
uv_mutex_init_checked(&m_bansLock);
|
||||
uv_mutex_init_checked(&m_pendingConnectionsLock);
|
||||
uv_mutex_init_checked(&m_writeBuffersLock);
|
||||
|
||||
m_writeBuffers.resize(DEFAULT_BACKLOG);
|
||||
for (size_t i = 0; i < m_writeBuffers.size(); ++i) {
|
||||
m_writeBuffers[i] = new WriteBuf();
|
||||
}
|
||||
|
||||
m_preallocatedClients.reserve(DEFAULT_BACKLOG);
|
||||
for (int i = 0; i < DEFAULT_BACKLOG; ++i) {
|
||||
m_preallocatedClients.emplace_back(m_allocateNewClient());
|
||||
}
|
||||
|
||||
m_connectedClientsList = m_allocateNewClient();
|
||||
m_connectedClientsList->m_next = m_connectedClientsList;
|
||||
@@ -185,7 +179,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
|
||||
|
||||
err = uv_tcp_bind(socket, reinterpret_cast<sockaddr*>(&addr6), UV_TCP_IPV6ONLY);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to bind tcp server IPv6 socket, error " << uv_err_name(err));
|
||||
LOGERR(1, "failed to bind tcp server IPv6 socket " << address << ", error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
}
|
||||
@@ -199,14 +193,14 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
|
||||
|
||||
err = uv_tcp_bind(socket, reinterpret_cast<sockaddr*>(&addr), 0);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to bind tcp server IPv4 socket, error " << uv_err_name(err));
|
||||
LOGERR(1, "failed to bind tcp server IPv4 socket " << address << ", error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
err = uv_listen(reinterpret_cast<uv_stream_t*>(socket), DEFAULT_BACKLOG, on_new_connection);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to listen on tcp server socket, error " << uv_err_name(err));
|
||||
LOGERR(1, "failed to listen on tcp server socket " << address << ", error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
|
||||
@@ -228,8 +222,6 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(bool is_v6, const
|
||||
return false;
|
||||
}
|
||||
|
||||
MutexLock lock(m_clientsListLock);
|
||||
|
||||
if (m_finished.load()) {
|
||||
return false;
|
||||
}
|
||||
@@ -247,48 +239,27 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(bool is_v6, const
|
||||
|
||||
client->m_owner = this;
|
||||
client->m_port = port;
|
||||
client->m_isV6 = is_v6;
|
||||
|
||||
if (!str_to_ip(is_v6, ip, client->m_addr)) {
|
||||
m_preallocatedClients.push_back(client);
|
||||
return false;
|
||||
}
|
||||
|
||||
log::Stream s(client->m_addrString);
|
||||
|
||||
sockaddr_storage addr;
|
||||
if (is_v6) {
|
||||
sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(&addr);
|
||||
const int err = uv_ip6_addr(ip, port, addr6);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to parse IPv6 address " << ip << ", error " << uv_err_name(err));
|
||||
m_preallocatedClients.push_back(client);
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(client->m_addr.data, &addr6->sin6_addr, sizeof(in6_addr));
|
||||
|
||||
s << '[' << ip << "]:" << port << '\0';
|
||||
}
|
||||
else {
|
||||
sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(&addr);
|
||||
const int err = uv_ip4_addr(ip, port, addr4);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to parse IPv4 address " << ip << ", error " << uv_err_name(err));
|
||||
m_preallocatedClients.push_back(client);
|
||||
return false;
|
||||
}
|
||||
|
||||
client->m_addr = {};
|
||||
client->m_addr.data[10] = 0xFF;
|
||||
client->m_addr.data[11] = 0xFF;
|
||||
memcpy(client->m_addr.data + 12, &addr4->sin_addr, sizeof(in_addr));
|
||||
|
||||
s << ip << ':' << port << '\0';
|
||||
}
|
||||
|
||||
return connect_to_peer_nolock(client, is_v6, reinterpret_cast<sockaddr*>(&addr));
|
||||
return connect_to_peer(client);
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(bool is_v6, const raw_ip& ip, int port)
|
||||
{
|
||||
MutexLock lock(m_clientsListLock);
|
||||
|
||||
if (m_finished.load()) {
|
||||
return false;
|
||||
}
|
||||
@@ -307,29 +278,10 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(bool is_v6, const
|
||||
client->m_owner = this;
|
||||
client->m_addr = ip;
|
||||
client->m_port = port;
|
||||
client->m_isV6 = is_v6;
|
||||
client->init_addr_string();
|
||||
|
||||
sockaddr_storage addr{};
|
||||
|
||||
if (is_v6) {
|
||||
sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(&addr);
|
||||
addr6->sin6_family = AF_INET6;
|
||||
memcpy(&addr6->sin6_addr, ip.data, sizeof(in6_addr));
|
||||
addr6->sin6_port = htons(static_cast<uint16_t>(port));
|
||||
}
|
||||
else {
|
||||
sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(&addr);
|
||||
addr4->sin_family = AF_INET;
|
||||
memcpy(&addr4->sin_addr, ip.data + 12, sizeof(in_addr));
|
||||
addr4->sin_port = htons(static_cast<uint16_t>(port));
|
||||
}
|
||||
|
||||
client->init_addr_string(is_v6, &addr);
|
||||
return connect_to_peer_nolock(client, is_v6, reinterpret_cast<sockaddr*>(&addr));
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connect_failed(bool, const raw_ip&, int)
|
||||
{
|
||||
return connect_to_peer(client);
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
@@ -356,7 +308,7 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::is_banned(const raw_ip& ip)
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer_nolock(Client* client, bool is_v6, const sockaddr* addr)
|
||||
bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(Client* client)
|
||||
{
|
||||
if (is_banned(client->m_addr)) {
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(client->m_addrString) << log::NoColor() << " is banned, not connecting to it");
|
||||
@@ -364,7 +316,11 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer_nolock(Client* cl
|
||||
return false;
|
||||
}
|
||||
|
||||
client->m_isV6 = is_v6;
|
||||
if (!m_pendingConnections.insert(client->m_addr).second) {
|
||||
LOGINFO(6, "there is already a pending connection to this IP, not connecting to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
m_preallocatedClients.push_back(client);
|
||||
return false;
|
||||
}
|
||||
|
||||
int err = uv_tcp_init(&m_loop, &client->m_socket);
|
||||
if (err) {
|
||||
@@ -381,27 +337,52 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer_nolock(Client* cl
|
||||
return false;
|
||||
}
|
||||
|
||||
MutexLock lock(m_pendingConnectionsLock);
|
||||
|
||||
if (!m_pendingConnections.insert(client->m_addr).second) {
|
||||
LOGINFO(6, "there is already a pending connection to this IP, not connecting to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&client->m_socket), on_connection_error);
|
||||
return false;
|
||||
}
|
||||
static_assert(sizeof(client->m_readBuf) >= sizeof(uv_connect_t), "READ_BUF_SIZE must be large enough");
|
||||
|
||||
uv_connect_t* connect_request = reinterpret_cast<uv_connect_t*>(client->m_readBuf);
|
||||
memset(connect_request, 0, sizeof(uv_connect_t));
|
||||
|
||||
connect_request->data = client;
|
||||
err = uv_tcp_connect(connect_request, &client->m_socket, addr, on_connect);
|
||||
|
||||
sockaddr_storage addr{};
|
||||
|
||||
if (m_socks5Proxy.empty()) {
|
||||
if (client->m_isV6) {
|
||||
sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(&addr);
|
||||
addr6->sin6_family = AF_INET6;
|
||||
memcpy(&addr6->sin6_addr, client->m_addr.data, sizeof(in6_addr));
|
||||
addr6->sin6_port = htons(static_cast<uint16_t>(client->m_port));
|
||||
}
|
||||
else {
|
||||
sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(&addr);
|
||||
addr4->sin_family = AF_INET;
|
||||
memcpy(&addr4->sin_addr, client->m_addr.data + 12, sizeof(in_addr));
|
||||
addr4->sin_port = htons(static_cast<uint16_t>(client->m_port));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (m_socks5ProxyV6) {
|
||||
sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(&addr);
|
||||
addr6->sin6_family = AF_INET6;
|
||||
memcpy(&addr6->sin6_addr, m_socks5ProxyIP.data, sizeof(in6_addr));
|
||||
addr6->sin6_port = htons(static_cast<uint16_t>(m_socks5ProxyPort));
|
||||
}
|
||||
else {
|
||||
sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(&addr);
|
||||
addr4->sin_family = AF_INET;
|
||||
memcpy(&addr4->sin_addr, m_socks5ProxyIP.data + 12, sizeof(in_addr));
|
||||
addr4->sin_port = htons(static_cast<uint16_t>(m_socks5ProxyPort));
|
||||
}
|
||||
}
|
||||
|
||||
err = uv_tcp_connect(connect_request, &client->m_socket, reinterpret_cast<sockaddr*>(&addr), on_connect);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to initiate tcp connection, error " << uv_err_name(err));
|
||||
LOGERR(1, "failed to initiate tcp connection to " << static_cast<const char*>(client->m_addrString) << ", error " << uv_err_name(err));
|
||||
m_pendingConnections.erase(client->m_addr);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&client->m_socket), on_connection_error);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
LOGINFO(5, "connecting to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
LOGINFO(5, "connecting to " << log::Gray() << static_cast<const char*>(client->m_addrString));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -429,15 +410,16 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::close_sockets(bool listen_sockets
|
||||
}
|
||||
}
|
||||
|
||||
MutexLock lock(m_clientsListLock);
|
||||
|
||||
size_t numClosed = 0;
|
||||
{
|
||||
MutexLock lock(m_clientsListLock);
|
||||
|
||||
for (Client* c = m_connectedClientsList->m_next; c != m_connectedClientsList; c = c->m_next) {
|
||||
uv_handle_t* h = reinterpret_cast<uv_handle_t*>(&c->m_socket);
|
||||
if (!uv_is_closing(h)) {
|
||||
uv_close(h, on_connection_close);
|
||||
++numClosed;
|
||||
for (Client* c = m_connectedClientsList->m_next; c != m_connectedClientsList; c = c->m_next) {
|
||||
uv_handle_t* h = reinterpret_cast<uv_handle_t*>(&c->m_socket);
|
||||
if (!uv_is_closing(h)) {
|
||||
uv_close(h, on_connection_close);
|
||||
++numClosed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,52 +436,10 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::shutdown_tcp()
|
||||
}
|
||||
|
||||
uv_async_send(&m_shutdownAsync);
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
const auto start_time = steady_clock::now();
|
||||
int64_t counter = 0;
|
||||
uv_async_t asy;
|
||||
|
||||
constexpr uint32_t timeout_seconds = 30;
|
||||
|
||||
while (!m_loopStopped) {
|
||||
const int64_t elapsed_time = duration_cast<milliseconds>(steady_clock::now() - start_time).count();
|
||||
|
||||
if (elapsed_time >= (counter + 1) * 1000) {
|
||||
++counter;
|
||||
if (counter < timeout_seconds) {
|
||||
LOGINFO(1, "waiting for event loop to stop for " << (timeout_seconds - counter) << " more seconds...");
|
||||
}
|
||||
else {
|
||||
LOGWARN(1, "timed out while waiting for event loop to stop");
|
||||
uv_async_init(&m_loop, &asy, [](uv_async_t* h) { uv_close(reinterpret_cast<uv_handle_t*>(h), nullptr); });
|
||||
uv_stop(&m_loop);
|
||||
uv_async_send(&asy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(milliseconds(1));
|
||||
}
|
||||
|
||||
uv_thread_join(&m_loopThread);
|
||||
|
||||
for (Client* c : m_preallocatedClients) {
|
||||
delete c;
|
||||
}
|
||||
|
||||
uv_mutex_destroy(&m_clientsListLock);
|
||||
uv_mutex_destroy(&m_bansLock);
|
||||
uv_mutex_destroy(&m_pendingConnectionsLock);
|
||||
|
||||
{
|
||||
MutexLock lock(m_writeBuffersLock);
|
||||
for (WriteBuf* buf : m_writeBuffers) {
|
||||
delete buf;
|
||||
}
|
||||
}
|
||||
uv_mutex_destroy(&m_writeBuffersLock);
|
||||
|
||||
LOGINFO(1, "stopped");
|
||||
}
|
||||
@@ -531,17 +471,9 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::print_bans()
|
||||
using namespace std::chrono;
|
||||
const auto cur_time = steady_clock::now();
|
||||
|
||||
std::vector<std::pair<raw_ip, std::chrono::steady_clock::time_point>> bans;
|
||||
{
|
||||
MutexLock lock(m_bansLock);
|
||||
MutexLock lock(m_bansLock);
|
||||
|
||||
bans.reserve(m_bans.size());
|
||||
for (const auto& b : m_bans) {
|
||||
bans.emplace_back(std::make_pair(b.first, b.second));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& b : bans) {
|
||||
for (const auto& b : m_bans) {
|
||||
if (cur_time < b.second) {
|
||||
const uint64_t t = duration_cast<seconds>(b.second - cur_time).count();
|
||||
LOGINFO(0, b.first << " is banned (" << t << " seconds left)");
|
||||
@@ -556,17 +488,18 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
|
||||
LOGERR(1, "sending data from another thread, this is not thread safe");
|
||||
}
|
||||
|
||||
WriteBuf* buf = nullptr;
|
||||
|
||||
{
|
||||
MutexLock lock(m_writeBuffersLock);
|
||||
if (!m_writeBuffers.empty()) {
|
||||
buf = m_writeBuffers.back();
|
||||
m_writeBuffers.pop_back();
|
||||
}
|
||||
if (client->m_isClosing) {
|
||||
LOGWARN(5, "client " << static_cast<const char*>(client->m_addrString) << " is being disconnected, can't send any more data");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!buf) {
|
||||
WriteBuf* buf;
|
||||
|
||||
if (!m_writeBuffers.empty()) {
|
||||
buf = m_writeBuffers.back();
|
||||
m_writeBuffers.pop_back();
|
||||
}
|
||||
else {
|
||||
buf = new WriteBuf();
|
||||
}
|
||||
|
||||
@@ -581,10 +514,7 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
|
||||
|
||||
if (bytes_written == 0) {
|
||||
LOGWARN(1, "send callback wrote 0 bytes, nothing to do");
|
||||
{
|
||||
MutexLock lock(m_writeBuffersLock);
|
||||
m_writeBuffers.push_back(buf);
|
||||
}
|
||||
m_writeBuffers.push_back(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -599,11 +529,8 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
|
||||
|
||||
const int err = uv_write(&buf->m_write, reinterpret_cast<uv_stream_t*>(&client->m_socket), bufs, 1, Client::on_write);
|
||||
if (err) {
|
||||
{
|
||||
MutexLock lock(m_writeBuffersLock);
|
||||
m_writeBuffers.push_back(buf);
|
||||
}
|
||||
LOGWARN(1, "failed to start writing data to client connection " << static_cast<const char*>(client->m_addrString) << ", error " << uv_err_name(err));
|
||||
m_writeBuffers.push_back(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -616,10 +543,35 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::loop(void* data)
|
||||
LOGINFO(1, "event loop started");
|
||||
server_event_loop_thread = true;
|
||||
TCPServer* server = static_cast<TCPServer*>(data);
|
||||
uv_run(&server->m_loop, UV_RUN_DEFAULT);
|
||||
uv_loop_close(&server->m_loop);
|
||||
|
||||
server->m_writeBuffers.resize(DEFAULT_BACKLOG);
|
||||
server->m_preallocatedClients.reserve(DEFAULT_BACKLOG);
|
||||
for (size_t i = 0; i < DEFAULT_BACKLOG; ++i) {
|
||||
server->m_writeBuffers[i] = new WriteBuf();
|
||||
server->m_preallocatedClients.emplace_back(server->m_allocateNewClient());
|
||||
}
|
||||
|
||||
int err = uv_run(&server->m_loop, UV_RUN_DEFAULT);
|
||||
if (err) {
|
||||
LOGWARN(1, "uv_run returned " << err);
|
||||
}
|
||||
|
||||
err = uv_loop_close(&server->m_loop);
|
||||
if (err) {
|
||||
LOGWARN(1, "uv_loop_close returned error " << uv_err_name(err));
|
||||
}
|
||||
|
||||
for (WriteBuf* buf : server->m_writeBuffers) {
|
||||
delete buf;
|
||||
}
|
||||
server->m_writeBuffers.clear();
|
||||
|
||||
for (Client* c : server->m_preallocatedClients) {
|
||||
delete c;
|
||||
}
|
||||
server->m_preallocatedClients.clear();
|
||||
|
||||
LOGINFO(1, "event loop stopped");
|
||||
server->m_loopStopped = true;
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
@@ -642,10 +594,6 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_connection(uv_stream_t* se
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connection_close(uv_handle_t* handle)
|
||||
{
|
||||
if (!server_event_loop_thread) {
|
||||
LOGERR(1, "on_connection_close called from another thread, this is not thread safe");
|
||||
}
|
||||
|
||||
Client* client = static_cast<Client*>(handle->data);
|
||||
TCPServer* owner = client->m_owner;
|
||||
|
||||
@@ -680,10 +628,7 @@ template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connection_error(uv_handle_t* handle)
|
||||
{
|
||||
Client* client = reinterpret_cast<Client*>(handle->data);
|
||||
TCPServer* server = client->m_owner;
|
||||
|
||||
MutexLock lock(server->m_clientsListLock);
|
||||
server->m_preallocatedClients.push_back(client);
|
||||
client->m_owner->m_preallocatedClients.push_back(client);
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
@@ -696,12 +641,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connect(uv_connect_t* req, int
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
MutexLock lock(server->m_pendingConnectionsLock);
|
||||
server->m_pendingConnections.erase(client->m_addr);
|
||||
}
|
||||
|
||||
MutexLock lock(server->m_clientsListLock);
|
||||
server->m_pendingConnections.erase(client->m_addr);
|
||||
|
||||
if (status) {
|
||||
if (status == UV_ETIMEDOUT) {
|
||||
@@ -715,14 +655,12 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connect(uv_connect_t* req, int
|
||||
return;
|
||||
}
|
||||
|
||||
server->on_new_client_nolock(nullptr, client);
|
||||
server->on_new_client(nullptr, client);
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_client(uv_stream_t* server)
|
||||
{
|
||||
MutexLock lock(m_clientsListLock);
|
||||
|
||||
if (m_finished.load()) {
|
||||
return;
|
||||
}
|
||||
@@ -761,62 +699,53 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_client(uv_stream_t* server
|
||||
return;
|
||||
}
|
||||
|
||||
on_new_client_nolock(server, client);
|
||||
on_new_client(server, client);
|
||||
}
|
||||
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_client_nolock(uv_stream_t* server, Client* client)
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_client(uv_stream_t* server, Client* client)
|
||||
{
|
||||
MutexLock lock(m_clientsListLock);
|
||||
|
||||
client->m_prev = m_connectedClientsList;
|
||||
client->m_next = m_connectedClientsList->m_next;
|
||||
m_connectedClientsList->m_next->m_prev = client;
|
||||
m_connectedClientsList->m_next = client;
|
||||
|
||||
++m_numConnections;
|
||||
client->m_isIncoming = false;
|
||||
|
||||
sockaddr_storage peer_addr;
|
||||
int peer_addr_len = static_cast<int>(sizeof(peer_addr));
|
||||
int err = uv_tcp_getpeername(&client->m_socket, reinterpret_cast<sockaddr*>(&peer_addr), &peer_addr_len);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to get IP address of the client connection, error " << uv_err_name(err));
|
||||
client->close();
|
||||
return;
|
||||
}
|
||||
client->m_isIncoming = (server != nullptr);
|
||||
|
||||
bool is_v6;
|
||||
if (server) {
|
||||
is_v6 = (std::find(m_listenSockets6.begin(), m_listenSockets6.end(), reinterpret_cast<uv_tcp_t*>(server)) != m_listenSockets6.end());
|
||||
client->m_isV6 = is_v6;
|
||||
}
|
||||
else {
|
||||
is_v6 = client->m_isV6;
|
||||
}
|
||||
|
||||
if (is_v6) {
|
||||
memcpy(client->m_addr.data, &reinterpret_cast<sockaddr_in6*>(&peer_addr)->sin6_addr, sizeof(in6_addr));
|
||||
client->m_port = ntohs(reinterpret_cast<sockaddr_in6*>(&peer_addr)->sin6_port);
|
||||
}
|
||||
else {
|
||||
client->m_addr = {};
|
||||
client->m_addr.data[10] = 0xFF;
|
||||
client->m_addr.data[11] = 0xFF;
|
||||
memcpy(client->m_addr.data + 12, &reinterpret_cast<sockaddr_in*>(&peer_addr)->sin_addr, sizeof(in_addr));
|
||||
client->m_port = ntohs(reinterpret_cast<sockaddr_in*>(&peer_addr)->sin_port);
|
||||
}
|
||||
|
||||
client->init_addr_string(is_v6, &peer_addr);
|
||||
|
||||
if (server) {
|
||||
LOGINFO(5, "new connection from " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
client->m_isIncoming = true;
|
||||
if (client->m_isIncoming) {
|
||||
++m_numIncomingConnections;
|
||||
|
||||
client->m_isV6 = (std::find(m_listenSockets6.begin(), m_listenSockets6.end(), reinterpret_cast<uv_tcp_t*>(server)) != m_listenSockets6.end());
|
||||
|
||||
sockaddr_storage peer_addr;
|
||||
int peer_addr_len = static_cast<int>(sizeof(peer_addr));
|
||||
int err = uv_tcp_getpeername(&client->m_socket, reinterpret_cast<sockaddr*>(&peer_addr), &peer_addr_len);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to get IP address of the client connection, error " << uv_err_name(err));
|
||||
client->close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->m_isV6) {
|
||||
memcpy(client->m_addr.data, &reinterpret_cast<sockaddr_in6*>(&peer_addr)->sin6_addr, sizeof(in6_addr));
|
||||
client->m_port = ntohs(reinterpret_cast<sockaddr_in6*>(&peer_addr)->sin6_port);
|
||||
}
|
||||
else {
|
||||
client->m_addr = {};
|
||||
client->m_addr.data[10] = 0xFF;
|
||||
client->m_addr.data[11] = 0xFF;
|
||||
memcpy(client->m_addr.data + 12, &reinterpret_cast<sockaddr_in*>(&peer_addr)->sin_addr, sizeof(in_addr));
|
||||
client->m_port = ntohs(reinterpret_cast<sockaddr_in*>(&peer_addr)->sin_port);
|
||||
}
|
||||
|
||||
client->init_addr_string();
|
||||
}
|
||||
else {
|
||||
LOGINFO(5, "new connection to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
client->m_isIncoming = false;
|
||||
}
|
||||
|
||||
LOGINFO(5, "new connection " << (client->m_isIncoming ? "from " : "to ") << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
|
||||
if (is_banned(client->m_addr)) {
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(client->m_addrString) << log::NoColor() << " is banned, disconnecting");
|
||||
@@ -824,18 +753,111 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_client_nolock(uv_stream_t*
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->m_owner->m_finished.load() || !client->on_connect()) {
|
||||
TCPServer* owner = client->m_owner;
|
||||
|
||||
if (owner->m_finished.load()) {
|
||||
client->close();
|
||||
return;
|
||||
}
|
||||
|
||||
err = uv_read_start(reinterpret_cast<uv_stream_t*>(&client->m_socket), Client::on_alloc, Client::on_read);
|
||||
if (client->m_isIncoming || owner->m_socks5Proxy.empty()) {
|
||||
if (!client->on_connect()) {
|
||||
client->close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const bool result = owner->send(client,
|
||||
[](void* buf, size_t buf_size) -> size_t
|
||||
{
|
||||
if (buf_size < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t* p = reinterpret_cast<uint8_t*>(buf);
|
||||
p[0] = 5; // Protocol version (SOCKS5)
|
||||
p[1] = 1; // NMETHODS
|
||||
p[2] = 0; // Method 0 (no authentication)
|
||||
|
||||
return 3;
|
||||
});
|
||||
|
||||
if (result) {
|
||||
client->m_socks5ProxyState = Client::Socks5ProxyState::MethodSelectionSent;
|
||||
}
|
||||
else {
|
||||
client->close();
|
||||
}
|
||||
}
|
||||
|
||||
const int err = uv_read_start(reinterpret_cast<uv_stream_t*>(&client->m_socket), Client::on_alloc, Client::on_read);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to start reading from client connection, error " << uv_err_name(err));
|
||||
client->close();
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_shutdown(uv_async_t* async)
|
||||
{
|
||||
TCPServer* s = reinterpret_cast<TCPServer*>(async->data);
|
||||
s->on_shutdown();
|
||||
s->close_sockets(true);
|
||||
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&s->m_dropConnectionsAsync), nullptr);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&s->m_shutdownAsync), nullptr);
|
||||
|
||||
delete GetLoopUserData(&s->m_loop, false);
|
||||
|
||||
s->m_numHandles = 0;
|
||||
uv_walk(&s->m_loop, [](uv_handle_t*, void* n) { (*reinterpret_cast<size_t*>(n))++; }, &s->m_numHandles);
|
||||
|
||||
uv_prepare_init(&s->m_loop, &s->m_shutdownPrepare);
|
||||
s->m_shutdownPrepare.data = s;
|
||||
|
||||
uv_timer_init(&s->m_loop, &s->m_shutdownTimer);
|
||||
s->m_shutdownTimer.data = s;
|
||||
s->m_shutdownCountdown = 30;
|
||||
|
||||
uv_timer_start(&s->m_shutdownTimer,
|
||||
[](uv_timer_t* h)
|
||||
{
|
||||
TCPServer* s = reinterpret_cast<TCPServer*>(h->data);
|
||||
const uint32_t k = --s->m_shutdownCountdown;
|
||||
if (k > 0) {
|
||||
LOGINFO(1, "waiting for event loop to stop for " << k << " more seconds (" << s->m_numHandles << " handles left)...");
|
||||
}
|
||||
else {
|
||||
LOGINFO(1, "force stopping the event loop...");
|
||||
uv_timer_stop(&s->m_shutdownTimer);
|
||||
uv_prepare_stop(&s->m_shutdownPrepare);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&s->m_shutdownTimer), nullptr);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&s->m_shutdownPrepare), nullptr);
|
||||
uv_stop(&s->m_loop);
|
||||
}
|
||||
}, 1000, 1000);
|
||||
|
||||
uv_prepare_start(&s->m_shutdownPrepare,
|
||||
[](uv_prepare_t* h)
|
||||
{
|
||||
TCPServer* s = reinterpret_cast<TCPServer*>(h->data);
|
||||
|
||||
s->m_numHandles = 0;
|
||||
uv_walk(&s->m_loop, [](uv_handle_t*, void* n) { (*reinterpret_cast<size_t*>(n))++; }, &s->m_numHandles);
|
||||
|
||||
if (s->m_numHandles > 2) {
|
||||
// Don't count m_shutdownTimer and m_shutdownPrepare
|
||||
s->m_numHandles -= 2;
|
||||
}
|
||||
else {
|
||||
uv_timer_stop(&s->m_shutdownTimer);
|
||||
uv_prepare_stop(&s->m_shutdownPrepare);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&s->m_shutdownTimer), nullptr);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&s->m_shutdownPrepare), nullptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::Client()
|
||||
: m_owner(nullptr)
|
||||
@@ -845,10 +867,12 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::Client()
|
||||
, m_isV6(false)
|
||||
, m_isIncoming(false)
|
||||
, m_readBufInUse(false)
|
||||
, m_isClosing(false)
|
||||
, m_numRead(0)
|
||||
, m_addr{}
|
||||
, m_port(0)
|
||||
, m_addrString{}
|
||||
, m_socks5ProxyState(Socks5ProxyState::Default)
|
||||
, m_resetCounter{ 0 }
|
||||
{
|
||||
m_readBuf[0] = '\0';
|
||||
@@ -867,10 +891,14 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::reset()
|
||||
m_isV6 = false;
|
||||
m_isIncoming = false;
|
||||
m_readBufInUse = false;
|
||||
m_isClosing = false;
|
||||
m_numRead = 0;
|
||||
m_addr = {};
|
||||
m_port = -1;
|
||||
m_addrString[0] = '\0';
|
||||
m_socks5ProxyState = Socks5ProxyState::Default;
|
||||
m_readBuf[0] = '\0';
|
||||
m_readBuf[READ_BUF_SIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
@@ -900,29 +928,161 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::on_alloc(uv_handle_t* han
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
|
||||
{
|
||||
Client* pThis = static_cast<Client*>(stream->data);
|
||||
pThis->m_readBufInUse = false;
|
||||
Client* client = static_cast<Client*>(stream->data);
|
||||
client->m_readBufInUse = false;
|
||||
|
||||
if (client->m_isClosing) {
|
||||
LOGWARN(5, "client " << static_cast<const char*>(client->m_addrString) << " is being disconnected but data received from it, nread = " << nread << ". Ignoring it.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nread > 0) {
|
||||
if (pThis->m_owner && !pThis->m_owner->m_finished.load()) {
|
||||
if (!pThis->on_read(buf->base, static_cast<uint32_t>(nread))) {
|
||||
pThis->close();
|
||||
if (client->m_owner && !client->m_owner->m_finished.load()) {
|
||||
if (client->m_socks5ProxyState == Socks5ProxyState::Default) {
|
||||
if (!client->on_read(buf->base, static_cast<uint32_t>(nread))) {
|
||||
client->close();
|
||||
}
|
||||
}
|
||||
else if (!client->on_proxy_handshake(buf->base, static_cast<uint32_t>(nread))) {
|
||||
client->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (nread < 0) {
|
||||
if (nread != UV_EOF) {
|
||||
const int err = static_cast<int>(nread);
|
||||
LOGWARN(5, "client " << static_cast<const char*>(pThis->m_addrString) << " failed to read response, err = " << uv_err_name(err));
|
||||
pThis->on_read_failed(err);
|
||||
LOGWARN(5, "client " << static_cast<const char*>(client->m_addrString) << " failed to read response, err = " << uv_err_name(err));
|
||||
client->on_read_failed(err);
|
||||
}
|
||||
else {
|
||||
pThis->on_disconnected();
|
||||
client->on_disconnected();
|
||||
}
|
||||
pThis->close();
|
||||
client->close();
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::on_proxy_handshake(char* data, uint32_t size)
|
||||
{
|
||||
if ((data != m_readBuf + m_numRead) || (data + size > m_readBuf + sizeof(m_readBuf))) {
|
||||
LOGERR(1, "peer " << static_cast<char*>(m_addrString) << " invalid data pointer or size in on_read()");
|
||||
return false;
|
||||
}
|
||||
m_numRead += size;
|
||||
|
||||
uint32_t n = 0;
|
||||
|
||||
switch (m_socks5ProxyState) {
|
||||
case Socks5ProxyState::MethodSelectionSent:
|
||||
if (m_numRead >= 2) {
|
||||
if ((m_readBuf[0] != 5) && (m_readBuf[1] != 0)) {
|
||||
LOGWARN(5, "SOCKS5 proxy returned an invalid METHOD selection message");
|
||||
return false;
|
||||
}
|
||||
n = 2;
|
||||
|
||||
const bool result = m_owner->send(this,
|
||||
[this](void* buf, size_t buf_size) -> size_t
|
||||
{
|
||||
if (buf_size < 22) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t* p = reinterpret_cast<uint8_t*>(buf);
|
||||
p[0] = 5; // Protocol version (SOCKS5)
|
||||
p[1] = 1; // CONNECT
|
||||
p[2] = 0; // RESERVED
|
||||
if (m_isV6) {
|
||||
p[3] = 4; // ATYP
|
||||
memcpy(p + 4, m_addr.data, 16);
|
||||
p[20] = static_cast<uint8_t>(m_port >> 8);
|
||||
p[21] = static_cast<uint8_t>(m_port & 0xFF);
|
||||
}
|
||||
else {
|
||||
p[3] = 1; // ATYP
|
||||
memcpy(p + 4, m_addr.data + 12, 4);
|
||||
p[8] = static_cast<uint8_t>(m_port >> 8);
|
||||
p[9] = static_cast<uint8_t>(m_port & 0xFF);
|
||||
}
|
||||
|
||||
return m_isV6 ? 22 : 10;
|
||||
});
|
||||
|
||||
if (result) {
|
||||
m_socks5ProxyState = Socks5ProxyState::ConnectRequestSent;
|
||||
}
|
||||
else {
|
||||
close();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Socks5ProxyState::ConnectRequestSent:
|
||||
if (m_numRead >= 4) {
|
||||
uint8_t* p = reinterpret_cast<uint8_t*>(m_readBuf);
|
||||
if ((p[0] != 5) && (p[1] != 0) && p[2] != 0) {
|
||||
LOGWARN(5, "SOCKS5 proxy returned an invalid reply to CONNECT");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (p[3]) {
|
||||
case 1:
|
||||
if (m_numRead >= 10) {
|
||||
m_socks5ProxyState = Socks5ProxyState::Default;
|
||||
n = 10;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (m_numRead >= 5) {
|
||||
const uint32_t len = p[4];
|
||||
if (m_numRead >= 7 + len) {
|
||||
m_socks5ProxyState = Socks5ProxyState::Default;
|
||||
n = 7 + len;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (m_numRead >= 22) {
|
||||
m_socks5ProxyState = Socks5ProxyState::Default;
|
||||
n = 22;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGWARN(5, "SOCKS5 proxy returned an invalid reply to CONNECT (invalid address type " << p[3] << ')');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move the possible unfinished message to the beginning of m_readBuf to free up more space for reading
|
||||
if (n > 0) {
|
||||
m_numRead -= n;
|
||||
if (m_numRead > 0) {
|
||||
memmove(m_readBuf, m_readBuf + n, m_numRead);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_socks5ProxyState == Socks5ProxyState::Default) {
|
||||
if (!on_connect()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_numRead > 0) {
|
||||
const uint32_t nread = m_numRead;
|
||||
m_numRead = 0;
|
||||
if (!on_read(m_readBuf, nread)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -931,7 +1091,6 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::on_write(uv_write_t* req,
|
||||
TCPServer* server = client->m_owner;
|
||||
|
||||
if (server) {
|
||||
MutexLock lock(server->m_writeBuffersLock);
|
||||
server->m_writeBuffers.push_back(buf);
|
||||
}
|
||||
|
||||
@@ -944,11 +1103,13 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::on_write(uv_write_t* req,
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::close()
|
||||
{
|
||||
if (!m_owner) {
|
||||
if (m_isClosing || !m_owner) {
|
||||
// Already closed
|
||||
return;
|
||||
}
|
||||
|
||||
m_isClosing = true;
|
||||
|
||||
uv_read_stop(reinterpret_cast<uv_stream_t*>(&m_socket));
|
||||
|
||||
uv_tcp_t* s = &m_socket;
|
||||
@@ -968,16 +1129,16 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::ban(uint64_t seconds)
|
||||
}
|
||||
|
||||
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::init_addr_string(bool is_v6, const sockaddr_storage* peer_addr)
|
||||
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::init_addr_string()
|
||||
{
|
||||
const char* addr_str;
|
||||
char addr_str_buf[64];
|
||||
|
||||
if (is_v6) {
|
||||
addr_str = inet_ntop(AF_INET6, &reinterpret_cast<const sockaddr_in6*>(peer_addr)->sin6_addr, addr_str_buf, sizeof(addr_str_buf));
|
||||
if (m_isV6) {
|
||||
addr_str = inet_ntop(AF_INET6, m_addr.data, addr_str_buf, sizeof(addr_str_buf));
|
||||
}
|
||||
else {
|
||||
addr_str = inet_ntop(AF_INET, &reinterpret_cast<const sockaddr_in*>(peer_addr)->sin_addr, addr_str_buf, sizeof(addr_str_buf));
|
||||
addr_str = inet_ntop(AF_INET, m_addr.data + 12, addr_str_buf, sizeof(addr_str_buf));
|
||||
}
|
||||
|
||||
if (addr_str) {
|
||||
@@ -987,11 +1148,11 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::init_addr_string(bool is_
|
||||
}
|
||||
|
||||
log::Stream s(m_addrString);
|
||||
if (is_v6) {
|
||||
s << '[' << log::const_buf(addr_str, n) << "]:" << ntohs(reinterpret_cast<const sockaddr_in6*>(peer_addr)->sin6_port) << '\0';
|
||||
if (m_isV6) {
|
||||
s << '[' << log::const_buf(addr_str, n) << "]:" << m_port << '\0';
|
||||
}
|
||||
else {
|
||||
s << log::const_buf(addr_str, n) << ':' << ntohs(reinterpret_cast<const sockaddr_in*>(peer_addr)->sin_port) << '\0';
|
||||
s << log::const_buf(addr_str, n) << ':' << m_port << '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+68
-1
@@ -32,7 +32,7 @@ namespace p2pool {
|
||||
#define STR2(X) STR(X)
|
||||
#define STR(X) #X
|
||||
|
||||
const char* VERSION = "v2.2 (built"
|
||||
const char* VERSION = "v2.5 (built"
|
||||
#if defined(__clang__)
|
||||
" with clang/" __clang_version__
|
||||
#elif defined(__GNUC__)
|
||||
@@ -205,6 +205,15 @@ std::istream& operator>>(std::istream& s, hash& h)
|
||||
return s;
|
||||
}
|
||||
|
||||
void uv_cond_init_checked(uv_cond_t* cond)
|
||||
{
|
||||
const int result = uv_cond_init(cond);
|
||||
if (result) {
|
||||
LOGERR(1, "failed to create conditional variable, error " << uv_err_name(result));
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
void uv_mutex_init_checked(uv_mutex_t* mutex)
|
||||
{
|
||||
const int result = uv_mutex_init(mutex);
|
||||
@@ -344,8 +353,15 @@ static thread_local bool main_thread = false;
|
||||
void set_main_thread() { main_thread = true; }
|
||||
bool is_main_thread() { return main_thread; }
|
||||
|
||||
bool disable_resolve_host = false;
|
||||
|
||||
bool resolve_host(std::string& host, bool& is_v6)
|
||||
{
|
||||
if (disable_resolve_host) {
|
||||
LOGERR(1, "resolve_host was called with DNS disabled for host " << host);
|
||||
return false;
|
||||
}
|
||||
|
||||
addrinfo hints{};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
@@ -423,6 +439,57 @@ NOINLINE uint64_t bsr_reference(uint64_t x)
|
||||
return bsr8_table.data[y >> 24] - n0 - n1 - n2;
|
||||
}
|
||||
|
||||
bool str_to_ip(bool is_v6, const char* ip, raw_ip& result)
|
||||
{
|
||||
sockaddr_storage addr;
|
||||
|
||||
if (is_v6) {
|
||||
sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(&addr);
|
||||
const int err = uv_ip6_addr(ip, 0, addr6);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to parse IPv6 address " << ip << ", error " << uv_err_name(err));
|
||||
return false;
|
||||
}
|
||||
memcpy(result.data, &addr6->sin6_addr, sizeof(in6_addr));
|
||||
}
|
||||
else {
|
||||
sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(&addr);
|
||||
const int err = uv_ip4_addr(ip, 0, addr4);
|
||||
if (err) {
|
||||
LOGERR(1, "failed to parse IPv4 address " << ip << ", error " << uv_err_name(err));
|
||||
return false;
|
||||
}
|
||||
result = {};
|
||||
result.data[10] = 0xFF;
|
||||
result.data[11] = 0xFF;
|
||||
memcpy(result.data + 12, &addr4->sin_addr, sizeof(in_addr));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_localhost(const std::string& host)
|
||||
{
|
||||
if (host.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (host.compare("localhost") == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (host.find_first_not_of("0123456789.:") != std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
raw_ip addr;
|
||||
if (!str_to_ip(host.find(':') != std::string::npos, host.c_str(), addr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return addr.is_localhost();
|
||||
}
|
||||
|
||||
UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create)
|
||||
{
|
||||
UV_LoopUserData* data = reinterpret_cast<UV_LoopUserData*>(loop->data);
|
||||
|
||||
+5
-1
@@ -68,7 +68,7 @@ private:
|
||||
|
||||
template<typename T> FORCEINLINE ScopeGuard<T> on_scope_leave(T&& handler) { return ScopeGuard<T>(std::move(handler)); }
|
||||
|
||||
#define ON_SCOPE_LEAVE(x) auto CONCAT(scope_guard_, __LINE__) = on_scope_leave(x);
|
||||
#define ON_SCOPE_LEAVE(...) auto CONCAT(scope_guard_, __LINE__) = on_scope_leave(__VA_ARGS__);
|
||||
|
||||
struct MinerCallbackHandler
|
||||
{
|
||||
@@ -171,6 +171,7 @@ extern BackgroundJobTracker bkg_jobs_tracker;
|
||||
void set_main_thread();
|
||||
bool is_main_thread();
|
||||
|
||||
extern bool disable_resolve_host;
|
||||
bool resolve_host(std::string& host, bool& is_v6);
|
||||
|
||||
template <typename Key, typename T>
|
||||
@@ -219,6 +220,9 @@ FORCEINLINE uint64_t bsr(uint64_t x)
|
||||
#define bsr bsr_reference
|
||||
#endif
|
||||
|
||||
bool str_to_ip(bool is_v6, const char* ip, raw_ip& result);
|
||||
bool is_localhost(const std::string& host);
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
void memory_tracking_start();
|
||||
|
||||
+26
-2
@@ -57,6 +57,7 @@ private:
|
||||
|
||||
typedef RWLock<true> WriteLock;
|
||||
|
||||
void uv_cond_init_checked(uv_cond_t* cond);
|
||||
void uv_mutex_init_checked(uv_mutex_t* mutex);
|
||||
void uv_rwlock_init_checked(uv_rwlock_t* lock);
|
||||
uv_loop_t* uv_default_loop_checked();
|
||||
@@ -137,9 +138,12 @@ struct UV_LoopUserData
|
||||
UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create = true);
|
||||
|
||||
template<typename T>
|
||||
void CallOnLoop(uv_loop_t* loop, T&& callback)
|
||||
bool CallOnLoop(uv_loop_t* loop, T&& callback)
|
||||
{
|
||||
UV_LoopUserData* data = GetLoopUserData(loop, false);
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UV_LoopCallbackBase* cb = new UV_LoopCallback<T>(std::move(callback));
|
||||
{
|
||||
@@ -147,7 +151,27 @@ void CallOnLoop(uv_loop_t* loop, T&& callback)
|
||||
data->m_callbacks.push_back(cb);
|
||||
}
|
||||
|
||||
uv_async_send(data->m_async);
|
||||
if (uv_async_send(data->m_async) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clean up after uv_async_send error
|
||||
bool found = false;
|
||||
{
|
||||
MutexLock lock(data->m_callbacksLock);
|
||||
|
||||
auto it = std::find(data->m_callbacks.begin(), data->m_callbacks.end(), cb);
|
||||
if (it != data->m_callbacks.end()) {
|
||||
found = true;
|
||||
data->m_callbacks.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
delete cb;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+15
-5
@@ -24,14 +24,20 @@ static constexpr char log_category_prefix[] = "ZMQReader ";
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
ZMQReader::ZMQReader(const char* address, uint32_t zmq_port, MinerCallbackHandler* handler)
|
||||
ZMQReader::ZMQReader(const std::string& address, uint32_t zmq_port, const std::string& proxy, MinerCallbackHandler* handler)
|
||||
: m_address(address)
|
||||
, m_zmqPort(zmq_port)
|
||||
, m_proxy(proxy)
|
||||
, m_handler(handler)
|
||||
, m_tx()
|
||||
, m_minerData()
|
||||
, m_chainmainData()
|
||||
{
|
||||
if (!m_proxy.empty() && is_localhost(address)) {
|
||||
LOGINFO(5, "not using proxy to connect to localhost address " << log::Gray() << address);
|
||||
m_proxy.clear();
|
||||
}
|
||||
|
||||
for (uint32_t i = m_publisherPort; i < std::numeric_limits<uint16_t>::max(); ++i) {
|
||||
try {
|
||||
m_publisherPort = 0;
|
||||
@@ -84,14 +90,18 @@ void ZMQReader::run_wrapper(void* arg)
|
||||
void ZMQReader::run()
|
||||
{
|
||||
try {
|
||||
char addr[32];
|
||||
if (!m_proxy.empty()) {
|
||||
m_subscriber.set(zmq::sockopt::socks_proxy, zmq::const_buffer(m_proxy.c_str(), m_proxy.length()));
|
||||
}
|
||||
|
||||
snprintf(addr, sizeof(addr), "tcp://%s:%u", m_address, m_zmqPort);
|
||||
std::string addr = "tcp://" + m_address + ':' + std::to_string(m_zmqPort);
|
||||
if (!connect(addr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(addr, sizeof(addr), "tcp://127.0.0.1:%u", m_publisherPort);
|
||||
m_subscriber.set(zmq::sockopt::socks_proxy, zmq::const_buffer());
|
||||
|
||||
addr = "tcp://127.0.0.1:" + std::to_string(m_publisherPort);
|
||||
if (!connect(addr)) {
|
||||
return;
|
||||
}
|
||||
@@ -128,7 +138,7 @@ void ZMQReader::run()
|
||||
}
|
||||
}
|
||||
|
||||
bool ZMQReader::connect(const char* address)
|
||||
bool ZMQReader::connect(const std::string& address)
|
||||
{
|
||||
struct ConnectMonitor : public zmq::monitor_t
|
||||
{
|
||||
|
||||
+4
-3
@@ -24,18 +24,19 @@ namespace p2pool {
|
||||
|
||||
class ZMQReader {
|
||||
public:
|
||||
ZMQReader(const char* address, uint32_t zmq_port, MinerCallbackHandler* handler);
|
||||
ZMQReader(const std::string& address, uint32_t zmq_port, const std::string& proxy, MinerCallbackHandler* handler);
|
||||
~ZMQReader();
|
||||
|
||||
private:
|
||||
static void run_wrapper(void* arg);
|
||||
void run();
|
||||
bool connect(const char* address);
|
||||
bool connect(const std::string& address);
|
||||
|
||||
void parse(char* data, size_t size);
|
||||
|
||||
const char* m_address;
|
||||
std::string m_address;
|
||||
uint32_t m_zmqPort;
|
||||
std::string m_proxy;
|
||||
MinerCallbackHandler* m_handler;
|
||||
|
||||
uv_thread_t m_worker{};
|
||||
|
||||
@@ -70,6 +70,9 @@ include_directories(googletest/googletest/include)
|
||||
|
||||
if (WIN32)
|
||||
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi wldap32)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||
set(LIBS ${LIBS} bcrypt)
|
||||
endif()
|
||||
add_definitions(-DCURL_STATICLIB)
|
||||
elseif (NOT APPLE)
|
||||
set(LIBS ${LIBS} pthread gss dl)
|
||||
@@ -90,6 +93,8 @@ else()
|
||||
find_library(UV_LIBRARY NAMES uv_a PATHS "../external/lib/libuv/Release")
|
||||
find_library(CURL_LIBRARY_DEBUG NAMES libcurld PATHS "../external/lib/libcurl/Debug")
|
||||
find_library(CURL_LIBRARY NAMES libcurl PATHS "../external/lib/libcurl/Release")
|
||||
add_definitions(-D_DISABLE_VECTOR_ANNOTATION)
|
||||
add_definitions(-D_DISABLE_STRING_ANNOTATION)
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
|
||||
find_library(ZMQ_LIBRARY_DEBUG NAMES zmq libzmq.a)
|
||||
find_library(ZMQ_LIBRARY NAMES zmq libzmq.a)
|
||||
@@ -148,3 +153,4 @@ target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${U
|
||||
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/crypto_tests.txt" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
|
||||
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/mainnet_test2_block.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
|
||||
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/sidechain_dump.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
|
||||
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/sidechain_dump_mini.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
|
||||
|
||||
+1
-1
Submodule tests/googletest updated: 955c7f837e...58d77fa807
@@ -49,13 +49,18 @@ TEST(pool_block, deserialize)
|
||||
f.read(reinterpret_cast<char*>(buf.data()), buf.size());
|
||||
ASSERT_EQ(f.good(), true);
|
||||
|
||||
ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain), 0);
|
||||
ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain, nullptr), 0);
|
||||
|
||||
size_t header_size, miner_tx_size;
|
||||
int outputs_offset, outputs_blob_size;
|
||||
const std::vector<uint8_t> mainchain_data = b.serialize_mainchain_data(&header_size, &miner_tx_size, &outputs_offset, &outputs_blob_size);
|
||||
|
||||
ASSERT_EQ(mainchain_data.size(), 5607);
|
||||
ASSERT_EQ(header_size, 43);
|
||||
ASSERT_EQ(miner_tx_size, 506);
|
||||
ASSERT_EQ(outputs_offset, 54);
|
||||
ASSERT_EQ(outputs_blob_size, 420);
|
||||
|
||||
ASSERT_EQ(b.m_mainChainData.size(), 5607);
|
||||
ASSERT_EQ(b.m_mainChainHeaderSize, 43);
|
||||
ASSERT_EQ(b.m_mainChainMinerTxSize, 506);
|
||||
ASSERT_EQ(b.m_mainChainOutputsOffset, 54);
|
||||
ASSERT_EQ(b.m_mainChainOutputsBlobSize, 420);
|
||||
ASSERT_EQ(b.m_majorVersion, 14);
|
||||
ASSERT_EQ(b.m_minorVersion, 14);
|
||||
ASSERT_EQ(b.m_timestamp, 1630934403);
|
||||
@@ -65,14 +70,12 @@ TEST(pool_block, deserialize)
|
||||
ASSERT_EQ(b.m_extraNonceSize, 4);
|
||||
ASSERT_EQ(b.m_extraNonce, 28);
|
||||
ASSERT_EQ(b.m_transactions.size(), 159);
|
||||
ASSERT_EQ(b.m_sideChainData.size(), 146);
|
||||
ASSERT_EQ(b.m_uncles.size(), 0);
|
||||
ASSERT_EQ(b.m_sidechainHeight, 53450);
|
||||
ASSERT_EQ(b.m_difficulty.lo, 319296691);
|
||||
ASSERT_EQ(b.m_difficulty.hi, 0);
|
||||
ASSERT_EQ(b.m_cumulativeDifficulty.lo, 12544665764606ull);
|
||||
ASSERT_EQ(b.m_cumulativeDifficulty.hi, 0);
|
||||
ASSERT_EQ(b.m_tmpTxExtra.size(), 0);
|
||||
ASSERT_EQ(b.m_depth, 0);
|
||||
ASSERT_EQ(b.m_verified, false);
|
||||
ASSERT_EQ(b.m_invalid, false);
|
||||
@@ -104,38 +107,52 @@ TEST(pool_block, verify)
|
||||
{
|
||||
init_crypto_cache();
|
||||
|
||||
PoolBlock b;
|
||||
SideChain sidechain(nullptr, NetworkType::Mainnet);
|
||||
struct STest
|
||||
{
|
||||
const char* m_poolName;
|
||||
const char* m_fileName;
|
||||
uint64_t m_txinGenHeight;
|
||||
uint64_t m_sidechainHeight;
|
||||
} tests[2] = {
|
||||
{ "default", "sidechain_dump.dat", 2483901, 522805 },
|
||||
{ "mini", "sidechain_dump_mini.dat", 2696040, 2424349 },
|
||||
};
|
||||
|
||||
std::ifstream f("sidechain_dump.dat", std::ios::binary | std::ios::ate);
|
||||
ASSERT_EQ(f.good() && f.is_open(), true);
|
||||
for (const STest& t : tests)
|
||||
{
|
||||
PoolBlock b;
|
||||
SideChain sidechain(nullptr, NetworkType::Mainnet, t.m_poolName);
|
||||
|
||||
std::vector<uint8_t> buf(f.tellg());
|
||||
f.seekg(0);
|
||||
f.read(reinterpret_cast<char*>(buf.data()), buf.size());
|
||||
ASSERT_EQ(f.good(), true);
|
||||
std::ifstream f(t.m_fileName, std::ios::binary | std::ios::ate);
|
||||
ASSERT_EQ(f.good() && f.is_open(), 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);
|
||||
std::vector<uint8_t> buf(f.tellg());
|
||||
f.seekg(0);
|
||||
f.read(reinterpret_cast<char*>(buf.data()), buf.size());
|
||||
ASSERT_EQ(f.good(), true);
|
||||
|
||||
ASSERT_TRUE(p + n <= e);
|
||||
ASSERT_EQ(b.deserialize(p, n, sidechain), 0);
|
||||
p += n;
|
||||
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);
|
||||
|
||||
sidechain.add_block(b);
|
||||
ASSERT_TRUE(sidechain.find_block(b.m_sidechainId) != nullptr);
|
||||
ASSERT_TRUE(p + n <= e);
|
||||
ASSERT_EQ(b.deserialize(p, n, sidechain, nullptr), 0);
|
||||
p += n;
|
||||
|
||||
sidechain.add_block(b);
|
||||
ASSERT_TRUE(sidechain.find_block(b.m_sidechainId) != nullptr);
|
||||
}
|
||||
|
||||
const PoolBlock* tip = sidechain.chainTip();
|
||||
ASSERT_TRUE(tip != nullptr);
|
||||
ASSERT_TRUE(tip->m_verified);
|
||||
ASSERT_FALSE(tip->m_invalid);
|
||||
|
||||
ASSERT_EQ(tip->m_txinGenHeight, t.m_txinGenHeight);
|
||||
ASSERT_EQ(tip->m_sidechainHeight, t.m_sidechainHeight);
|
||||
}
|
||||
|
||||
const PoolBlock* tip = sidechain.chainTip();
|
||||
ASSERT_TRUE(tip != nullptr);
|
||||
ASSERT_TRUE(tip->m_verified);
|
||||
ASSERT_FALSE(tip->m_invalid);
|
||||
|
||||
ASSERT_EQ(tip->m_txinGenHeight, 2483901);
|
||||
ASSERT_EQ(tip->m_sidechainHeight, 522805);
|
||||
|
||||
destroy_crypto_cache();
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user