mirror of
https://github.com/wownero/p2pool-wow.git
synced 2026-10-30 20:48:37 +01:00
Compare 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 | ||
|
|
c5ea4dca67 | ||
|
|
deaf47155f | ||
|
|
a1d1420ec3 | ||
|
|
a133a25cc3 | ||
|
|
65e15b5205 | ||
|
|
ac123939e5 | ||
|
|
2cafc570a8 | ||
|
|
9e5d5cecd4 | ||
|
|
deb8d59130 | ||
|
|
14bbf96feb | ||
|
|
ee1c5a0a56 | ||
|
|
a281db1cbb | ||
|
|
b25e5449c8 | ||
|
|
b267c19a45 | ||
|
|
0bda37057a | ||
|
|
2aa8d2bdcc | ||
|
|
eebcbb5cc2 | ||
|
|
acf37ff10f | ||
|
|
56c239a368 | ||
|
|
ae7386e7be | ||
|
|
9ddf510716 | ||
|
|
d79fefe546 | ||
|
|
921cbb0b69 | ||
|
|
7f1b849394 | ||
|
|
9f4388843a | ||
|
|
37fba30c79 | ||
|
|
8f1fd55e2f | ||
|
|
8af9e9b27d | ||
|
|
8b681d6efb | ||
|
|
75b9a0228b | ||
|
|
cbc00e6416 | ||
|
|
a0778c8d7b | ||
|
|
0c4dc82c65 | ||
|
|
a35d4b2701 | ||
|
|
05b0973a23 | ||
|
|
09f46beeb4 | ||
|
|
b4c7eea4d9 | ||
|
|
8159ecfce4 | ||
|
|
662e2b5ba0 | ||
|
|
5ca9f968aa | ||
|
|
6ef34bb3f5 | ||
|
|
9a5be0a321 | ||
|
|
c203f6eb0a | ||
|
|
b713f4e702 | ||
|
|
111087d96a | ||
|
|
9e7666d12f | ||
|
|
cb147773b5 | ||
|
|
d84bef8f14 | ||
|
|
32bb318af4 | ||
|
|
055b5e2256 | ||
|
|
c1754d122f | ||
|
|
6d121122f8 | ||
|
|
bfa8334aeb | ||
|
|
0e222afd6a | ||
|
|
b191962d32 | ||
|
|
1cf13e142a | ||
|
|
8f1f81749d | ||
|
|
72d70eba16 | ||
|
|
ae3612f595 | ||
|
|
37b6457054 | ||
|
|
9579be5c7d | ||
|
|
d05d47fa75 | ||
|
|
bd88c04e9e | ||
|
|
eb3a3872eb | ||
|
|
5cea7be405 | ||
|
|
516c382fe4 | ||
|
|
710e55422a | ||
|
|
3406cf78b4 | ||
|
|
8fa354796c | ||
|
|
6c6ef1c1b8 | ||
|
|
c3c326c680 | ||
|
|
92e20485b2 | ||
|
|
edd782ac23 | ||
|
|
a4d84e318d | ||
|
|
08ee599f79 | ||
|
|
134f2d68a3 | ||
|
|
d23c46ff84 | ||
|
|
820c5db5e8 | ||
|
|
990916ab62 | ||
|
|
d4e362cd76 | ||
|
|
1dd06cc509 | ||
|
|
2e2bd1d137 | ||
|
|
0818d91f4f | ||
|
|
6e1f34b168 | ||
|
|
1a35177633 | ||
|
|
c45689b02b | ||
|
|
8f27d940e3 | ||
|
|
6b4640c413 |
@@ -0,0 +1,2 @@
|
|||||||
|
**/build
|
||||||
|
**/docker-compose
|
||||||
+177
-29
@@ -1,10 +1,6 @@
|
|||||||
name: C/C++ CI
|
name: C/C++ CI
|
||||||
|
|
||||||
on:
|
on: [push, pull_request]
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-ubuntu:
|
build-ubuntu:
|
||||||
@@ -14,18 +10,18 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
config:
|
config:
|
||||||
- {os: ubuntu-latest, c: gcc-11, cpp: g++-11}
|
- {os: ubuntu-20.04, c: gcc-11, cpp: g++-11}
|
||||||
- {os: ubuntu-18.04, c: gcc, cpp: g++}
|
- {os: ubuntu-22.04, c: gcc-12, cpp: g++-12}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install -y git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev ${{ matrix.config.c }} ${{ matrix.config.cpp }}
|
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
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
@@ -34,7 +30,7 @@ jobs:
|
|||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
|
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
|
||||||
make -j2
|
make -j$(nproc)
|
||||||
|
|
||||||
- name: Build tests
|
- name: Build tests
|
||||||
run: |
|
run: |
|
||||||
@@ -42,17 +38,139 @@ jobs:
|
|||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
|
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
|
||||||
make -j2
|
make -j$(nproc)
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cd tests/build && ./p2pool_tests
|
run: cd tests/build && ./p2pool_tests
|
||||||
|
|
||||||
- name: Archive binary
|
- name: Archive binary
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: p2pool-${{ matrix.config.os }}
|
name: p2pool-${{ matrix.config.os }}
|
||||||
path: build/p2pool
|
path: build/p2pool
|
||||||
|
|
||||||
|
build-ubuntu-static-libs:
|
||||||
|
|
||||||
|
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-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@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Build libcurl
|
||||||
|
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-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
|
||||||
|
run: |
|
||||||
|
cd external/src/libuv
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DBUILD_TESTING=OFF
|
||||||
|
make -j$(nproc)
|
||||||
|
|
||||||
|
- name: Build libzmq
|
||||||
|
run: |
|
||||||
|
cd external/src/libzmq
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
|
||||||
|
make -j$(nproc)
|
||||||
|
|
||||||
|
- name: Build p2pool
|
||||||
|
run: |
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DSTATIC_LIBS=ON
|
||||||
|
make -j$(nproc)
|
||||||
|
|
||||||
|
- name: Build tests
|
||||||
|
run: |
|
||||||
|
cd tests
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DSTATIC_LIBS=ON
|
||||||
|
make -j$(nproc)
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: cd tests/build && ./p2pool_tests
|
||||||
|
|
||||||
|
- name: Archive binary
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: p2pool-ubuntu-static-libs
|
||||||
|
path: build/p2pool
|
||||||
|
|
||||||
|
build-ubuntu-aarch64:
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.config.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
config:
|
||||||
|
- {os: ubuntu-18.04}
|
||||||
|
- {os: ubuntu-20.04}
|
||||||
|
- {os: ubuntu-22.04}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
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@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Build libcurl
|
||||||
|
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-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
|
||||||
|
run: |
|
||||||
|
cd external/src/libuv
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DBUILD_TESTING=OFF
|
||||||
|
make -j$(nproc)
|
||||||
|
|
||||||
|
- name: Build libzmq
|
||||||
|
run: |
|
||||||
|
cd external/src/libzmq
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
|
||||||
|
make -j$(nproc)
|
||||||
|
|
||||||
|
- name: Build p2pool
|
||||||
|
run: |
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DCMAKE_STRIP=/usr/bin/aarch64-linux-gnu-strip -DSTATIC_LIBS=ON -DARCH_ID=aarch64
|
||||||
|
make -j$(nproc)
|
||||||
|
|
||||||
|
- name: Archive binary
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: p2pool-${{ matrix.config.os }}-aarch64
|
||||||
|
path: build/p2pool
|
||||||
|
|
||||||
build-windows-msys2:
|
build-windows-msys2:
|
||||||
|
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
@@ -63,7 +181,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
@@ -71,22 +189,45 @@ jobs:
|
|||||||
uses: eine/setup-msys2@v2
|
uses: eine/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
update: true
|
update: true
|
||||||
install: mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-libuv
|
install: mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake make autoconf libtool automake
|
||||||
|
|
||||||
|
- name: Build libcurl
|
||||||
|
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-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
|
||||||
|
run: |
|
||||||
|
cd external/src/libuv
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -G "Unix Makefiles" -DBUILD_TESTING=OFF
|
||||||
|
make -j$(nproc)
|
||||||
|
|
||||||
|
- name: Build libzmq
|
||||||
|
run: |
|
||||||
|
cd external/src/libzmq
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -G "Unix Makefiles" -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF -DZMQ_HAVE_IPC=OFF
|
||||||
|
make -j$(nproc)
|
||||||
|
|
||||||
- name: Build p2pool
|
- name: Build p2pool
|
||||||
run: |
|
run: |
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -G "Unix Makefiles"
|
cmake .. -G "Unix Makefiles" -DSTATIC_LIBS=ON
|
||||||
make -j2
|
make -j$(nproc)
|
||||||
|
|
||||||
- name: Build tests
|
- name: Build tests
|
||||||
run: |
|
run: |
|
||||||
cd tests
|
cd tests
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -G "Unix Makefiles"
|
cmake .. -G "Unix Makefiles" -DSTATIC_LIBS=ON
|
||||||
make -j2
|
make -j$(nproc)
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
@@ -94,7 +235,7 @@ jobs:
|
|||||||
./p2pool_tests.exe
|
./p2pool_tests.exe
|
||||||
|
|
||||||
- name: Archive binary
|
- name: Archive binary
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: p2pool-msys2.exe
|
name: p2pool-msys2.exe
|
||||||
path: build/p2pool.exe
|
path: build/p2pool.exe
|
||||||
@@ -112,7 +253,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
@@ -132,15 +273,15 @@ jobs:
|
|||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -G "${{ matrix.config.vs }}"
|
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
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
cd tests/build/Release
|
cd tests/build/Debug
|
||||||
./p2pool_tests.exe
|
./p2pool_tests.exe
|
||||||
|
|
||||||
- name: Archive binary
|
- name: Archive binary
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}.exe
|
name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}.exe
|
||||||
path: build/Release/p2pool.exe
|
path: build/Release/p2pool.exe
|
||||||
@@ -151,23 +292,30 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-10.15, macos-11]
|
os: [macos-11, macos-12]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
|
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake autoconf libtool automake
|
||||||
|
|
||||||
|
- name: Build libcurl
|
||||||
|
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-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
|
- name: Build libuv
|
||||||
run: |
|
run: |
|
||||||
cd external/src/libuv
|
cd external/src/libuv
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake .. -DBUILD_TESTING=OFF
|
||||||
make -j3
|
make -j3
|
||||||
|
|
||||||
- name: Build libzmq
|
- name: Build libzmq
|
||||||
@@ -175,7 +323,7 @@ jobs:
|
|||||||
cd external/src/libzmq
|
cd external/src/libzmq
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF
|
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
|
||||||
make -j3
|
make -j3
|
||||||
|
|
||||||
- name: Build p2pool
|
- name: Build p2pool
|
||||||
@@ -199,7 +347,7 @@ jobs:
|
|||||||
./p2pool_tests
|
./p2pool_tests
|
||||||
|
|
||||||
- name: Archive binary
|
- name: Archive binary
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: p2pool-${{ matrix.os }}
|
name: p2pool-${{ matrix.os }}
|
||||||
path: build/p2pool
|
path: build/p2pool
|
||||||
|
|||||||
@@ -13,10 +13,7 @@ name: "CodeQL"
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
|
||||||
branches: [ master ]
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '44 11 * * 0'
|
- cron: '44 11 * * 0'
|
||||||
|
|
||||||
@@ -40,16 +37,16 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev
|
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
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -60,7 +57,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v1
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -74,4 +71,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v1
|
uses: github/codeql-action/analyze@v2
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
name: cppcheck
|
name: cppcheck
|
||||||
|
|
||||||
on:
|
on: [push, pull_request]
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cppcheck-ubuntu:
|
cppcheck-ubuntu:
|
||||||
@@ -12,14 +8,22 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install cppcheck
|
|
||||||
run: sudo apt update && sudo apt install cppcheck
|
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
|
- name: Checkout cppcheck
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: danmar/cppcheck
|
||||||
|
path: cppcheck-main
|
||||||
|
|
||||||
|
- name: Build cppcheck
|
||||||
|
run: |
|
||||||
|
cd cppcheck-main
|
||||||
|
make -j$(nproc) cppcheck
|
||||||
|
|
||||||
- name: Run cppcheck
|
- name: Run cppcheck
|
||||||
run: |
|
run: |
|
||||||
cd cppcheck
|
cd cppcheck
|
||||||
@@ -30,14 +34,22 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install cppcheck
|
|
||||||
run: choco install cppcheck
|
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
|
- name: Checkout cppcheck
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: danmar/cppcheck
|
||||||
|
path: cppcheck-main
|
||||||
|
|
||||||
|
- name: Build cppcheck
|
||||||
|
run: |
|
||||||
|
cd cppcheck-main
|
||||||
|
& "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\msbuild.exe" /m /p:Configuration=Release /p:Platform=x64
|
||||||
|
|
||||||
- name: Setup cmake
|
- name: Setup cmake
|
||||||
uses: lukka/get-cmake@latest
|
uses: lukka/get-cmake@latest
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ name: Microsoft C++ Code Analysis
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '40 10 * * 0'
|
- cron: '40 10 * * 0'
|
||||||
|
|
||||||
@@ -28,7 +26,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
@@ -40,7 +38,7 @@ jobs:
|
|||||||
# run: cmake --build ${{ env.build }}
|
# run: cmake --build ${{ env.build }}
|
||||||
|
|
||||||
- name: Initialize MSVC Code Analysis
|
- 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
|
# Provide a unique ID to access the sarif output path
|
||||||
id: run-analysis
|
id: run-analysis
|
||||||
with:
|
with:
|
||||||
@@ -51,13 +49,13 @@ jobs:
|
|||||||
|
|
||||||
# Upload SARIF file to GitHub Code Scanning Alerts
|
# Upload SARIF file to GitHub Code Scanning Alerts
|
||||||
- name: Upload SARIF to GitHub
|
- name: Upload SARIF to GitHub
|
||||||
uses: github/codeql-action/upload-sarif@v1
|
uses: github/codeql-action/upload-sarif@v2
|
||||||
with:
|
with:
|
||||||
sarif_file: ${{ steps.run-analysis.outputs.sarif }}
|
sarif_file: ${{ steps.run-analysis.outputs.sarif }}
|
||||||
|
|
||||||
# Upload SARIF file as an Artifact to download and view
|
# Upload SARIF file as an Artifact to download and view
|
||||||
- name: Upload SARIF as an Artifact
|
- name: Upload SARIF as an Artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: sarif-file
|
name: sarif-file
|
||||||
path: ${{ steps.run-analysis.outputs.sarif }}
|
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
|
||||||
@@ -19,3 +19,6 @@
|
|||||||
[submodule "external/src/robin-hood-hashing"]
|
[submodule "external/src/robin-hood-hashing"]
|
||||||
path = external/src/robin-hood-hashing
|
path = external/src/robin-hood-hashing
|
||||||
url = https://github.com/SChernykh/robin-hood-hashing
|
url = https://github.com/SChernykh/robin-hood-hashing
|
||||||
|
[submodule "external/src/curl"]
|
||||||
|
path = external/src/curl
|
||||||
|
url = https://github.com/SChernykh/curl
|
||||||
|
|||||||
+82
-15
@@ -1,14 +1,15 @@
|
|||||||
cmake_minimum_required(VERSION 2.8.12)
|
cmake_minimum_required(VERSION 3.6)
|
||||||
project(p2pool)
|
project(p2pool)
|
||||||
|
|
||||||
option(STATIC_BINARY "Build static binary" OFF)
|
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(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(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
if (${CMAKE_VERSION} VERSION_GREATER "3.5.2")
|
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
|
||||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (WITH_RANDOMX)
|
if (WITH_RANDOMX)
|
||||||
add_definitions(-DWITH_RANDOMX)
|
add_definitions(-DWITH_RANDOMX)
|
||||||
@@ -16,11 +17,14 @@ if (WITH_RANDOMX)
|
|||||||
set(LIBS randomx)
|
set(LIBS randomx)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (DEV_TEST_SYNC)
|
||||||
|
add_definitions(-DDEV_TEST_SYNC)
|
||||||
|
endif()
|
||||||
|
|
||||||
include(cmake/flags.cmake)
|
include(cmake/flags.cmake)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
external/src/cryptonote/crypto-ops.h
|
external/src/cryptonote/crypto-ops.h
|
||||||
external/src/llhttp/llhttp.h
|
|
||||||
src/block_cache.h
|
src/block_cache.h
|
||||||
src/block_template.h
|
src/block_template.h
|
||||||
src/common.h
|
src/common.h
|
||||||
@@ -51,9 +55,6 @@ set(HEADERS
|
|||||||
set(SOURCES
|
set(SOURCES
|
||||||
external/src/cryptonote/crypto-ops-data.c
|
external/src/cryptonote/crypto-ops-data.c
|
||||||
external/src/cryptonote/crypto-ops.c
|
external/src/cryptonote/crypto-ops.c
|
||||||
external/src/llhttp/api.c
|
|
||||||
external/src/llhttp/http.c
|
|
||||||
external/src/llhttp/llhttp.c
|
|
||||||
src/block_cache.cpp
|
src/block_cache.cpp
|
||||||
src/block_template.cpp
|
src/block_template.cpp
|
||||||
src/console_commands.cpp
|
src/console_commands.cpp
|
||||||
@@ -82,13 +83,22 @@ if (WITH_RANDOMX)
|
|||||||
set(SOURCES ${SOURCES} src/miner.cpp)
|
set(SOURCES ${SOURCES} src/miner.cpp)
|
||||||
endif()
|
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(src)
|
||||||
include_directories(external/src)
|
include_directories(external/src)
|
||||||
include_directories(external/src/cryptonote)
|
include_directories(external/src/cryptonote)
|
||||||
include_directories(external/src/libuv/include)
|
include_directories(external/src/libuv/include)
|
||||||
include_directories(external/src/cppzmq)
|
include_directories(external/src/cppzmq)
|
||||||
include_directories(external/src/libzmq/include)
|
include_directories(external/src/libzmq/include)
|
||||||
include_directories(external/src/llhttp)
|
|
||||||
if (WITH_RANDOMX)
|
if (WITH_RANDOMX)
|
||||||
include_directories(external/src/RandomX/src)
|
include_directories(external/src/RandomX/src)
|
||||||
endif()
|
endif()
|
||||||
@@ -97,10 +107,14 @@ include_directories(external/src/robin-hood-hashing/src/include)
|
|||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
|
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||||
|
set(LIBS ${LIBS} bcrypt)
|
||||||
|
endif()
|
||||||
|
add_definitions(-DCURL_STATICLIB)
|
||||||
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
|
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
|
||||||
set(LIBS ${LIBS} pthread)
|
set(LIBS ${LIBS} pthread)
|
||||||
elseif (NOT APPLE)
|
elseif (NOT APPLE)
|
||||||
set(LIBS ${LIBS} pthread gss dl)
|
set(LIBS ${LIBS} pthread dl)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
||||||
@@ -108,11 +122,27 @@ if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
|||||||
find_library(ZMQ_LIBRARY NAMES libzmq-v142-mt-s-4_3_5 PATHS "external/lib/libzmq/Release")
|
find_library(ZMQ_LIBRARY NAMES libzmq-v142-mt-s-4_3_5 PATHS "external/lib/libzmq/Release")
|
||||||
find_library(UV_LIBRARY_DEBUG NAMES uv_a PATHS "external/lib/libuv/Debug")
|
find_library(UV_LIBRARY_DEBUG NAMES uv_a PATHS "external/lib/libuv/Debug")
|
||||||
find_library(UV_LIBRARY NAMES uv_a PATHS "external/lib/libuv/Release")
|
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)
|
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_DEBUG NAMES zmq libzmq.a)
|
||||||
find_library(ZMQ_LIBRARY NAMES zmq libzmq.a)
|
find_library(ZMQ_LIBRARY NAMES zmq libzmq.a)
|
||||||
find_library(UV_LIBRARY_DEBUG NAMES uv libuv.a)
|
find_library(UV_LIBRARY_DEBUG NAMES uv libuv.a)
|
||||||
find_library(UV_LIBRARY NAMES uv libuv.a)
|
find_library(UV_LIBRARY NAMES uv libuv.a)
|
||||||
|
if (WIN32)
|
||||||
|
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()
|
||||||
|
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)
|
find_library(SODIUM_LIBRARY sodium)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -131,28 +161,65 @@ if (SODIUM_LIBRARY)
|
|||||||
set(LIBS ${LIBS} ${SODIUM_LIBRARY})
|
set(LIBS ${LIBS} ${SODIUM_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
find_library(FOUNDATION_LIB Foundation)
|
||||||
|
find_library(CORE_FOUNDATION_LIB CoreFoundation)
|
||||||
|
find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration)
|
||||||
|
set(LIBS ${LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB})
|
||||||
|
endif()
|
||||||
|
|
||||||
add_definitions(/DZMQ_STATIC)
|
add_definitions(/DZMQ_STATIC)
|
||||||
|
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${GENERAL_FLAGS}")
|
||||||
|
|
||||||
|
include(CheckCXXSourceCompiles)
|
||||||
|
|
||||||
|
check_cxx_source_compiles("int main(){ return __builtin_clzll(1);}" HAVE_BUILTIN_CLZLL)
|
||||||
|
check_cxx_source_compiles("#include <intrin.h>\n#pragma intrinsic(_BitScanReverse64)\nint main(){unsigned long r;_BitScanReverse64(&r,1);return r;}" HAVE_BITSCANREVERSE64)
|
||||||
|
|
||||||
|
if (HAVE_BUILTIN_CLZLL)
|
||||||
|
add_definitions(/DHAVE_BUILTIN_CLZLL)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (HAVE_BITSCANREVERSE64)
|
||||||
|
add_definitions(/DHAVE_BITSCANREVERSE64)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
|
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
|
||||||
|
|
||||||
if (STATIC_BINARY)
|
if (STATIC_BINARY OR STATIC_LIBS)
|
||||||
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME})
|
if (WIN32)
|
||||||
|
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} "${CMAKE_PROJECT_NAME}.exe")
|
||||||
|
else()
|
||||||
|
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME})
|
||||||
|
endif()
|
||||||
|
|
||||||
if (WITH_RANDOMX)
|
if (WITH_RANDOMX)
|
||||||
set(STATIC_LIBS randomx)
|
set(STATIC_LIBS randomx)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
|
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)
|
set(STATIC_LIBS ${STATIC_LIBS} pthread)
|
||||||
elseif (NOT APPLE)
|
elseif (APPLE)
|
||||||
|
find_library(FOUNDATION_LIB Foundation)
|
||||||
|
find_library(CORE_FOUNDATION_LIB CoreFoundation)
|
||||||
|
find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration)
|
||||||
|
set(STATIC_LIBS ${STATIC_LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB})
|
||||||
|
else()
|
||||||
set(STATIC_LIBS ${STATIC_LIBS} pthread dl)
|
set(STATIC_LIBS ${STATIC_LIBS} pthread dl)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||||
"${CMAKE_SOURCE_DIR}/external/src/libzmq/build/lib/libzmq.a"
|
"${CMAKE_SOURCE_DIR}/external/src/libzmq/build/lib/libzmq.a"
|
||||||
"${CMAKE_SOURCE_DIR}/external/src/libuv/build/libuv_a.a"
|
"${CMAKE_SOURCE_DIR}/external/src/libuv/build/libuv_a.a"
|
||||||
|
"${CMAKE_SOURCE_DIR}/external/src/curl/lib/.libs/libcurl.a"
|
||||||
${STATIC_LIBS}
|
${STATIC_LIBS}
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} ${LIBS})
|
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} debug ${CURL_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} optimized ${CURL_LIBRARY} ${LIBS})
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
+6
-3
@@ -1,4 +1,4 @@
|
|||||||
FROM ubuntu:latest
|
FROM ubuntu:20.04
|
||||||
|
|
||||||
RUN set -e && \
|
RUN set -e && \
|
||||||
apt-get update -q -y --no-install-recommends && \
|
apt-get update -q -y --no-install-recommends && \
|
||||||
@@ -12,7 +12,8 @@ RUN set -e && \
|
|||||||
libsodium-dev \
|
libsodium-dev \
|
||||||
libpgm-dev \
|
libpgm-dev \
|
||||||
libnorm-dev \
|
libnorm-dev \
|
||||||
libgss-dev
|
libgss-dev \
|
||||||
|
libcurl4-openssl-dev
|
||||||
|
|
||||||
ADD . /usr/src/p2pool
|
ADD . /usr/src/p2pool
|
||||||
WORKDIR /usr/src/p2pool
|
WORKDIR /usr/src/p2pool
|
||||||
@@ -24,7 +25,7 @@ RUN git submodule update --init --recursive && \
|
|||||||
|
|
||||||
# ---
|
# ---
|
||||||
|
|
||||||
FROM ubuntu:latest
|
FROM ubuntu:20.04
|
||||||
COPY --from=0 /usr/src/p2pool/build/p2pool /
|
COPY --from=0 /usr/src/p2pool/build/p2pool /
|
||||||
|
|
||||||
RUN set -e && \
|
RUN set -e && \
|
||||||
@@ -32,6 +33,7 @@ RUN set -e && \
|
|||||||
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
|
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
|
||||||
libzmq5 \
|
libzmq5 \
|
||||||
libuv1 \
|
libuv1 \
|
||||||
|
libcurl4 \
|
||||||
&& \
|
&& \
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
|
||||||
@@ -43,6 +45,7 @@ USER p2pool
|
|||||||
|
|
||||||
EXPOSE 3333
|
EXPOSE 3333
|
||||||
EXPOSE 37889
|
EXPOSE 37889
|
||||||
|
EXPOSE 37888
|
||||||
|
|
||||||
VOLUME /home/p2pool/.p2pool
|
VOLUME /home/p2pool/.p2pool
|
||||||
|
|
||||||
|
|||||||
@@ -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 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
|
|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 August 13th, 2022)|v0.18.0.0 or newer|v2.2 or newer
|
||||||
|v15, v16 (active after July 16th, 2022)|v0.18.0.0 or newer|v2.0 or newer
|
|
||||||
|
|
||||||
## How to mine on P2Pool
|
## How to mine on P2Pool
|
||||||
|
|
||||||
### General Considerations
|
### 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/).
|
- 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 for mining. While P2Pool has been battle-tested for a long time now, any software may have unknown bugs/vulnerabilities.
|
- 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.
|
- 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.
|
- 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!
|
- 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**.
|
- 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**
|
**Wallet software compatible with P2Pool payouts**
|
||||||
- [Official Monero CLI and GUI v0.17.2.3 and newer](https://www.getmonero.org/downloads/)
|
- [Official Monero CLI and GUI v0.18.0.0 and newer](https://www.getmonero.org/downloads/)
|
||||||
- [Monerujo v2.1.0 "Vertant" and newer](https://www.monerujo.io/)
|
- [Monerujo v3.0.2 "Fluorine Fermi" and newer](https://www.monerujo.io/)
|
||||||
- [Cake Wallet v4.2.7 and newer](https://cakewallet.com/)
|
- [Cake Wallet v4.4.5 and newer](https://cakewallet.com/)
|
||||||
- [Monero.com by Cake Wallet](https://monero.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/)
|
- [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
|
### GNU/Linux
|
||||||
|
|
||||||
1. Download the latest P2Pool binaries [here](https://github.com/SChernykh/p2pool/releases/latest).
|
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):
|
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 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.
|
- 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.*
|
**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
|
.\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.
|
**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. Wait until the initial P2Pool sync is finished (shouldn't take more than 5-10 minutes).
|
||||||
10. Start XMRig with the following command/options:
|
11. Start XMRig with the following command/options:
|
||||||
```
|
```
|
||||||
.\xmrig.exe -o 127.0.0.1:3333
|
.\xmrig.exe -o 127.0.0.1:3333
|
||||||
```
|
```
|
||||||
@@ -180,17 +185,17 @@ nocreate
|
|||||||
```
|
```
|
||||||
xmrig.exe -u x+10000 -o 127.0.0.1:3333
|
xmrig.exe -u x+10000 -o 127.0.0.1:3333
|
||||||
```
|
```
|
||||||
11. XMRig should connect and start mining!
|
12. 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`.
|
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
|
@ECHO OFF
|
||||||
start cmd /k %~dp0\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
|
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.
|
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
|
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.
|
ECHO Wait until the daemon shows fully synced before continuing. This can take some time.
|
||||||
PAUSE
|
PAUSE
|
||||||
%~dp0\xmrig.exe -u x+30000 -o 127.0.0.1
|
%~dp0\xmrig.exe -o 127.0.0.1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build instructions
|
## Build instructions
|
||||||
@@ -200,7 +205,7 @@ Please see the relevant instructions for your platform:
|
|||||||
|
|
||||||
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on Ubuntu 20.04:
|
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on Ubuntu 20.04:
|
||||||
```
|
```
|
||||||
sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev
|
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
|
||||||
git clone --recursive https://github.com/SChernykh/p2pool
|
git clone --recursive https://github.com/SChernykh/p2pool
|
||||||
cd p2pool
|
cd p2pool
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
@@ -208,9 +213,11 @@ cmake ..
|
|||||||
make -j$(nproc)
|
make -j$(nproc)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Arch Linux [AUR](https://wiki.archlinux.org/title/Arch_User_Repository)
|
### [Arch Linux](https://archlinux.org/packages/community/x86_64/p2pool/)
|
||||||
|
|
||||||
Make the package: [p2pool-git](https://aur.archlinux.org/packages/p2pool-git/)
|
```
|
||||||
|
pacman -S p2pool
|
||||||
|
```
|
||||||
|
|
||||||
### [Nix/NixOS](https://nixos.org)
|
### [Nix/NixOS](https://nixos.org)
|
||||||
|
|
||||||
@@ -224,12 +231,12 @@ nix shell github:nixos/nix/master
|
|||||||
|
|
||||||
Run the binary:
|
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:
|
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
|
### Windows
|
||||||
@@ -251,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:
|
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
|
git clone --recursive https://github.com/SChernykh/p2pool
|
||||||
cd p2pool
|
cd p2pool
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
@@ -263,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:
|
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
|
git clone --recursive https://github.com/SChernykh/p2pool
|
||||||
cd p2pool
|
cd p2pool
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
@@ -271,6 +278,18 @@ cmake ..
|
|||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Android (Termux)
|
||||||
|
|
||||||
|
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally in Termux:
|
||||||
|
```
|
||||||
|
pkg install git build-essential cmake libuv libzmq libcurl
|
||||||
|
git clone --recursive https://github.com/SChernykh/p2pool
|
||||||
|
cd p2pool
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake ..
|
||||||
|
make -j$(nproc)
|
||||||
|
```
|
||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
If you'd like to support further development of Monero P2Pool, you're welcome to send any amount of XMR to the following address:
|
If you'd like to support further development of Monero P2Pool, you're welcome to send any amount of XMR to the following address:
|
||||||
|
|||||||
+19
-17
@@ -6,14 +6,15 @@ set(CMAKE_C_STANDARD 99)
|
|||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||||
set(WARNING_FLAGS "-Wall -Wextra -Werror")
|
set(GENERAL_FLAGS "-pthread")
|
||||||
|
set(WARNING_FLAGS "-Wall -Wextra -Wcast-align -Wcast-qual -Wlogical-op -Wstrict-overflow=2 -Wundef -Wformat=2 -Wpointer-arith -Werror")
|
||||||
set(OPTIMIZATION_FLAGS "-Ofast -s")
|
set(OPTIMIZATION_FLAGS "-Ofast -s")
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
|
||||||
@@ -25,26 +26,27 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
||||||
|
set(GENERAL_FLAGS "/MP")
|
||||||
set(WARNING_FLAGS "/Wall /WX /sdl")
|
set(WARNING_FLAGS "/Wall /WX /sdl")
|
||||||
set(SECURITY_FLAGS "/GS /guard:cf")
|
set(SECURITY_FLAGS "/GS /guard:cf")
|
||||||
set(OPTIMIZATION_FLAGS "/O2 /Oi /Ob2 /Ot /DNDEBUG /GL")
|
set(OPTIMIZATION_FLAGS "/O2 /Oi /Ob2 /Ot /DNDEBUG /GL")
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS_DEBUG "${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
|
set(CMAKE_C_FLAGS_DEBUG "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
|
set(CMAKE_CXX_FLAGS_DEBUG "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS_RELEASE "${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
|
set(CMAKE_C_FLAGS_RELEASE "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
|
set(CMAKE_CXX_FLAGS_RELEASE "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
|
|
||||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
|
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
|
||||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
|
||||||
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal -Werror")
|
set(GENERAL_FLAGS "-pthread")
|
||||||
|
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal -Wunreachable-code-aggressive -Wmissing-prototypes -Wmissing-variable-declarations -Werror")
|
||||||
set(OPTIMIZATION_FLAGS "-Ofast -funroll-loops -fmerge-all-constants")
|
set(OPTIMIZATION_FLAGS "-Ofast -funroll-loops -fmerge-all-constants")
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
../src/
|
../src/
|
||||||
../external/src/
|
../external/src/
|
||||||
../external/src/cryptonote/
|
../external/src/cryptonote/
|
||||||
../external/src/libuv/
|
../external/src/libuv/include/
|
||||||
../external/src/cppzmq/
|
../external/src/cppzmq/
|
||||||
../external/src/libzmq/
|
../external/src/libzmq/include/
|
||||||
../external/src/llhttp/
|
../external/src/curl/include
|
||||||
../external/src/RandomX/src/
|
../external/src/RandomX/src/
|
||||||
../external/src/rapidjson/include
|
../external/src/rapidjson/include
|
||||||
../external/src/robin-hood-hashing/src/include
|
../external/src/robin-hood-hashing/src/include
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
@echo off
|
@echo off
|
||||||
"C:\Program Files\cppcheck\cppcheck.exe" --project=..\build\p2pool.vcxproj --project-configuration="Release|x64" -DZMQ_STATIC --platform=win64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" --includes-file=includes.txt --suppressions-list=suppressions.txt --output-file=errors_full.txt
|
"..\cppcheck-main\bin\cppcheck.exe" --project=..\build\p2pool.vcxproj --project-configuration="Release|x64" -DSIZE_MAX=UINT64_MAX -DRAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN --platform=win64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" --includes-file=includes.txt --suppressions-list=suppressions.txt --output-file=errors_full.txt
|
||||||
findstr /V /C:"external\src" errors_full.txt > errors_filtered.txt
|
findstr /V /C:"external\src" errors_full.txt > errors_filtered.txt
|
||||||
for /f %%i in ("errors_filtered.txt") do set size=%%~zi
|
for /f %%i in ("errors_filtered.txt") do set size=%%~zi
|
||||||
if %size% gtr 0 (
|
if %size% gtr 0 (
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
cppcheck ../src -DZMQ_STATIC --platform=unix64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" -I ../src/ -I ../external/src/ -I ../external/src/cryptonote/ -I ../external/src/libuv/ -I ../external/src/cppzmq/ -I ../external/src/libzmq/ -I ../external/src/llhttp/ -I ../external/src/RandomX/src/ -I ../external/src/rapidjson/include -I ../external/src/robin-hood-hashing/src/include --suppressions-list=suppressions.txt --output-file=errors_full.txt
|
../cppcheck-main/cppcheck ../src -DSIZE_MAX=UINT64_MAX -DRAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN --platform=unix64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" --includes-file=includes.txt --suppressions-list=suppressions.txt --output-file=errors_full.txt
|
||||||
grep -v 'external' errors_full.txt | grep -v 'unmatchedSuppression' > errors_filtered.txt
|
grep -v 'external' errors_full.txt > errors_filtered.txt
|
||||||
if [ -s errors_filtered.txt ]; then
|
if [ -s errors_filtered.txt ]; then
|
||||||
cat errors_filtered.txt
|
cat errors_filtered.txt
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
missingIncludeSystem
|
missingIncludeSystem
|
||||||
unusedFunction
|
unusedFunction
|
||||||
useStlAlgorithm
|
useStlAlgorithm
|
||||||
functionStatic
|
|
||||||
functionConst
|
|
||||||
unmatchedSuppression
|
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
##
|
|
||||||
# Monero Wallet Address for mining rewards (replace this with your own address)
|
|
||||||
WALLET_ADDRESS="44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg"
|
|
||||||
|
|
||||||
##
|
|
||||||
# p2pool settings
|
|
||||||
#
|
|
||||||
# Use p2pool-mini (uncomment to enable)
|
|
||||||
#P2POOL_MINI="--mini"
|
|
||||||
#
|
|
||||||
# Which port to listen for miner connections
|
|
||||||
P2POOL_STRATUM_PORT=3333
|
|
||||||
#
|
|
||||||
# How much logging - (Less) 0 - 6 (More)
|
|
||||||
P2POOL_LOGLEVEL=2
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# Monero Node Settings
|
|
||||||
#
|
|
||||||
# Version of Monero to build. Must be v0.17.3.0 or later for p2pool support. "latest" pulls the most recent release tag
|
|
||||||
MONERO_GIT_TAG=latest
|
|
||||||
#
|
|
||||||
# Limit the size of the blockchain on disk (comment to disable)
|
|
||||||
PRUNE_NODE="--prune-blockchain"
|
|
||||||
#
|
|
||||||
# Other monerod commandline options
|
|
||||||
MONERO_EXTRA_OPTIONS=""
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# Xmrig Miner Settings
|
|
||||||
#
|
|
||||||
# Submit shares at a lower fixed difficulty to show mining progress (comment to disable, edit to customize)
|
|
||||||
FIXED_MINING_DIFFICULTY="-u x+500000"
|
|
||||||
#
|
|
||||||
# set process priority (0 idle, 2 normal to 5 highest)
|
|
||||||
MINING_CPU_PRIORITY="--cpu-priority=2"
|
|
||||||
#
|
|
||||||
# Other XMRIG commandline options
|
|
||||||
# See: https://xmrig.com/docs/miner/command-line-options
|
|
||||||
XMRIG_EXTRA_OPTIONS=""
|
|
||||||
+20
-15
@@ -8,6 +8,8 @@ Run your own <b>Monero Node + P2Pool + XMRig</b> in Docker
|
|||||||
[Install Docker](https://docs.docker.com/engine/install/)
|
[Install Docker](https://docs.docker.com/engine/install/)
|
||||||
[Install Docker Compose](https://docs.docker.com/compose/install/)
|
[Install Docker Compose](https://docs.docker.com/compose/install/)
|
||||||
|
|
||||||
|
Note: The docker compose plugin uses the command "docker compose" while the pip installed command is "docker-compose".
|
||||||
|
|
||||||
#### Clone the P2Pool project
|
#### Clone the P2Pool project
|
||||||
```
|
```
|
||||||
git clone --recursive https://github.com/SChernykh/p2pool
|
git clone --recursive https://github.com/SChernykh/p2pool
|
||||||
@@ -16,37 +18,40 @@ git clone --recursive https://github.com/SChernykh/p2pool
|
|||||||
#### Configure your Monero address for mining rewards
|
#### Configure your Monero address for mining rewards
|
||||||
```
|
```
|
||||||
cd p2pool/docker-compose
|
cd p2pool/docker-compose
|
||||||
vi .env
|
./configure
|
||||||
```
|
```
|
||||||
**WALLET_ADDRESS** is the only setting that needs to be updated in that file
|
Make sure to set your own monero **Wallet Address**. The default is to donate mining to P2Pool development.
|
||||||
|
|
||||||
#### Build the docker containers
|
#### Build the docker containers
|
||||||
```
|
```
|
||||||
docker-compose build
|
docker compose build --no-cache
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Run the node, pool, and CPU miner
|
#### Run the node, pool, and CPU miner (or updated configuration)
|
||||||
```
|
```
|
||||||
docker-compose up
|
docker compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Optional
|
#### 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
|
* 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)
|
* 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
|
#### Other usefull commands
|
||||||
* You can **run everythng in the background** by adding the "-d" argument to the "docker-compose up" command: ```docker-compose up -d```
|
* You can **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 **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 pause mining with: ```docker-compose pause xmrig``` and resume mining with: ```docker-compose unpause xmrig```
|
* You can see logs when running in the background for with the "docker compose logs" command: ```docker compose logs -f```
|
||||||
* You can disable mining with: ```docker-compose stop xmrig``` and re-enable mining with: ```docker-compose start xmrig```
|
* 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
|
#### Uninstall
|
||||||
Change to p2pool/docker-compose directory
|
Change to p2pool/docker-compose directory <br />
|
||||||
Stop and remove all containers: ```docker-compose down```
|
Stop and remove all containers: ```docker compose down``` <br />
|
||||||
Remove the p2pool data: ```docker volume rm p2pool```
|
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```
|
Remove the monero data: ```docker volume rm monero```
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
FROM python:3.10.5-bullseye
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ADD requirements.txt /app/
|
||||||
|
RUN /usr/local/bin/pip3 install -r requirements.txt
|
||||||
|
|
||||||
|
ADD configure.py /app/
|
||||||
|
ADD defaults /app/
|
||||||
|
ADD current_config.jinja2 /app/
|
||||||
|
ADD docker-compose.jinja2 /app/
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/python3"]
|
||||||
|
CMD ["/app/configure.py"]
|
||||||
Executable
+951
@@ -0,0 +1,951 @@
|
|||||||
|
#!/bin/env python3
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
from jinja2 import Template
|
||||||
|
import npyscreen
|
||||||
|
|
||||||
|
# Help text in a box at the bottom of the screen
|
||||||
|
class HelpBoxBase(npyscreen.BoxTitle):
|
||||||
|
def splitlines(self):
|
||||||
|
all_help = []
|
||||||
|
for fld, msg in self.help_msgs.items():
|
||||||
|
if fld[-1] != ":":
|
||||||
|
fld += ":"
|
||||||
|
message = [m.lstrip() for m in msg if m != ""]
|
||||||
|
helpline = f"{fld:<28}{'. '.join(message)}"
|
||||||
|
all_help.append(helpline)
|
||||||
|
all_help.append("")
|
||||||
|
return all_help
|
||||||
|
|
||||||
|
def display_help_message(self, field):
|
||||||
|
self.set_values(
|
||||||
|
self.help_msgs.get(field, "No help availabe for {}".format(field))
|
||||||
|
)
|
||||||
|
self.clear()
|
||||||
|
self.display()
|
||||||
|
|
||||||
|
|
||||||
|
class P2PoolHelpBox(HelpBoxBase):
|
||||||
|
help_msgs = {
|
||||||
|
"Wallet Address:": [
|
||||||
|
"Your monero wallet address for receiving mining reqards",
|
||||||
|
"",
|
||||||
|
"Note: You have to use a primary wallet address for mining",
|
||||||
|
" Subaddresses and integrated addresses are not supported!",
|
||||||
|
],
|
||||||
|
"P2Pool Sidechain:": [
|
||||||
|
"Which P2Pool sidechain to mine on",
|
||||||
|
" use main for faster miners",
|
||||||
|
" use mini for slower miners",
|
||||||
|
],
|
||||||
|
"Enable Server Statistics": [
|
||||||
|
"Provide access to your P2Pool server statictics via a web interface",
|
||||||
|
],
|
||||||
|
"Statistics Port:": [
|
||||||
|
"Port number (or IP:Port) to expose web access to your P2Pool server",
|
||||||
|
"statictics",
|
||||||
|
],
|
||||||
|
"Expose Stratum Port": [
|
||||||
|
"Expose the P2Pool stratum port to your network so external miners",
|
||||||
|
"can connect",
|
||||||
|
"Note: You may choose to open this port in your hosts firewall and/or",
|
||||||
|
" router to allow miners outside your network to connect",
|
||||||
|
],
|
||||||
|
"Stratum Port:": [
|
||||||
|
"Port number (or IP:Port) to expose P2Pool stratum to your network to",
|
||||||
|
"allow external miners to connect",
|
||||||
|
"Note: You may choose to open this port in your hosts firewall and/or",
|
||||||
|
" router to allow miners outside your network to connect",
|
||||||
|
],
|
||||||
|
"P2Pool Log Level:": [
|
||||||
|
"Verbosity of the log; (Less) 0 - 6 (More)",
|
||||||
|
],
|
||||||
|
"Enable Autodiff": [
|
||||||
|
"Use automatic difficulty adjustment for miners connected to stratum",
|
||||||
|
],
|
||||||
|
"Enable Light Mode": [
|
||||||
|
"Don't allocate RandomX dataset, saves 2GB of RAM",
|
||||||
|
],
|
||||||
|
"Disable Cache": [
|
||||||
|
"Disable p2pool.cache (not recommended)",
|
||||||
|
],
|
||||||
|
"Additional P2Pool Options:": [
|
||||||
|
"Additional options to pass to p2pool commandline",
|
||||||
|
"",
|
||||||
|
"Note: Advanced - Only add options if you know what you are doing",
|
||||||
|
" See ouput of 'p2pool --help' for available options",
|
||||||
|
],
|
||||||
|
"Next": [
|
||||||
|
"Next configuration menu",
|
||||||
|
],
|
||||||
|
"Save": [
|
||||||
|
"Save current configuration and exit",
|
||||||
|
],
|
||||||
|
"Cancel": [
|
||||||
|
"Exit without saving",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MoneroHelpBox(HelpBoxBase):
|
||||||
|
help_msgs = {
|
||||||
|
"Configure Monero Node": [
|
||||||
|
"Configure and run a Monero Node",
|
||||||
|
"",
|
||||||
|
"Note: You must either configure a local node or specify a public node",
|
||||||
|
],
|
||||||
|
"Monero Version:": [
|
||||||
|
"Version of Monero to build; 'latest' for the most recent release",
|
||||||
|
"",
|
||||||
|
"Note: Must be v0.17.3.0 or later for p2pool support",
|
||||||
|
" See: https://github.com/monero-project/monero/tags",
|
||||||
|
],
|
||||||
|
"Prune Blockchain": [
|
||||||
|
"Prune the Monero node to limit the size of the blockchain on disk",
|
||||||
|
],
|
||||||
|
"Monero Log Level:": [
|
||||||
|
"Verbosity of the log; (Less) 0 - 4 (More)",
|
||||||
|
"",
|
||||||
|
"Note: settings above 0 are very noisy",
|
||||||
|
],
|
||||||
|
"Expose RPC Port": [
|
||||||
|
"Expose restricted RPC API port to your network so external services",
|
||||||
|
"(wallets for example) can connect",
|
||||||
|
"Note: You may choose to open this port in your hosts firewall and/or",
|
||||||
|
" router to allow services outside your network to connect",
|
||||||
|
],
|
||||||
|
"RPC Port:": [
|
||||||
|
"TCP port to listen on for RPC connections",
|
||||||
|
],
|
||||||
|
"RPC Login:": [
|
||||||
|
"Specify username[:password] required to connect to the RPC API",
|
||||||
|
],
|
||||||
|
"Limit Data Rates": [
|
||||||
|
"Set a limit value for incoming and outgoing data transfer",
|
||||||
|
],
|
||||||
|
"Rate Limit Up:": [
|
||||||
|
"Set outgoing data transfer limit [kB/s]",
|
||||||
|
],
|
||||||
|
"Rate Limit Down:": [
|
||||||
|
"Set incoming data transfer limit [kB/s]",
|
||||||
|
],
|
||||||
|
"Sync Pruned Blocks": [
|
||||||
|
"Accept pruned blocks instead of pruning yourself to save",
|
||||||
|
"network transfer",
|
||||||
|
],
|
||||||
|
"Fast Block Sync": [
|
||||||
|
'Sync up most of the way by using embedded, "known" (old) block',
|
||||||
|
"hashes without calculating the block hash to verify the proof of work",
|
||||||
|
"",
|
||||||
|
"Note: Faster initial sync by trusting the monerod binary",
|
||||||
|
],
|
||||||
|
"Public Node:": [
|
||||||
|
"Public Monero Node to Use",
|
||||||
|
"",
|
||||||
|
"Note: The public node must have both Monero RPC and zmq-pub ports",
|
||||||
|
" available",
|
||||||
|
],
|
||||||
|
"Node Login:": [
|
||||||
|
"Specify username[:password] required to connect to public monero",
|
||||||
|
"node RPC API (if required)",
|
||||||
|
],
|
||||||
|
"Additional monerod Options:": [
|
||||||
|
"Additional options to pass to monerod commandline",
|
||||||
|
"",
|
||||||
|
"Note: Advanced - Only add options if you know what you are doing",
|
||||||
|
" See 'https://monerodocs.org/interacting/monerod-reference/'",
|
||||||
|
],
|
||||||
|
"Prev": [
|
||||||
|
"Previous configuration menu",
|
||||||
|
],
|
||||||
|
"Next": [
|
||||||
|
"Next configuration menu",
|
||||||
|
],
|
||||||
|
"Save": [
|
||||||
|
"Save current configuration and exit",
|
||||||
|
],
|
||||||
|
"Cancel": [
|
||||||
|
"Exit without saving",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class XMRigHelpBox(HelpBoxBase):
|
||||||
|
help_msgs = {
|
||||||
|
"Configure XMRig CPU Miner": [
|
||||||
|
"Configure and run an XMRig CPU Miner",
|
||||||
|
"",
|
||||||
|
"Note: You must either configure am XMRig CPU Miner or expose the",
|
||||||
|
" P2Pool stratum port and connect an external miner, or both",
|
||||||
|
],
|
||||||
|
"Username:": [
|
||||||
|
"Set a username for the miner",
|
||||||
|
],
|
||||||
|
"Use Fixed Difficulty": [
|
||||||
|
"Used a fixed minig difficulty",
|
||||||
|
"",
|
||||||
|
"Note: Allows you to see XMRig submitting shares below P2Pool threshold",
|
||||||
|
],
|
||||||
|
"Fixed Difficulty:": [
|
||||||
|
"Set a fixed mining difficulty",
|
||||||
|
"",
|
||||||
|
"Note: Allows you to see XMRig submitting shares below P2Pool threshold",
|
||||||
|
],
|
||||||
|
"CPU Use %:": [
|
||||||
|
"Maximum CPU threads count (in percentage) hint for autoconfig",
|
||||||
|
"Note: Applies to cores only. If you have HyperThreading enabled you",
|
||||||
|
" should divide this value by 2 (use 0-50%). Reference:",
|
||||||
|
" https://github.com/xmrig/xmrig/issues/1670#issuecomment-644433778",
|
||||||
|
],
|
||||||
|
"CPU Priority:": [
|
||||||
|
"Set process priority (0 idle, 2 normal to 5 highest)",
|
||||||
|
],
|
||||||
|
"Additional XMRig Options:": [
|
||||||
|
"Additional options to pass to xmrig",
|
||||||
|
"",
|
||||||
|
"Note: Advanced - Only add options if you know what you are doing",
|
||||||
|
" See 'https://xmrig.com/docs/miner/command-line-options'",
|
||||||
|
],
|
||||||
|
"Prev": [
|
||||||
|
"Previous configuration menu",
|
||||||
|
],
|
||||||
|
"Save": [
|
||||||
|
"Save current configuration and exit",
|
||||||
|
],
|
||||||
|
"Cancel": [
|
||||||
|
"Exit without saving",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Custom (integer) values for title slider
|
||||||
|
class IntegerSlider(npyscreen.Slider):
|
||||||
|
def translate_value(self):
|
||||||
|
from_val = int(str(self.value).split(".")[0])
|
||||||
|
out_of_val = int(str(self.out_of).split(".")[0])
|
||||||
|
if from_val >= 1000:
|
||||||
|
from_val = str(from_val / 1000).split(".")[0] + "K"
|
||||||
|
out_of_val = str(out_of_val / 1000).split(".")[0] + "K"
|
||||||
|
return "{}/{}".format(from_val, out_of_val)
|
||||||
|
|
||||||
|
|
||||||
|
class TitleIntegerSlider(npyscreen.TitleSlider):
|
||||||
|
_entry_type = IntegerSlider
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Patched updateDependents for FormControlCheckbox
|
||||||
|
class PatchedFormControlCheckbox(npyscreen.FormControlCheckbox):
|
||||||
|
def updateDependents(self):
|
||||||
|
if self.value:
|
||||||
|
for w in self._visibleWhenSelected:
|
||||||
|
try:
|
||||||
|
w.fc_visible
|
||||||
|
except AttributeError:
|
||||||
|
w.fc_visible = {}
|
||||||
|
w.fc_visible[self.name] = True
|
||||||
|
for w in self._notVisibleWhenSelected:
|
||||||
|
try:
|
||||||
|
w.fc_visible
|
||||||
|
except AttributeError:
|
||||||
|
w.fc_visible = {}
|
||||||
|
w.fc_visible[self.name] = False
|
||||||
|
else:
|
||||||
|
for w in self._visibleWhenSelected:
|
||||||
|
try:
|
||||||
|
w.fc_visible
|
||||||
|
except AttributeError:
|
||||||
|
w.fc_visible = {}
|
||||||
|
w.fc_visible[self.name] = False
|
||||||
|
for w in self._notVisibleWhenSelected:
|
||||||
|
try:
|
||||||
|
w.fc_visible
|
||||||
|
except AttributeError:
|
||||||
|
w.fc_visible = {}
|
||||||
|
w.fc_visible[self.name] = True
|
||||||
|
for w in self._visibleWhenSelected + self._notVisibleWhenSelected:
|
||||||
|
w.hidden = False in w.fc_visible.values()
|
||||||
|
w.editable = not False in w.fc_visible.values()
|
||||||
|
self.parent.display()
|
||||||
|
|
||||||
|
def set_value(self, value):
|
||||||
|
self.value = value
|
||||||
|
self.display()
|
||||||
|
|
||||||
|
def display(self):
|
||||||
|
self.updateDependents()
|
||||||
|
super()
|
||||||
|
|
||||||
|
|
||||||
|
class PrevButton(npyscreen.Button):
|
||||||
|
def whenToggled(self):
|
||||||
|
self.value = False
|
||||||
|
self.parent.prev_form()
|
||||||
|
|
||||||
|
|
||||||
|
class NextButton(npyscreen.Button):
|
||||||
|
def whenToggled(self):
|
||||||
|
self.value = False
|
||||||
|
self.parent.next_form()
|
||||||
|
|
||||||
|
|
||||||
|
class SaveButton(npyscreen.Button):
|
||||||
|
def whenToggled(self):
|
||||||
|
self.find_parent_app().save_and_exit()
|
||||||
|
|
||||||
|
|
||||||
|
class CancelButton(npyscreen.Button):
|
||||||
|
def whenToggled(self):
|
||||||
|
self.find_parent_app().cancel_and_exit()
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Config Forms Base Class
|
||||||
|
class ConfigFormBase(npyscreen.FormBaseNew):
|
||||||
|
name_size = 20
|
||||||
|
indent = 5
|
||||||
|
current_config = None
|
||||||
|
defaults = None
|
||||||
|
ALLOW_RESIZE = False
|
||||||
|
|
||||||
|
def while_editing(self, arg):
|
||||||
|
self.help.display_help_message(arg.name)
|
||||||
|
self.display()
|
||||||
|
|
||||||
|
def get_default_config(self):
|
||||||
|
# Return defaults
|
||||||
|
if self.defaults is None:
|
||||||
|
with open("defaults") as defaults_file:
|
||||||
|
self.defaults = json.load(defaults_file)
|
||||||
|
return self.defaults
|
||||||
|
|
||||||
|
def get_current_config(self):
|
||||||
|
# Return current config
|
||||||
|
if self.current_config is None:
|
||||||
|
# Read current config
|
||||||
|
with open("/docker-compose/current_config") as current_config:
|
||||||
|
self.current_config = json.load(current_config)
|
||||||
|
return self.current_config
|
||||||
|
|
||||||
|
def reset_defaults(self, arg):
|
||||||
|
# Set config to default values
|
||||||
|
ok = npyscreen.notify_ok_cancel(
|
||||||
|
"Set current form values to defaults", title="Reset to Defaults"
|
||||||
|
)
|
||||||
|
if not ok:
|
||||||
|
return
|
||||||
|
defaults = self.get_default_config()
|
||||||
|
self.set_config(defaults)
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# P2Pool Configuration Form
|
||||||
|
class P2PoolConfigForm(ConfigFormBase):
|
||||||
|
def create(self):
|
||||||
|
# Add Hot-Key Controls
|
||||||
|
self.add_handlers({"^D": self.reset_defaults})
|
||||||
|
# Add P2Pool Configuration
|
||||||
|
self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="## P2Pool Configuration",
|
||||||
|
editable=False,
|
||||||
|
begin_entry_at=50,
|
||||||
|
)
|
||||||
|
self.wallet_address = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Wallet Address:",
|
||||||
|
value="",
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.sidechain = self.add(
|
||||||
|
npyscreen.TitleSelectOne,
|
||||||
|
name="P2Pool Sidechain:",
|
||||||
|
values=["main", "mini"],
|
||||||
|
scroll_exit=True,
|
||||||
|
max_height=2,
|
||||||
|
value=[
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.enable_statistics = self.add(
|
||||||
|
PatchedFormControlCheckbox,
|
||||||
|
name="Enable Server Statistics",
|
||||||
|
value=True,
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.statistics_port = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Statistics Port:",
|
||||||
|
value="3334",
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.enable_statistics.addVisibleWhenSelected(self.statistics_port)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.expose_stratum_port = self.add(
|
||||||
|
PatchedFormControlCheckbox,
|
||||||
|
name="Expose Stratum Port",
|
||||||
|
value=True,
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.stratum_port = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Stratum Port:",
|
||||||
|
value="3333",
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.expose_stratum_port.addVisibleWhenSelected(self.stratum_port)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.p2pool_log_level = self.add(
|
||||||
|
TitleIntegerSlider,
|
||||||
|
name="P2Pool Log Level:",
|
||||||
|
out_of=6,
|
||||||
|
value=3,
|
||||||
|
lowest=0,
|
||||||
|
step=1,
|
||||||
|
width=43,
|
||||||
|
begin_entry_at=20,
|
||||||
|
label=True,
|
||||||
|
block_color=None,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.autodiff = self.add(
|
||||||
|
PatchedFormControlCheckbox,
|
||||||
|
name="Enable Autodiff",
|
||||||
|
value=True,
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.light_mode = self.add(
|
||||||
|
PatchedFormControlCheckbox,
|
||||||
|
name="Enable Light Mode",
|
||||||
|
value=False,
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.no_cache = self.add(
|
||||||
|
npyscreen.Checkbox,
|
||||||
|
name="Disable Cache",
|
||||||
|
value=False,
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.p2pool_extra = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Additional P2Pool Options:",
|
||||||
|
value="",
|
||||||
|
begin_entry_at=self.name_size + 10,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.nextrely += 1
|
||||||
|
# Add "Next", "Save", and "Cancel" buttons
|
||||||
|
self.next_button = self.add(NextButton, name="Next", relx=1)
|
||||||
|
self.nextrely -= 1
|
||||||
|
self.save_button = self.add(SaveButton, name="Save", relx=8)
|
||||||
|
self.nextrely -= 1
|
||||||
|
self.cancel_button = self.add(CancelButton, name="Cancel", relx=15)
|
||||||
|
self.nextrely += 1
|
||||||
|
# Add Help Box
|
||||||
|
self.help = self.add(
|
||||||
|
P2PoolHelpBox,
|
||||||
|
name="Commands: ^D: Load Defaults - ^C: Exit Without Saving",
|
||||||
|
values=[""],
|
||||||
|
editable=False,
|
||||||
|
)
|
||||||
|
# Start with current config
|
||||||
|
self.set_config(self.get_current_config())
|
||||||
|
|
||||||
|
def next_form(self):
|
||||||
|
self.find_parent_app().switchForm("MONERO")
|
||||||
|
|
||||||
|
def set_config(self, config):
|
||||||
|
self.wallet_address.set_value(config["wallet_address"])
|
||||||
|
self.sidechain.set_value(config["sidechain"])
|
||||||
|
self.enable_statistics.set_value(config["enable_statistics"])
|
||||||
|
self.statistics_port.set_value(config["statistics_port"])
|
||||||
|
self.expose_stratum_port.set_value(config["expose_stratum_port"])
|
||||||
|
self.stratum_port.set_value(config["stratum_port"])
|
||||||
|
self.p2pool_log_level.set_value(config["p2pool_log_level"])
|
||||||
|
self.autodiff.set_value(config["enable_autodiff"])
|
||||||
|
self.light_mode.set_value(config["light_mode"])
|
||||||
|
self.no_cache.value = config["no_cache"]
|
||||||
|
self.p2pool_extra.set_value(config["p2pool_options"])
|
||||||
|
self.DISPLAY()
|
||||||
|
|
||||||
|
def get_config(self):
|
||||||
|
config = {
|
||||||
|
"wallet_address": self.wallet_address.value,
|
||||||
|
"sidechain": self.sidechain.value,
|
||||||
|
"enable_statistics": self.enable_statistics.value,
|
||||||
|
"statistics_port": self.statistics_port.value,
|
||||||
|
"expose_stratum_port": self.expose_stratum_port.value,
|
||||||
|
"stratum_port": self.stratum_port.value,
|
||||||
|
"p2pool_log_level": self.p2pool_log_level.value,
|
||||||
|
"enable_autodiff": self.autodiff.value,
|
||||||
|
"light_mode": self.light_mode.value,
|
||||||
|
"no_cache": self.no_cache.value,
|
||||||
|
"p2pool_options": self.p2pool_extra.value,
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Monero Configuration Form
|
||||||
|
class MoneroConfigForm(ConfigFormBase):
|
||||||
|
def create(self):
|
||||||
|
# Add Hot-Key Controls
|
||||||
|
self.add_handlers({"^D": self.reset_defaults})
|
||||||
|
# Add Monero Configuration
|
||||||
|
self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="## Monero Node Configuration",
|
||||||
|
editable=False,
|
||||||
|
begin_entry_at=50,
|
||||||
|
)
|
||||||
|
self.configure_monero_node = self.add(
|
||||||
|
PatchedFormControlCheckbox,
|
||||||
|
name="Configure Monero Node",
|
||||||
|
value=True,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.monero_git_tag = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Monero Version:",
|
||||||
|
value="latest",
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_monero_node.addVisibleWhenSelected(self.monero_git_tag)
|
||||||
|
self.prune_node = self.add(
|
||||||
|
PatchedFormControlCheckbox,
|
||||||
|
name="Prune Blockchain",
|
||||||
|
value=True,
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_monero_node.addVisibleWhenSelected(self.prune_node)
|
||||||
|
self.monero_log_level = self.add(
|
||||||
|
TitleIntegerSlider,
|
||||||
|
name="Monero Log Level:",
|
||||||
|
out_of=4,
|
||||||
|
value=0,
|
||||||
|
lowest=0,
|
||||||
|
step=1,
|
||||||
|
width=43,
|
||||||
|
begin_entry_at=20,
|
||||||
|
label=True,
|
||||||
|
block_color=None,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_monero_node.addVisibleWhenSelected(self.monero_log_level)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.expose_rpc_port = self.add(
|
||||||
|
PatchedFormControlCheckbox,
|
||||||
|
name="Expose RPC Port",
|
||||||
|
value=False,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_monero_node.addVisibleWhenSelected(self.expose_rpc_port)
|
||||||
|
self.rpc_port = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="RPC Port:",
|
||||||
|
value="18081",
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_monero_node.addVisibleWhenSelected(self.rpc_port)
|
||||||
|
self.expose_rpc_port.addVisibleWhenSelected(self.rpc_port)
|
||||||
|
self.rpc_login = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="RPC Login:",
|
||||||
|
value="",
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.expose_rpc_port.addVisibleWhenSelected(self.rpc_login)
|
||||||
|
self.configure_monero_node.addVisibleWhenSelected(self.rpc_login)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.limit_data_rates = self.add(
|
||||||
|
PatchedFormControlCheckbox,
|
||||||
|
name="Limit Data Rates",
|
||||||
|
value=False,
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_monero_node.addVisibleWhenSelected(self.limit_data_rates)
|
||||||
|
self.rate_limit_up = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Rate Limit Up:",
|
||||||
|
value="2048",
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.limit_data_rates.addVisibleWhenSelected(self.rate_limit_up)
|
||||||
|
self.configure_monero_node.addVisibleWhenSelected(self.rate_limit_up)
|
||||||
|
self.rate_limit_down = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Rate Limit Down:",
|
||||||
|
value="8192",
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.limit_data_rates.addVisibleWhenSelected(self.rate_limit_down)
|
||||||
|
self.configure_monero_node.addVisibleWhenSelected(self.rate_limit_down)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.sync_pruned_blocks = self.add(
|
||||||
|
npyscreen.Checkbox,
|
||||||
|
name="Sync Pruned Blocks",
|
||||||
|
value=False,
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_monero_node.addVisibleWhenSelected(self.sync_pruned_blocks)
|
||||||
|
self.prune_node.addVisibleWhenSelected(self.sync_pruned_blocks)
|
||||||
|
self.fast_sync = self.add(
|
||||||
|
npyscreen.Checkbox,
|
||||||
|
name="Fast Block Sync",
|
||||||
|
value=False,
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_monero_node.addVisibleWhenSelected(self.fast_sync)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.public_node = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Public Node:",
|
||||||
|
value="",
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_monero_node.addInvisibleWhenSelected(self.public_node)
|
||||||
|
self.node_login = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Node Login:",
|
||||||
|
value="",
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_monero_node.addInvisibleWhenSelected(self.node_login)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.monero_extra = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Additional monerod Options:",
|
||||||
|
value="",
|
||||||
|
begin_entry_at=self.name_size + 10,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_monero_node.addVisibleWhenSelected(self.monero_extra)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.nextrely += 1
|
||||||
|
# Add "Prev", "Next", "Save", and "Cancel" buttons
|
||||||
|
self.prev_button = self.add(PrevButton, name="Prev", relx=1)
|
||||||
|
self.nextrely -= 1
|
||||||
|
self.next_button = self.add(NextButton, name="Next", relx=8)
|
||||||
|
self.nextrely -= 1
|
||||||
|
self.save_button = self.add(SaveButton, name="Save", relx=15)
|
||||||
|
self.nextrely -= 1
|
||||||
|
self.cancel_button = self.add(CancelButton, name="Cancel", relx=22)
|
||||||
|
self.nextrely += 1
|
||||||
|
# Add Help Box
|
||||||
|
self.help = self.add(
|
||||||
|
MoneroHelpBox,
|
||||||
|
name="Commands: ^D: Load Defaults - ^C: Exit Without Saving",
|
||||||
|
values=[""],
|
||||||
|
editable=False,
|
||||||
|
)
|
||||||
|
# Start with current config
|
||||||
|
self.set_config(self.get_current_config())
|
||||||
|
|
||||||
|
def prev_form(self):
|
||||||
|
self.find_parent_app().switchForm("MAIN")
|
||||||
|
|
||||||
|
def next_form(self):
|
||||||
|
self.find_parent_app().switchForm("XMRIG")
|
||||||
|
|
||||||
|
def set_config(self, config):
|
||||||
|
self.configure_monero_node.set_value(config["configure_monero"])
|
||||||
|
self.monero_git_tag.set_value(config["monero_version"])
|
||||||
|
self.prune_node.value = config["prune_blockchain"]
|
||||||
|
self.monero_log_level.set_value(config["monero_log_level"])
|
||||||
|
self.expose_rpc_port.set_value(config["expose_rpc_port"])
|
||||||
|
self.rpc_port.set_value(config["rpc_port"])
|
||||||
|
self.rpc_login.set_value(config["rpc_login"])
|
||||||
|
self.limit_data_rates.set_value(config["limit_data_rates"])
|
||||||
|
self.rate_limit_up.set_value(config["rate_limit_up"])
|
||||||
|
self.rate_limit_down.set_value(config["rate_limit_down"])
|
||||||
|
self.sync_pruned_blocks.value = config["sync_pruned_blocks"]
|
||||||
|
self.fast_sync.value = config["fast_sync"]
|
||||||
|
self.monero_extra.set_value(config["monero_options"])
|
||||||
|
self.public_node.set_value(config["public_monero_node"])
|
||||||
|
self.node_login.set_value(config["monero_node_login"])
|
||||||
|
self.DISPLAY()
|
||||||
|
|
||||||
|
def get_config(self):
|
||||||
|
config = {
|
||||||
|
"configure_monero": self.configure_monero_node.value,
|
||||||
|
"monero_version": self.monero_git_tag.value,
|
||||||
|
"prune_blockchain": self.prune_node.value,
|
||||||
|
"monero_log_level": self.monero_log_level.value,
|
||||||
|
"expose_rpc_port": self.expose_rpc_port.value,
|
||||||
|
"rpc_port": self.rpc_port.value,
|
||||||
|
"rpc_login": self.rpc_login.value,
|
||||||
|
"limit_data_rates": self.limit_data_rates.value,
|
||||||
|
"rate_limit_up": self.rate_limit_up.value,
|
||||||
|
"rate_limit_down": self.rate_limit_down.value,
|
||||||
|
"sync_pruned_blocks": self.sync_pruned_blocks.value,
|
||||||
|
"fast_sync": self.fast_sync.value,
|
||||||
|
"monero_options": self.monero_extra.value,
|
||||||
|
"public_monero_node": self.public_node.value,
|
||||||
|
"monero_node_login": self.node_login.value,
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# XMRig Configuration Form
|
||||||
|
class XMRigConfigForm(ConfigFormBase):
|
||||||
|
def create(self):
|
||||||
|
# Add Hot-Key Controls
|
||||||
|
self.add_handlers({"^D": self.reset_defaults})
|
||||||
|
# Hidden caryover from P2Pool form
|
||||||
|
self.autodiff = self.add(
|
||||||
|
PatchedFormControlCheckbox,
|
||||||
|
name="Enable Autodiff",
|
||||||
|
value=True,
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.autodiff.hide = True
|
||||||
|
self.autodiff.editable = False
|
||||||
|
self.nextrely -= 1
|
||||||
|
# Add XMRig Configuration
|
||||||
|
self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="## XMRig Miner Configuration",
|
||||||
|
editable=False,
|
||||||
|
begin_entry_at=50,
|
||||||
|
)
|
||||||
|
self.configure_xmrig_miner = self.add(
|
||||||
|
PatchedFormControlCheckbox,
|
||||||
|
name="Configure XMRig CPU Miner",
|
||||||
|
value=True,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.username = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Username:",
|
||||||
|
value="",
|
||||||
|
begin_entry_at=self.name_size,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_xmrig_miner.addVisibleWhenSelected(self.username)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.use_fixed_difficulty = self.add(
|
||||||
|
PatchedFormControlCheckbox,
|
||||||
|
name="Use Fixed Difficulty",
|
||||||
|
value=True,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_xmrig_miner.addVisibleWhenSelected(self.use_fixed_difficulty)
|
||||||
|
self.autodiff.addInvisibleWhenSelected(self.use_fixed_difficulty)
|
||||||
|
self.fixed_difficulty = self.add(
|
||||||
|
TitleIntegerSlider,
|
||||||
|
name="Fixed Difficulty:",
|
||||||
|
out_of=2000000,
|
||||||
|
value=500000,
|
||||||
|
lowest=50000,
|
||||||
|
step=50000,
|
||||||
|
width=51,
|
||||||
|
begin_entry_at=20,
|
||||||
|
label=True,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_xmrig_miner.addVisibleWhenSelected(self.fixed_difficulty)
|
||||||
|
self.use_fixed_difficulty.addVisibleWhenSelected(self.fixed_difficulty)
|
||||||
|
self.autodiff.addInvisibleWhenSelected(self.fixed_difficulty)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.cpu_threads = self.add(
|
||||||
|
TitleIntegerSlider,
|
||||||
|
name="CPU Use %:",
|
||||||
|
out_of=100,
|
||||||
|
value=100,
|
||||||
|
lowest=1,
|
||||||
|
step=10,
|
||||||
|
width=48,
|
||||||
|
begin_entry_at=20,
|
||||||
|
label=True,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_xmrig_miner.addVisibleWhenSelected(self.cpu_threads)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.cpu_priority = self.add(
|
||||||
|
TitleIntegerSlider,
|
||||||
|
name="CPU Priority:",
|
||||||
|
out_of=5,
|
||||||
|
value=2,
|
||||||
|
lowest=0,
|
||||||
|
step=1,
|
||||||
|
width=48,
|
||||||
|
begin_entry_at=20,
|
||||||
|
label=True,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_xmrig_miner.addVisibleWhenSelected(self.cpu_priority)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.xmrig_extra = self.add(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Additional XMRig Options:",
|
||||||
|
value="",
|
||||||
|
begin_entry_at=self.name_size + 10,
|
||||||
|
relx=self.indent,
|
||||||
|
)
|
||||||
|
self.configure_xmrig_miner.addVisibleWhenSelected(self.xmrig_extra)
|
||||||
|
self.nextrely += 1
|
||||||
|
self.nextrely += 1
|
||||||
|
# Add "Prev", "Save", and "Cancel" buttons
|
||||||
|
self.prev_button = self.add(PrevButton, name="Prev", relx=1)
|
||||||
|
self.nextrely -= 1
|
||||||
|
self.save_button = self.add(SaveButton, name="Save", relx=8)
|
||||||
|
self.nextrely -= 1
|
||||||
|
self.cancel_button = self.add(CancelButton, name="Cancel", relx=15)
|
||||||
|
self.nextrely += 1
|
||||||
|
# Add Help Box
|
||||||
|
self.help = self.add(
|
||||||
|
XMRigHelpBox,
|
||||||
|
name="Commands: ^D: Load Defaults - ^C: Exit Without Saving",
|
||||||
|
values=[""],
|
||||||
|
editable=False,
|
||||||
|
)
|
||||||
|
# Start with current config
|
||||||
|
self.set_config(self.get_current_config())
|
||||||
|
|
||||||
|
def prev_form(self):
|
||||||
|
self.find_parent_app().switchForm("MONERO")
|
||||||
|
|
||||||
|
def next_form(self):
|
||||||
|
self.find_parent_app().switchForm(None)
|
||||||
|
|
||||||
|
def beforeEditing(self):
|
||||||
|
# Cary autodiff value from P2Pool config form
|
||||||
|
enable_autodiff = self.find_parent_app().get_p2pool_config()["enable_autodiff"]
|
||||||
|
self.autodiff.set_value(enable_autodiff)
|
||||||
|
|
||||||
|
def set_config(self, config):
|
||||||
|
self.configure_xmrig_miner.set_value(config["configure_xmrig"])
|
||||||
|
self.username.set_value(config["xmrig_username"])
|
||||||
|
self.use_fixed_difficulty.set_value(config["use_fixed_difficulty"])
|
||||||
|
self.fixed_difficulty.set_value(config["fixed_difficulty"])
|
||||||
|
self.cpu_threads.set_value(config["cpu_percent"])
|
||||||
|
self.cpu_priority.set_value(config["cpu_priority"])
|
||||||
|
self.xmrig_extra.set_value(config["xmrig_options"])
|
||||||
|
self.DISPLAY()
|
||||||
|
|
||||||
|
def get_config(self):
|
||||||
|
config = {
|
||||||
|
"configure_xmrig": self.configure_xmrig_miner.value,
|
||||||
|
"xmrig_username": self.username.value,
|
||||||
|
"use_fixed_difficulty": self.use_fixed_difficulty.value,
|
||||||
|
"fixed_difficulty": self.fixed_difficulty.value,
|
||||||
|
"cpu_percent": self.cpu_threads.value,
|
||||||
|
"cpu_priority": self.cpu_priority.value,
|
||||||
|
"xmrig_options": self.xmrig_extra.value,
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Our P2Pool configuration App
|
||||||
|
class ConfigApp(npyscreen.NPSAppManaged):
|
||||||
|
current_config = None
|
||||||
|
defaults = None
|
||||||
|
|
||||||
|
def onStart(self):
|
||||||
|
self.p2pool_form = self.addForm(
|
||||||
|
"MAIN",
|
||||||
|
P2PoolConfigForm,
|
||||||
|
name="P2Pool for docker-compose: P2Pool Configuration",
|
||||||
|
minimum_lines=35,
|
||||||
|
minimum_columns=80,
|
||||||
|
)
|
||||||
|
self.monero_form = self.addForm(
|
||||||
|
"MONERO",
|
||||||
|
MoneroConfigForm,
|
||||||
|
name="P2Pool for docker-compose: Monero Configuration",
|
||||||
|
minimum_lines=35,
|
||||||
|
minimum_columns=80,
|
||||||
|
)
|
||||||
|
self.xmrig_form = self.addForm(
|
||||||
|
"XMRIG",
|
||||||
|
XMRigConfigForm,
|
||||||
|
name="P2Pool for docker-compose: XMRig Configuration",
|
||||||
|
minimum_lines=35,
|
||||||
|
minimum_columns=80,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_p2pool_config(self):
|
||||||
|
return self.p2pool_form.get_config()
|
||||||
|
|
||||||
|
def get_config(self):
|
||||||
|
p2pool_config = self.p2pool_form.get_config()
|
||||||
|
monero_config = self.monero_form.get_config()
|
||||||
|
xmrig_config = self.xmrig_form.get_config()
|
||||||
|
return p2pool_config | monero_config | xmrig_config
|
||||||
|
|
||||||
|
def save_and_exit(self):
|
||||||
|
# Get config from all forms
|
||||||
|
config = self.get_config()
|
||||||
|
# Save "current config" values file
|
||||||
|
with open("current_config.jinja2", "r") as current_config:
|
||||||
|
template = current_config.read()
|
||||||
|
rendered = Template(template).render(config)
|
||||||
|
with open("/docker-compose/current_config", "w") as current_config:
|
||||||
|
current_config.write(rendered)
|
||||||
|
# Render and save docker-compose file
|
||||||
|
with open("docker-compose.jinja2", "r") as compose_file:
|
||||||
|
template = compose_file.read()
|
||||||
|
rendered = Template(template).render(config, trim_blocks=True)
|
||||||
|
with open("/docker-compose/docker-compose.yml", "w") as compose_file:
|
||||||
|
compose_file.write(rendered)
|
||||||
|
npyscreen.notify("Saved current settings", title="Saved")
|
||||||
|
self.switchForm(None)
|
||||||
|
self.saved = True
|
||||||
|
|
||||||
|
def cancel_and_exit(self):
|
||||||
|
self.switchForm(None)
|
||||||
|
self.saved = False
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
time.sleep(1) # Give docker a second to initialize the terminal
|
||||||
|
App = ConfigApp()
|
||||||
|
App.run()
|
||||||
|
print("\n\n")
|
||||||
|
if App.saved:
|
||||||
|
print("Configuration Saved")
|
||||||
|
else:
|
||||||
|
print("Configuration Aborted")
|
||||||
|
sys.exit(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("Configuration Aborted")
|
||||||
|
sys.exit(1)
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"wallet_address": {{ wallet_address | tojson(indent=2) }},
|
||||||
|
"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) }},
|
||||||
|
"xmrig_username": {{ xmrig_username | tojson(indent=2) }},
|
||||||
|
"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) }}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"wallet_address": "44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg",
|
||||||
|
"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": "",
|
||||||
|
"xmrig_username": "p2pool",
|
||||||
|
"use_fixed_difficulty": true,
|
||||||
|
"fixed_difficulty": 500000,
|
||||||
|
"cpu_percent": 100,
|
||||||
|
"cpu_priority": 2,
|
||||||
|
"xmrig_options": ""
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
---
|
||||||
|
version: '3.4'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
p2pool:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
p2pool:
|
||||||
|
{% if sidechain[0] == 0 %}
|
||||||
|
name: p2pool
|
||||||
|
{% else %}
|
||||||
|
name: p2pool-mini
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if configure_monero == True %}
|
||||||
|
monero:
|
||||||
|
name: monero
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
services:
|
||||||
|
p2pool:
|
||||||
|
image: p2pool:latest
|
||||||
|
build: ../
|
||||||
|
container_name: p2pool-p2pool
|
||||||
|
networks:
|
||||||
|
- p2pool
|
||||||
|
privileged: true
|
||||||
|
ports:
|
||||||
|
{% if sidechain[0] == 0 %}
|
||||||
|
- 37888:37888/tcp
|
||||||
|
{% else %}
|
||||||
|
- 37889:37889/tcp
|
||||||
|
{% endif %}
|
||||||
|
{% if expose_stratum_port == True %}
|
||||||
|
- {{ stratum_port }}:3333/tcp
|
||||||
|
{% endif %}
|
||||||
|
volumes:
|
||||||
|
- p2pool:/home/p2pool/.p2pool:rw
|
||||||
|
- /dev/null:/home/p2pool/.p2pool/p2pool.log:rw
|
||||||
|
- /dev/hugepages:/dev/hugepages:rw
|
||||||
|
{% if configure_monero == True %}
|
||||||
|
depends_on:
|
||||||
|
- monero
|
||||||
|
{% endif %}
|
||||||
|
restart: unless-stopped
|
||||||
|
command: >-
|
||||||
|
{% if configure_monero == True %}
|
||||||
|
--host monero
|
||||||
|
{% else %}
|
||||||
|
--host {{ public_monero_node }}
|
||||||
|
--rpc-login {{ monero_node_login }}
|
||||||
|
{% endif %}
|
||||||
|
--wallet {{ wallet_address }}
|
||||||
|
--loglevel {{ p2pool_log_level | int }}
|
||||||
|
{% if sidechain[0] == 1 %}
|
||||||
|
--mini
|
||||||
|
{% endif %}
|
||||||
|
{% 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
|
||||||
|
build:
|
||||||
|
context: monero
|
||||||
|
args:
|
||||||
|
- MONERO_GIT_TAG={{ monero_version }}
|
||||||
|
container_name: p2pool-monero
|
||||||
|
networks:
|
||||||
|
- 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
|
||||||
|
- /dev/hugepages:/dev/hugepages:rw
|
||||||
|
restart: unless-stopped
|
||||||
|
command: >-
|
||||||
|
--zmq-pub tcp://0.0.0.0:18083
|
||||||
|
--disable-dns-checkpoints
|
||||||
|
--enable-dns-blocklist
|
||||||
|
--non-interactive
|
||||||
|
--p2p-bind-ip=0.0.0.0
|
||||||
|
--p2p-bind-port=18080
|
||||||
|
--rpc-bind-ip=0.0.0.0
|
||||||
|
--rpc-bind-port=18081
|
||||||
|
--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 }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if configure_xmrig == True %}
|
||||||
|
xmrig:
|
||||||
|
image: xmrig:latest
|
||||||
|
build: xmrig
|
||||||
|
container_name: p2pool-xmrig
|
||||||
|
networks:
|
||||||
|
- p2pool
|
||||||
|
privileged: true
|
||||||
|
volumes:
|
||||||
|
- /dev:/dev:ro
|
||||||
|
- /lib/modules:/lib/modules:ro
|
||||||
|
- /dev/hugepages:/dev/hugepages:rw
|
||||||
|
depends_on:
|
||||||
|
- p2pool
|
||||||
|
restart: unless-stopped
|
||||||
|
command: >-
|
||||||
|
--randomx-1gb-pages
|
||||||
|
-o p2pool:3333
|
||||||
|
{% if enable_autodiff == False and use_fixed_difficulty == True %}
|
||||||
|
-u {{ xmrig_username }}+{{ fixed_difficulty | int }}
|
||||||
|
{% else %}
|
||||||
|
-u {{ xmrig_username }}
|
||||||
|
{% endif %}
|
||||||
|
--cpu-max-threads-hint={{ cpu_percent | int }}
|
||||||
|
--cpu-priority={{ cpu_priority | int }}
|
||||||
|
{% if xmrig_options != "" %}
|
||||||
|
{{ xmrig_options }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
npyscreen==4.10.5
|
||||||
|
MarkupSafe==2.0.1
|
||||||
|
jinja2==2.11.3
|
||||||
+45
@@ -0,0 +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
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"wallet_address": "44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg",
|
||||||
|
"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": "",
|
||||||
|
"xmrig_username": "p2pool",
|
||||||
|
"use_fixed_difficulty": true,
|
||||||
|
"fixed_difficulty": 500000,
|
||||||
|
"cpu_percent": 100,
|
||||||
|
"cpu_priority": 2,
|
||||||
|
"xmrig_options": ""
|
||||||
|
}
|
||||||
@@ -7,22 +7,86 @@ networks:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
p2pool:
|
p2pool:
|
||||||
|
|
||||||
name: p2pool
|
name: p2pool
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
monero:
|
monero:
|
||||||
name: monero
|
name: monero
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
p2pool:
|
||||||
|
image: p2pool:latest
|
||||||
|
build: ../
|
||||||
|
container_name: p2pool-p2pool
|
||||||
|
networks:
|
||||||
|
- p2pool
|
||||||
|
privileged: true
|
||||||
|
ports:
|
||||||
|
|
||||||
|
- 37888:37888/tcp
|
||||||
|
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- p2pool:/home/p2pool/.p2pool:rw
|
||||||
|
- /dev/null:/home/p2pool/.p2pool/p2pool.log:rw
|
||||||
|
- /dev/hugepages:/dev/hugepages:rw
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- monero
|
||||||
|
|
||||||
|
restart: unless-stopped
|
||||||
|
command: >-
|
||||||
|
|
||||||
|
--host monero
|
||||||
|
|
||||||
|
--wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg
|
||||||
|
--loglevel 3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
monero:
|
monero:
|
||||||
image: monero:latest
|
image: monero:latest
|
||||||
build:
|
build:
|
||||||
context: monero
|
context: monero
|
||||||
args:
|
args:
|
||||||
- MONERO_GIT_TAG=${MONERO_GIT_TAG}
|
- MONERO_GIT_TAG=latest
|
||||||
container_name: p2pool-monero
|
container_name: p2pool-monero
|
||||||
networks:
|
networks:
|
||||||
- p2pool
|
- p2pool
|
||||||
ports:
|
ports:
|
||||||
- 18080:18080/tcp
|
- 18080:18080/tcp
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- monero:/home/monero/.bitmonero:rw
|
- monero:/home/monero/.bitmonero:rw
|
||||||
- /dev/null:/home/monero/.bitmonero/bitmonero.log:rw
|
- /dev/null:/home/monero/.bitmonero/bitmonero.log:rw
|
||||||
@@ -37,32 +101,22 @@ services:
|
|||||||
--p2p-bind-port=18080
|
--p2p-bind-port=18080
|
||||||
--rpc-bind-ip=0.0.0.0
|
--rpc-bind-ip=0.0.0.0
|
||||||
--rpc-bind-port=18081
|
--rpc-bind-port=18081
|
||||||
|
--restricted-rpc
|
||||||
--confirm-external-bind
|
--confirm-external-bind
|
||||||
${PRUNE_NODE}
|
--log-level=0
|
||||||
${MONERO_EXTRA_OPTIONS}
|
|
||||||
|
--prune-blockchain
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--fast-block-sync=0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
p2pool:
|
|
||||||
image: p2pool:latest
|
|
||||||
build: ../
|
|
||||||
container_name: p2pool-p2pool
|
|
||||||
networks:
|
|
||||||
- p2pool
|
|
||||||
ports:
|
|
||||||
- ${P2POOL_STRATUM_PORT}:3333/tcp
|
|
||||||
- 37888:37888/tcp
|
|
||||||
- 37889:37889/tcp
|
|
||||||
volumes:
|
|
||||||
- p2pool:/home/p2pool/.p2pool:rw
|
|
||||||
- /dev/null:/home/p2pool/.p2pool/p2pool.log:rw
|
|
||||||
- /dev/hugepages:/dev/hugepages:rw
|
|
||||||
depends_on:
|
|
||||||
- monero
|
|
||||||
restart: unless-stopped
|
|
||||||
command: >-
|
|
||||||
--host monero
|
|
||||||
--wallet ${WALLET_ADDRESS}
|
|
||||||
--loglevel ${P2POOL_LOGLEVEL}
|
|
||||||
${P2POOL_MINI}
|
|
||||||
|
|
||||||
xmrig:
|
xmrig:
|
||||||
image: xmrig:latest
|
image: xmrig:latest
|
||||||
@@ -79,8 +133,11 @@ services:
|
|||||||
- p2pool
|
- p2pool
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: >-
|
command: >-
|
||||||
${FIXED_MINING_DIFFICULTY}
|
|
||||||
${MINING_CPU_PRIORITY}
|
|
||||||
${XMRIG_EXTRA_OPTIONS}
|
|
||||||
--randomx-1gb-pages
|
--randomx-1gb-pages
|
||||||
-o p2pool:3333
|
-o p2pool:3333
|
||||||
|
|
||||||
|
-u p2pool
|
||||||
|
|
||||||
|
--cpu-max-threads-hint=100
|
||||||
|
--cpu-priority=2
|
||||||
|
|
||||||
|
|||||||
@@ -1,79 +1,59 @@
|
|||||||
FROM ubuntu:latest
|
# Multistage docker build, requires docker 17.05
|
||||||
|
|
||||||
|
# builder stage
|
||||||
|
FROM ubuntu:20.04 as builder
|
||||||
ARG MONERO_GIT_TAG="latest"
|
ARG MONERO_GIT_TAG="latest"
|
||||||
|
|
||||||
RUN set -e && \
|
RUN set -e && \
|
||||||
apt-get update -q -y --no-install-recommends && \
|
apt-get update -q -y --no-install-recommends && \
|
||||||
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
|
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
|
||||||
git \
|
automake \
|
||||||
ca-certificates \
|
autotools-dev \
|
||||||
|
bsdmainutils \
|
||||||
build-essential \
|
build-essential \
|
||||||
pkg-config \
|
ca-certificates \
|
||||||
cmake \
|
|
||||||
libuv1-dev \
|
|
||||||
libzmq3-dev \
|
|
||||||
libsodium-dev \
|
|
||||||
libpgm-dev \
|
|
||||||
libnorm-dev \
|
|
||||||
libgss-dev \
|
|
||||||
libssl-dev \
|
|
||||||
libzmq3-dev \
|
|
||||||
libunbound-dev \
|
|
||||||
libsodium-dev \
|
|
||||||
libunwind8-dev \
|
|
||||||
liblzma-dev \
|
|
||||||
libreadline6-dev \
|
|
||||||
libldns-dev \
|
|
||||||
libexpat1-dev \
|
|
||||||
libpgm-dev \
|
|
||||||
qttools5-dev-tools \
|
|
||||||
libhidapi-dev \
|
|
||||||
libusb-1.0-0-dev \
|
|
||||||
libprotobuf-dev \
|
|
||||||
protobuf-compiler \
|
|
||||||
libudev-dev \
|
|
||||||
libboost-chrono-dev \
|
|
||||||
libboost-date-time-dev \
|
|
||||||
libboost-filesystem-dev \
|
|
||||||
libboost-locale-dev \
|
|
||||||
libboost-program-options-dev \
|
|
||||||
libboost-regex-dev \
|
|
||||||
libboost-serialization-dev \
|
|
||||||
libboost-system-dev \
|
|
||||||
libboost-thread-dev \
|
|
||||||
ccache \
|
ccache \
|
||||||
doxygen \
|
cmake \
|
||||||
graphviz
|
curl \
|
||||||
|
git \
|
||||||
|
libtool \
|
||||||
|
pkg-config \
|
||||||
|
gperf
|
||||||
|
|
||||||
WORKDIR /usr/src
|
|
||||||
|
WORKDIR /src
|
||||||
RUN git clone --recursive https://github.com/monero-project/monero && \
|
RUN git clone --recursive https://github.com/monero-project/monero && \
|
||||||
cd monero && \
|
cd monero && \
|
||||||
if [ "x$MONERO_GIT_TAG" = "xlatest" ]; then MONERO_GIT_TAG=$(git describe --tags $(git rev-list --tags --max-count=1)); fi && \
|
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 checkout $MONERO_GIT_TAG && \
|
||||||
git submodule sync && git submodule update --init --force --recursive && \
|
git submodule sync && git submodule update --init --force --recursive && \
|
||||||
make release-static -j$(nproc)
|
make -j$(nproc) depends target=$(contrib/depends/config.guess)
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
|
|
||||||
FROM ubuntu:latest
|
# 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 && \
|
RUN set -e && \
|
||||||
apt-get update -q -y --no-install-recommends && \
|
apt-get update -q -y --no-install-recommends && \
|
||||||
DEBIAN_FRONTEND="noninteractive" apt-get install -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
|
&& \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt
|
||||||
|
|
||||||
RUN groupadd -r monero -g 1000 && \
|
RUN groupadd -r monero -g 1000 && \
|
||||||
useradd -u 1000 -r -g monero -s /sbin/nologin -c "monero node user" monero
|
adduser --uid 1000 --gid 1000 --system --disabled-password monero && \
|
||||||
RUN mkdir -p /home/monero/.bitmonero && \
|
mkdir -p /home/monero/.bitmonero && \
|
||||||
chown monero.monero /home/monero /home/monero/.bitmonero
|
chown -R monero:monero /home/monero/.bitmonero
|
||||||
|
|
||||||
USER monero
|
USER monero
|
||||||
|
WORKDIR /home/monero
|
||||||
|
|
||||||
EXPOSE 18080 18081 18083
|
EXPOSE 18080 18081 18083
|
||||||
|
|
||||||
VOLUME /home/monero/.bitmonero
|
VOLUME /home/monero/.bitmonero
|
||||||
|
|
||||||
WORKDIR /home/monero
|
ENTRYPOINT ["/usr/local/bin/monerod"]
|
||||||
ENTRYPOINT ["/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>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM ubuntu:latest
|
FROM ubuntu:20.04
|
||||||
|
|
||||||
RUN set -e && \
|
RUN set -e && \
|
||||||
apt-get update -q -y --no-install-recommends && \
|
apt-get update -q -y --no-install-recommends && \
|
||||||
@@ -20,7 +20,7 @@ RUN git clone https://github.com/xmrig/xmrig.git && \
|
|||||||
|
|
||||||
# ---
|
# ---
|
||||||
|
|
||||||
FROM ubuntu:latest
|
FROM ubuntu:20.04
|
||||||
|
|
||||||
COPY --from=0 /usr/src/xmrig/build/xmrig /
|
COPY --from=0 /usr/src/xmrig/build/xmrig /
|
||||||
|
|
||||||
|
|||||||
@@ -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: 1adc7d8efd...d10edcbc93
Vendored
+1
-1
Submodule external/src/cppzmq updated: a98fa4a91d...381f699d37
+1
Submodule external/src/curl added at 93d092867f
Vendored
+1
-1
Submodule external/src/libuv updated: 03a698b9c6...0c1fa696aa
Vendored
+1
-1
Submodule external/src/libzmq updated: 572eb008f1...ec6f3b1dba
Vendored
-22
@@ -1,22 +0,0 @@
|
|||||||
This software is licensed under the MIT License.
|
|
||||||
|
|
||||||
Copyright Fedor Indutny, 2018.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
||||||
persons to whom the Software is furnished to do so, subject to the
|
|
||||||
following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included
|
|
||||||
in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
||||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
||||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
||||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
Vendored
-375
@@ -1,375 +0,0 @@
|
|||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(disable : 4100 4668 4710 4711 4820)
|
|
||||||
#elif defined __GNUC__
|
|
||||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "llhttp.h"
|
|
||||||
|
|
||||||
#define CALLBACK_MAYBE(PARSER, NAME) \
|
|
||||||
do { \
|
|
||||||
const llhttp_settings_t* settings; \
|
|
||||||
settings = (const llhttp_settings_t*) (PARSER)->settings; \
|
|
||||||
if (settings == NULL || settings->NAME == NULL) { \
|
|
||||||
err = 0; \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
err = settings->NAME((PARSER)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \
|
|
||||||
do { \
|
|
||||||
const llhttp_settings_t* settings; \
|
|
||||||
settings = (const llhttp_settings_t*) (PARSER)->settings; \
|
|
||||||
if (settings == NULL || settings->NAME == NULL) { \
|
|
||||||
err = 0; \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
err = settings->NAME((PARSER), (START), (LEN)); \
|
|
||||||
if (err == -1) { \
|
|
||||||
err = HPE_USER; \
|
|
||||||
llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
|
|
||||||
const llhttp_settings_t* settings) {
|
|
||||||
llhttp__internal_init(parser);
|
|
||||||
|
|
||||||
parser->type = type;
|
|
||||||
parser->settings = (void*) settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(__wasm__)
|
|
||||||
|
|
||||||
extern int wasm_on_message_begin(llhttp_t * p);
|
|
||||||
extern int wasm_on_url(llhttp_t* p, const char* at, size_t length);
|
|
||||||
extern int wasm_on_status(llhttp_t* p, const char* at, size_t length);
|
|
||||||
extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length);
|
|
||||||
extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length);
|
|
||||||
extern int wasm_on_headers_complete(llhttp_t * p, int status_code,
|
|
||||||
uint8_t upgrade, int should_keep_alive);
|
|
||||||
extern int wasm_on_body(llhttp_t* p, const char* at, size_t length);
|
|
||||||
extern int wasm_on_message_complete(llhttp_t * p);
|
|
||||||
|
|
||||||
static int wasm_on_headers_complete_wrap(llhttp_t* p) {
|
|
||||||
return wasm_on_headers_complete(p, p->status_code, p->upgrade,
|
|
||||||
llhttp_should_keep_alive(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
const llhttp_settings_t wasm_settings = {
|
|
||||||
wasm_on_message_begin,
|
|
||||||
wasm_on_url,
|
|
||||||
wasm_on_status,
|
|
||||||
wasm_on_header_field,
|
|
||||||
wasm_on_header_value,
|
|
||||||
wasm_on_headers_complete_wrap,
|
|
||||||
wasm_on_body,
|
|
||||||
wasm_on_message_complete,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
llhttp_t* llhttp_alloc(llhttp_type_t type) {
|
|
||||||
llhttp_t* parser = malloc(sizeof(llhttp_t));
|
|
||||||
llhttp_init(parser, type, &wasm_settings);
|
|
||||||
return parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
void llhttp_free(llhttp_t* parser) {
|
|
||||||
free(parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Some getters required to get stuff from the parser */
|
|
||||||
|
|
||||||
uint8_t llhttp_get_type(llhttp_t* parser) {
|
|
||||||
return parser->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t llhttp_get_http_major(llhttp_t* parser) {
|
|
||||||
return parser->http_major;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t llhttp_get_http_minor(llhttp_t* parser) {
|
|
||||||
return parser->http_minor;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t llhttp_get_method(llhttp_t* parser) {
|
|
||||||
return parser->method;
|
|
||||||
}
|
|
||||||
|
|
||||||
int llhttp_get_status_code(llhttp_t* parser) {
|
|
||||||
return parser->status_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t llhttp_get_upgrade(llhttp_t* parser) {
|
|
||||||
return parser->upgrade;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // defined(__wasm__)
|
|
||||||
|
|
||||||
|
|
||||||
void llhttp_reset(llhttp_t* parser) {
|
|
||||||
llhttp_type_t type = parser->type;
|
|
||||||
const llhttp_settings_t* settings = parser->settings;
|
|
||||||
void* data = parser->data;
|
|
||||||
uint8_t lenient_flags = parser->lenient_flags;
|
|
||||||
|
|
||||||
llhttp__internal_init(parser);
|
|
||||||
|
|
||||||
parser->type = type;
|
|
||||||
parser->settings = (void*) settings;
|
|
||||||
parser->data = data;
|
|
||||||
parser->lenient_flags = lenient_flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
|
|
||||||
return llhttp__internal_execute(parser, data, data + len);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void llhttp_settings_init(llhttp_settings_t* settings) {
|
|
||||||
memset(settings, 0, sizeof(*settings));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
llhttp_errno_t llhttp_finish(llhttp_t* parser) {
|
|
||||||
int err;
|
|
||||||
|
|
||||||
/* We're in an error state. Don't bother doing anything. */
|
|
||||||
if (parser->error != 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (parser->finish) {
|
|
||||||
case HTTP_FINISH_SAFE_WITH_CB:
|
|
||||||
CALLBACK_MAYBE(parser, on_message_complete);
|
|
||||||
if (err != HPE_OK) return err;
|
|
||||||
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
case HTTP_FINISH_SAFE:
|
|
||||||
return HPE_OK;
|
|
||||||
case HTTP_FINISH_UNSAFE:
|
|
||||||
parser->reason = "Invalid EOF state";
|
|
||||||
return HPE_INVALID_EOF_STATE;
|
|
||||||
default:
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void llhttp_pause(llhttp_t* parser) {
|
|
||||||
if (parser->error != HPE_OK) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parser->error = HPE_PAUSED;
|
|
||||||
parser->reason = "Paused";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void llhttp_resume(llhttp_t* parser) {
|
|
||||||
if (parser->error != HPE_PAUSED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parser->error = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void llhttp_resume_after_upgrade(llhttp_t* parser) {
|
|
||||||
if (parser->error != HPE_PAUSED_UPGRADE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parser->error = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
|
|
||||||
return parser->error;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const char* llhttp_get_error_reason(const llhttp_t* parser) {
|
|
||||||
return parser->reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
|
|
||||||
parser->reason = reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const char* llhttp_get_error_pos(const llhttp_t* parser) {
|
|
||||||
return parser->error_pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const char* llhttp_errno_name(llhttp_errno_t err) {
|
|
||||||
#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
|
|
||||||
switch (err) {
|
|
||||||
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
|
||||||
default: abort();
|
|
||||||
}
|
|
||||||
#undef HTTP_ERRNO_GEN
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const char* llhttp_method_name(llhttp_method_t method) {
|
|
||||||
#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING;
|
|
||||||
switch (method) {
|
|
||||||
HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN)
|
|
||||||
default: abort();
|
|
||||||
}
|
|
||||||
#undef HTTP_METHOD_GEN
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) {
|
|
||||||
if (enabled) {
|
|
||||||
parser->lenient_flags |= LENIENT_HEADERS;
|
|
||||||
} else {
|
|
||||||
parser->lenient_flags &= ~LENIENT_HEADERS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) {
|
|
||||||
if (enabled) {
|
|
||||||
parser->lenient_flags |= LENIENT_CHUNKED_LENGTH;
|
|
||||||
} else {
|
|
||||||
parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) {
|
|
||||||
if (enabled) {
|
|
||||||
parser->lenient_flags |= LENIENT_KEEP_ALIVE;
|
|
||||||
} else {
|
|
||||||
parser->lenient_flags &= ~LENIENT_KEEP_ALIVE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Callbacks */
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
CALLBACK_MAYBE(s, on_message_begin);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
CALLBACK_MAYBE(s, on_url_complete);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
CALLBACK_MAYBE(s, on_status_complete);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
CALLBACK_MAYBE(s, on_header_field_complete);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
CALLBACK_MAYBE(s, on_header_value_complete);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
CALLBACK_MAYBE(s, on_headers_complete);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
CALLBACK_MAYBE(s, on_message_complete);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
CALLBACK_MAYBE(s, on_chunk_header);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
|
|
||||||
int err;
|
|
||||||
CALLBACK_MAYBE(s, on_chunk_complete);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Private */
|
|
||||||
|
|
||||||
|
|
||||||
void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
|
|
||||||
const char* msg) {
|
|
||||||
if (p == endp) {
|
|
||||||
fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
|
|
||||||
s->flags, msg);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s,
|
|
||||||
s->type, s->flags, *p, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
-155
@@ -1,155 +0,0 @@
|
|||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(disable : 4100 4668 4710 4711 4820)
|
|
||||||
#elif defined __GNUC__
|
|
||||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#ifndef LLHTTP__TEST
|
|
||||||
# include "llhttp.h"
|
|
||||||
#else
|
|
||||||
# define llhttp_t llparse_t
|
|
||||||
#endif /* */
|
|
||||||
|
|
||||||
int llhttp_message_needs_eof(const llhttp_t* parser);
|
|
||||||
int llhttp_should_keep_alive(const llhttp_t* parser);
|
|
||||||
|
|
||||||
int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
|
|
||||||
const char* endp) {
|
|
||||||
/* Set this here so that on_headers_complete() callbacks can see it */
|
|
||||||
if ((parser->flags & F_UPGRADE) &&
|
|
||||||
(parser->flags & F_CONNECTION_UPGRADE)) {
|
|
||||||
/* For responses, "Upgrade: foo" and "Connection: upgrade" are
|
|
||||||
* mandatory only when it is a 101 Switching Protocols response,
|
|
||||||
* otherwise it is purely informational, to announce support.
|
|
||||||
*/
|
|
||||||
parser->upgrade =
|
|
||||||
(parser->type == HTTP_REQUEST || parser->status_code == 101);
|
|
||||||
} else {
|
|
||||||
parser->upgrade = (parser->method == HTTP_CONNECT);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Return values:
|
|
||||||
* 0 - No body, `restart`, message_complete
|
|
||||||
* 1 - CONNECT request, `restart`, message_complete, and pause
|
|
||||||
* 2 - chunk_size_start
|
|
||||||
* 3 - body_identity
|
|
||||||
* 4 - body_identity_eof
|
|
||||||
* 5 - invalid transfer-encoding for request
|
|
||||||
*/
|
|
||||||
int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
|
|
||||||
const char* endp) {
|
|
||||||
int hasBody;
|
|
||||||
|
|
||||||
hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;
|
|
||||||
if (parser->upgrade && (parser->method == HTTP_CONNECT ||
|
|
||||||
(parser->flags & F_SKIPBODY) || !hasBody)) {
|
|
||||||
/* Exit, the rest of the message is in a different protocol. */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser->flags & F_SKIPBODY) {
|
|
||||||
return 0;
|
|
||||||
} else if (parser->flags & F_CHUNKED) {
|
|
||||||
/* chunked encoding - ignore Content-Length header, prepare for a chunk */
|
|
||||||
return 2;
|
|
||||||
} else if (parser->flags & F_TRANSFER_ENCODING) {
|
|
||||||
if (parser->type == HTTP_REQUEST &&
|
|
||||||
(parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0) {
|
|
||||||
/* RFC 7230 3.3.3 */
|
|
||||||
|
|
||||||
/* If a Transfer-Encoding header field
|
|
||||||
* is present in a request and the chunked transfer coding is not
|
|
||||||
* the final encoding, the message body length cannot be determined
|
|
||||||
* reliably; the server MUST respond with the 400 (Bad Request)
|
|
||||||
* status code and then close the connection.
|
|
||||||
*/
|
|
||||||
return 5;
|
|
||||||
} else {
|
|
||||||
/* RFC 7230 3.3.3 */
|
|
||||||
|
|
||||||
/* If a Transfer-Encoding header field is present in a response and
|
|
||||||
* the chunked transfer coding is not the final encoding, the
|
|
||||||
* message body length is determined by reading the connection until
|
|
||||||
* it is closed by the server.
|
|
||||||
*/
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!(parser->flags & F_CONTENT_LENGTH)) {
|
|
||||||
if (!llhttp_message_needs_eof(parser)) {
|
|
||||||
/* Assume content-length 0 - read the next */
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
/* Read body until EOF */
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
} else if (parser->content_length == 0) {
|
|
||||||
/* Content-Length header given but zero: Content-Length: 0\r\n */
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
/* Content-Length header given and non-zero */
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp__after_message_complete(llhttp_t* parser, const char* p,
|
|
||||||
const char* endp) {
|
|
||||||
int should_keep_alive;
|
|
||||||
|
|
||||||
should_keep_alive = llhttp_should_keep_alive(parser);
|
|
||||||
parser->finish = HTTP_FINISH_SAFE;
|
|
||||||
parser->flags = 0;
|
|
||||||
|
|
||||||
/* NOTE: this is ignored in loose parsing mode */
|
|
||||||
return should_keep_alive;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp_message_needs_eof(const llhttp_t* parser) {
|
|
||||||
if (parser->type == HTTP_REQUEST) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See RFC 2616 section 4.4 */
|
|
||||||
if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
|
|
||||||
parser->status_code == 204 || /* No Content */
|
|
||||||
parser->status_code == 304 || /* Not Modified */
|
|
||||||
(parser->flags & F_SKIPBODY)) { /* response to a HEAD request */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */
|
|
||||||
if ((parser->flags & F_TRANSFER_ENCODING) &&
|
|
||||||
(parser->flags & F_CHUNKED) == 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int llhttp_should_keep_alive(const llhttp_t* parser) {
|
|
||||||
if (parser->http_major > 0 && parser->http_minor > 0) {
|
|
||||||
/* HTTP/1.1 */
|
|
||||||
if (parser->flags & F_CONNECTION_CLOSE) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* HTTP/1.0 or earlier */
|
|
||||||
if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !llhttp_message_needs_eof(parser);
|
|
||||||
}
|
|
||||||
Vendored
-14933
File diff suppressed because it is too large
Load Diff
Vendored
-564
@@ -1,564 +0,0 @@
|
|||||||
#ifndef INCLUDE_LLHTTP_H_
|
|
||||||
#define INCLUDE_LLHTTP_H_
|
|
||||||
|
|
||||||
#define LLHTTP_VERSION_MAJOR 6
|
|
||||||
#define LLHTTP_VERSION_MINOR 0
|
|
||||||
#define LLHTTP_VERSION_PATCH 4
|
|
||||||
|
|
||||||
#ifndef LLHTTP_STRICT_MODE
|
|
||||||
# define LLHTTP_STRICT_MODE 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef INCLUDE_LLHTTP_ITSELF_H_
|
|
||||||
#define INCLUDE_LLHTTP_ITSELF_H_
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
typedef struct llhttp__internal_s llhttp__internal_t;
|
|
||||||
struct llhttp__internal_s {
|
|
||||||
int32_t _index;
|
|
||||||
void* _span_pos0;
|
|
||||||
void* _span_cb0;
|
|
||||||
int32_t error;
|
|
||||||
const char* reason;
|
|
||||||
const char* error_pos;
|
|
||||||
void* data;
|
|
||||||
void* _current;
|
|
||||||
uint64_t content_length;
|
|
||||||
uint8_t type;
|
|
||||||
uint8_t method;
|
|
||||||
uint8_t http_major;
|
|
||||||
uint8_t http_minor;
|
|
||||||
uint8_t header_state;
|
|
||||||
uint8_t lenient_flags;
|
|
||||||
uint8_t upgrade;
|
|
||||||
uint8_t finish;
|
|
||||||
uint16_t flags;
|
|
||||||
uint16_t status_code;
|
|
||||||
void* settings;
|
|
||||||
};
|
|
||||||
|
|
||||||
int llhttp__internal_init(llhttp__internal_t* s);
|
|
||||||
int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
#endif /* INCLUDE_LLHTTP_ITSELF_H_ */
|
|
||||||
|
|
||||||
#ifndef LLLLHTTP_C_HEADERS_
|
|
||||||
#define LLLLHTTP_C_HEADERS_
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum llhttp_errno {
|
|
||||||
HPE_OK = 0,
|
|
||||||
HPE_INTERNAL = 1,
|
|
||||||
HPE_STRICT = 2,
|
|
||||||
HPE_LF_EXPECTED = 3,
|
|
||||||
HPE_UNEXPECTED_CONTENT_LENGTH = 4,
|
|
||||||
HPE_CLOSED_CONNECTION = 5,
|
|
||||||
HPE_INVALID_METHOD = 6,
|
|
||||||
HPE_INVALID_URL = 7,
|
|
||||||
HPE_INVALID_CONSTANT = 8,
|
|
||||||
HPE_INVALID_VERSION = 9,
|
|
||||||
HPE_INVALID_HEADER_TOKEN = 10,
|
|
||||||
HPE_INVALID_CONTENT_LENGTH = 11,
|
|
||||||
HPE_INVALID_CHUNK_SIZE = 12,
|
|
||||||
HPE_INVALID_STATUS = 13,
|
|
||||||
HPE_INVALID_EOF_STATE = 14,
|
|
||||||
HPE_INVALID_TRANSFER_ENCODING = 15,
|
|
||||||
HPE_CB_MESSAGE_BEGIN = 16,
|
|
||||||
HPE_CB_HEADERS_COMPLETE = 17,
|
|
||||||
HPE_CB_MESSAGE_COMPLETE = 18,
|
|
||||||
HPE_CB_CHUNK_HEADER = 19,
|
|
||||||
HPE_CB_CHUNK_COMPLETE = 20,
|
|
||||||
HPE_PAUSED = 21,
|
|
||||||
HPE_PAUSED_UPGRADE = 22,
|
|
||||||
HPE_PAUSED_H2_UPGRADE = 23,
|
|
||||||
HPE_USER = 24
|
|
||||||
};
|
|
||||||
typedef enum llhttp_errno llhttp_errno_t;
|
|
||||||
|
|
||||||
enum llhttp_flags {
|
|
||||||
F_CONNECTION_KEEP_ALIVE = 0x1,
|
|
||||||
F_CONNECTION_CLOSE = 0x2,
|
|
||||||
F_CONNECTION_UPGRADE = 0x4,
|
|
||||||
F_CHUNKED = 0x8,
|
|
||||||
F_UPGRADE = 0x10,
|
|
||||||
F_CONTENT_LENGTH = 0x20,
|
|
||||||
F_SKIPBODY = 0x40,
|
|
||||||
F_TRAILING = 0x80,
|
|
||||||
F_TRANSFER_ENCODING = 0x200
|
|
||||||
};
|
|
||||||
typedef enum llhttp_flags llhttp_flags_t;
|
|
||||||
|
|
||||||
enum llhttp_lenient_flags {
|
|
||||||
LENIENT_HEADERS = 0x1,
|
|
||||||
LENIENT_CHUNKED_LENGTH = 0x2,
|
|
||||||
LENIENT_KEEP_ALIVE = 0x4
|
|
||||||
};
|
|
||||||
typedef enum llhttp_lenient_flags llhttp_lenient_flags_t;
|
|
||||||
|
|
||||||
enum llhttp_type {
|
|
||||||
HTTP_BOTH = 0,
|
|
||||||
HTTP_REQUEST = 1,
|
|
||||||
HTTP_RESPONSE = 2
|
|
||||||
};
|
|
||||||
typedef enum llhttp_type llhttp_type_t;
|
|
||||||
|
|
||||||
enum llhttp_finish {
|
|
||||||
HTTP_FINISH_SAFE = 0,
|
|
||||||
HTTP_FINISH_SAFE_WITH_CB = 1,
|
|
||||||
HTTP_FINISH_UNSAFE = 2
|
|
||||||
};
|
|
||||||
typedef enum llhttp_finish llhttp_finish_t;
|
|
||||||
|
|
||||||
enum llhttp_method {
|
|
||||||
HTTP_DELETE = 0,
|
|
||||||
HTTP_GET = 1,
|
|
||||||
HTTP_HEAD = 2,
|
|
||||||
HTTP_POST = 3,
|
|
||||||
HTTP_PUT = 4,
|
|
||||||
HTTP_CONNECT = 5,
|
|
||||||
HTTP_OPTIONS = 6,
|
|
||||||
HTTP_TRACE = 7,
|
|
||||||
HTTP_COPY = 8,
|
|
||||||
HTTP_LOCK = 9,
|
|
||||||
HTTP_MKCOL = 10,
|
|
||||||
HTTP_MOVE = 11,
|
|
||||||
HTTP_PROPFIND = 12,
|
|
||||||
HTTP_PROPPATCH = 13,
|
|
||||||
HTTP_SEARCH = 14,
|
|
||||||
HTTP_UNLOCK = 15,
|
|
||||||
HTTP_BIND = 16,
|
|
||||||
HTTP_REBIND = 17,
|
|
||||||
HTTP_UNBIND = 18,
|
|
||||||
HTTP_ACL = 19,
|
|
||||||
HTTP_REPORT = 20,
|
|
||||||
HTTP_MKACTIVITY = 21,
|
|
||||||
HTTP_CHECKOUT = 22,
|
|
||||||
HTTP_MERGE = 23,
|
|
||||||
HTTP_MSEARCH = 24,
|
|
||||||
HTTP_NOTIFY = 25,
|
|
||||||
HTTP_SUBSCRIBE = 26,
|
|
||||||
HTTP_UNSUBSCRIBE = 27,
|
|
||||||
HTTP_PATCH = 28,
|
|
||||||
HTTP_PURGE = 29,
|
|
||||||
HTTP_MKCALENDAR = 30,
|
|
||||||
HTTP_LINK = 31,
|
|
||||||
HTTP_UNLINK = 32,
|
|
||||||
HTTP_SOURCE = 33,
|
|
||||||
HTTP_PRI = 34,
|
|
||||||
HTTP_DESCRIBE = 35,
|
|
||||||
HTTP_ANNOUNCE = 36,
|
|
||||||
HTTP_SETUP = 37,
|
|
||||||
HTTP_PLAY = 38,
|
|
||||||
HTTP_PAUSE = 39,
|
|
||||||
HTTP_TEARDOWN = 40,
|
|
||||||
HTTP_GET_PARAMETER = 41,
|
|
||||||
HTTP_SET_PARAMETER = 42,
|
|
||||||
HTTP_REDIRECT = 43,
|
|
||||||
HTTP_RECORD = 44,
|
|
||||||
HTTP_FLUSH = 45
|
|
||||||
};
|
|
||||||
typedef enum llhttp_method llhttp_method_t;
|
|
||||||
|
|
||||||
#define HTTP_ERRNO_MAP(XX) \
|
|
||||||
XX(0, OK, OK) \
|
|
||||||
XX(1, INTERNAL, INTERNAL) \
|
|
||||||
XX(2, STRICT, STRICT) \
|
|
||||||
XX(3, LF_EXPECTED, LF_EXPECTED) \
|
|
||||||
XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
|
|
||||||
XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
|
|
||||||
XX(6, INVALID_METHOD, INVALID_METHOD) \
|
|
||||||
XX(7, INVALID_URL, INVALID_URL) \
|
|
||||||
XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
|
|
||||||
XX(9, INVALID_VERSION, INVALID_VERSION) \
|
|
||||||
XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
|
|
||||||
XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
|
|
||||||
XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
|
|
||||||
XX(13, INVALID_STATUS, INVALID_STATUS) \
|
|
||||||
XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
|
|
||||||
XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \
|
|
||||||
XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
|
|
||||||
XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
|
|
||||||
XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
|
|
||||||
XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
|
|
||||||
XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
|
|
||||||
XX(21, PAUSED, PAUSED) \
|
|
||||||
XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \
|
|
||||||
XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \
|
|
||||||
XX(24, USER, USER) \
|
|
||||||
|
|
||||||
|
|
||||||
#define HTTP_METHOD_MAP(XX) \
|
|
||||||
XX(0, DELETE, DELETE) \
|
|
||||||
XX(1, GET, GET) \
|
|
||||||
XX(2, HEAD, HEAD) \
|
|
||||||
XX(3, POST, POST) \
|
|
||||||
XX(4, PUT, PUT) \
|
|
||||||
XX(5, CONNECT, CONNECT) \
|
|
||||||
XX(6, OPTIONS, OPTIONS) \
|
|
||||||
XX(7, TRACE, TRACE) \
|
|
||||||
XX(8, COPY, COPY) \
|
|
||||||
XX(9, LOCK, LOCK) \
|
|
||||||
XX(10, MKCOL, MKCOL) \
|
|
||||||
XX(11, MOVE, MOVE) \
|
|
||||||
XX(12, PROPFIND, PROPFIND) \
|
|
||||||
XX(13, PROPPATCH, PROPPATCH) \
|
|
||||||
XX(14, SEARCH, SEARCH) \
|
|
||||||
XX(15, UNLOCK, UNLOCK) \
|
|
||||||
XX(16, BIND, BIND) \
|
|
||||||
XX(17, REBIND, REBIND) \
|
|
||||||
XX(18, UNBIND, UNBIND) \
|
|
||||||
XX(19, ACL, ACL) \
|
|
||||||
XX(20, REPORT, REPORT) \
|
|
||||||
XX(21, MKACTIVITY, MKACTIVITY) \
|
|
||||||
XX(22, CHECKOUT, CHECKOUT) \
|
|
||||||
XX(23, MERGE, MERGE) \
|
|
||||||
XX(24, MSEARCH, M-SEARCH) \
|
|
||||||
XX(25, NOTIFY, NOTIFY) \
|
|
||||||
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
|
||||||
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
|
||||||
XX(28, PATCH, PATCH) \
|
|
||||||
XX(29, PURGE, PURGE) \
|
|
||||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
|
||||||
XX(31, LINK, LINK) \
|
|
||||||
XX(32, UNLINK, UNLINK) \
|
|
||||||
XX(33, SOURCE, SOURCE) \
|
|
||||||
|
|
||||||
|
|
||||||
#define RTSP_METHOD_MAP(XX) \
|
|
||||||
XX(1, GET, GET) \
|
|
||||||
XX(3, POST, POST) \
|
|
||||||
XX(6, OPTIONS, OPTIONS) \
|
|
||||||
XX(35, DESCRIBE, DESCRIBE) \
|
|
||||||
XX(36, ANNOUNCE, ANNOUNCE) \
|
|
||||||
XX(37, SETUP, SETUP) \
|
|
||||||
XX(38, PLAY, PLAY) \
|
|
||||||
XX(39, PAUSE, PAUSE) \
|
|
||||||
XX(40, TEARDOWN, TEARDOWN) \
|
|
||||||
XX(41, GET_PARAMETER, GET_PARAMETER) \
|
|
||||||
XX(42, SET_PARAMETER, SET_PARAMETER) \
|
|
||||||
XX(43, REDIRECT, REDIRECT) \
|
|
||||||
XX(44, RECORD, RECORD) \
|
|
||||||
XX(45, FLUSH, FLUSH) \
|
|
||||||
|
|
||||||
|
|
||||||
#define HTTP_ALL_METHOD_MAP(XX) \
|
|
||||||
XX(0, DELETE, DELETE) \
|
|
||||||
XX(1, GET, GET) \
|
|
||||||
XX(2, HEAD, HEAD) \
|
|
||||||
XX(3, POST, POST) \
|
|
||||||
XX(4, PUT, PUT) \
|
|
||||||
XX(5, CONNECT, CONNECT) \
|
|
||||||
XX(6, OPTIONS, OPTIONS) \
|
|
||||||
XX(7, TRACE, TRACE) \
|
|
||||||
XX(8, COPY, COPY) \
|
|
||||||
XX(9, LOCK, LOCK) \
|
|
||||||
XX(10, MKCOL, MKCOL) \
|
|
||||||
XX(11, MOVE, MOVE) \
|
|
||||||
XX(12, PROPFIND, PROPFIND) \
|
|
||||||
XX(13, PROPPATCH, PROPPATCH) \
|
|
||||||
XX(14, SEARCH, SEARCH) \
|
|
||||||
XX(15, UNLOCK, UNLOCK) \
|
|
||||||
XX(16, BIND, BIND) \
|
|
||||||
XX(17, REBIND, REBIND) \
|
|
||||||
XX(18, UNBIND, UNBIND) \
|
|
||||||
XX(19, ACL, ACL) \
|
|
||||||
XX(20, REPORT, REPORT) \
|
|
||||||
XX(21, MKACTIVITY, MKACTIVITY) \
|
|
||||||
XX(22, CHECKOUT, CHECKOUT) \
|
|
||||||
XX(23, MERGE, MERGE) \
|
|
||||||
XX(24, MSEARCH, M-SEARCH) \
|
|
||||||
XX(25, NOTIFY, NOTIFY) \
|
|
||||||
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
|
||||||
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
|
||||||
XX(28, PATCH, PATCH) \
|
|
||||||
XX(29, PURGE, PURGE) \
|
|
||||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
|
||||||
XX(31, LINK, LINK) \
|
|
||||||
XX(32, UNLINK, UNLINK) \
|
|
||||||
XX(33, SOURCE, SOURCE) \
|
|
||||||
XX(34, PRI, PRI) \
|
|
||||||
XX(35, DESCRIBE, DESCRIBE) \
|
|
||||||
XX(36, ANNOUNCE, ANNOUNCE) \
|
|
||||||
XX(37, SETUP, SETUP) \
|
|
||||||
XX(38, PLAY, PLAY) \
|
|
||||||
XX(39, PAUSE, PAUSE) \
|
|
||||||
XX(40, TEARDOWN, TEARDOWN) \
|
|
||||||
XX(41, GET_PARAMETER, GET_PARAMETER) \
|
|
||||||
XX(42, SET_PARAMETER, SET_PARAMETER) \
|
|
||||||
XX(43, REDIRECT, REDIRECT) \
|
|
||||||
XX(44, RECORD, RECORD) \
|
|
||||||
XX(45, FLUSH, FLUSH) \
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
#endif /* LLLLHTTP_C_HEADERS_ */
|
|
||||||
|
|
||||||
#ifndef INCLUDE_LLHTTP_API_H_
|
|
||||||
#define INCLUDE_LLHTTP_API_H_
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#if defined(__wasm__)
|
|
||||||
#define LLHTTP_EXPORT __attribute__((visibility("default")))
|
|
||||||
#else
|
|
||||||
#define LLHTTP_EXPORT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef llhttp__internal_t llhttp_t;
|
|
||||||
typedef struct llhttp_settings_s llhttp_settings_t;
|
|
||||||
|
|
||||||
typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
|
|
||||||
typedef int (*llhttp_cb)(llhttp_t*);
|
|
||||||
|
|
||||||
struct llhttp_settings_s {
|
|
||||||
/* Possible return values 0, -1, `HPE_PAUSED` */
|
|
||||||
llhttp_cb on_message_begin;
|
|
||||||
|
|
||||||
/* Possible return values 0, -1, HPE_USER */
|
|
||||||
llhttp_data_cb on_url;
|
|
||||||
llhttp_data_cb on_status;
|
|
||||||
llhttp_data_cb on_header_field;
|
|
||||||
llhttp_data_cb on_header_value;
|
|
||||||
|
|
||||||
/* Possible return values:
|
|
||||||
* 0 - Proceed normally
|
|
||||||
* 1 - Assume that request/response has no body, and proceed to parsing the
|
|
||||||
* next message
|
|
||||||
* 2 - Assume absence of body (as above) and make `llhttp_execute()` return
|
|
||||||
* `HPE_PAUSED_UPGRADE`
|
|
||||||
* -1 - Error
|
|
||||||
* `HPE_PAUSED`
|
|
||||||
*/
|
|
||||||
llhttp_cb on_headers_complete;
|
|
||||||
|
|
||||||
/* Possible return values 0, -1, HPE_USER */
|
|
||||||
llhttp_data_cb on_body;
|
|
||||||
|
|
||||||
/* Possible return values 0, -1, `HPE_PAUSED` */
|
|
||||||
llhttp_cb on_message_complete;
|
|
||||||
|
|
||||||
/* When on_chunk_header is called, the current chunk length is stored
|
|
||||||
* in parser->content_length.
|
|
||||||
* Possible return values 0, -1, `HPE_PAUSED`
|
|
||||||
*/
|
|
||||||
llhttp_cb on_chunk_header;
|
|
||||||
llhttp_cb on_chunk_complete;
|
|
||||||
|
|
||||||
/* Information-only callbacks, return value is ignored */
|
|
||||||
llhttp_cb on_url_complete;
|
|
||||||
llhttp_cb on_status_complete;
|
|
||||||
llhttp_cb on_header_field_complete;
|
|
||||||
llhttp_cb on_header_value_complete;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Initialize the parser with specific type and user settings.
|
|
||||||
*
|
|
||||||
* NOTE: lifetime of `settings` has to be at least the same as the lifetime of
|
|
||||||
* the `parser` here. In practice, `settings` has to be either a static
|
|
||||||
* variable or be allocated with `malloc`, `new`, etc.
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
|
|
||||||
const llhttp_settings_t* settings);
|
|
||||||
|
|
||||||
#if defined(__wasm__)
|
|
||||||
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
llhttp_t* llhttp_alloc(llhttp_type_t type);
|
|
||||||
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
void llhttp_free(llhttp_t* parser);
|
|
||||||
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
uint8_t llhttp_get_type(llhttp_t* parser);
|
|
||||||
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
uint8_t llhttp_get_http_major(llhttp_t* parser);
|
|
||||||
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
uint8_t llhttp_get_http_minor(llhttp_t* parser);
|
|
||||||
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
uint8_t llhttp_get_method(llhttp_t* parser);
|
|
||||||
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
int llhttp_get_status_code(llhttp_t* parser);
|
|
||||||
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
uint8_t llhttp_get_upgrade(llhttp_t* parser);
|
|
||||||
|
|
||||||
#endif // defined(__wasm__)
|
|
||||||
|
|
||||||
/* Reset an already initialized parser back to the start state, preserving the
|
|
||||||
* existing parser type, callback settings, user data, and lenient flags.
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
void llhttp_reset(llhttp_t* parser);
|
|
||||||
|
|
||||||
/* Initialize the settings object */
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
void llhttp_settings_init(llhttp_settings_t* settings);
|
|
||||||
|
|
||||||
/* Parse full or partial request/response, invoking user callbacks along the
|
|
||||||
* way.
|
|
||||||
*
|
|
||||||
* If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
|
|
||||||
* interrupts, and such errno is returned from `llhttp_execute()`. If
|
|
||||||
* `HPE_PAUSED` was used as a errno, the execution can be resumed with
|
|
||||||
* `llhttp_resume()` call.
|
|
||||||
*
|
|
||||||
* In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
|
|
||||||
* is returned after fully parsing the request/response. If the user wishes to
|
|
||||||
* continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
|
|
||||||
*
|
|
||||||
* NOTE: if this function ever returns a non-pause type error, it will continue
|
|
||||||
* to return the same error upon each successive call up until `llhttp_init()`
|
|
||||||
* is called.
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
|
|
||||||
|
|
||||||
/* This method should be called when the other side has no further bytes to
|
|
||||||
* send (e.g. shutdown of readable side of the TCP connection.)
|
|
||||||
*
|
|
||||||
* Requests without `Content-Length` and other messages might require treating
|
|
||||||
* all incoming bytes as the part of the body, up to the last byte of the
|
|
||||||
* connection. This method will invoke `on_message_complete()` callback if the
|
|
||||||
* request was terminated safely. Otherwise a error code would be returned.
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
llhttp_errno_t llhttp_finish(llhttp_t* parser);
|
|
||||||
|
|
||||||
/* Returns `1` if the incoming message is parsed until the last byte, and has
|
|
||||||
* to be completed by calling `llhttp_finish()` on EOF
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
int llhttp_message_needs_eof(const llhttp_t* parser);
|
|
||||||
|
|
||||||
/* Returns `1` if there might be any other messages following the last that was
|
|
||||||
* successfully parsed.
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
int llhttp_should_keep_alive(const llhttp_t* parser);
|
|
||||||
|
|
||||||
/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
|
|
||||||
* appropriate error reason.
|
|
||||||
*
|
|
||||||
* Important: do not call this from user callbacks! User callbacks must return
|
|
||||||
* `HPE_PAUSED` if pausing is required.
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
void llhttp_pause(llhttp_t* parser);
|
|
||||||
|
|
||||||
/* Might be called to resume the execution after the pause in user's callback.
|
|
||||||
* See `llhttp_execute()` above for details.
|
|
||||||
*
|
|
||||||
* Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
void llhttp_resume(llhttp_t* parser);
|
|
||||||
|
|
||||||
/* Might be called to resume the execution after the pause in user's callback.
|
|
||||||
* See `llhttp_execute()` above for details.
|
|
||||||
*
|
|
||||||
* Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
void llhttp_resume_after_upgrade(llhttp_t* parser);
|
|
||||||
|
|
||||||
/* Returns the latest return error */
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
|
|
||||||
|
|
||||||
/* Returns the verbal explanation of the latest returned error.
|
|
||||||
*
|
|
||||||
* Note: User callback should set error reason when returning the error. See
|
|
||||||
* `llhttp_set_error_reason()` for details.
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
const char* llhttp_get_error_reason(const llhttp_t* parser);
|
|
||||||
|
|
||||||
/* Assign verbal description to the returned error. Must be called in user
|
|
||||||
* callbacks right before returning the errno.
|
|
||||||
*
|
|
||||||
* Note: `HPE_USER` error code might be useful in user callbacks.
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
|
|
||||||
|
|
||||||
/* Returns the pointer to the last parsed byte before the returned error. The
|
|
||||||
* pointer is relative to the `data` argument of `llhttp_execute()`.
|
|
||||||
*
|
|
||||||
* Note: this method might be useful for counting the number of parsed bytes.
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
const char* llhttp_get_error_pos(const llhttp_t* parser);
|
|
||||||
|
|
||||||
/* Returns textual name of error code */
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
const char* llhttp_errno_name(llhttp_errno_t err);
|
|
||||||
|
|
||||||
/* Returns textual name of HTTP method */
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
const char* llhttp_method_name(llhttp_method_t method);
|
|
||||||
|
|
||||||
|
|
||||||
/* Enables/disables lenient header value parsing (disabled by default).
|
|
||||||
*
|
|
||||||
* Lenient parsing disables header value token checks, extending llhttp's
|
|
||||||
* protocol support to highly non-compliant clients/server. No
|
|
||||||
* `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
|
|
||||||
* lenient parsing is "on".
|
|
||||||
*
|
|
||||||
* **(USE AT YOUR OWN RISK)**
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
void llhttp_set_lenient_headers(llhttp_t* parser, int enabled);
|
|
||||||
|
|
||||||
|
|
||||||
/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and
|
|
||||||
* `Content-Length` headers (disabled by default).
|
|
||||||
*
|
|
||||||
* Normally `llhttp` would error when `Transfer-Encoding` is present in
|
|
||||||
* conjunction with `Content-Length`. This error is important to prevent HTTP
|
|
||||||
* request smuggling, but may be less desirable for small number of cases
|
|
||||||
* involving legacy servers.
|
|
||||||
*
|
|
||||||
* **(USE AT YOUR OWN RISK)**
|
|
||||||
*/
|
|
||||||
LLHTTP_EXPORT
|
|
||||||
void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);
|
|
||||||
|
|
||||||
|
|
||||||
/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0
|
|
||||||
* requests responses.
|
|
||||||
*
|
|
||||||
* Normally `llhttp` would error on (in strict mode) or discard (in loose mode)
|
|
||||||
* the HTTP request/response after the request/response with `Connection: close`
|
|
||||||
* and `Content-Length`. This is important to prevent cache poisoning attacks,
|
|
||||||
* but might interact badly with outdated and insecure clients. With this flag
|
|
||||||
* the extra request/response will be parsed normally.
|
|
||||||
*
|
|
||||||
* **(USE AT YOUR OWN RISK)**
|
|
||||||
*/
|
|
||||||
void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
#endif /* INCLUDE_LLHTTP_API_H_ */
|
|
||||||
|
|
||||||
#endif /* INCLUDE_LLHTTP_H_ */
|
|
||||||
Vendored
+1
-1
Submodule external/src/rapidjson updated: 3c8d1dc5a5...914b772dfd
@@ -23,7 +23,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
buildInputs = builtins.attrValues {
|
buildInputs = builtins.attrValues {
|
||||||
inherit (pkgs) libuv zeromq libsodium gss;
|
inherit (pkgs) libuv zeromq libsodium gss curl;
|
||||||
};
|
};
|
||||||
|
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
|
|||||||
+8
-5
@@ -165,8 +165,11 @@ BlockCache::~BlockCache()
|
|||||||
|
|
||||||
void BlockCache::store(const PoolBlock& block)
|
void BlockCache::store(const PoolBlock& block)
|
||||||
{
|
{
|
||||||
const size_t n1 = block.m_mainChainData.size();
|
const std::vector<uint8_t> mainchain_data = block.serialize_mainchain_data();
|
||||||
const size_t n2 = block.m_sideChainData.size();
|
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)) {
|
if (!m_impl->m_data || (sizeof(uint32_t) + n1 + n2 > BLOCK_SIZE)) {
|
||||||
return;
|
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);
|
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);
|
*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), mainchain_data.data(), n1);
|
||||||
memcpy(data + sizeof(uint32_t) + n1, block.m_sideChainData.data(), n2);
|
memcpy(data + sizeof(uint32_t) + n1, sidechain_data.data(), n2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
|
void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
|
||||||
@@ -198,7 +201,7 @@ void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
|
|||||||
continue;
|
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);
|
server.add_cached_block(block);
|
||||||
++blocks_loaded;
|
++blocks_loaded;
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-38
@@ -50,9 +50,15 @@ BlockTemplate::BlockTemplate(p2pool* pool)
|
|||||||
, m_difficulty{}
|
, m_difficulty{}
|
||||||
, m_seedHash{}
|
, m_seedHash{}
|
||||||
, m_timestamp(0)
|
, m_timestamp(0)
|
||||||
|
, m_txkeyPub{}
|
||||||
|
, m_txkeySec{}
|
||||||
, m_poolBlockTemplate(new PoolBlock())
|
, m_poolBlockTemplate(new PoolBlock())
|
||||||
, m_finalReward(0)
|
, 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);
|
uv_rwlock_init_checked(&m_lock);
|
||||||
|
|
||||||
m_blockHeader.reserve(64);
|
m_blockHeader.reserve(64);
|
||||||
@@ -73,8 +79,6 @@ BlockTemplate::BlockTemplate(p2pool* pool)
|
|||||||
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
||||||
m_knapsack.reserve(512 * 309375);
|
m_knapsack.reserve(512 * 309375);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
update_tx_keys();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockTemplate::~BlockTemplate()
|
BlockTemplate::~BlockTemplate()
|
||||||
@@ -115,7 +119,7 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
|
|||||||
m_extraNonceOffsetInTemplate = b.m_extraNonceOffsetInTemplate;
|
m_extraNonceOffsetInTemplate = b.m_extraNonceOffsetInTemplate;
|
||||||
m_numTransactionHashes = b.m_numTransactionHashes;
|
m_numTransactionHashes = b.m_numTransactionHashes;
|
||||||
m_prevId = b.m_prevId;
|
m_prevId = b.m_prevId;
|
||||||
m_height = b.m_height;
|
m_height = b.m_height.load();
|
||||||
m_difficulty = b.m_difficulty;
|
m_difficulty = b.m_difficulty;
|
||||||
m_seedHash = b.m_seedHash;
|
m_seedHash = b.m_seedHash;
|
||||||
m_timestamp = b.m_timestamp;
|
m_timestamp = b.m_timestamp;
|
||||||
@@ -133,6 +137,8 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
|
|||||||
m_mempoolTxsOrder.clear();
|
m_mempoolTxsOrder.clear();
|
||||||
m_shares.clear();
|
m_shares.clear();
|
||||||
|
|
||||||
|
m_rng = b.m_rng;
|
||||||
|
|
||||||
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
||||||
m_knapsack.clear();
|
m_knapsack.clear();
|
||||||
#endif
|
#endif
|
||||||
@@ -171,6 +177,14 @@ static FORCEINLINE uint64_t get_block_reward(uint64_t base_reward, uint64_t medi
|
|||||||
return reward + fees;
|
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)
|
void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet)
|
||||||
{
|
{
|
||||||
if (data.major_version > HARDFORK_SUPPORTED_VERSION) {
|
if (data.major_version > HARDFORK_SUPPORTED_VERSION) {
|
||||||
@@ -197,6 +211,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
|||||||
*this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)];
|
*this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
get_tx_keys(m_txkeyPub, m_txkeySec, miner_wallet->spend_public_key(), data.prev_id);
|
||||||
|
|
||||||
m_height = data.height;
|
m_height = data.height;
|
||||||
m_difficulty = data.difficulty;
|
m_difficulty = data.difficulty;
|
||||||
m_seedHash = data.seed_hash;
|
m_seedHash = data.seed_hash;
|
||||||
@@ -313,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 a block doesn't get into the penalty zone, just pick all transactions
|
||||||
if (total_tx_weight + miner_tx_weight <= data.median_weight) {
|
if (total_tx_weight + miner_tx_weight <= data.median_weight) {
|
||||||
m_numTransactionHashes = 0;
|
|
||||||
|
|
||||||
final_fees = 0;
|
final_fees = 0;
|
||||||
final_weight = miner_tx_weight;
|
final_weight = miner_tx_weight;
|
||||||
|
|
||||||
m_transactionHashes.assign(HASH_SIZE, 0);
|
shuffle_tx_order();
|
||||||
for (const TxMempoolData& tx : m_mempoolTxs) {
|
|
||||||
m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
|
|
||||||
++m_numTransactionHashes;
|
|
||||||
|
|
||||||
|
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_fees += tx.fee;
|
||||||
final_weight += tx.weight;
|
final_weight += tx.weight;
|
||||||
}
|
}
|
||||||
@@ -396,6 +412,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
|||||||
final_fees = 0;
|
final_fees = 0;
|
||||||
final_weight = miner_tx_weight;
|
final_weight = miner_tx_weight;
|
||||||
|
|
||||||
|
shuffle_tx_order();
|
||||||
|
|
||||||
m_numTransactionHashes = m_mempoolTxsOrder.size();
|
m_numTransactionHashes = m_mempoolTxsOrder.size();
|
||||||
m_transactionHashes.assign(HASH_SIZE, 0);
|
m_transactionHashes.assign(HASH_SIZE, 0);
|
||||||
for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
|
for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
|
||||||
@@ -526,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_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);
|
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 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, "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());
|
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) {
|
for (size_t i = 0, n = std::min(mainchain_data.size(), m_blockTemplateBlob.size()); i < n; ++i) {
|
||||||
if (m_poolBlockTemplate->m_mainChainData[i] != m_blockTemplateBlob[i]) {
|
if (mainchain_data[i] != m_blockTemplateBlob[i]) {
|
||||||
LOGERR(1, "m_poolBlockTemplate->m_mainChainData is different at offset " << i);
|
LOGERR(1, "m_poolBlockTemplate->m_mainChainData is different at offset " << i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -543,10 +562,10 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
|||||||
|
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> buf = m_blockTemplateBlob;
|
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;
|
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) {
|
if (result != 0) {
|
||||||
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
|
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
|
||||||
}
|
}
|
||||||
@@ -575,7 +594,6 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
|||||||
m_rewards.clear();
|
m_rewards.clear();
|
||||||
m_mempoolTxs.clear();
|
m_mempoolTxs.clear();
|
||||||
m_mempoolTxsOrder.clear();
|
m_mempoolTxsOrder.clear();
|
||||||
m_shares.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
||||||
@@ -711,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);
|
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_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) {
|
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||||
@@ -744,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;
|
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_SIZE) {
|
||||||
if (corrected_extra_nonce_size > EXTRA_NONCE_MAX_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;
|
return -3;
|
||||||
}
|
}
|
||||||
LOGINFO(4, "increased EXTRA_NONCE from " << EXTRA_NONCE_SIZE << " to " << corrected_extra_nonce_size << " bytes to maintain miner tx weight");
|
LOGINFO(4, "increased EXTRA_NONCE from " << EXTRA_NONCE_SIZE << " to " << corrected_extra_nonce_size << " bytes to maintain miner tx weight");
|
||||||
@@ -783,8 +801,9 @@ hash BlockTemplate::calc_sidechain_hash() const
|
|||||||
const int blob_size = static_cast<int>(m_blockTemplateBlob.size());
|
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>& 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));
|
uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(m_nonceOffset));
|
||||||
if (k < NONCE_SIZE) {
|
if (k < NONCE_SIZE) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -805,15 +824,15 @@ hash BlockTemplate::calc_sidechain_hash() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
const int side_chain_data_offsset = offset - blob_size;
|
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) {
|
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;
|
const int consensus_id_offset = side_chain_data_offsset - side_chain_data_size;
|
||||||
return consensus_id[consensus_id_offset];
|
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;
|
return sidechain_hash;
|
||||||
}
|
}
|
||||||
@@ -909,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);
|
ReadLock lock(m_lock);
|
||||||
|
|
||||||
if (template_id == m_templateId) {
|
if (template_id == m_templateId) {
|
||||||
|
height = m_height;
|
||||||
mainchain_difficulty = m_difficulty;
|
mainchain_difficulty = m_difficulty;
|
||||||
sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
|
sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
|
||||||
return true;
|
return true;
|
||||||
@@ -922,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)];
|
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
|
||||||
|
|
||||||
if (old && (template_id == old->m_templateId)) {
|
if (old && (template_id == old->m_templateId)) {
|
||||||
return old->get_difficulties(template_id, mainchain_difficulty, sidechain_difficulty);
|
return old->get_difficulties(template_id, height, mainchain_difficulty, sidechain_difficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -1058,13 +1078,6 @@ std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id
|
|||||||
return m_blockTemplateBlob;
|
return m_blockTemplateBlob;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockTemplate::update_tx_keys()
|
|
||||||
{
|
|
||||||
WriteLock lock(m_lock);
|
|
||||||
|
|
||||||
generate_keys(m_txkeyPub, m_txkeySec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
|
void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
|
||||||
{
|
{
|
||||||
WriteLock lock(m_lock);
|
WriteLock lock(m_lock);
|
||||||
@@ -1072,18 +1085,21 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
|
|||||||
if (template_id == m_templateId) {
|
if (template_id == m_templateId) {
|
||||||
m_poolBlockTemplate->m_nonce = nonce;
|
m_poolBlockTemplate->m_nonce = nonce;
|
||||||
m_poolBlockTemplate->m_extraNonce = extra_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();
|
SideChain& side_chain = m_pool->side_chain();
|
||||||
|
|
||||||
#if POOL_BLOCK_DEBUG
|
#if POOL_BLOCK_DEBUG
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> buf = m_poolBlockTemplate->m_mainChainData;
|
std::vector<uint8_t> buf = m_poolBlockTemplate->serialize_mainchain_data();
|
||||||
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end());
|
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;
|
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) {
|
if (result != 0) {
|
||||||
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
|
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,22 +40,20 @@ public:
|
|||||||
|
|
||||||
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet);
|
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet);
|
||||||
|
|
||||||
bool get_difficulties(const uint32_t template_id, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
|
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(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const;
|
||||||
|
|
||||||
uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
|
uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
|
||||||
uint32_t get_hashing_blobs(uint32_t extra_nonce_start, uint32_t count, std::vector<uint8_t>& blobs, uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
|
uint32_t get_hashing_blobs(uint32_t extra_nonce_start, uint32_t count, std::vector<uint8_t>& blobs, uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
|
||||||
|
|
||||||
std::vector<uint8_t> get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const;
|
std::vector<uint8_t> get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const;
|
||||||
void update_tx_keys();
|
|
||||||
|
|
||||||
FORCEINLINE uint64_t height() const { return m_height; }
|
FORCEINLINE uint64_t height() const { return m_height; }
|
||||||
FORCEINLINE uint64_t timestamp() const { return m_timestamp; }
|
|
||||||
FORCEINLINE difficulty_type difficulty() const { return m_difficulty; }
|
FORCEINLINE difficulty_type difficulty() const { return m_difficulty; }
|
||||||
|
|
||||||
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
|
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
|
||||||
|
|
||||||
FORCEINLINE uint64_t final_reward() const { return m_finalReward; }
|
FORCEINLINE const std::vector<MinerShare>& shares() const { return m_shares; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
p2pool* m_pool;
|
p2pool* m_pool;
|
||||||
@@ -83,7 +81,7 @@ private:
|
|||||||
|
|
||||||
size_t m_numTransactionHashes;
|
size_t m_numTransactionHashes;
|
||||||
hash m_prevId;
|
hash m_prevId;
|
||||||
uint64_t m_height;
|
std::atomic<uint64_t> m_height;
|
||||||
difficulty_type m_difficulty;
|
difficulty_type m_difficulty;
|
||||||
hash m_seedHash;
|
hash m_seedHash;
|
||||||
|
|
||||||
@@ -108,6 +106,10 @@ private:
|
|||||||
std::vector<int> m_mempoolTxsOrder;
|
std::vector<int> m_mempoolTxsOrder;
|
||||||
std::vector<MinerShare> m_shares;
|
std::vector<MinerShare> m_shares;
|
||||||
|
|
||||||
|
std::mt19937_64 m_rng;
|
||||||
|
|
||||||
|
void shuffle_tx_order();
|
||||||
|
|
||||||
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
#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);
|
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);
|
||||||
|
|
||||||
|
|||||||
+28
-1
@@ -19,22 +19,25 @@
|
|||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
#pragma warning(disable : 4005 4061 4365 4464 4625 4626 4668 4710 4711 4804 4820 5039 5045 5220)
|
#pragma warning(disable : 4005 4061 4365 4464 4625 4626 4668 4710 4711 4804 4820 5039 5045 5220 5246)
|
||||||
#define FORCEINLINE __forceinline
|
#define FORCEINLINE __forceinline
|
||||||
#define NOINLINE __declspec(noinline)
|
#define NOINLINE __declspec(noinline)
|
||||||
#define LIKELY(expression) expression
|
#define LIKELY(expression) expression
|
||||||
|
#define MSVC_PRAGMA(...) __pragma(__VA_ARGS__)
|
||||||
|
|
||||||
#elif __GNUC__
|
#elif __GNUC__
|
||||||
|
|
||||||
#define FORCEINLINE __attribute__((always_inline)) inline
|
#define FORCEINLINE __attribute__((always_inline)) inline
|
||||||
#define NOINLINE __attribute__((noinline))
|
#define NOINLINE __attribute__((noinline))
|
||||||
#define LIKELY(expression) __builtin_expect(expression, 1)
|
#define LIKELY(expression) __builtin_expect(expression, 1)
|
||||||
|
#define MSVC_PRAGMA(...)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define FORCEINLINE inline
|
#define FORCEINLINE inline
|
||||||
#define NOINLINE
|
#define NOINLINE
|
||||||
#define LIKELY(expression) expression
|
#define LIKELY(expression) expression
|
||||||
|
#define MSVC_PRAGMA(...)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -78,6 +81,14 @@
|
|||||||
|
|
||||||
#endif
|
#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 {
|
namespace p2pool {
|
||||||
|
|
||||||
constexpr size_t HASH_SIZE = 32;
|
constexpr size_t HASH_SIZE = 32;
|
||||||
@@ -198,11 +209,16 @@ struct difficulty_type
|
|||||||
FORCEINLINE bool operator==(const difficulty_type& other) const { return (lo == other.lo) && (hi == other.hi); }
|
FORCEINLINE bool operator==(const difficulty_type& other) const { return (lo == other.lo) && (hi == other.hi); }
|
||||||
FORCEINLINE bool operator!=(const difficulty_type& other) const { return (lo != other.lo) || (hi != other.hi); }
|
FORCEINLINE bool operator!=(const difficulty_type& other) const { return (lo != other.lo) || (hi != other.hi); }
|
||||||
|
|
||||||
|
FORCEINLINE bool operator==(uint64_t other) const { return (lo == other) && (hi == 0); }
|
||||||
|
FORCEINLINE bool operator!=(uint64_t other) const { return (lo != other) || (hi != 0); }
|
||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream& s, const difficulty_type& d);
|
friend std::ostream& operator<<(std::ostream& s, const difficulty_type& d);
|
||||||
friend std::istream& operator>>(std::istream& s, difficulty_type& d);
|
friend std::istream& operator>>(std::istream& s, difficulty_type& d);
|
||||||
|
|
||||||
FORCEINLINE double to_double() const { return static_cast<double>(hi) * 18446744073709551616.0 + static_cast<double>(lo); }
|
FORCEINLINE double to_double() const { return static_cast<double>(hi) * 18446744073709551616.0 + static_cast<double>(lo); }
|
||||||
|
|
||||||
|
FORCEINLINE bool empty() const { return (lo == 0) && (hi == 0); }
|
||||||
|
|
||||||
// Finds a 64-bit target for mining (target = 2^64 / difficulty) and rounds up the result of division
|
// Finds a 64-bit target for mining (target = 2^64 / difficulty) and rounds up the result of division
|
||||||
// Because of that, there's a very small chance that miners will find a hash that meets the target but is still wrong (hash * difficulty >= 2^256)
|
// Because of that, there's a very small chance that miners will find a hash that meets the target but is still wrong (hash * difficulty >= 2^256)
|
||||||
// A proper difficulty check is in check_pow()
|
// A proper difficulty check is in check_pow()
|
||||||
@@ -309,6 +325,14 @@ struct raw_ip
|
|||||||
}
|
}
|
||||||
|
|
||||||
FORCEINLINE bool operator!=(const raw_ip& other) const { return !operator==(other); }
|
FORCEINLINE bool operator!=(const raw_ip& other) const { return !operator==(other); }
|
||||||
|
|
||||||
|
FORCEINLINE bool is_localhost() const
|
||||||
|
{
|
||||||
|
static constexpr raw_ip localhost_ipv4 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01 };
|
||||||
|
static constexpr raw_ip localhost_ipv6 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
|
||||||
|
|
||||||
|
return (*this == localhost_ipv4) || (*this == localhost_ipv6);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(raw_ip) == 16, "struct raw_ip has invalid size");
|
static_assert(sizeof(raw_ip) == 16, "struct raw_ip has invalid size");
|
||||||
@@ -317,6 +341,9 @@ void* malloc_hook(size_t n) noexcept;
|
|||||||
void* realloc_hook(void* ptr, size_t size) noexcept;
|
void* realloc_hook(void* ptr, size_t size) noexcept;
|
||||||
void* calloc_hook(size_t count, size_t size) noexcept;
|
void* calloc_hook(size_t count, size_t size) noexcept;
|
||||||
void free_hook(void* p) noexcept;
|
void free_hook(void* p) noexcept;
|
||||||
|
char* strdup_hook(const char* s) noexcept;
|
||||||
|
|
||||||
|
extern const char* BLOCK_FOUND;
|
||||||
|
|
||||||
} // namespace p2pool
|
} // namespace p2pool
|
||||||
|
|
||||||
|
|||||||
+131
-71
@@ -31,27 +31,57 @@ static constexpr char log_category_prefix[] = "ConsoleCommands ";
|
|||||||
|
|
||||||
namespace p2pool {
|
namespace p2pool {
|
||||||
|
|
||||||
bool ConsoleCommands::stopped = false;
|
|
||||||
|
|
||||||
ConsoleCommands::ConsoleCommands(p2pool* pool)
|
ConsoleCommands::ConsoleCommands(p2pool* pool)
|
||||||
: m_pool(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()
|
ConsoleCommands::~ConsoleCommands()
|
||||||
{
|
{
|
||||||
stopped = true;
|
uv_async_send(&m_shutdownAsync);
|
||||||
|
uv_thread_join(&m_loopThread);
|
||||||
#ifdef _WIN32
|
|
||||||
TerminateThread(reinterpret_cast<HANDLE>(m_worker->native_handle()), 0);
|
|
||||||
#else
|
|
||||||
pthread_cancel(m_worker->native_handle());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_worker->join();
|
|
||||||
delete m_worker;
|
|
||||||
|
|
||||||
LOGINFO(1, "stopped");
|
LOGINFO(1, "stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +93,7 @@ typedef struct strconst {
|
|||||||
#define STRCONST(x) {x, sizeof(x)-1}
|
#define STRCONST(x) {x, sizeof(x)-1}
|
||||||
#define STRCNULL {NULL, 0}
|
#define STRCNULL {NULL, 0}
|
||||||
|
|
||||||
typedef int (cmdfunc)(p2pool *pool, const char *args);
|
typedef void (cmdfunc)(p2pool *pool, const char *args);
|
||||||
|
|
||||||
typedef struct cmd {
|
typedef struct cmd {
|
||||||
strconst name;
|
strconst name;
|
||||||
@@ -72,7 +102,7 @@ typedef struct cmd {
|
|||||||
cmdfunc *func;
|
cmdfunc *func;
|
||||||
} cmd;
|
} cmd;
|
||||||
|
|
||||||
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_showpeers, do_showbans, do_outpeers, do_inpeers, do_exit;
|
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_showpeers, do_showworkers, do_showbans, do_outpeers, do_inpeers, do_exit;
|
||||||
|
|
||||||
#ifdef WITH_RANDOMX
|
#ifdef WITH_RANDOMX
|
||||||
static cmdfunc do_start_mining, do_stop_mining;
|
static cmdfunc do_start_mining, do_stop_mining;
|
||||||
@@ -85,6 +115,7 @@ static cmd cmds[] = {
|
|||||||
{ STRCONST("addpeers"), "<peeraddr>", "add peer", do_addpeers },
|
{ STRCONST("addpeers"), "<peeraddr>", "add peer", do_addpeers },
|
||||||
{ STRCONST("droppeers"), "", "disconnect all peers", do_droppeers },
|
{ STRCONST("droppeers"), "", "disconnect all peers", do_droppeers },
|
||||||
{ STRCONST("peers"), "", "show all peers", do_showpeers },
|
{ STRCONST("peers"), "", "show all peers", do_showpeers },
|
||||||
|
{ STRCONST("workers"), "", "show all connected workers", do_showworkers },
|
||||||
{ STRCONST("bans"), "", "show all banned IPs", do_showbans },
|
{ STRCONST("bans"), "", "show all banned IPs", do_showbans },
|
||||||
{ STRCONST("outpeers"), "", "set maximum number of outgoing connections", do_outpeers },
|
{ STRCONST("outpeers"), "", "set maximum number of outgoing connections", do_outpeers },
|
||||||
{ STRCONST("inpeers"), "", "set maximum number of incoming connections", do_inpeers },
|
{ STRCONST("inpeers"), "", "set maximum number of incoming connections", do_inpeers },
|
||||||
@@ -96,16 +127,15 @@ static cmd cmds[] = {
|
|||||||
{ STRCNULL, NULL, NULL, NULL }
|
{ 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");
|
LOGINFO(0, "List of commands");
|
||||||
for (int i = 0; cmds[i].name.len; ++i) {
|
for (int i = 0; cmds[i].name.len; ++i) {
|
||||||
LOGINFO(0, cmds[i].name.str << " " << cmds[i].arg << "\t" << cmds[i].descr);
|
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();
|
m_pool->side_chain().print_status();
|
||||||
if (m_pool->stratum_server()) {
|
if (m_pool->stratum_server()) {
|
||||||
@@ -118,43 +148,45 @@ static int do_status(p2pool *m_pool, const char * /* args */)
|
|||||||
m_pool->print_miner_status();
|
m_pool->print_miner_status();
|
||||||
#endif
|
#endif
|
||||||
bkg_jobs_tracker.print_status();
|
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);
|
int level = strtol(args, nullptr, 10);
|
||||||
level = std::min(std::max(level, 0), log::MAX_GLOBAL_LOG_LEVEL);
|
level = std::min(std::max(level, 0), log::MAX_GLOBAL_LOG_LEVEL);
|
||||||
log::GLOBAL_LOG_LEVEL = level;
|
log::GLOBAL_LOG_LEVEL = level;
|
||||||
LOGINFO(0, "log level set to " << 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()) {
|
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()) {
|
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()) {
|
if (m_pool->p2p_server()) {
|
||||||
m_pool->p2p_server()->show_peers();
|
m_pool->p2p_server()->show_peers_async();
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_showbans(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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_showbans(p2pool* m_pool, const char* /* args */)
|
||||||
{
|
{
|
||||||
if (m_pool->stratum_server()) {
|
if (m_pool->stratum_server()) {
|
||||||
m_pool->stratum_server()->print_bans();
|
m_pool->stratum_server()->print_bans();
|
||||||
@@ -162,87 +194,115 @@ static int do_showbans(p2pool* m_pool, const char* /* args */)
|
|||||||
if (m_pool->p2p_server()) {
|
if (m_pool->p2p_server()) {
|
||||||
m_pool->p2p_server()->print_bans();
|
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()) {
|
if (m_pool->p2p_server()) {
|
||||||
m_pool->p2p_server()->set_max_outgoing_peers(strtoul(args, nullptr, 10));
|
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());
|
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()) {
|
if (m_pool->p2p_server()) {
|
||||||
m_pool->p2p_server()->set_max_incoming_peers(strtoul(args, nullptr, 10));
|
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());
|
LOGINFO(0, "max incoming peers set to " << m_pool->p2p_server()->max_incoming_peers());
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_RANDOMX
|
#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);
|
uint32_t threads = strtoul(args, nullptr, 10);
|
||||||
threads = std::min(std::max(threads, 1u), 64u);
|
threads = std::min(std::max(threads, 1u), 64u);
|
||||||
m_pool->start_mining(threads);
|
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();
|
m_pool->stop_mining();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
#endif
|
#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();
|
bkg_jobs_tracker.wait();
|
||||||
m_pool->stop();
|
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;
|
if (pThis->m_readBufInUse) {
|
||||||
command.reserve(1024);
|
buf->len = 0;
|
||||||
|
buf->base = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
buf->len = sizeof(pThis->m_readBuf);
|
||||||
std::getline(std::cin, command);
|
buf->base = pThis->m_readBuf;
|
||||||
|
pThis->m_readBufInUse = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (std::cin.eof()) {
|
void ConsoleCommands::stdinReadCallback(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
|
||||||
LOGINFO(1, "EOF, stopping");
|
{
|
||||||
return;
|
ConsoleCommands* pThis = static_cast<ConsoleCommands*>(stream->data);
|
||||||
}
|
|
||||||
|
|
||||||
if (stopped) {
|
if (nread > 0) {
|
||||||
LOGINFO(1, "stopping");
|
std::string& command = pThis->m_command;
|
||||||
return;
|
command.append(buf->base, nread);
|
||||||
}
|
|
||||||
|
|
||||||
cmd* c = cmds;
|
do {
|
||||||
for (; c->name.len; ++c) {
|
size_t k = command.find_first_of("\r\n");
|
||||||
if (!strncmp(command.c_str(), c->name.str, c->name.len)) {
|
if (k == std::string::npos) {
|
||||||
const char *args = command.c_str() + c->name.len + 1;
|
|
||||||
if (c->func(m_pool, args)) {
|
|
||||||
LOGINFO(1, "exit requested, stopping");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
command[k] = '\0';
|
||||||
|
|
||||||
if (!c->name.len) {
|
cmd* c = cmds;
|
||||||
LOGWARN(0, "Unknown command " << command);
|
for (; c->name.len; ++c) {
|
||||||
}
|
if (!strncmp(command.c_str(), c->name.str, c->name.len)) {
|
||||||
} while (true);
|
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
|
} // namespace p2pool
|
||||||
|
|||||||
+22
-4
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <thread>
|
#include "uv_util.h"
|
||||||
|
|
||||||
namespace p2pool {
|
namespace p2pool {
|
||||||
|
|
||||||
@@ -31,10 +31,28 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
p2pool* m_pool;
|
p2pool* m_pool;
|
||||||
std::thread* m_worker;
|
|
||||||
|
|
||||||
static bool stopped;
|
uv_loop_t m_loop;
|
||||||
void run();
|
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
|
} // namespace p2pool
|
||||||
|
|||||||
+155
-42
@@ -24,6 +24,10 @@ extern "C" {
|
|||||||
#include "crypto-ops.h"
|
#include "crypto-ops.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// l = 2^252 + 27742317777372353535851937790883648493.
|
||||||
|
// l fits 15 times in 32 bytes (iow, 15 l is the highest multiple of l that fits in 32 bytes)
|
||||||
|
static constexpr uint8_t limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 };
|
||||||
|
|
||||||
namespace p2pool {
|
namespace p2pool {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -79,10 +83,6 @@ static FORCEINLINE bool less32(const uint8_t* k0, const uint8_t* k1)
|
|||||||
// cppcheck-suppress constParameter
|
// cppcheck-suppress constParameter
|
||||||
void generate_keys(hash& pub, hash& sec)
|
void generate_keys(hash& pub, hash& sec)
|
||||||
{
|
{
|
||||||
// l = 2^252 + 27742317777372353535851937790883648493.
|
|
||||||
// l fits 15 times in 32 bytes (iow, 15 l is the highest multiple of l that fits in 32 bytes)
|
|
||||||
static constexpr uint8_t limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 };
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
do { randomBytes(sec.h); } while (!less32(sec.h, limit));
|
do { randomBytes(sec.h); } while (!less32(sec.h, limit));
|
||||||
sc_reduce32(sec.h);
|
sc_reduce32(sec.h);
|
||||||
@@ -93,6 +93,29 @@ void generate_keys(hash& pub, hash& sec)
|
|||||||
ge_p3_tobytes(pub.h, &point);
|
ge_p3_tobytes(pub.h, &point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len)
|
||||||
|
{
|
||||||
|
uint32_t counter = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
++counter;
|
||||||
|
keccak_custom([entropy, len, counter](int offset)
|
||||||
|
{
|
||||||
|
if (offset < static_cast<int>(len)) {
|
||||||
|
return entropy[offset];
|
||||||
|
}
|
||||||
|
return static_cast<uint8_t>(counter >> ((offset - len) * 8));
|
||||||
|
}, static_cast<int>(len + sizeof(counter)), sec.h, HASH_SIZE);
|
||||||
|
} while (!less32(sec.h, limit));
|
||||||
|
sc_reduce32(sec.h);
|
||||||
|
} while (!sc_isnonzero(sec.h));
|
||||||
|
|
||||||
|
ge_p3 point;
|
||||||
|
ge_scalarmult_base(&point, sec.h);
|
||||||
|
ge_p3_tobytes(pub.h, &point);
|
||||||
|
}
|
||||||
|
|
||||||
bool check_keys(const hash& pub, const hash& sec)
|
bool check_keys(const hash& pub, const hash& sec)
|
||||||
{
|
{
|
||||||
// From ge_scalarmult_base's comment: "preconditions a[31] <= 127"
|
// From ge_scalarmult_base's comment: "preconditions a[31] <= 127"
|
||||||
@@ -131,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);
|
hash_to_scalar(data, static_cast<int>(p - data), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Cache
|
class Cache : public nocopy_nomove
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Cache()
|
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()
|
~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)
|
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(), key1.h, HASH_SIZE);
|
||||||
memcpy(index.data() + HASH_SIZE, key2.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);
|
ReadLock lock(derivations_lock);
|
||||||
auto it = derivations.find(index);
|
auto it = derivations->find(index);
|
||||||
if (it != derivations.end()) {
|
if (it != derivations->end()) {
|
||||||
derivation = it->second.derivation;
|
const DerivationEntry& entry = it->second;
|
||||||
view_tag = it->second.view_tag;
|
derivation = entry.m_derivation;
|
||||||
return true;
|
if (entry.find_view_tag(output_index, view_tag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ge_p3 point;
|
if (derivation.empty()) {
|
||||||
ge_p2 point2;
|
ge_p3 point;
|
||||||
ge_p1p1 point3;
|
ge_p2 point2;
|
||||||
|
ge_p1p1 point3;
|
||||||
|
|
||||||
if (ge_frombytes_vartime(&point, key1.h) != 0) {
|
if (ge_frombytes_vartime(&point, key1.h) != 0) {
|
||||||
return false;
|
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);
|
derive_view_tag(derivation, output_index, view_tag);
|
||||||
|
|
||||||
{
|
{
|
||||||
MutexLock lock(m);
|
WriteLock lock(derivations_lock);
|
||||||
derivations.emplace(index, DerivationEntry{ derivation, view_tag } );
|
|
||||||
|
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;
|
return true;
|
||||||
@@ -192,9 +236,9 @@ public:
|
|||||||
memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t));
|
memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t));
|
||||||
|
|
||||||
{
|
{
|
||||||
MutexLock lock(m);
|
ReadLock lock(public_keys_lock);
|
||||||
auto it = public_keys.find(index);
|
auto it = public_keys->find(index);
|
||||||
if (it != public_keys.end()) {
|
if (it != public_keys->end()) {
|
||||||
derived_key = it->second;
|
derived_key = it->second;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -219,31 +263,93 @@ public:
|
|||||||
ge_tobytes(derived_key.h, &point5);
|
ge_tobytes(derived_key.h, &point5);
|
||||||
|
|
||||||
{
|
{
|
||||||
MutexLock lock(m);
|
WriteLock lock(public_keys_lock);
|
||||||
public_keys.emplace(index, derived_key);
|
public_keys->emplace(index, derived_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id)
|
||||||
|
{
|
||||||
|
std::array<uint8_t, HASH_SIZE * 2> index;
|
||||||
|
memcpy(index.data(), wallet_spend_key.h, HASH_SIZE);
|
||||||
|
memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE);
|
||||||
|
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr char domain[] = "tx_secret_key";
|
||||||
|
static constexpr size_t N = sizeof(domain) - 1;
|
||||||
|
uint8_t entropy[N + HASH_SIZE * 2];
|
||||||
|
|
||||||
|
memcpy(entropy, domain, N);
|
||||||
|
memcpy(entropy + N, wallet_spend_key.h, HASH_SIZE);
|
||||||
|
memcpy(entropy + N + HASH_SIZE, monero_block_id.h, HASH_SIZE);
|
||||||
|
|
||||||
|
generate_keys_deterministic(pub, sec, entropy, sizeof(entropy));
|
||||||
|
|
||||||
|
{
|
||||||
|
WriteLock lock(tx_keys_lock);
|
||||||
|
tx_keys->emplace(index, std::pair<hash, hash>(pub, sec));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
MutexLock lock(m);
|
{
|
||||||
|
WriteLock lock(derivations_lock);
|
||||||
derivations.clear();
|
delete derivations;
|
||||||
public_keys.clear();
|
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:
|
private:
|
||||||
struct DerivationEntry
|
struct DerivationEntry
|
||||||
{
|
{
|
||||||
hash derivation;
|
hash m_derivation;
|
||||||
uint8_t view_tag;
|
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;
|
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, DerivationEntry> DerivationsMap;
|
||||||
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, DerivationEntry> derivations;
|
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> PublicKeysMap;
|
||||||
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
|
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;
|
static Cache* cache = nullptr;
|
||||||
@@ -258,6 +364,11 @@ bool derive_public_key(const hash& derivation, size_t output_index, const hash&
|
|||||||
return cache->get_public_key(derivation, output_index, base, derived_key);
|
return cache->get_public_key(derivation, output_index, base, derived_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id)
|
||||||
|
{
|
||||||
|
cache->get_tx_keys(pub, sec, wallet_spend_key, monero_block_id);
|
||||||
|
}
|
||||||
|
|
||||||
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag)
|
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag)
|
||||||
{
|
{
|
||||||
constexpr uint8_t salt[] = "view_tag";
|
constexpr uint8_t salt[] = "view_tag";
|
||||||
@@ -291,7 +402,9 @@ void destroy_crypto_cache()
|
|||||||
|
|
||||||
void clear_crypto_cache()
|
void clear_crypto_cache()
|
||||||
{
|
{
|
||||||
cache->clear();
|
if (cache) {
|
||||||
|
cache->clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace p2pool
|
} // namespace p2pool
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
namespace p2pool {
|
namespace p2pool {
|
||||||
|
|
||||||
void generate_keys(hash& pub, hash& sec);
|
void generate_keys(hash& pub, hash& sec);
|
||||||
|
void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len);
|
||||||
|
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id);
|
||||||
bool check_keys(const hash& pub, const hash& sec);
|
bool check_keys(const hash& pub, const hash& sec);
|
||||||
bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag);
|
bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag);
|
||||||
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key);
|
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key);
|
||||||
|
|||||||
+1
-1
@@ -104,7 +104,7 @@ struct parse_wrapper<T, difficulty_type>
|
|||||||
if (!from_hex(s[i], d)) {
|
if (!from_hex(s[i], d)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_value.hi = (out_value.hi << 4) || (out_value.lo >> 60);
|
out_value.hi = (out_value.hi << 4) | (out_value.lo >> 60);
|
||||||
out_value.lo = (out_value.lo << 4) | d;
|
out_value.lo = (out_value.lo << 4) | d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+429
-201
@@ -18,235 +18,463 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "uv_util.h"
|
#include "uv_util.h"
|
||||||
#include "json_rpc_request.h"
|
#include "json_rpc_request.h"
|
||||||
#include "llhttp.h"
|
#include <curl/curl.h>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
static constexpr char log_category_prefix[] = "JSONRPCRequest ";
|
static constexpr char log_category_prefix[] = "JSONRPCRequest ";
|
||||||
|
|
||||||
namespace p2pool {
|
namespace p2pool {
|
||||||
|
namespace JSONRPCRequest {
|
||||||
|
|
||||||
JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
|
struct CurlContext
|
||||||
: m_socket{}
|
|
||||||
, m_connect{}
|
|
||||||
, m_write{}
|
|
||||||
, m_callback(cb)
|
|
||||||
, m_closeCallback(close_cb)
|
|
||||||
, m_contentLength(0)
|
|
||||||
, m_contentLengthHeader(false)
|
|
||||||
, m_readBufInUse(false)
|
|
||||||
, m_valid(true)
|
|
||||||
{
|
{
|
||||||
m_readBuf[0] = '\0';
|
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();
|
||||||
|
|
||||||
uv_tcp_init(loop ? loop : uv_default_loop_checked(), &m_socket);
|
static int socket_func(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp)
|
||||||
uv_tcp_nodelay(&m_socket, 1);
|
{
|
||||||
|
CurlContext* ctx = reinterpret_cast<CurlContext*>(socketp ? socketp : userp);
|
||||||
|
return ctx->on_socket(easy, s, action);
|
||||||
|
}
|
||||||
|
|
||||||
sockaddr_storage addr;
|
static int timer_func(CURLM* multi, long timeout_ms, void* ctx)
|
||||||
if (uv_ip4_addr(address, port, reinterpret_cast<sockaddr_in*>(&addr)) != 0) {
|
{
|
||||||
const int err = uv_ip6_addr(address, port, reinterpret_cast<sockaddr_in6*>(&addr));
|
return reinterpret_cast<CurlContext*>(ctx)->on_timer(multi, timeout_ms);
|
||||||
if (err) {
|
}
|
||||||
LOGERR(1, "invalid IP address " << address << " or port " << port);
|
|
||||||
m_valid = false;
|
static size_t write_func(const void* buffer, size_t size, size_t count, void* ctx)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<CurlContext*>(ctx)->on_write(buffer, size, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
int on_socket(CURL* easy, curl_socket_t s, int action);
|
||||||
|
int on_timer(CURLM* multi, long timeout_ms);
|
||||||
|
|
||||||
|
static void on_timeout(uv_handle_t* req);
|
||||||
|
|
||||||
|
size_t on_write(const void* buffer, size_t size, size_t count);
|
||||||
|
|
||||||
|
static void curl_perform(uv_poll_t* req, int status, int events);
|
||||||
|
void check_multi_info();
|
||||||
|
|
||||||
|
static void on_close(uv_handle_t* h);
|
||||||
|
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
std::vector<std::pair<curl_socket_t, uv_poll_t*>> m_pollHandles;
|
||||||
|
|
||||||
|
CallbackBase* m_callback;
|
||||||
|
CallbackBase* m_closeCallback;
|
||||||
|
|
||||||
|
uv_loop_t* m_loop;
|
||||||
|
uv_timer_t m_timer;
|
||||||
|
uv_async_t m_async;
|
||||||
|
CURLM* m_multiHandle;
|
||||||
|
CURL* m_handle;
|
||||||
|
|
||||||
|
std::string m_url;
|
||||||
|
std::string m_req;
|
||||||
|
|
||||||
|
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, 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{}
|
||||||
|
, m_async{}
|
||||||
|
, m_multiHandle(nullptr)
|
||||||
|
, m_handle(nullptr)
|
||||||
|
, m_req(req)
|
||||||
|
, m_headers(nullptr)
|
||||||
|
{
|
||||||
|
m_pollHandles.reserve(2);
|
||||||
|
|
||||||
|
{
|
||||||
|
char buf[log::Stream::BUF_SIZE + 1];
|
||||||
|
buf[0] = '\0';
|
||||||
|
|
||||||
|
log::Stream s(buf);
|
||||||
|
s << "http://" << address << ':' << port;
|
||||||
|
|
||||||
|
if (!m_req.empty() && (m_req.front() == '/')) {
|
||||||
|
s << m_req.c_str() << '\0';
|
||||||
|
m_req.clear();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
s << "/json_rpc\0";
|
||||||
|
}
|
||||||
|
|
||||||
|
m_url = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = uv_timer_init(m_loop, &m_timer);
|
||||||
|
if (err) {
|
||||||
|
LOGERR(1, "uv_timer_init failed, error " << uv_err_name(err));
|
||||||
|
throw std::runtime_error("uv_timer_init failed");
|
||||||
|
}
|
||||||
|
m_timer.data = this;
|
||||||
|
|
||||||
|
err = uv_async_init(m_loop, &m_async, reinterpret_cast<uv_async_cb>(on_timeout));
|
||||||
|
if (err) {
|
||||||
|
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
||||||
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
|
||||||
|
throw std::runtime_error("uv_async_init failed");
|
||||||
|
}
|
||||||
|
m_async.data = this;
|
||||||
|
|
||||||
|
m_multiHandle = curl_multi_init();
|
||||||
|
if (!m_multiHandle) {
|
||||||
|
static constexpr char msg[] = "curl_multi_init() failed";
|
||||||
|
LOGERR(1, msg);
|
||||||
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
|
||||||
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define curl_multi_setopt_checked(...) \
|
||||||
|
do { \
|
||||||
|
const CURLMcode r = curl_multi_setopt(__VA_ARGS__); \
|
||||||
|
if (r != CURLM_OK) { \
|
||||||
|
static constexpr char msg[] = "curl_multi_setopt(" #__VA_ARGS__ ") failed"; \
|
||||||
|
LOGERR(1, msg << ": " << curl_multi_strerror(r)); \
|
||||||
|
throw std::runtime_error(msg); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_SOCKETFUNCTION, socket_func);
|
||||||
|
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_SOCKETDATA, this);
|
||||||
|
|
||||||
|
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_TIMERFUNCTION, timer_func);
|
||||||
|
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_TIMERDATA, this);
|
||||||
|
|
||||||
|
m_handle = curl_easy_init();
|
||||||
|
if (!m_handle) {
|
||||||
|
static constexpr char msg[] = "curl_easy_init() failed";
|
||||||
|
LOGERR(1, msg);
|
||||||
|
curl_multi_cleanup(m_multiHandle);
|
||||||
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
|
||||||
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define curl_easy_setopt_checked(...) \
|
||||||
|
do { \
|
||||||
|
const CURLcode r = curl_easy_setopt(__VA_ARGS__); \
|
||||||
|
if (r != CURLE_OK) { \
|
||||||
|
static constexpr char msg[] = "curl_easy_setopt(" #__VA_ARGS__ ") failed"; \
|
||||||
|
LOGERR(1, msg << ": " << curl_easy_strerror(r)); \
|
||||||
|
throw std::runtime_error(msg); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
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, timeout);
|
||||||
|
curl_easy_setopt_checked(m_handle, CURLOPT_TIMEOUT, timeout * 10);
|
||||||
|
|
||||||
|
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, 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);
|
||||||
|
if (curl_err != CURLM_OK) {
|
||||||
|
LOGERR(1, "curl_multi_add_handle failed, error " << curl_multi_strerror(curl_err));
|
||||||
|
curl_easy_cleanup(m_handle);
|
||||||
|
curl_multi_cleanup(m_multiHandle);
|
||||||
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
|
||||||
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
|
||||||
|
throw std::runtime_error("curl_multi_add_handle failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CurlContext::~CurlContext()
|
||||||
|
{
|
||||||
|
if (m_error.empty() && !m_response.empty()) {
|
||||||
|
(*m_callback)(m_response.data(), m_response.size());
|
||||||
|
}
|
||||||
|
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:
|
||||||
|
{
|
||||||
|
uv_poll_t* h = nullptr;
|
||||||
|
|
||||||
|
if (it != m_pollHandles.end()) {
|
||||||
|
h = it->second;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CURL_POLL_REMOVE:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CurlContext::on_timer(CURLM* /*multi*/, long timeout_ms)
|
||||||
|
{
|
||||||
|
if (timeout_ms < 0) {
|
||||||
|
uv_timer_stop(&m_timer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout_ms == 0) {
|
||||||
|
// 0 ms timeout, but we can't just call on_timeout() here - we have to kick the UV loop
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurlContext::on_timeout(uv_handle_t* req)
|
||||||
|
{
|
||||||
|
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
|
||||||
|
|
||||||
|
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 + realsize);
|
||||||
|
return realsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurlContext::curl_perform(uv_poll_t* req, int status, int events)
|
||||||
|
{
|
||||||
|
int flags = 0;
|
||||||
|
if (status < 0) {
|
||||||
|
flags |= CURL_CSELECT_ERR;
|
||||||
|
LOGERR(1, "uv_poll_start returned error " << uv_err_name(status));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (events & UV_READABLE) flags |= CURL_CSELECT_IN;
|
||||||
|
if (events & UV_WRITABLE) flags |= CURL_CSELECT_OUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
int pending;
|
||||||
|
while (CURLMsg* message = curl_multi_info_read(m_multiHandle, &pending)) {
|
||||||
|
if (message->msg == CURLMSG_DONE) {
|
||||||
|
if (message->data.result != CURLE_OK) {
|
||||||
|
m_error = curl_easy_strerror(message->data.result);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
long http_code = 0;
|
||||||
|
curl_easy_getinfo(message->easy_handle, CURLINFO_RESPONSE_CODE, &http_code);
|
||||||
|
|
||||||
|
if (http_code != 200) {
|
||||||
|
char buf[32] = {};
|
||||||
|
log::Stream s(buf);
|
||||||
|
s << "HTTP error " << static_cast<int>(http_code) << '\0';
|
||||||
|
m_error = buf;
|
||||||
|
}
|
||||||
|
else if (m_response.empty()) {
|
||||||
|
m_error = "empty response";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_multi_remove_handle(m_multiHandle, m_handle);
|
||||||
|
curl_easy_cleanup(m_handle);
|
||||||
|
curl_multi_cleanup(m_multiHandle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_socket.data = this;
|
void CurlContext::on_close(uv_handle_t* h)
|
||||||
m_connect.data = this;
|
{
|
||||||
m_write.data = this;
|
CurlContext* ctx = reinterpret_cast<CurlContext*>(h->data);
|
||||||
|
h->data = nullptr;
|
||||||
|
|
||||||
const char* uri = "/json_rpc";
|
if (ctx->m_timer.data || ctx->m_async.data) {
|
||||||
|
|
||||||
size_t len = req ? strlen(req) : 0;
|
|
||||||
if (!len) {
|
|
||||||
LOGERR(1, "Empty JSONRPCRequest, fix the code!");
|
|
||||||
m_valid = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req[0] == '/') {
|
delete ctx;
|
||||||
uri = req;
|
}
|
||||||
len = 0;
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_request.reserve(std::max<size_t>(len + 128, log::Stream::BUF_SIZE + 1));
|
if (m_timer.data && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_timer))) {
|
||||||
m_request.resize(log::Stream::BUF_SIZE + 1);
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), on_close);
|
||||||
|
|
||||||
log::Stream s(m_request.data());
|
|
||||||
s << "POST " << uri << " HTTP/1.1\nContent-Type: application/json\nContent-Length: " << len << "\n\n";
|
|
||||||
|
|
||||||
m_request.resize(s.m_pos);
|
|
||||||
m_request.insert(m_request.end(), req, req + len);
|
|
||||||
|
|
||||||
m_response.reserve(sizeof(m_readBuf));
|
|
||||||
|
|
||||||
const int err = uv_tcp_connect(&m_connect, &m_socket, reinterpret_cast<const sockaddr*>(&addr), on_connect);
|
|
||||||
if (err) {
|
|
||||||
LOGERR(1, "failed to initiate tcp connection to " << address << ", error " << uv_err_name(err));
|
|
||||||
m_valid = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JSONRPCRequest::on_connect(uv_connect_t* req, int status)
|
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)
|
||||||
{
|
{
|
||||||
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(req->data);
|
if (!loop) {
|
||||||
|
loop = uv_default_loop();
|
||||||
if (status != 0) {
|
|
||||||
pThis->m_error = uv_err_name(status);
|
|
||||||
LOGERR(1, "failed to connect, error " << pThis->m_error);
|
|
||||||
pThis->close();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uv_buf_t buf[1];
|
const bool result = CallOnLoop(loop,
|
||||||
buf[0].base = pThis->m_request.data();
|
[=]()
|
||||||
buf[0].len = static_cast<uint32_t>(pThis->m_request.size());
|
{
|
||||||
|
try {
|
||||||
uv_write(&pThis->m_write, reinterpret_cast<uv_stream_t*>(&pThis->m_socket), buf, 1, on_write);
|
new CurlContext(address, port, req, auth, proxy, cb, close_cb, loop);
|
||||||
}
|
|
||||||
|
|
||||||
void JSONRPCRequest::on_write(uv_write_t* handle, int status)
|
|
||||||
{
|
|
||||||
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(handle->data);
|
|
||||||
|
|
||||||
if (status != 0) {
|
|
||||||
pThis->m_error = uv_err_name(status);
|
|
||||||
LOGERR(1, "failed to send request, error " << pThis->m_error);
|
|
||||||
pThis->close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uv_read_start(reinterpret_cast<uv_stream_t*>(&pThis->m_socket), on_alloc, on_read);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JSONRPCRequest::on_alloc(uv_handle_t* handle, size_t /*suggested_size*/, uv_buf_t* buf)
|
|
||||||
{
|
|
||||||
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(handle->data);
|
|
||||||
|
|
||||||
if (pThis->m_readBufInUse) {
|
|
||||||
LOGERR(1, "read buffer is already in use");
|
|
||||||
}
|
|
||||||
|
|
||||||
buf->len = sizeof(pThis->m_readBuf);
|
|
||||||
buf->base = pThis->m_readBuf;
|
|
||||||
pThis->m_readBufInUse = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JSONRPCRequest::on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
|
|
||||||
{
|
|
||||||
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(stream->data);
|
|
||||||
pThis->m_readBufInUse = false;
|
|
||||||
|
|
||||||
if (nread > 0) {
|
|
||||||
pThis->on_read(buf->base, nread);
|
|
||||||
}
|
|
||||||
else if (nread < 0) {
|
|
||||||
if (nread != UV_EOF){
|
|
||||||
pThis->m_error = uv_err_name(static_cast<int>(nread));
|
|
||||||
LOGERR(1, "failed to read response, error " << pThis->m_error);
|
|
||||||
}
|
|
||||||
pThis->close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JSONRPCRequest::on_read(const char* data, size_t size)
|
|
||||||
{
|
|
||||||
m_response.append(data, size);
|
|
||||||
|
|
||||||
static constexpr char headers_end[] = "\r\n\r\n";
|
|
||||||
if (m_response.find(headers_end) == std::string::npos) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
llhttp_settings_t settings{};
|
|
||||||
|
|
||||||
settings.on_status = [](llhttp_t*, const char* at, size_t length)
|
|
||||||
{
|
|
||||||
if ((length == 2) && (!memcmp(at, "Ok", 2) || !memcmp(at, "OK", 2))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
settings.on_header_field = [](llhttp_t* parser, const char* at, size_t length)
|
|
||||||
{
|
|
||||||
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(parser->data);
|
|
||||||
static const char header[] = "Content-Length";
|
|
||||||
pThis->m_contentLengthHeader = ((length == sizeof(header) - 1) && (memcmp(at, header, length) == 0));
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
settings.on_header_value = [](llhttp_t* parser, const char* at, size_t length)
|
|
||||||
{
|
|
||||||
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(parser->data);
|
|
||||||
if (pThis->m_contentLengthHeader) {
|
|
||||||
uint32_t k = 0;
|
|
||||||
for (const char* p = at; p < at + length; ++p) {
|
|
||||||
if ('0' <= *p && *p <= '9') {
|
|
||||||
k = k * 10 + (*p - '0');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!k) {
|
catch (const std::exception& e) {
|
||||||
return -1;
|
const char* msg = e.what();
|
||||||
|
(*close_cb)(msg, strlen(msg));
|
||||||
}
|
}
|
||||||
pThis->m_contentLength = k;
|
});
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
settings.on_body = [](llhttp_t* parser, const char* at, size_t length)
|
if (!result) {
|
||||||
{
|
LOGERR(1, "JSON RPC \"" << req << "\" failed");
|
||||||
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(parser->data);
|
|
||||||
if (pThis->m_contentLength && (length >= pThis->m_contentLength) && pThis->m_callback) {
|
|
||||||
(*pThis->m_callback)(at, length);
|
|
||||||
delete pThis->m_callback;
|
|
||||||
pThis->m_callback = nullptr;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
llhttp_t parser;
|
|
||||||
llhttp_init(&parser, HTTP_RESPONSE, &settings);
|
|
||||||
|
|
||||||
parser.data = this;
|
|
||||||
|
|
||||||
const llhttp_errno result = llhttp_execute(&parser, m_response.c_str(), m_response.length());
|
|
||||||
if (result != HPE_OK) {
|
|
||||||
m_error = "failed to parse response";
|
|
||||||
LOGERR(1, m_error << ", result = " << static_cast<int>(result));
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_callback) {
|
|
||||||
close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JSONRPCRequest::close()
|
} // namespace JSONRPCRequest
|
||||||
{
|
|
||||||
uv_handle_t* h = reinterpret_cast<uv_handle_t*>(&m_socket);
|
|
||||||
if (!uv_is_closing(h)) {
|
|
||||||
uv_close(h, on_close);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JSONRPCRequest::on_close(uv_handle_t* handle)
|
|
||||||
{
|
|
||||||
JSONRPCRequest* req = static_cast<JSONRPCRequest*>(handle->data);
|
|
||||||
if (req->m_closeCallback) {
|
|
||||||
(*req->m_closeCallback)(req->m_error.c_str(), req->m_error.length());
|
|
||||||
}
|
|
||||||
delete req;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONRPCRequest::~JSONRPCRequest()
|
|
||||||
{
|
|
||||||
delete m_callback;
|
|
||||||
delete m_closeCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace p2pool
|
} // namespace p2pool
|
||||||
|
|||||||
+24
-69
@@ -18,77 +18,32 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace p2pool {
|
namespace p2pool {
|
||||||
|
namespace JSONRPCRequest {
|
||||||
|
|
||||||
class JSONRPCRequest
|
struct CallbackBase
|
||||||
{
|
{
|
||||||
public:
|
virtual ~CallbackBase() {}
|
||||||
template<typename T>
|
virtual void operator()(const char* data, size_t size) = 0;
|
||||||
static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb)
|
|
||||||
{
|
|
||||||
// It will be deleted in one of the tcp callbacks eventually
|
|
||||||
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), nullptr, nullptr);
|
|
||||||
if (!r->m_valid) {
|
|
||||||
delete r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename U>
|
|
||||||
static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr)
|
|
||||||
{
|
|
||||||
// It will be deleted in one of the tcp callbacks eventually
|
|
||||||
CallbackBase* close_callback = new Callback<U>(std::move(close_cb));
|
|
||||||
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), close_callback, loop);
|
|
||||||
if (!r->m_valid) {
|
|
||||||
constexpr char err[] = "internal error";
|
|
||||||
(*close_callback)(err, sizeof(err) - 1);
|
|
||||||
delete r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct CallbackBase
|
|
||||||
{
|
|
||||||
virtual ~CallbackBase() {}
|
|
||||||
virtual void operator()(const char* data, size_t size) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct Callback : public CallbackBase
|
|
||||||
{
|
|
||||||
explicit FORCEINLINE Callback(T&& cb) : m_cb(std::move(cb)) {}
|
|
||||||
void operator()(const char* data, size_t size) override { m_cb(data, size); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Callback& operator=(Callback&&) = delete;
|
|
||||||
T m_cb;
|
|
||||||
};
|
|
||||||
|
|
||||||
JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
|
|
||||||
~JSONRPCRequest();
|
|
||||||
|
|
||||||
static void on_connect(uv_connect_t* req, int status);
|
|
||||||
static void on_write(uv_write_t* handle, int status);
|
|
||||||
static void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
|
|
||||||
static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf);
|
|
||||||
void on_read(const char* data, size_t size);
|
|
||||||
void close();
|
|
||||||
static void on_close(uv_handle_t* handle);
|
|
||||||
|
|
||||||
uv_tcp_t m_socket;
|
|
||||||
uv_connect_t m_connect;
|
|
||||||
uv_write_t m_write;
|
|
||||||
|
|
||||||
CallbackBase* m_callback;
|
|
||||||
CallbackBase* m_closeCallback;
|
|
||||||
uint32_t m_contentLength;
|
|
||||||
bool m_contentLengthHeader;
|
|
||||||
|
|
||||||
std::vector<char> m_request;
|
|
||||||
std::string m_response;
|
|
||||||
char m_readBuf[65536];
|
|
||||||
bool m_readBufInUse;
|
|
||||||
bool m_valid;
|
|
||||||
std::string m_error;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct Callback : public CallbackBase
|
||||||
|
{
|
||||||
|
explicit FORCEINLINE Callback(T&& cb) : m_cb(std::move(cb)) {}
|
||||||
|
void operator()(const char* data, size_t size) override { m_cb(data, size); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Callback& operator=(Callback&&) = delete;
|
||||||
|
T m_cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
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, const std::string& proxy, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr)
|
||||||
|
{
|
||||||
|
Call(address, port, req, auth, proxy, new Callback<T>(std::move(cb)), new Callback<U>(std::move(close_cb)), loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace JSONRPCRequest
|
||||||
} // namespace p2pool
|
} // namespace p2pool
|
||||||
|
|||||||
+24
-57
@@ -48,7 +48,7 @@ NOINLINE void keccakf(uint64_t* st)
|
|||||||
bc[3] = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
|
bc[3] = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
|
||||||
bc[4] = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
|
bc[4] = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
|
||||||
|
|
||||||
#define X(i) { \
|
#define THETA(i) { \
|
||||||
const uint64_t t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); \
|
const uint64_t t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); \
|
||||||
st[i + 0 ] ^= t; \
|
st[i + 0 ] ^= t; \
|
||||||
st[i + 5] ^= t; \
|
st[i + 5] ^= t; \
|
||||||
@@ -57,9 +57,11 @@ NOINLINE void keccakf(uint64_t* st)
|
|||||||
st[i + 20] ^= t; \
|
st[i + 20] ^= t; \
|
||||||
}
|
}
|
||||||
|
|
||||||
X(0); X(1); X(2); X(3); X(4);
|
THETA(0);
|
||||||
|
THETA(1);
|
||||||
#undef X
|
THETA(2);
|
||||||
|
THETA(3);
|
||||||
|
THETA(4);
|
||||||
|
|
||||||
// Rho Pi
|
// Rho Pi
|
||||||
const uint64_t t = st[1];
|
const uint64_t t = st[1];
|
||||||
@@ -89,60 +91,25 @@ NOINLINE void keccakf(uint64_t* st)
|
|||||||
st[10] = ROTL64(t, 1);
|
st[10] = ROTL64(t, 1);
|
||||||
|
|
||||||
// Chi
|
// Chi
|
||||||
// unrolled loop, where only last iteration is different
|
#define CHI(j) { \
|
||||||
int j = 0;
|
const uint64_t st0 = st[j ]; \
|
||||||
bc[0] = st[j + 0];
|
const uint64_t st1 = st[j + 1]; \
|
||||||
bc[1] = st[j + 1];
|
const uint64_t st2 = st[j + 2]; \
|
||||||
|
const uint64_t st3 = st[j + 3]; \
|
||||||
|
const uint64_t st4 = st[j + 4]; \
|
||||||
|
st[j ] ^= ~st1 & st2; \
|
||||||
|
st[j + 1] ^= ~st2 & st3; \
|
||||||
|
st[j + 2] ^= ~st3 & st4; \
|
||||||
|
st[j + 3] ^= ~st4 & st0; \
|
||||||
|
st[j + 4] ^= ~st0 & st1; \
|
||||||
|
}
|
||||||
|
|
||||||
st[j + 0] ^= (~st[j + 1]) & st[j + 2];
|
CHI( 0);
|
||||||
st[j + 1] ^= (~st[j + 2]) & st[j + 3];
|
CHI( 5);
|
||||||
st[j + 2] ^= (~st[j + 3]) & st[j + 4];
|
CHI(10);
|
||||||
st[j + 3] ^= (~st[j + 4]) & bc[0];
|
CHI(15);
|
||||||
st[j + 4] ^= (~bc[0]) & bc[1];
|
CHI(20);
|
||||||
|
|
||||||
j = 5;
|
|
||||||
bc[0] = st[j + 0];
|
|
||||||
bc[1] = st[j + 1];
|
|
||||||
|
|
||||||
st[j + 0] ^= (~st[j + 1]) & st[j + 2];
|
|
||||||
st[j + 1] ^= (~st[j + 2]) & st[j + 3];
|
|
||||||
st[j + 2] ^= (~st[j + 3]) & st[j + 4];
|
|
||||||
st[j + 3] ^= (~st[j + 4]) & bc[0];
|
|
||||||
st[j + 4] ^= (~bc[0]) & bc[1];
|
|
||||||
|
|
||||||
j = 10;
|
|
||||||
bc[0] = st[j + 0];
|
|
||||||
bc[1] = st[j + 1];
|
|
||||||
|
|
||||||
st[j + 0] ^= (~st[j + 1]) & st[j + 2];
|
|
||||||
st[j + 1] ^= (~st[j + 2]) & st[j + 3];
|
|
||||||
st[j + 2] ^= (~st[j + 3]) & st[j + 4];
|
|
||||||
st[j + 3] ^= (~st[j + 4]) & bc[0];
|
|
||||||
st[j + 4] ^= (~bc[0]) & bc[1];
|
|
||||||
|
|
||||||
j = 15;
|
|
||||||
bc[0] = st[j + 0];
|
|
||||||
bc[1] = st[j + 1];
|
|
||||||
|
|
||||||
st[j + 0] ^= (~st[j + 1]) & st[j + 2];
|
|
||||||
st[j + 1] ^= (~st[j + 2]) & st[j + 3];
|
|
||||||
st[j + 2] ^= (~st[j + 3]) & st[j + 4];
|
|
||||||
st[j + 3] ^= (~st[j + 4]) & bc[0];
|
|
||||||
st[j + 4] ^= (~bc[0]) & bc[1];
|
|
||||||
|
|
||||||
j = 20;
|
|
||||||
bc[0] = st[j + 0];
|
|
||||||
bc[1] = st[j + 1];
|
|
||||||
bc[2] = st[j + 2];
|
|
||||||
bc[3] = st[j + 3];
|
|
||||||
bc[4] = st[j + 4];
|
|
||||||
|
|
||||||
st[j + 0] ^= (~bc[1]) & bc[2];
|
|
||||||
st[j + 1] ^= (~bc[2]) & bc[3];
|
|
||||||
st[j + 2] ^= (~bc[3]) & bc[4];
|
|
||||||
st[j + 3] ^= (~bc[4]) & bc[0];
|
|
||||||
st[j + 4] ^= (~bc[0]) & bc[1];
|
|
||||||
|
|
||||||
// Iota
|
// Iota
|
||||||
st[0] ^= keccakf_rndc[round];
|
st[0] ^= keccakf_rndc[round];
|
||||||
}
|
}
|
||||||
@@ -173,7 +140,7 @@ NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen)
|
|||||||
temp[rsiz - 1] |= 0x80;
|
temp[rsiz - 1] |= 0x80;
|
||||||
|
|
||||||
for (int i = 0; i < rsizw; i++) {
|
for (int i = 0; i < rsizw; i++) {
|
||||||
st[i] ^= ((uint64_t*)temp)[i];
|
st[i] ^= reinterpret_cast<uint64_t*>(temp)[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
keccakf(st);
|
keccakf(st);
|
||||||
|
|||||||
+7
-10
@@ -194,15 +194,6 @@ private:
|
|||||||
fwrite(p, 1, size, (severity == 1) ? stdout : stderr);
|
fwrite(p, 1, size, (severity == 1) ? stdout : stderr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Reopen the log file if it's been moved (logrotate support)
|
|
||||||
if (m_logFile.is_open()) {
|
|
||||||
struct stat buf;
|
|
||||||
if (stat(log_file_name, &buf) != 0) {
|
|
||||||
m_logFile.close();
|
|
||||||
m_logFile.open(log_file_name, std::ios::app | std::ios::binary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_logFile.is_open()) {
|
if (m_logFile.is_open()) {
|
||||||
if (c) {
|
if (c) {
|
||||||
strip_colors(p, size);
|
strip_colors(p, size);
|
||||||
@@ -232,6 +223,13 @@ private:
|
|||||||
// Flush the log file only after all pending log lines have been written
|
// Flush the log file only after all pending log lines have been written
|
||||||
if (m_logFile.is_open()) {
|
if (m_logFile.is_open()) {
|
||||||
m_logFile.flush();
|
m_logFile.flush();
|
||||||
|
|
||||||
|
// Reopen the log file if it's been moved (logrotate support)
|
||||||
|
struct stat buf;
|
||||||
|
if (stat(log_file_name, &buf) != 0) {
|
||||||
|
m_logFile.close();
|
||||||
|
m_logFile.open(log_file_name, std::ios::app | std::ios::binary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} while (!stopped);
|
} while (!stopped);
|
||||||
}
|
}
|
||||||
@@ -278,7 +276,6 @@ static Worker worker;
|
|||||||
|
|
||||||
#endif // P2POOL_LOG_DISABLE
|
#endif // P2POOL_LOG_DISABLE
|
||||||
|
|
||||||
// cppcheck-suppress uninitMemberVar
|
|
||||||
NOINLINE Writer::Writer(Severity severity) : Stream(m_stackBuf)
|
NOINLINE Writer::Writer(Severity severity) : Stream(m_stackBuf)
|
||||||
{
|
{
|
||||||
m_buf[0] = static_cast<char>(severity);
|
m_buf[0] = static_cast<char>(severity);
|
||||||
|
|||||||
@@ -35,8 +35,10 @@ struct Stream
|
|||||||
{
|
{
|
||||||
enum params : int { BUF_SIZE = 1024 - 1 };
|
enum params : int { BUF_SIZE = 1024 - 1 };
|
||||||
|
|
||||||
explicit FORCEINLINE Stream(char* buf) : m_pos(0), m_numberWidth(1), m_buf(buf), m_bufSize(BUF_SIZE) {}
|
template<size_t N>
|
||||||
FORCEINLINE Stream(char* buf, size_t size) : m_pos(0), m_numberWidth(1), m_buf(buf), m_bufSize(static_cast<int>(size) - 1) {}
|
explicit FORCEINLINE Stream(char (&buf)[N]) : m_pos(0), m_numberWidth(1), m_buf(buf), m_bufSize(N - 1) {}
|
||||||
|
|
||||||
|
FORCEINLINE Stream(void* buf, size_t size) : m_pos(0), m_numberWidth(1), m_buf(reinterpret_cast<char*>(buf)), m_bufSize(static_cast<int>(size) - 1) {}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct Entry
|
struct Entry
|
||||||
@@ -182,7 +184,10 @@ INT_ENTRY(unsigned long)
|
|||||||
template<typename T, int base>
|
template<typename T, int base>
|
||||||
struct BasedValue
|
struct BasedValue
|
||||||
{
|
{
|
||||||
explicit FORCEINLINE BasedValue(T value) : m_value(value) {}
|
explicit FORCEINLINE BasedValue(T value) : m_value(value)
|
||||||
|
{
|
||||||
|
static_assert(std::is_integral<T>::value, "Must be an integer type here");
|
||||||
|
}
|
||||||
|
|
||||||
T m_value;
|
T m_value;
|
||||||
};
|
};
|
||||||
@@ -190,7 +195,7 @@ struct BasedValue
|
|||||||
template<typename T, int base>
|
template<typename T, int base>
|
||||||
struct Stream::Entry<BasedValue<T, base>>
|
struct Stream::Entry<BasedValue<T, base>>
|
||||||
{
|
{
|
||||||
static FORCEINLINE void put(BasedValue<T, base>&& data, Stream* wrapper)
|
static FORCEINLINE void put(BasedValue<T, base> data, Stream* wrapper)
|
||||||
{
|
{
|
||||||
wrapper->writeInt<T, base>(data.m_value);
|
wrapper->writeInt<T, base>(data.m_value);
|
||||||
}
|
}
|
||||||
@@ -302,6 +307,7 @@ struct Hashrate
|
|||||||
{
|
{
|
||||||
FORCEINLINE Hashrate() : m_data(0), m_valid(false) {}
|
FORCEINLINE Hashrate() : m_data(0), m_valid(false) {}
|
||||||
explicit FORCEINLINE Hashrate(uint64_t data) : m_data(data), m_valid(true) {}
|
explicit FORCEINLINE Hashrate(uint64_t data) : m_data(data), m_valid(true) {}
|
||||||
|
FORCEINLINE Hashrate(uint64_t data, bool valid) : m_data(data), m_valid(valid) {}
|
||||||
|
|
||||||
uint64_t m_data;
|
uint64_t m_data;
|
||||||
bool m_valid;
|
bool m_valid;
|
||||||
@@ -309,7 +315,7 @@ struct Hashrate
|
|||||||
|
|
||||||
template<> struct log::Stream::Entry<Hashrate>
|
template<> struct log::Stream::Entry<Hashrate>
|
||||||
{
|
{
|
||||||
static NOINLINE void put(Hashrate&& value, Stream* wrapper)
|
static NOINLINE void put(const Hashrate& value, Stream* wrapper)
|
||||||
{
|
{
|
||||||
if (!value.m_valid) {
|
if (!value.m_valid) {
|
||||||
return;
|
return;
|
||||||
@@ -354,7 +360,7 @@ struct XMRAmount
|
|||||||
|
|
||||||
template<> struct log::Stream::Entry<XMRAmount>
|
template<> struct log::Stream::Entry<XMRAmount>
|
||||||
{
|
{
|
||||||
static NOINLINE void put(XMRAmount&& value, Stream* wrapper)
|
static NOINLINE void put(XMRAmount value, Stream* wrapper)
|
||||||
{
|
{
|
||||||
constexpr uint64_t denomination = 1000000000000ULL;
|
constexpr uint64_t denomination = 1000000000000ULL;
|
||||||
|
|
||||||
@@ -372,8 +378,7 @@ template<> struct log::Stream::Entry<XMRAmount>
|
|||||||
|
|
||||||
template<> struct log::Stream::Entry<NetworkType>
|
template<> struct log::Stream::Entry<NetworkType>
|
||||||
{
|
{
|
||||||
// cppcheck-suppress constParameter
|
static NOINLINE void put(NetworkType value, Stream* wrapper)
|
||||||
static NOINLINE void put(const NetworkType& value, Stream* wrapper)
|
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case NetworkType::Invalid: *wrapper << "invalid"; break;
|
case NetworkType::Invalid: *wrapper << "invalid"; break;
|
||||||
@@ -384,6 +389,68 @@ template<> struct log::Stream::Entry<NetworkType>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Duration
|
||||||
|
{
|
||||||
|
explicit FORCEINLINE Duration(uint64_t data) : m_data(data) {}
|
||||||
|
|
||||||
|
uint64_t m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct log::Stream::Entry<Duration>
|
||||||
|
{
|
||||||
|
static NOINLINE void put(Duration value, Stream* wrapper)
|
||||||
|
{
|
||||||
|
const uint64_t uptime = value.m_data;
|
||||||
|
|
||||||
|
const int64_t s = uptime % 60;
|
||||||
|
const int64_t m = (uptime / 60) % 60;
|
||||||
|
const int64_t h = (uptime / 3600) % 24;
|
||||||
|
const int64_t d = uptime / 86400;
|
||||||
|
|
||||||
|
if (d > 0) {
|
||||||
|
*wrapper << d << "d ";
|
||||||
|
}
|
||||||
|
*wrapper << h << "h " << m << "m " << s << 's';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct PadRight
|
||||||
|
{
|
||||||
|
FORCEINLINE PadRight(const T& value, int len) : m_value(value), m_len(len) {}
|
||||||
|
|
||||||
|
const T& m_value;
|
||||||
|
int m_len;
|
||||||
|
|
||||||
|
// Declare it to make compiler happy
|
||||||
|
PadRight(const PadRight&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PadRight& operator=(const PadRight&) = delete;
|
||||||
|
PadRight& operator=(PadRight&&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> FORCEINLINE PadRight<T> pad_right(const T& value, int len) { return PadRight<T>(value, len); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct log::Stream::Entry<PadRight<T>>
|
||||||
|
{
|
||||||
|
static NOINLINE void put(PadRight<T>&& data, Stream* wrapper)
|
||||||
|
{
|
||||||
|
char buf[log::Stream::BUF_SIZE + 1];
|
||||||
|
log::Stream s(buf);
|
||||||
|
s << data.m_value;
|
||||||
|
|
||||||
|
const int len = std::min<int>(data.m_len, log::Stream::BUF_SIZE);
|
||||||
|
if (s.m_pos < len) {
|
||||||
|
memset(buf + s.m_pos, ' ', static_cast<size_t>(len) - s.m_pos);
|
||||||
|
s.m_pos = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper->writeBuf(buf, s.m_pos);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void put_rawip(const raw_ip& value, Stream* wrapper);
|
void put_rawip(const raw_ip& value, Stream* wrapper);
|
||||||
|
|
||||||
template<> struct log::Stream::Entry<raw_ip>
|
template<> struct log::Stream::Entry<raw_ip>
|
||||||
@@ -402,16 +469,47 @@ namespace {
|
|||||||
#define CONCAT(a, b) CONCAT2(a, b)
|
#define CONCAT(a, b) CONCAT2(a, b)
|
||||||
#define CONCAT2(a, b) a##b
|
#define CONCAT2(a, b) a##b
|
||||||
|
|
||||||
|
// This is to check that LOG() call doesn't modify variables in scope, making program behavior dependent on the log level:
|
||||||
|
//
|
||||||
|
// int some_func(int& n) { return ++n; }
|
||||||
|
// ...
|
||||||
|
// LOGINFO(1, "Some important value: " << some_func(n));
|
||||||
|
//
|
||||||
|
// will not compile because the dummy lambda capture uses const-qualified copies of all variables.
|
||||||
|
//
|
||||||
|
// The check is "free": compiler will remove it entirely in release builds.
|
||||||
|
|
||||||
|
struct DummyStream
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
FORCEINLINE DummyStream& operator<<(const T&)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SIDE_EFFECT_CHECK(level, ...) \
|
||||||
|
do { \
|
||||||
|
if (0) { \
|
||||||
|
MSVC_PRAGMA(warning(suppress:26444)) \
|
||||||
|
[=]() { \
|
||||||
|
log::DummyStream x; \
|
||||||
|
x << level << __VA_ARGS__; \
|
||||||
|
}; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#ifdef P2POOL_LOG_DISABLE
|
#ifdef P2POOL_LOG_DISABLE
|
||||||
|
|
||||||
#define LOGINFO(level, ...)
|
#define LOGINFO(level, ...) SIDE_EFFECT_CHECK(level, __VA_ARGS__)
|
||||||
#define LOGWARN(level, ...)
|
#define LOGWARN(level, ...) SIDE_EFFECT_CHECK(level, __VA_ARGS__)
|
||||||
#define LOGERR(level, ...)
|
#define LOGERR(level, ...) SIDE_EFFECT_CHECK(level, __VA_ARGS__)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define LOG(level, severity, ...) \
|
#define LOG(level, severity, ...) \
|
||||||
do { \
|
do { \
|
||||||
|
SIDE_EFFECT_CHECK(level, __VA_ARGS__); \
|
||||||
if (level <= log::GLOBAL_LOG_LEVEL) { \
|
if (level <= log::GLOBAL_LOG_LEVEL) { \
|
||||||
log::Writer CONCAT(log_wrapper_, __LINE__)(severity); \
|
log::Writer CONCAT(log_wrapper_, __LINE__)(severity); \
|
||||||
CONCAT(log_wrapper_, __LINE__) << log::Gray() << log_category_prefix; \
|
CONCAT(log_wrapper_, __LINE__) << log::Gray() << log_category_prefix; \
|
||||||
|
|||||||
+22
-6
@@ -20,6 +20,7 @@
|
|||||||
#include "p2pool.h"
|
#include "p2pool.h"
|
||||||
#include "stratum_server.h"
|
#include "stratum_server.h"
|
||||||
#include "p2p_server.h"
|
#include "p2p_server.h"
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
void p2pool_usage()
|
void p2pool_usage()
|
||||||
{
|
{
|
||||||
@@ -41,10 +42,15 @@ void p2pool_usage()
|
|||||||
"--no-cache Disable p2pool.cache\n"
|
"--no-cache Disable p2pool.cache\n"
|
||||||
"--no-color Disable colors in console output\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"
|
"--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"
|
"--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 1000)\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"
|
"--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"
|
"--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"
|
"--help Show this help message\n\n"
|
||||||
"Example command line:\n\n"
|
"Example command line:\n\n"
|
||||||
"%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",
|
"%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",
|
||||||
@@ -79,15 +85,25 @@ int main(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int result;
|
|
||||||
|
|
||||||
memory_tracking_start();
|
memory_tracking_start();
|
||||||
|
|
||||||
p2pool::init_crypto_cache();
|
p2pool::init_crypto_cache();
|
||||||
{
|
|
||||||
|
int result = static_cast<int>(curl_global_init_mem(CURL_GLOBAL_ALL, p2pool::malloc_hook, p2pool::free_hook, p2pool::realloc_hook, p2pool::strdup_hook, p2pool::calloc_hook));
|
||||||
|
if (result != CURLE_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
p2pool::p2pool pool(argc, argv);
|
p2pool::p2pool pool(argc, argv);
|
||||||
result = pool.run();
|
result = pool.run();
|
||||||
}
|
}
|
||||||
|
catch (...) {
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_global_cleanup();
|
||||||
|
|
||||||
p2pool::destroy_crypto_cache();
|
p2pool::destroy_crypto_cache();
|
||||||
|
|
||||||
memory_tracking_stop();
|
memory_tracking_stop();
|
||||||
|
|||||||
+119
-34
@@ -32,15 +32,48 @@ namespace p2pool {
|
|||||||
|
|
||||||
static bool track_memory = false;
|
static bool track_memory = false;
|
||||||
|
|
||||||
constexpr size_t N = 1048576;
|
constexpr size_t N = 1 << 22;
|
||||||
constexpr size_t MAX_FRAMES = 30;
|
constexpr size_t MAX_FRAMES = 29;
|
||||||
|
|
||||||
struct TrackedAllocation
|
struct TrackedAllocation
|
||||||
{
|
{
|
||||||
void* p;
|
void* p;
|
||||||
void* stack_trace[MAX_FRAMES];
|
void* stack_trace[MAX_FRAMES];
|
||||||
|
uint64_t allocated_size;
|
||||||
uint32_t thread_id;
|
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, "");
|
static_assert(sizeof(TrackedAllocation) == 256, "");
|
||||||
@@ -51,15 +84,62 @@ uint32_t first[N];
|
|||||||
uint32_t next[N];
|
uint32_t next[N];
|
||||||
TrackedAllocation allocations[N];
|
TrackedAllocation allocations[N];
|
||||||
uint32_t num_allocations = 0;
|
uint32_t num_allocations = 0;
|
||||||
|
uint64_t total_allocated = 0;
|
||||||
uint32_t cur_allocation_index = 1;
|
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)
|
FORCEINLINE static void add_alocation(void* p, size_t size)
|
||||||
{
|
{
|
||||||
if (!track_memory) {
|
if (!track_memory) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* stack_trace[MAX_FRAMES];
|
void* stack_trace[MAX_FRAMES] = {};
|
||||||
DWORD hash;
|
DWORD hash;
|
||||||
CaptureStackBackTrace(1, MAX_FRAMES, stack_trace, &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
|
// Make N two times bigger if this triggers
|
||||||
__debugbreak();
|
__debugbreak();
|
||||||
}
|
}
|
||||||
|
total_allocated += size;
|
||||||
|
|
||||||
for (uint64_t i = cur_allocation_index;; i = (i + 1) & (N - 1)) {
|
for (uint64_t i = cur_allocation_index;; i = (i + 1) & (N - 1)) {
|
||||||
if (i && !allocations[i].allocated_size) {
|
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]) {
|
for (uint32_t prev = 0, k = first[index]; k != 0; prev = k, k = next[k]) {
|
||||||
if (allocations[k].p == p) {
|
if (allocations[k].p == p) {
|
||||||
|
total_allocated -= allocations[k].allocated_size;
|
||||||
allocations[k].allocated_size = 0;
|
allocations[k].allocated_size = 0;
|
||||||
if (prev) {
|
if (prev) {
|
||||||
next[prev] = next[k];
|
next[prev] = next[k];
|
||||||
@@ -144,6 +226,19 @@ void free_hook(void* p) noexcept
|
|||||||
free(p);
|
free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* strdup_hook(const char* s) noexcept
|
||||||
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
char* s1 = _strdup(s);
|
||||||
|
#else
|
||||||
|
char* s1 = strdup(s);
|
||||||
|
#endif
|
||||||
|
if (s1) {
|
||||||
|
add_alocation(s1, strlen(s) + 1);
|
||||||
|
}
|
||||||
|
return s1;
|
||||||
|
}
|
||||||
|
|
||||||
void* realloc_hook(void* ptr, size_t size) noexcept
|
void* realloc_hook(void* ptr, size_t size) noexcept
|
||||||
{
|
{
|
||||||
remove_allocation(ptr);
|
remove_allocation(ptr);
|
||||||
@@ -168,6 +263,8 @@ void* calloc_hook(size_t count, size_t size) noexcept
|
|||||||
|
|
||||||
void memory_tracking_start()
|
void memory_tracking_start()
|
||||||
{
|
{
|
||||||
|
SymInitialize(GetCurrentProcess(), NULL, TRUE);
|
||||||
|
|
||||||
using namespace p2pool;
|
using namespace p2pool;
|
||||||
|
|
||||||
uv_replace_allocator(malloc_hook, realloc_hook, calloc_hook, free_hook);
|
uv_replace_allocator(malloc_hook, realloc_hook, calloc_hook, free_hook);
|
||||||
@@ -183,47 +280,24 @@ void memory_tracking_stop()
|
|||||||
uv_mutex_destroy(&allocation_lock);
|
uv_mutex_destroy(&allocation_lock);
|
||||||
|
|
||||||
const HANDLE h = GetCurrentProcess();
|
const HANDLE h = GetCurrentProcess();
|
||||||
SymInitialize(h, NULL, TRUE);
|
|
||||||
|
|
||||||
uint64_t total_leaks = 0;
|
uint64_t total_leaks = 0;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < N; ++i) {
|
for (uint32_t i = 0; i < N; ++i) {
|
||||||
if (allocations[i].allocated_size) {
|
const TrackedAllocation& t = allocations[i];
|
||||||
total_leaks += allocations[i].allocated_size;
|
if (t.allocated_size) {
|
||||||
|
total_leaks += t.allocated_size;
|
||||||
|
|
||||||
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = {};
|
printf("Memory leak detected, %I64u bytes allocated at %p by thread %u:\n", t.allocated_size, t.p, t.thread_id);
|
||||||
PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
|
t.print(h);
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (total_leaks > 0) {
|
if (total_leaks > 0) {
|
||||||
printf("%I64u bytes leaked\n\n", total_leaks);
|
printf("%I64u bytes leaked\n\n", total_leaks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SymCleanup(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
NOINLINE void* operator new(size_t n) { return p2pool::allocate(n); }
|
NOINLINE void* operator new(size_t n) { return p2pool::allocate(n); }
|
||||||
@@ -236,7 +310,9 @@ NOINLINE void operator delete(void* p, size_t) noexcept { p2pool::free_hook(p);
|
|||||||
NOINLINE void operator delete[](void* p, size_t) noexcept { p2pool::free_hook(p); }
|
NOINLINE void operator delete[](void* p, size_t) noexcept { p2pool::free_hook(p); }
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
// cppcheck-suppress functionStatic
|
||||||
void memory_tracking_start() {}
|
void memory_tracking_start() {}
|
||||||
|
// cppcheck-suppress functionStatic
|
||||||
void memory_tracking_stop() {}
|
void memory_tracking_stop() {}
|
||||||
|
|
||||||
namespace p2pool {
|
namespace p2pool {
|
||||||
@@ -246,5 +322,14 @@ void* realloc_hook(void* ptr, size_t size) noexcept { return realloc(ptr, size);
|
|||||||
void* calloc_hook(size_t count, size_t size) noexcept { return calloc(count, size); }
|
void* calloc_hook(size_t count, size_t size) noexcept { return calloc(count, size); }
|
||||||
void free_hook(void* p) noexcept { free(p); }
|
void free_hook(void* p) noexcept { free(p); }
|
||||||
|
|
||||||
|
char* strdup_hook(const char* s) noexcept
|
||||||
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
return _strdup(s);
|
||||||
|
#else
|
||||||
|
return strdup(s);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+17
-6
@@ -24,6 +24,8 @@
|
|||||||
#include "randomx.h"
|
#include "randomx.h"
|
||||||
#include "params.h"
|
#include "params.h"
|
||||||
#include "p2pool_api.h"
|
#include "p2pool_api.h"
|
||||||
|
#include "side_chain.h"
|
||||||
|
#include "p2p_server.h"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
static constexpr char log_category_prefix[] = "Miner ";
|
static constexpr char log_category_prefix[] = "Miner ";
|
||||||
@@ -39,7 +41,7 @@ Miner::Miner(p2pool* pool, uint32_t threads)
|
|||||||
, m_startTimestamp(high_resolution_clock::now())
|
, m_startTimestamp(high_resolution_clock::now())
|
||||||
, m_nonce(0)
|
, m_nonce(0)
|
||||||
, m_nonceTimestamp(m_startTimestamp)
|
, m_nonceTimestamp(m_startTimestamp)
|
||||||
, m_extraNonce(0xF19E3779U)
|
, m_extraNonce(static_cast<uint32_t>(pool->p2p_server()->get_random64()))
|
||||||
, m_totalHashes(0)
|
, m_totalHashes(0)
|
||||||
, m_sharesFound(0)
|
, m_sharesFound(0)
|
||||||
, m_job{}
|
, m_job{}
|
||||||
@@ -79,8 +81,9 @@ void Miner::print_status()
|
|||||||
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
|
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
|
||||||
|
|
||||||
LOGINFO(0, "status" <<
|
LOGINFO(0, "status" <<
|
||||||
"\nThreads = " << m_threads <<
|
"\nThreads = " << m_threads <<
|
||||||
"\nHashrate = " << log::Hashrate(hr)
|
"\nHashrate = " << log::Hashrate(hr) <<
|
||||||
|
"\nShares found = " << m_sharesFound.load()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,22 +104,31 @@ void Miner::on_block(const BlockTemplate& block)
|
|||||||
m_totalHashes += hash_count;
|
m_totalHashes += hash_count;
|
||||||
|
|
||||||
if (m_pool->api() && m_pool->params().m_localStats) {
|
if (m_pool->api() && m_pool->params().m_localStats) {
|
||||||
|
const double block_reward_share_percent = m_pool->side_chain().get_reward_share(m_pool->params().m_wallet) * 100.0;
|
||||||
|
|
||||||
m_pool->api()->set(p2pool_api::Category::LOCAL, "miner",
|
m_pool->api()->set(p2pool_api::Category::LOCAL, "miner",
|
||||||
[cur_ts, hash_count, dt, this](log::Stream& s)
|
[cur_ts, hash_count, dt, block_reward_share_percent, this](log::Stream& s)
|
||||||
{
|
{
|
||||||
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
|
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
|
||||||
const double time_running = static_cast<double>(duration_cast<milliseconds>(cur_ts - m_startTimestamp).count()) / 1e3;
|
const double time_running = static_cast<double>(duration_cast<milliseconds>(cur_ts - m_startTimestamp).count()) / 1e3;
|
||||||
|
|
||||||
s << "{\"current_hashrate\":" << hr
|
s << "{\"current_hashrate\":" << hr
|
||||||
<< ",\"total_hashes\":" << m_totalHashes
|
<< ",\"total_hashes\":" << m_totalHashes.load()
|
||||||
<< ",\"time_running\":" << time_running
|
<< ",\"time_running\":" << time_running
|
||||||
<< ",\"shares_found\":" << m_sharesFound.load()
|
<< ",\"shares_found\":" << m_sharesFound.load()
|
||||||
|
<< ",\"block_reward_share_percent\":" << block_reward_share_percent
|
||||||
<< ",\"threads\":" << m_threads
|
<< ",\"threads\":" << m_threads
|
||||||
<< "}";
|
<< "}";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Miner::reset_share_counters()
|
||||||
|
{
|
||||||
|
m_totalHashes = 0;
|
||||||
|
m_sharesFound = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void Miner::run(void* data)
|
void Miner::run(void* data)
|
||||||
{
|
{
|
||||||
WorkerData* d = static_cast<WorkerData*>(data);
|
WorkerData* d = static_cast<WorkerData*>(data);
|
||||||
@@ -194,7 +206,6 @@ void Miner::run(WorkerData* data)
|
|||||||
if (j.m_diff.check_pow(h)) {
|
if (j.m_diff.check_pow(h)) {
|
||||||
LOGINFO(0, log::Green() << "worker thread " << data->m_index << '/' << data->m_count << " found a mainchain block, submitting it");
|
LOGINFO(0, log::Green() << "worker thread " << data->m_index << '/' << data->m_count << " found a mainchain block, submitting it");
|
||||||
m_pool->submit_block_async(j.m_templateId, j.m_nonce, j.m_extraNonce);
|
m_pool->submit_block_async(j.m_templateId, j.m_nonce, j.m_extraNonce);
|
||||||
m_pool->block_template().update_tx_keys();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j.m_sidechainDiff.check_pow(h)) {
|
if (j.m_sidechainDiff.check_pow(h)) {
|
||||||
|
|||||||
+2
-1
@@ -33,6 +33,7 @@ public:
|
|||||||
|
|
||||||
void print_status();
|
void print_status();
|
||||||
void on_block(const BlockTemplate& block);
|
void on_block(const BlockTemplate& block);
|
||||||
|
void reset_share_counters();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void run(void* data);
|
static void run(void* data);
|
||||||
@@ -57,7 +58,7 @@ private:
|
|||||||
std::chrono::high_resolution_clock::time_point m_nonceTimestamp;
|
std::chrono::high_resolution_clock::time_point m_nonceTimestamp;
|
||||||
const uint32_t m_extraNonce;
|
const uint32_t m_extraNonce;
|
||||||
|
|
||||||
uint64_t m_totalHashes;
|
std::atomic<uint64_t> m_totalHashes;
|
||||||
std::atomic<uint32_t> m_sharesFound;
|
std::atomic<uint32_t> m_sharesFound;
|
||||||
|
|
||||||
struct Job
|
struct Job
|
||||||
|
|||||||
+341
-183
File diff suppressed because it is too large
Load Diff
+34
-8
@@ -51,6 +51,7 @@ public:
|
|||||||
void clear_cached_blocks();
|
void clear_cached_blocks();
|
||||||
void store_in_cache(const PoolBlock& block);
|
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 connect_to_peers(const std::string& peer_list);
|
||||||
void on_connect_failed(bool is_v6, const raw_ip& ip, int port) override;
|
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_request(const uint8_t* buf);
|
||||||
bool on_peer_list_response(const uint8_t* buf) const;
|
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 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);
|
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;
|
uint64_t m_peerId;
|
||||||
MessageId m_expectedMessage;
|
MessageId m_expectedMessage;
|
||||||
uint64_t m_handshakeChallenge;
|
uint64_t m_handshakeChallenge;
|
||||||
@@ -132,14 +135,19 @@ public:
|
|||||||
uint64_t get_peerId() const { return m_peerId; }
|
uint64_t get_peerId() const { return m_peerId; }
|
||||||
|
|
||||||
void print_status() override;
|
void print_status() override;
|
||||||
void show_peers();
|
void show_peers_async();
|
||||||
size_t peer_list_size() const { return m_peerList.size(); }
|
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_outgoing_peers() const { return m_maxOutgoingPeers; }
|
||||||
uint32_t max_incoming_peers() const { return m_maxIncomingPeers; }
|
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_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), 1000U); }
|
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:
|
private:
|
||||||
p2pool* m_pool;
|
p2pool* m_pool;
|
||||||
@@ -161,6 +169,7 @@ private:
|
|||||||
void check_zmq();
|
void check_zmq();
|
||||||
void update_peer_connections();
|
void update_peer_connections();
|
||||||
void update_peer_list();
|
void update_peer_list();
|
||||||
|
void send_peer_list_request(P2PClient* client, uint64_t cur_time);
|
||||||
void save_peer_list_async();
|
void save_peer_list_async();
|
||||||
void save_peer_list();
|
void save_peer_list();
|
||||||
void load_peer_list();
|
void load_peer_list();
|
||||||
@@ -174,6 +183,8 @@ private:
|
|||||||
|
|
||||||
uv_mutex_t m_blockLock;
|
uv_mutex_t m_blockLock;
|
||||||
PoolBlock* m_block;
|
PoolBlock* m_block;
|
||||||
|
std::vector<uint8_t> m_blockDeserializeBuf;
|
||||||
|
int m_blockDeserializeResult;
|
||||||
|
|
||||||
uv_timer_t m_timer;
|
uv_timer_t m_timer;
|
||||||
uint64_t m_timerCounter;
|
uint64_t m_timerCounter;
|
||||||
@@ -181,7 +192,7 @@ private:
|
|||||||
|
|
||||||
uint64_t m_peerId;
|
uint64_t m_peerId;
|
||||||
|
|
||||||
uv_mutex_t m_peerListLock;
|
mutable uv_mutex_t m_peerListLock;
|
||||||
|
|
||||||
struct Peer
|
struct Peer
|
||||||
{
|
{
|
||||||
@@ -194,7 +205,7 @@ private:
|
|||||||
|
|
||||||
std::vector<Peer> m_peerList;
|
std::vector<Peer> m_peerList;
|
||||||
std::vector<Peer> m_peerListMonero;
|
std::vector<Peer> m_peerListMonero;
|
||||||
uint64_t m_peerListLastSaved;
|
std::atomic<uint64_t> m_peerListLastSaved;
|
||||||
|
|
||||||
struct Broadcast
|
struct Broadcast
|
||||||
{
|
{
|
||||||
@@ -207,11 +218,26 @@ private:
|
|||||||
uv_async_t m_broadcastAsync;
|
uv_async_t m_broadcastAsync;
|
||||||
std::vector<Broadcast*> m_broadcastQueue;
|
std::vector<Broadcast*> m_broadcastQueue;
|
||||||
|
|
||||||
uv_mutex_t m_missingBlockRequestsLock;
|
bool m_lookForMissingBlocks;
|
||||||
unordered_set<std::pair<uint64_t, uint64_t>> m_missingBlockRequests;
|
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(); }
|
static void on_broadcast(uv_async_t* handle) { reinterpret_cast<P2PServer*>(handle->data)->on_broadcast(); }
|
||||||
void 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
|
} // namespace p2pool
|
||||||
|
|||||||
+205
-51
@@ -35,6 +35,7 @@
|
|||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
#include "p2pool_api.h"
|
#include "p2pool_api.h"
|
||||||
#include "pool_block.h"
|
#include "pool_block.h"
|
||||||
|
#include "keccak.h"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
@@ -60,13 +61,21 @@ p2pool::p2pool(int argc, char* argv[])
|
|||||||
|
|
||||||
if (!m_params->m_wallet.valid()) {
|
if (!m_params->m_wallet.valid()) {
|
||||||
LOGERR(1, "Invalid wallet address. Try \"p2pool --help\".");
|
LOGERR(1, "Invalid wallet address. Try \"p2pool --help\".");
|
||||||
panic();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_v6;
|
if (m_params->m_socks5Proxy.empty()) {
|
||||||
if (!resolve_host(m_params->m_host, is_v6)) {
|
if (m_params->m_dns) {
|
||||||
LOGERR(1, "resolve_host failed for " << m_params->m_host);
|
bool is_v6;
|
||||||
panic();
|
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;
|
hash pub, sec, eph_public_key;
|
||||||
@@ -75,7 +84,7 @@ p2pool::p2pool(int argc, char* argv[])
|
|||||||
uint8_t view_tag;
|
uint8_t view_tag;
|
||||||
if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key, view_tag)) {
|
if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key, view_tag)) {
|
||||||
LOGERR(1, "Invalid wallet address: get_eph_public_key failed");
|
LOGERR(1, "Invalid wallet address: get_eph_public_key failed");
|
||||||
panic();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
const NetworkType type = m_params->m_wallet.type();
|
const NetworkType type = m_params->m_wallet.type();
|
||||||
@@ -90,24 +99,31 @@ p2pool::p2pool(int argc, char* argv[])
|
|||||||
int err = uv_async_init(uv_default_loop_checked(), &m_submitBlockAsync, on_submit_block);
|
int err = uv_async_init(uv_default_loop_checked(), &m_submitBlockAsync, on_submit_block);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
||||||
panic();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
m_submitBlockAsync.data = this;
|
m_submitBlockAsync.data = this;
|
||||||
|
|
||||||
err = uv_async_init(uv_default_loop_checked(), &m_blockTemplateAsync, on_update_block_template);
|
err = uv_async_init(uv_default_loop_checked(), &m_blockTemplateAsync, on_update_block_template);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
||||||
panic();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
m_blockTemplateAsync.data = this;
|
m_blockTemplateAsync.data = this;
|
||||||
|
|
||||||
err = uv_async_init(uv_default_loop_checked(), &m_stopAsync, on_stop);
|
err = uv_async_init(uv_default_loop_checked(), &m_stopAsync, on_stop);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
||||||
panic();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
m_stopAsync.data = this;
|
m_stopAsync.data = this;
|
||||||
|
|
||||||
|
err = uv_async_init(uv_default_loop_checked(), &m_restartZMQAsync, on_restart_zmq);
|
||||||
|
if (err) {
|
||||||
|
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
m_restartZMQAsync.data = this;
|
||||||
|
|
||||||
uv_rwlock_init_checked(&m_mainchainLock);
|
uv_rwlock_init_checked(&m_mainchainLock);
|
||||||
uv_rwlock_init_checked(&m_minerDataLock);
|
uv_rwlock_init_checked(&m_minerDataLock);
|
||||||
uv_mutex_init_checked(&m_foundBlocksLock);
|
uv_mutex_init_checked(&m_foundBlocksLock);
|
||||||
@@ -120,7 +136,7 @@ p2pool::p2pool(int argc, char* argv[])
|
|||||||
|
|
||||||
if (m_params->m_localStats && !m_api) {
|
if (m_params->m_localStats && !m_api) {
|
||||||
LOGERR(1, "--local-api and --stratum-api command line parameters can't be used without --data-api");
|
LOGERR(1, "--local-api and --stratum-api command line parameters can't be used without --data-api");
|
||||||
panic();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sideChain = new SideChain(this, type, m_params->m_mini ? "mini" : nullptr);
|
m_sideChain = new SideChain(this, type, m_params->m_mini ? "mini" : nullptr);
|
||||||
@@ -148,7 +164,14 @@ p2pool::p2pool(int argc, char* argv[])
|
|||||||
|
|
||||||
m_blockTemplate = new BlockTemplate(this);
|
m_blockTemplate = new BlockTemplate(this);
|
||||||
m_mempool = new Mempool();
|
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()
|
p2pool::~p2pool()
|
||||||
@@ -167,7 +190,6 @@ p2pool::~p2pool()
|
|||||||
delete m_blockTemplate;
|
delete m_blockTemplate;
|
||||||
delete m_mempool;
|
delete m_mempool;
|
||||||
delete m_params;
|
delete m_params;
|
||||||
delete m_consoleCommands;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result)
|
bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result)
|
||||||
@@ -279,6 +301,11 @@ void p2pool::handle_miner_data(MinerData& data)
|
|||||||
"\n---------------------------------------------------------------------------------------------------------------"
|
"\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()) {
|
if (!is_main_thread()) {
|
||||||
update_block_template_async();
|
update_block_template_async();
|
||||||
}
|
}
|
||||||
@@ -294,7 +321,8 @@ void p2pool::handle_miner_data(MinerData& data)
|
|||||||
WriteLock lock(m_mainchainLock);
|
WriteLock lock(m_mainchainLock);
|
||||||
|
|
||||||
for (uint64_t h = data.height; h && (h + BLOCK_HEADERS_REQUIRED > data.height); --h) {
|
for (uint64_t h = data.height; h && (h + BLOCK_HEADERS_REQUIRED > data.height); --h) {
|
||||||
if (m_mainchainByHeight.find(h) == m_mainchainByHeight.end()) {
|
auto it = m_mainchainByHeight.find(h);
|
||||||
|
if ((it == m_mainchainByHeight.end()) || it->second.difficulty.empty()) {
|
||||||
LOGWARN(3, "Mainchain data for height " << h << " is missing, requesting it from monerod again");
|
LOGWARN(3, "Mainchain data for height " << h << " is missing, requesting it from monerod again");
|
||||||
missing_heights.push_back(h);
|
missing_heights.push_back(h);
|
||||||
}
|
}
|
||||||
@@ -302,11 +330,11 @@ void p2pool::handle_miner_data(MinerData& data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (uint64_t h : missing_heights) {
|
for (uint64_t h : missing_heights) {
|
||||||
char buf[log::Stream::BUF_SIZE + 1];
|
char buf[log::Stream::BUF_SIZE + 1] = {};
|
||||||
log::Stream s(buf);
|
log::Stream s(buf);
|
||||||
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << h << "}}\0";
|
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << h << "}}\0";
|
||||||
|
|
||||||
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
|
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)
|
[this, h](const char* data, size_t size)
|
||||||
{
|
{
|
||||||
ChainMain block;
|
ChainMain block;
|
||||||
@@ -381,7 +409,7 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
|
|||||||
if (payout) {
|
if (payout) {
|
||||||
LOGINFO(0, log::LightCyan() << "You received a payout of " << log::LightGreen() << log::XMRAmount(payout) << log::LightCyan() << " in block " << log::LightGreen() << data.height);
|
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 {
|
else {
|
||||||
side_chain().watch_mainchain_block(data, sidechain_id);
|
side_chain().watch_mainchain_block(data, sidechain_id);
|
||||||
@@ -404,13 +432,20 @@ void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t e
|
|||||||
m_submitBlockData.blob.clear();
|
m_submitBlockData.blob.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If p2pool is stopped, m_submitBlockAsync is most likely already closed
|
||||||
|
if (m_stopped) {
|
||||||
|
LOGWARN(0, "p2pool is shutting down, but a block was found. Trying to submit it anyway!");
|
||||||
|
submit_block();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const int err = uv_async_send(&m_submitBlockAsync);
|
const int err = uv_async_send(&m_submitBlockAsync);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOGERR(1, "uv_async_send failed, error " << uv_err_name(err));
|
LOGERR(1, "uv_async_send failed, error " << uv_err_name(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
MutexLock lock(m_submitBlockDataLock);
|
||||||
@@ -418,7 +453,14 @@ void p2pool::submit_block_async(const std::vector<uint8_t>& blob)
|
|||||||
m_submitBlockData.template_id = 0;
|
m_submitBlockData.template_id = 0;
|
||||||
m_submitBlockData.nonce = 0;
|
m_submitBlockData.nonce = 0;
|
||||||
m_submitBlockData.extra_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
|
||||||
|
if (m_stopped) {
|
||||||
|
LOGWARN(0, "p2pool is shutting down, but a block was found. Trying to submit it anyway!");
|
||||||
|
submit_block();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int err = uv_async_send(&m_submitBlockAsync);
|
const int err = uv_async_send(&m_submitBlockAsync);
|
||||||
@@ -427,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)
|
void p2pool::on_stop(uv_async_t* async)
|
||||||
{
|
{
|
||||||
p2pool* pool = reinterpret_cast<p2pool*>(async->data);
|
p2pool* pool = reinterpret_cast<p2pool*>(async->data);
|
||||||
|
|
||||||
|
delete pool->m_consoleCommands;
|
||||||
|
|
||||||
if (pool->m_api) {
|
if (pool->m_api) {
|
||||||
pool->m_api->on_stop();
|
pool->m_api->on_stop();
|
||||||
}
|
}
|
||||||
@@ -438,7 +484,13 @@ void p2pool::on_stop(uv_async_t* async)
|
|||||||
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_submitBlockAsync), nullptr);
|
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_submitBlockAsync), nullptr);
|
||||||
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_blockTemplateAsync), nullptr);
|
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_blockTemplateAsync), nullptr);
|
||||||
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_stopAsync), nullptr);
|
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_stopAsync), nullptr);
|
||||||
uv_stop(uv_default_loop());
|
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);
|
||||||
|
loop->data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void p2pool::submit_block() const
|
void p2pool::submit_block() const
|
||||||
@@ -498,7 +550,7 @@ void p2pool::submit_block() const
|
|||||||
}
|
}
|
||||||
request.append("\"]}");
|
request.append("\"]}");
|
||||||
|
|
||||||
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, request.c_str(),
|
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)
|
[height, diff, template_id, nonce, extra_nonce, is_external](const char* data, size_t size)
|
||||||
{
|
{
|
||||||
rapidjson::Document doc;
|
rapidjson::Document doc;
|
||||||
@@ -550,7 +602,7 @@ void p2pool::submit_block() const
|
|||||||
LOGWARN(3, "submit_block (external blob): RPC request failed, error " << log::const_buf(data, size));
|
LOGWARN(3, "submit_block (external blob): RPC request failed, error " << log::const_buf(data, size));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOGERR(0, "submit_block (external blob): RPC request failed, error " << log::const_buf(data, size));
|
LOGERR(0, "submit_block: RPC request failed, error " << log::const_buf(data, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -562,8 +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);
|
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);
|
const int err = uv_async_send(&m_blockTemplateAsync);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOGERR(1, "uv_async_send failed, error " << uv_err_name(err));
|
LOGERR(1, "uv_async_send failed, error " << uv_err_name(err));
|
||||||
@@ -581,6 +640,16 @@ void p2pool::update_block_template()
|
|||||||
m_blockTemplate->update(data, *m_mempool, &m_params->m_wallet);
|
m_blockTemplate->update(data, *m_mempool, &m_params->m_wallet);
|
||||||
stratum_on_block();
|
stratum_on_block();
|
||||||
api_update_pool_stats();
|
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)
|
void p2pool::download_block_headers(uint64_t current_height)
|
||||||
@@ -588,7 +657,7 @@ void p2pool::download_block_headers(uint64_t current_height)
|
|||||||
const uint64_t seed_height = get_seed_height(current_height);
|
const uint64_t seed_height = get_seed_height(current_height);
|
||||||
const uint64_t prev_seed_height = (seed_height > SEEDHASH_EPOCH_BLOCKS) ? (seed_height - SEEDHASH_EPOCH_BLOCKS) : 0;
|
const uint64_t prev_seed_height = (seed_height > SEEDHASH_EPOCH_BLOCKS) ? (seed_height - SEEDHASH_EPOCH_BLOCKS) : 0;
|
||||||
|
|
||||||
char buf[log::Stream::BUF_SIZE + 1];
|
char buf[log::Stream::BUF_SIZE + 1] = {};
|
||||||
log::Stream s(buf);
|
log::Stream s(buf);
|
||||||
|
|
||||||
// First download 2 RandomX seeds
|
// First download 2 RandomX seeds
|
||||||
@@ -597,7 +666,7 @@ void p2pool::download_block_headers(uint64_t current_height)
|
|||||||
s.m_pos = 0;
|
s.m_pos = 0;
|
||||||
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << height << "}}\0";
|
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << height << "}}\0";
|
||||||
|
|
||||||
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
|
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)
|
[this, prev_seed_height, height](const char* data, size_t size)
|
||||||
{
|
{
|
||||||
ChainMain block;
|
ChainMain block;
|
||||||
@@ -626,13 +695,20 @@ void p2pool::download_block_headers(uint64_t current_height)
|
|||||||
s.m_pos = 0;
|
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";
|
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_headers_range\",\"params\":{\"start_height\":" << start_height << ",\"end_height\":" << current_height - 1 << "}}\0";
|
||||||
|
|
||||||
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
|
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)
|
[this, start_height, current_height](const char* data, size_t size)
|
||||||
{
|
{
|
||||||
if (parse_block_headers_range(data, size) == current_height - start_height) {
|
if (parse_block_headers_range(data, size) == current_height - start_height) {
|
||||||
update_median_timestamp();
|
update_median_timestamp();
|
||||||
if (m_serversStarted.exchange(1) == 0) {
|
if (m_serversStarted.exchange(1) == 0) {
|
||||||
m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this);
|
try {
|
||||||
|
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());
|
||||||
|
panic();
|
||||||
|
}
|
||||||
|
|
||||||
m_stratumServer = new StratumServer(this);
|
m_stratumServer = new StratumServer(this);
|
||||||
m_p2pServer = new P2PServer(this);
|
m_p2pServer = new P2PServer(this);
|
||||||
#ifdef WITH_RANDOMX
|
#ifdef WITH_RANDOMX
|
||||||
@@ -727,7 +803,7 @@ void p2pool::stratum_on_block()
|
|||||||
|
|
||||||
void p2pool::get_info()
|
void p2pool::get_info()
|
||||||
{
|
{
|
||||||
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_info\"}",
|
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)
|
[this](const char* data, size_t size)
|
||||||
{
|
{
|
||||||
parse_get_info_rpc(data, size);
|
parse_get_info_rpc(data, size);
|
||||||
@@ -736,8 +812,10 @@ void p2pool::get_info()
|
|||||||
{
|
{
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
LOGWARN(1, "get_info RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second");
|
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));
|
if (!m_stopped) {
|
||||||
get_info();
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
|
get_info();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -753,34 +831,39 @@ void p2pool::load_found_blocks()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!f.eof()) {
|
while (f.good()) {
|
||||||
time_t timestamp;
|
time_t timestamp;
|
||||||
f >> timestamp;
|
f >> timestamp;
|
||||||
if (f.eof()) break;
|
if (!f.good()) break;
|
||||||
|
|
||||||
uint64_t height;
|
uint64_t height;
|
||||||
f >> height;
|
f >> height;
|
||||||
if (f.eof()) break;
|
if (!f.good()) break;
|
||||||
|
|
||||||
hash id;
|
hash id;
|
||||||
f >> id;
|
f >> id;
|
||||||
if (f.eof()) break;
|
if (!f.good()) break;
|
||||||
|
|
||||||
difficulty_type block_difficulty;
|
difficulty_type block_difficulty;
|
||||||
f >> block_difficulty;
|
f >> block_difficulty;
|
||||||
if (f.eof()) break;
|
if (!f.good()) break;
|
||||||
|
|
||||||
difficulty_type cumulative_difficulty;
|
difficulty_type cumulative_difficulty;
|
||||||
f >> cumulative_difficulty;
|
f >> cumulative_difficulty;
|
||||||
|
if (!f.good() && !f.eof()) break;
|
||||||
|
|
||||||
m_foundBlocks.emplace_back(timestamp, height, id, block_difficulty, cumulative_difficulty);
|
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)
|
void p2pool::parse_get_info_rpc(const char* data, size_t size)
|
||||||
{
|
{
|
||||||
|
if (m_stopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
rapidjson::Document doc;
|
rapidjson::Document doc;
|
||||||
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
|
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
|
||||||
|
|
||||||
@@ -833,7 +916,7 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
|
|||||||
|
|
||||||
void p2pool::get_version()
|
void p2pool::get_version()
|
||||||
{
|
{
|
||||||
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_version\"}",
|
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)
|
[this](const char* data, size_t size)
|
||||||
{
|
{
|
||||||
parse_get_version_rpc(data, size);
|
parse_get_version_rpc(data, size);
|
||||||
@@ -842,14 +925,20 @@ void p2pool::get_version()
|
|||||||
{
|
{
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
LOGWARN(1, "get_version RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second");
|
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));
|
if (!m_stopped) {
|
||||||
get_version();
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
|
get_version();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void p2pool::parse_get_version_rpc(const char* data, size_t size)
|
void p2pool::parse_get_version_rpc(const char* data, size_t size)
|
||||||
{
|
{
|
||||||
|
if (m_stopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
rapidjson::Document doc;
|
rapidjson::Document doc;
|
||||||
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
|
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
|
||||||
|
|
||||||
@@ -879,14 +968,16 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
|
|||||||
return;
|
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) {
|
if (version < required) {
|
||||||
const uint64_t version_hi = version >> 16;
|
const uint64_t version_hi = version >> 16;
|
||||||
const uint64_t version_lo = version & 65535;
|
const uint64_t version_lo = version & 65535;
|
||||||
const uint64_t required_version_hi = required >> 16;
|
const uint64_t required_version_hi = required >> 16;
|
||||||
const uint64_t required_version_lo = required & 65535;
|
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();
|
panic();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -895,7 +986,9 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
|
|||||||
|
|
||||||
void p2pool::get_miner_data()
|
void p2pool::get_miner_data()
|
||||||
{
|
{
|
||||||
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"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, m_params->m_socks5Proxy,
|
||||||
[this](const char* data, size_t size)
|
[this](const char* data, size_t size)
|
||||||
{
|
{
|
||||||
parse_get_miner_data_rpc(data, size);
|
parse_get_miner_data_rpc(data, size);
|
||||||
@@ -904,14 +997,31 @@ void p2pool::get_miner_data()
|
|||||||
{
|
{
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
LOGWARN(1, "get_miner_data RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second");
|
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));
|
if (!m_stopped) {
|
||||||
get_miner_data();
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
|
get_miner_data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_getMinerDataPending = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void p2pool::parse_get_miner_data_rpc(const char* data, size_t size)
|
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) {
|
||||||
|
LOGWARN(4, "Received a duplicate get_miner_data RPC response, ignoring it");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_getMinerDataHash = h;
|
||||||
|
|
||||||
rapidjson::Document doc;
|
rapidjson::Document doc;
|
||||||
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
|
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
|
||||||
|
|
||||||
@@ -1215,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) {
|
if (!m_api) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const time_t cur_time = time(nullptr);
|
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;
|
difficulty_type diff;
|
||||||
|
|
||||||
if (data && get_difficulty_at_height(data->height, diff)) {
|
if (data && get_difficulty_at_height(data->height, diff)) {
|
||||||
std::ofstream f(FOUND_BLOCKS_FILE, std::ios::app);
|
std::ofstream f(FOUND_BLOCKS_FILE, std::ios::app);
|
||||||
if (f.is_open()) {
|
if (f.is_open()) {
|
||||||
f << cur_time << ' ' << data->height << ' ' << data->id << ' ' << diff << ' ' << total_hashes << '\n';
|
f << cur_time << ' ' << data->height << ' ' << data->id << ' ' << diff << ' ' << total_hashes << '\n';
|
||||||
|
f.flush();
|
||||||
|
f.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1338,7 +1448,7 @@ static bool init_uv_threadpool()
|
|||||||
return (uv_queue_work(uv_default_loop_checked(), &dummy, [](uv_work_t*) {}, nullptr) == 0);
|
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
|
#ifdef SIGPIPE
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
@@ -1358,6 +1468,14 @@ static bool init_signals(p2pool* pool)
|
|||||||
|
|
||||||
static uv_signal_t signals[array_size(signal_names)];
|
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) {
|
for (size_t i = 0; i < array_size(signal_names); ++i) {
|
||||||
uv_signal_init(uv_default_loop_checked(), &signals[i]);
|
uv_signal_init(uv_default_loop_checked(), &signals[i]);
|
||||||
signals[i].data = pool;
|
signals[i].data = pool;
|
||||||
@@ -1373,12 +1491,43 @@ static bool init_signals(p2pool* pool)
|
|||||||
|
|
||||||
void p2pool::stop()
|
void p2pool::stop()
|
||||||
{
|
{
|
||||||
uv_async_send(&m_stopAsync);
|
// Can be called only once
|
||||||
|
if (m_stopped.exchange(true) == false) {
|
||||||
|
uv_async_send(&m_stopAsync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void p2pool::restart_zmq()
|
||||||
|
{
|
||||||
|
// If p2pool is stopped, m_restartZMQAsync is most likely already closed
|
||||||
|
if (m_stopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_main_thread()) {
|
||||||
|
uv_async_send(&m_restartZMQAsync);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_getMinerDataPending) {
|
||||||
|
get_miner_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete m_ZMQReader;
|
||||||
|
m_ZMQReader = nullptr;
|
||||||
|
|
||||||
|
try {
|
||||||
|
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) {
|
||||||
|
LOGERR(1, "Couldn't restart ZMQ reader: exception " << e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int p2pool::run()
|
int p2pool::run()
|
||||||
{
|
{
|
||||||
if (!m_params->ok()) {
|
if (!m_params->valid()) {
|
||||||
LOGERR(1, "Invalid or missing command line. Try \"p2pool --help\".");
|
LOGERR(1, "Invalid or missing command line. Try \"p2pool --help\".");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -1388,11 +1537,16 @@ int p2pool::run()
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!init_signals(this)) {
|
if (!init_signals(this, true)) {
|
||||||
LOGERR(1, "failed to initialize signal handlers");
|
LOGERR(1, "failed to initialize signal handlers");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init default loop user data before running it
|
||||||
|
uv_loop_t* loop = uv_default_loop_checked();
|
||||||
|
loop->data = nullptr;
|
||||||
|
GetLoopUserData(loop);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
get_info();
|
get_info();
|
||||||
load_found_blocks();
|
load_found_blocks();
|
||||||
|
|||||||
+14
-5
@@ -33,6 +33,7 @@ class Miner;
|
|||||||
class ConsoleCommands;
|
class ConsoleCommands;
|
||||||
class p2pool_api;
|
class p2pool_api;
|
||||||
class ZMQReader;
|
class ZMQReader;
|
||||||
|
struct PoolBlock;
|
||||||
|
|
||||||
class p2pool : public MinerCallbackHandler
|
class p2pool : public MinerCallbackHandler
|
||||||
{
|
{
|
||||||
@@ -74,17 +75,17 @@ public:
|
|||||||
virtual void handle_chain_main(ChainMain& data, const char* extra) override;
|
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(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 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 update_block_template();
|
||||||
|
|
||||||
void download_block_headers(uint64_t current_height);
|
void download_block_headers(uint64_t current_height);
|
||||||
|
|
||||||
bool chainmain_get_by_hash(const hash& id, ChainMain& data) const;
|
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);
|
bool get_difficulty_at_height(uint64_t height, difficulty_type& diff);
|
||||||
|
|
||||||
@@ -95,6 +96,7 @@ public:
|
|||||||
|
|
||||||
uint64_t zmq_last_active() const { return m_zmqLastActive; }
|
uint64_t zmq_last_active() const { return m_zmqLastActive; }
|
||||||
uint64_t start_time() const { return m_startTime; }
|
uint64_t start_time() const { return m_startTime; }
|
||||||
|
void restart_zmq();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
p2pool(const p2pool&) = delete;
|
p2pool(const p2pool&) = delete;
|
||||||
@@ -103,10 +105,11 @@ private:
|
|||||||
static void on_submit_block(uv_async_t* async) { reinterpret_cast<p2pool*>(async->data)->submit_block(); }
|
static void on_submit_block(uv_async_t* async) { reinterpret_cast<p2pool*>(async->data)->submit_block(); }
|
||||||
static void on_update_block_template(uv_async_t* async) { reinterpret_cast<p2pool*>(async->data)->update_block_template(); }
|
static void on_update_block_template(uv_async_t* async) { reinterpret_cast<p2pool*>(async->data)->update_block_template(); }
|
||||||
static void on_stop(uv_async_t*);
|
static void on_stop(uv_async_t*);
|
||||||
|
static void on_restart_zmq(uv_async_t* async) { reinterpret_cast<p2pool*>(async->data)->restart_zmq(); }
|
||||||
|
|
||||||
void submit_block() const;
|
void submit_block() const;
|
||||||
|
|
||||||
bool m_stopped;
|
std::atomic<bool> m_stopped;
|
||||||
|
|
||||||
Params* m_params;
|
Params* m_params;
|
||||||
|
|
||||||
@@ -140,7 +143,7 @@ private:
|
|||||||
void get_miner_data();
|
void get_miner_data();
|
||||||
void parse_get_miner_data_rpc(const char* data, size_t size);
|
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);
|
uint32_t parse_block_headers_range(const char* data, size_t size);
|
||||||
|
|
||||||
void api_update_network_stats();
|
void api_update_network_stats();
|
||||||
@@ -195,10 +198,16 @@ private:
|
|||||||
uv_async_t m_blockTemplateAsync;
|
uv_async_t m_blockTemplateAsync;
|
||||||
uv_async_t m_stopAsync;
|
uv_async_t m_stopAsync;
|
||||||
|
|
||||||
|
std::atomic<bool> m_isAlternativeBlock{ false };
|
||||||
|
|
||||||
std::atomic<uint64_t> m_zmqLastActive;
|
std::atomic<uint64_t> m_zmqLastActive;
|
||||||
uint64_t m_startTime;
|
uint64_t m_startTime;
|
||||||
|
uv_async_t m_restartZMQAsync;
|
||||||
|
|
||||||
ZMQReader* m_ZMQReader = nullptr;
|
ZMQReader* m_ZMQReader = nullptr;
|
||||||
|
|
||||||
|
hash m_getMinerDataHash;
|
||||||
|
bool m_getMinerDataPending = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace p2pool
|
} // namespace p2pool
|
||||||
|
|||||||
+42
-30
@@ -28,7 +28,9 @@ static constexpr char log_category_prefix[] = "P2Pool API ";
|
|||||||
|
|
||||||
namespace p2pool {
|
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()) {
|
if (m_apiPath.empty()) {
|
||||||
LOGERR(1, "api path is empty");
|
LOGERR(1, "api path is empty");
|
||||||
@@ -101,7 +103,7 @@ void p2pool_api::on_stop()
|
|||||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_dumpToFileAsync), nullptr);
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_dumpToFileAsync), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void p2pool_api::dump_to_file_async_internal(const Category& category, const char* filename, DumpFileCallbackBase&& callback)
|
void p2pool_api::dump_to_file_async_internal(Category category, const char* filename, DumpFileCallbackBase&& callback)
|
||||||
{
|
{
|
||||||
std::vector<char> buf(16384);
|
std::vector<char> buf(16384);
|
||||||
log::Stream s(buf.data(), buf.size());
|
log::Stream s(buf.data(), buf.size());
|
||||||
@@ -131,16 +133,12 @@ void p2pool_api::dump_to_file()
|
|||||||
{
|
{
|
||||||
MutexLock lock(m_dumpDataLock);
|
MutexLock lock(m_dumpDataLock);
|
||||||
data = std::move(m_dumpData);
|
data = std::move(m_dumpData);
|
||||||
// cppcheck-suppress accessMoved
|
|
||||||
m_dumpData.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& it : data) {
|
for (auto& it : data) {
|
||||||
// cppcheck-suppress uninitvar
|
DumpFileWork* work = new DumpFileWork{ {}, 0, it.first, it.first + std::to_string(m_counter), std::move(it.second) };
|
||||||
DumpFileWork* work = new DumpFileWork{ {}, {}, {}, it.first, std::move(it.second) };
|
work->req.data = work;
|
||||||
work->open_req.data = work;
|
++m_counter;
|
||||||
work->write_req.data = work;
|
|
||||||
work->close_req.data = work;
|
|
||||||
|
|
||||||
const int flags = O_WRONLY | O_CREAT | O_TRUNC
|
const int flags = O_WRONLY | O_CREAT | O_TRUNC
|
||||||
#ifdef O_BINARY
|
#ifdef O_BINARY
|
||||||
@@ -148,9 +146,9 @@ void p2pool_api::dump_to_file()
|
|||||||
#endif
|
#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) {
|
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;
|
delete work;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,11 +157,11 @@ void p2pool_api::dump_to_file()
|
|||||||
void p2pool_api::on_fs_open(uv_fs_t* req)
|
void p2pool_api::on_fs_open(uv_fs_t* req)
|
||||||
{
|
{
|
||||||
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
|
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) {
|
if (work->fd < 0) {
|
||||||
LOGWARN(4, "failed to open " << work->name << ", error " << uv_err_name(fd));
|
LOGWARN(4, "failed to open " << work->tmp_name << ", error " << uv_err_name(work->fd));
|
||||||
uv_fs_req_cleanup(req);
|
|
||||||
delete work;
|
delete work;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -172,15 +170,12 @@ void p2pool_api::on_fs_open(uv_fs_t* req)
|
|||||||
buf[0].base = work->buf.data();
|
buf[0].base = work->buf.data();
|
||||||
buf[0].len = static_cast<uint32_t>(work->buf.size());
|
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) {
|
if (result < 0) {
|
||||||
LOGWARN(4, "failed to write to " << work->name << ", error " << uv_err_name(result));
|
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
|
||||||
uv_fs_req_cleanup(req);
|
|
||||||
delete work;
|
delete work;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uv_fs_req_cleanup(req);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void p2pool_api::on_fs_write(uv_fs_t* req)
|
void p2pool_api::on_fs_write(uv_fs_t* req)
|
||||||
@@ -188,18 +183,17 @@ void p2pool_api::on_fs_write(uv_fs_t* req)
|
|||||||
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
|
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
|
||||||
|
|
||||||
if (req->result < 0) {
|
if (req->result < 0) {
|
||||||
LOGWARN(4, "failed to write to " << work->name << ", error " << uv_err_name(static_cast<int>(req->result)));
|
LOGWARN(4, "failed to write to " << work->tmp_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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uv_fs_req_cleanup(req);
|
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)
|
void p2pool_api::on_fs_close(uv_fs_t* req)
|
||||||
@@ -207,7 +201,25 @@ void p2pool_api::on_fs_close(uv_fs_t* req)
|
|||||||
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
|
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
|
||||||
|
|
||||||
if (req->result < 0) {
|
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);
|
uv_fs_req_cleanup(req);
|
||||||
|
|||||||
+9
-5
@@ -34,10 +34,11 @@ public:
|
|||||||
LOCAL,
|
LOCAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// cppcheck-suppress functionConst
|
||||||
void on_stop();
|
void on_stop();
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void set(const Category& category, const char* filename, T&& callback) { dump_to_file_async_internal(category, filename, DumpFileCallback<T>(std::move(callback))); }
|
void set(Category category, const char* filename, T&& callback) { dump_to_file_async_internal(category, filename, DumpFileCallback<T>(std::move(callback))); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void create_dir(const std::string& path);
|
void create_dir(const std::string& path);
|
||||||
@@ -45,11 +46,11 @@ private:
|
|||||||
static void on_dump_to_file(uv_async_t* async) { reinterpret_cast<p2pool_api*>(async->data)->dump_to_file(); }
|
static void on_dump_to_file(uv_async_t* async) { reinterpret_cast<p2pool_api*>(async->data)->dump_to_file(); }
|
||||||
|
|
||||||
struct DumpFileWork {
|
struct DumpFileWork {
|
||||||
uv_fs_t open_req;
|
uv_fs_t req;
|
||||||
uv_fs_t write_req;
|
int fd;
|
||||||
uv_fs_t close_req;
|
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
|
std::string tmp_name;
|
||||||
std::vector<char> buf;
|
std::vector<char> buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,11 +72,12 @@ private:
|
|||||||
T m_callback;
|
T m_callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
void dump_to_file_async_internal(const Category& category, const char* filename, DumpFileCallbackBase&& callback);
|
void dump_to_file_async_internal(Category category, const char* filename, DumpFileCallbackBase&& callback);
|
||||||
void dump_to_file();
|
void dump_to_file();
|
||||||
static void on_fs_open(uv_fs_t* req);
|
static void on_fs_open(uv_fs_t* req);
|
||||||
static void on_fs_write(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_close(uv_fs_t* req);
|
||||||
|
static void on_fs_rename(uv_fs_t* req);
|
||||||
|
|
||||||
std::string m_apiPath;
|
std::string m_apiPath;
|
||||||
std::string m_networkPath;
|
std::string m_networkPath;
|
||||||
@@ -86,6 +88,8 @@ private:
|
|||||||
unordered_map<std::string, std::vector<char>> m_dumpData;
|
unordered_map<std::string, std::vector<char>> m_dumpData;
|
||||||
|
|
||||||
uv_async_t m_dumpToFileAsync;
|
uv_async_t m_dumpToFileAsync;
|
||||||
|
|
||||||
|
uint64_t m_counter;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace p2pool
|
} // namespace p2pool
|
||||||
|
|||||||
+32
-6
@@ -35,12 +35,12 @@ Params::Params(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((strcmp(argv[i], "--rpc-port") == 0) && (i + 1 < argc)) {
|
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;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((strcmp(argv[i], "--zmq-port") == 0) && (i + 1 < argc)) {
|
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;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,12 +106,12 @@ Params::Params(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((strcmp(argv[i], "--out-peers") == 0) && (i + 1 < argc)) {
|
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;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((strcmp(argv[i], "--in-peers") == 0) && (i + 1 < argc)) {
|
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;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,10 +125,36 @@ Params::Params(int argc, char* argv[])
|
|||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcmp(argv[i], "--no-autodiff") == 0) {
|
||||||
|
m_autoDiff = false;
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((strcmp(argv[i], "--rpc-login") == 0) && (i + 1 < argc)) {
|
||||||
|
m_rpcLogin = argv[++i];
|
||||||
|
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) {
|
if (!ok) {
|
||||||
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
|
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
|
||||||
p2pool_usage();
|
p2pool_usage();
|
||||||
panic();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,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();
|
return !m_host.empty() && m_rpcPort && m_zmqPort && m_wallet.valid();
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-2
@@ -25,7 +25,7 @@ struct Params
|
|||||||
{
|
{
|
||||||
Params(int argc, char* argv[]);
|
Params(int argc, char* argv[]);
|
||||||
|
|
||||||
bool ok() const;
|
bool valid() const;
|
||||||
|
|
||||||
std::string m_host = "127.0.0.1";
|
std::string m_host = "127.0.0.1";
|
||||||
uint32_t m_rpcPort = 18081;
|
uint32_t m_rpcPort = 18081;
|
||||||
@@ -45,9 +45,14 @@ struct Params
|
|||||||
bool m_disableRandomX = true;
|
bool m_disableRandomX = true;
|
||||||
#endif
|
#endif
|
||||||
uint32_t m_maxOutgoingPeers = 10;
|
uint32_t m_maxOutgoingPeers = 10;
|
||||||
uint32_t m_maxIncomingPeers = 1000;
|
uint32_t m_maxIncomingPeers = 450;
|
||||||
uint32_t m_minerThreads = 0;
|
uint32_t m_minerThreads = 0;
|
||||||
bool m_mini = false;
|
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
|
} // namespace p2pool
|
||||||
|
|||||||
+139
-85
@@ -29,11 +29,7 @@ static constexpr char log_category_prefix[] = "PoolBlock ";
|
|||||||
namespace p2pool {
|
namespace p2pool {
|
||||||
|
|
||||||
PoolBlock::PoolBlock()
|
PoolBlock::PoolBlock()
|
||||||
: m_mainChainHeaderSize(0)
|
: m_majorVersion(0)
|
||||||
, m_mainChainMinerTxSize(0)
|
|
||||||
, m_mainChainOutputsOffset(0)
|
|
||||||
, m_mainChainOutputsBlobSize(0)
|
|
||||||
, m_majorVersion(0)
|
|
||||||
, m_minorVersion(0)
|
, m_minorVersion(0)
|
||||||
, m_timestamp(0)
|
, m_timestamp(0)
|
||||||
, m_prevId{}
|
, m_prevId{}
|
||||||
@@ -53,16 +49,10 @@ PoolBlock::PoolBlock()
|
|||||||
, m_invalid(false)
|
, m_invalid(false)
|
||||||
, m_broadcasted(false)
|
, m_broadcasted(false)
|
||||||
, m_wantBroadcast(false)
|
, m_wantBroadcast(false)
|
||||||
|
, m_precalculated(false)
|
||||||
, m_localTimestamp(seconds_since_epoch())
|
, m_localTimestamp(seconds_since_epoch())
|
||||||
{
|
{
|
||||||
uv_mutex_init_checked(&m_lock);
|
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)
|
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!");
|
LOGERR(1, "operator= uv_mutex_trylock failed. Fix the code!");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_mainChainData = b.m_mainChainData;
|
#if POOL_BLOCK_DEBUG
|
||||||
m_mainChainHeaderSize = b.m_mainChainHeaderSize;
|
m_mainChainDataDebug = b.m_mainChainDataDebug;
|
||||||
m_mainChainMinerTxSize = b.m_mainChainMinerTxSize;
|
m_sideChainDataDebug = b.m_sideChainDataDebug;
|
||||||
m_mainChainOutputsOffset = b.m_mainChainOutputsOffset;
|
#endif
|
||||||
m_mainChainOutputsBlobSize = b.m_mainChainOutputsBlobSize;
|
|
||||||
m_majorVersion = b.m_majorVersion;
|
m_majorVersion = b.m_majorVersion;
|
||||||
m_minorVersion = b.m_minorVersion;
|
m_minorVersion = b.m_minorVersion;
|
||||||
m_timestamp = b.m_timestamp;
|
m_timestamp = b.m_timestamp;
|
||||||
@@ -99,7 +89,6 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
|
|||||||
m_extraNonceSize = b.m_extraNonceSize;
|
m_extraNonceSize = b.m_extraNonceSize;
|
||||||
m_extraNonce = b.m_extraNonce;
|
m_extraNonce = b.m_extraNonce;
|
||||||
m_transactions = b.m_transactions;
|
m_transactions = b.m_transactions;
|
||||||
m_sideChainData = b.m_sideChainData;
|
|
||||||
m_minerWallet = b.m_minerWallet;
|
m_minerWallet = b.m_minerWallet;
|
||||||
m_txkeySec = b.m_txkeySec;
|
m_txkeySec = b.m_txkeySec;
|
||||||
m_parent = b.m_parent;
|
m_parent = b.m_parent;
|
||||||
@@ -108,12 +97,12 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
|
|||||||
m_difficulty = b.m_difficulty;
|
m_difficulty = b.m_difficulty;
|
||||||
m_cumulativeDifficulty = b.m_cumulativeDifficulty;
|
m_cumulativeDifficulty = b.m_cumulativeDifficulty;
|
||||||
m_sidechainId = b.m_sidechainId;
|
m_sidechainId = b.m_sidechainId;
|
||||||
m_tmpTxExtra = b.m_tmpTxExtra;
|
|
||||||
m_depth = b.m_depth;
|
m_depth = b.m_depth;
|
||||||
m_verified = b.m_verified;
|
m_verified = b.m_verified;
|
||||||
m_invalid = b.m_invalid;
|
m_invalid = b.m_invalid;
|
||||||
m_broadcasted = b.m_broadcasted;
|
m_broadcasted = b.m_broadcasted;
|
||||||
m_wantBroadcast = b.m_wantBroadcast;
|
m_wantBroadcast = b.m_wantBroadcast;
|
||||||
|
m_precalculated = b.m_precalculated;
|
||||||
|
|
||||||
m_localTimestamp = seconds_since_epoch();
|
m_localTimestamp = seconds_since_epoch();
|
||||||
|
|
||||||
@@ -129,104 +118,164 @@ PoolBlock::~PoolBlock()
|
|||||||
uv_mutex_destroy(&m_lock);
|
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);
|
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
|
// Header
|
||||||
m_mainChainData.push_back(m_majorVersion);
|
data.push_back(m_majorVersion);
|
||||||
m_mainChainData.push_back(m_minorVersion);
|
data.push_back(m_minorVersion);
|
||||||
writeVarint(m_timestamp, m_mainChainData);
|
writeVarint(m_timestamp, data);
|
||||||
m_mainChainData.insert(m_mainChainData.end(), m_prevId.h, m_prevId.h + HASH_SIZE);
|
data.insert(data.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.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
|
// Miner tx
|
||||||
m_mainChainData.push_back(TX_VERSION);
|
data.push_back(TX_VERSION);
|
||||||
writeVarint(m_txinGenHeight + MINER_REWARD_UNLOCK_TIME, m_mainChainData);
|
writeVarint(m_txinGenHeight + MINER_REWARD_UNLOCK_TIME, data);
|
||||||
m_mainChainData.push_back(1);
|
data.push_back(1);
|
||||||
m_mainChainData.push_back(TXIN_GEN);
|
data.push_back(TXIN_GEN);
|
||||||
writeVarint(m_txinGenHeight, m_mainChainData);
|
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) {
|
const uint8_t tx_type = get_tx_type();
|
||||||
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);
|
|
||||||
|
|
||||||
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
|
for (const TxOutput& output : m_outputs) {
|
||||||
m_mainChainData.push_back(output.m_viewTag);
|
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;
|
if (outputs_blob_size) {
|
||||||
|
*outputs_blob_size = static_cast<int>(data.size()) - outputs_offset0;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_tmpTxExtra.push_back(TX_EXTRA_MERGE_MINING_TAG);
|
uint8_t tx_extra[128];
|
||||||
writeVarint(HASH_SIZE, m_tmpTxExtra);
|
uint8_t* p = tx_extra;
|
||||||
m_tmpTxExtra.insert(m_tmpTxExtra.end(), sidechain_hash.h, sidechain_hash.h + HASH_SIZE);
|
|
||||||
|
|
||||||
writeVarint(m_tmpTxExtra.size(), m_mainChainData);
|
*(p++) = TX_EXTRA_TAG_PUBKEY;
|
||||||
m_mainChainData.insert(m_mainChainData.end(), m_tmpTxExtra.begin(), m_tmpTxExtra.end());
|
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);
|
*(p++) = TX_EXTRA_MERGE_MINING_TAG;
|
||||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(m_transactions.data());
|
*(p++) = HASH_SIZE;
|
||||||
m_mainChainData.insert(m_mainChainData.end(), data + HASH_SIZE, data + m_transactions.size() * 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);
|
MutexLock lock(m_lock);
|
||||||
|
|
||||||
m_sideChainData.clear();
|
data.reserve((m_uncles.size() + 4) * HASH_SIZE + 20);
|
||||||
m_sideChainData.reserve((m_uncles.size() + 4) * HASH_SIZE + 11);
|
|
||||||
|
|
||||||
const hash& spend = m_minerWallet.spend_public_key();
|
const hash& spend = m_minerWallet.spend_public_key();
|
||||||
const hash& view = m_minerWallet.view_public_key();
|
const hash& view = m_minerWallet.view_public_key();
|
||||||
|
|
||||||
m_sideChainData.insert(m_sideChainData.end(), spend.h, spend.h + HASH_SIZE);
|
data.insert(data.end(), spend.h, spend.h + HASH_SIZE);
|
||||||
m_sideChainData.insert(m_sideChainData.end(), view.h, view.h + HASH_SIZE);
|
data.insert(data.end(), view.h, view.h + HASH_SIZE);
|
||||||
m_sideChainData.insert(m_sideChainData.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE);
|
data.insert(data.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(), 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) {
|
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.lo, data);
|
||||||
writeVarint(m_difficulty.hi, m_sideChainData);
|
writeVarint(m_difficulty.hi, data);
|
||||||
|
|
||||||
writeVarint(m_cumulativeDifficulty.lo, m_sideChainData);
|
writeVarint(m_cumulativeDifficulty.lo, data);
|
||||||
writeVarint(m_cumulativeDifficulty.hi, m_sideChainData);
|
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)
|
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);
|
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");
|
LOGERR(1, "tried to calculate PoW of uninitialized block");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
blob_size = m_mainChainHeaderSize;
|
blob_size = header_size;
|
||||||
memcpy(blob, m_mainChainData.data(), blob_size);
|
memcpy(blob, mainchain_data.data(), blob_size);
|
||||||
|
|
||||||
uint8_t* miner_tx = m_mainChainData.data() + m_mainChainHeaderSize;
|
const uint8_t* miner_tx = mainchain_data.data() + header_size;
|
||||||
keccak(miner_tx, static_cast<int>(m_mainChainMinerTxSize) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE);
|
keccak(miner_tx, static_cast<int>(miner_tx_size) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE);
|
||||||
|
|
||||||
count = m_transactions.size();
|
count = m_transactions.size();
|
||||||
uint8_t* h = reinterpret_cast<uint8_t*>(m_transactions.data());
|
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
|
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) {
|
for (size_t i = 0, n = m_outputs.size(); i < n; ++i) {
|
||||||
const TxOutput& out = m_outputs[i];
|
const TxOutput& out = m_outputs[i];
|
||||||
hash eph_public_key;
|
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)) {
|
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;
|
return out.m_reward;
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-21
@@ -63,13 +63,12 @@ struct PoolBlock
|
|||||||
|
|
||||||
mutable uv_mutex_t m_lock;
|
mutable uv_mutex_t m_lock;
|
||||||
|
|
||||||
// Monero block template
|
#if POOL_BLOCK_DEBUG
|
||||||
std::vector<uint8_t> m_mainChainData;
|
std::vector<uint8_t> m_mainChainDataDebug;
|
||||||
size_t m_mainChainHeaderSize;
|
std::vector<uint8_t> m_sideChainDataDebug;
|
||||||
size_t m_mainChainMinerTxSize;
|
#endif
|
||||||
int m_mainChainOutputsOffset;
|
|
||||||
int m_mainChainOutputsBlobSize;
|
|
||||||
|
|
||||||
|
// Monero block template
|
||||||
uint8_t m_majorVersion;
|
uint8_t m_majorVersion;
|
||||||
uint8_t m_minorVersion;
|
uint8_t m_minorVersion;
|
||||||
uint64_t m_timestamp;
|
uint64_t m_timestamp;
|
||||||
@@ -81,15 +80,16 @@ struct PoolBlock
|
|||||||
|
|
||||||
struct TxOutput
|
struct TxOutput
|
||||||
{
|
{
|
||||||
FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey(), m_txType(0), m_viewTag(0) {}
|
FORCEINLINE TxOutput() : m_ephPublicKey(), m_reward(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(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;
|
hash m_ephPublicKey;
|
||||||
uint8_t m_txType;
|
uint64_t m_reward : 56;
|
||||||
uint8_t m_viewTag;
|
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;
|
std::vector<TxOutput> m_outputs;
|
||||||
|
|
||||||
hash m_txkeyPub;
|
hash m_txkeyPub;
|
||||||
@@ -99,9 +99,6 @@ struct PoolBlock
|
|||||||
// All block transaction hashes including the miner transaction hash at index 0
|
// All block transaction hashes including the miner transaction hash at index 0
|
||||||
std::vector<hash> m_transactions;
|
std::vector<hash> m_transactions;
|
||||||
|
|
||||||
// Side-chain data
|
|
||||||
std::vector<uint8_t> m_sideChainData;
|
|
||||||
|
|
||||||
// Miner's wallet
|
// Miner's wallet
|
||||||
Wallet m_minerWallet{ nullptr };
|
Wallet m_minerWallet{ nullptr };
|
||||||
|
|
||||||
@@ -122,22 +119,25 @@ struct PoolBlock
|
|||||||
hash m_sidechainId;
|
hash m_sidechainId;
|
||||||
|
|
||||||
// Just temporary stuff, not a part of the block
|
// Just temporary stuff, not a part of the block
|
||||||
std::vector<uint8_t> m_tmpTxExtra;
|
|
||||||
|
|
||||||
uint64_t m_depth;
|
uint64_t m_depth;
|
||||||
|
|
||||||
bool m_verified;
|
bool m_verified;
|
||||||
bool m_invalid;
|
bool m_invalid;
|
||||||
|
|
||||||
bool m_broadcasted;
|
mutable bool m_broadcasted;
|
||||||
bool m_wantBroadcast;
|
mutable bool m_wantBroadcast;
|
||||||
|
|
||||||
|
bool m_precalculated;
|
||||||
|
|
||||||
uint64_t m_localTimestamp;
|
uint64_t m_localTimestamp;
|
||||||
|
|
||||||
void serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash);
|
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;
|
||||||
void serialize_sidechain_data();
|
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);
|
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;
|
uint64_t get_payout(const Wallet& w) const;
|
||||||
|
|||||||
+47
-48
@@ -23,7 +23,7 @@ namespace p2pool {
|
|||||||
// Since data here can come from external and possibly malicious sources, check everything
|
// 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
|
// 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)
|
// 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 {
|
try {
|
||||||
// Sanity check
|
// 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);
|
const int nonce_offset = static_cast<int>(data - data_begin);
|
||||||
READ_BUF(&m_nonce, NONCE_SIZE);
|
READ_BUF(&m_nonce, NONCE_SIZE);
|
||||||
|
|
||||||
m_mainChainHeaderSize = data - data_begin;
|
|
||||||
|
|
||||||
EXPECT_BYTE(TX_VERSION);
|
EXPECT_BYTE(TX_VERSION);
|
||||||
|
|
||||||
uint64_t unlock_height;
|
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__;
|
if (unlock_height != m_txinGenHeight + MINER_REWARD_UNLOCK_TIME) return __LINE__;
|
||||||
|
|
||||||
std::vector<uint8_t> outputs_blob;
|
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;
|
uint64_t num_outputs;
|
||||||
READ_VARINT(num_outputs);
|
READ_VARINT(num_outputs);
|
||||||
|
|
||||||
uint64_t total_reward = 0;
|
uint64_t total_reward = 0;
|
||||||
|
int outputs_blob_size;
|
||||||
|
|
||||||
if (num_outputs > 0) {
|
if (num_outputs > 0) {
|
||||||
// Outputs are in the buffer, just read them
|
// 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 (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__;
|
if (static_cast<uint64_t>(data_end - data) < num_outputs * MIN_OUTPUT_SIZE) return __LINE__;
|
||||||
|
|
||||||
m_outputs.clear();
|
m_outputs.resize(num_outputs);
|
||||||
m_outputs.reserve(num_outputs);
|
m_outputs.shrink_to_fit();
|
||||||
|
|
||||||
const uint8_t expected_tx_type = get_tx_type();
|
const uint8_t expected_tx_type = get_tx_type();
|
||||||
|
|
||||||
for (uint64_t i = 0; i < num_outputs; ++i) {
|
for (uint64_t i = 0; i < num_outputs; ++i) {
|
||||||
TxOutput t;
|
TxOutput& t = m_outputs[i];
|
||||||
|
|
||||||
READ_VARINT(t.m_reward);
|
uint64_t reward;
|
||||||
total_reward += t.m_reward;
|
READ_VARINT(reward);
|
||||||
|
t.m_reward = reward;
|
||||||
|
total_reward += reward;
|
||||||
|
|
||||||
EXPECT_BYTE(expected_tx_type);
|
EXPECT_BYTE(expected_tx_type);
|
||||||
t.m_txType = expected_tx_type;
|
|
||||||
|
|
||||||
READ_BUF(t.m_ephPublicKey.h, HASH_SIZE);
|
READ_BUF(t.m_ephPublicKey.h, HASH_SIZE);
|
||||||
|
|
||||||
if (expected_tx_type == TXOUT_TO_TAGGED_KEY) {
|
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_size = static_cast<int>(data - data_begin) - outputs_offset;
|
||||||
outputs_blob.assign(data_begin + m_mainChainOutputsOffset, data);
|
outputs_blob.assign(data_begin + outputs_offset, data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Outputs are not in the buffer and must be calculated from sidechain data
|
// 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__;
|
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
|
// 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__;
|
return __LINE__;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int outputs_actual_blob_size = static_cast<int>(data - data_begin) - m_mainChainOutputsOffset;
|
const int outputs_actual_blob_size = static_cast<int>(data - data_begin) - outputs_offset;
|
||||||
|
if (outputs_blob_size < outputs_actual_blob_size) {
|
||||||
if (m_mainChainOutputsBlobSize < outputs_actual_blob_size) {
|
|
||||||
return __LINE__;
|
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;
|
uint64_t tx_extra_size;
|
||||||
READ_VARINT(tx_extra_size);
|
READ_VARINT(tx_extra_size);
|
||||||
@@ -187,13 +186,10 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
|||||||
const int sidechain_hash_offset = static_cast<int>((data - data_begin) + outputs_blob_size_diff);
|
const int sidechain_hash_offset = static_cast<int>((data - data_begin) + outputs_blob_size_diff);
|
||||||
READ_BUF(m_sidechainId.h, HASH_SIZE);
|
READ_BUF(m_sidechainId.h, HASH_SIZE);
|
||||||
|
|
||||||
// cppcheck-suppress duplicateExpression
|
|
||||||
if (static_cast<uint64_t>(data - tx_extra_begin) != tx_extra_size) return __LINE__;
|
if (static_cast<uint64_t>(data - tx_extra_begin) != tx_extra_size) return __LINE__;
|
||||||
|
|
||||||
EXPECT_BYTE(0);
|
EXPECT_BYTE(0);
|
||||||
|
|
||||||
m_mainChainMinerTxSize = (data - data_begin) + outputs_blob_size_diff - m_mainChainHeaderSize;
|
|
||||||
|
|
||||||
uint64_t num_transactions;
|
uint64_t num_transactions;
|
||||||
READ_VARINT(num_transactions);
|
READ_VARINT(num_transactions);
|
||||||
|
|
||||||
@@ -209,12 +205,14 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
|||||||
m_transactions.emplace_back(std::move(id));
|
m_transactions.emplace_back(std::move(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_mainChainData.reserve((data - data_begin) + outputs_blob_size_diff);
|
#if POOL_BLOCK_DEBUG
|
||||||
m_mainChainData.assign(data_begin, data_begin + m_mainChainOutputsOffset);
|
m_mainChainDataDebug.reserve((data - data_begin) + outputs_blob_size_diff);
|
||||||
m_mainChainData.insert(m_mainChainData.end(), m_mainChainOutputsBlobSize, 0);
|
m_mainChainDataDebug.assign(data_begin, data_begin + outputs_offset);
|
||||||
m_mainChainData.insert(m_mainChainData.end(), data_begin + m_mainChainOutputsOffset + outputs_actual_blob_size, data);
|
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;
|
const uint8_t* sidechain_data_begin = data;
|
||||||
|
#endif
|
||||||
|
|
||||||
hash spend_pub_key;
|
hash spend_pub_key;
|
||||||
hash view_pub_key;
|
hash view_pub_key;
|
||||||
@@ -230,6 +228,15 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
|||||||
return __LINE__;
|
return __LINE__;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enforce deterministic tx keys starting from v15
|
||||||
|
if (m_majorVersion >= HARDFORK_VIEW_TAGS_VERSION) {
|
||||||
|
hash pub, sec;
|
||||||
|
get_tx_keys(pub, sec, spend_pub_key, m_prevId);
|
||||||
|
if ((pub != m_txkeyPub) || (sec != m_txkeySec)) {
|
||||||
|
return __LINE__;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
READ_BUF(m_parent.h, HASH_SIZE);
|
READ_BUF(m_parent.h, HASH_SIZE);
|
||||||
|
|
||||||
uint64_t num_uncles;
|
uint64_t num_uncles;
|
||||||
@@ -264,20 +271,22 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
|||||||
return __LINE__;
|
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__;
|
return __LINE__;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (static_cast<int>(outputs_blob.size()) != m_mainChainOutputsBlobSize) {
|
if (static_cast<int>(outputs_blob.size()) != outputs_blob_size) {
|
||||||
return __LINE__;
|
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;
|
hash check;
|
||||||
const std::vector<uint8_t>& consensus_id = sidechain.consensus_id();
|
const std::vector<uint8_t>& consensus_id = sidechain.consensus_id();
|
||||||
keccak_custom(
|
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);
|
uint32_t k = static_cast<uint32_t>(offset - nonce_offset);
|
||||||
if (k < NONCE_SIZE) {
|
if (k < NONCE_SIZE) {
|
||||||
@@ -296,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);
|
const int data_size = static_cast<int>((data_end - data_begin) + outputs_blob_size_diff);
|
||||||
if (offset < data_size) {
|
if (offset < data_size) {
|
||||||
if (offset < m_mainChainOutputsOffset) {
|
if (offset < outputs_offset) {
|
||||||
return data_begin[offset];
|
return data_begin[offset];
|
||||||
}
|
}
|
||||||
else if (offset < m_mainChainOutputsOffset + m_mainChainOutputsBlobSize) {
|
else if (offset < outputs_offset + outputs_blob_size) {
|
||||||
const int tmp = offset - m_mainChainOutputsOffset;
|
const int tmp = offset - outputs_offset;
|
||||||
return outputs_blob[tmp];
|
return outputs_blob[tmp];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -317,7 +326,9 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
|||||||
return __LINE__;
|
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) {
|
catch (std::exception& e) {
|
||||||
const char* msg = e.what();
|
const char* msg = e.what();
|
||||||
@@ -325,19 +336,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
|
|||||||
return __LINE__;
|
return __LINE__;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defaults for off-chain variables
|
reset_offchain_data();
|
||||||
m_tmpTxExtra.clear();
|
|
||||||
|
|
||||||
m_depth = 0;
|
|
||||||
|
|
||||||
m_verified = false;
|
|
||||||
m_invalid = false;
|
|
||||||
|
|
||||||
m_broadcasted = false;
|
|
||||||
m_wantBroadcast = false;
|
|
||||||
|
|
||||||
m_localTimestamp = seconds_since_epoch();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-12
@@ -180,7 +180,6 @@ void RandomX_Hasher::set_seed(const hash& seed)
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// cppcheck-suppress unreadVariable
|
|
||||||
ON_SCOPE_LEAVE([this]() { uv_rwlock_wrunlock(&m_cacheLock); });
|
ON_SCOPE_LEAVE([this]() { uv_rwlock_wrunlock(&m_cacheLock); });
|
||||||
|
|
||||||
if (m_stopped.load()) {
|
if (m_stopped.load()) {
|
||||||
@@ -327,7 +326,6 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*
|
|||||||
{
|
{
|
||||||
// First try to use the dataset if it's ready
|
// First try to use the dataset if it's ready
|
||||||
if (uv_rwlock_tryrdlock(&m_datasetLock) == 0) {
|
if (uv_rwlock_tryrdlock(&m_datasetLock) == 0) {
|
||||||
// cppcheck-suppress unreadVariable
|
|
||||||
ON_SCOPE_LEAVE([this]() { uv_rwlock_rdunlock(&m_datasetLock); });
|
ON_SCOPE_LEAVE([this]() { uv_rwlock_rdunlock(&m_datasetLock); });
|
||||||
|
|
||||||
if (m_stopped.load()) {
|
if (m_stopped.load()) {
|
||||||
@@ -372,6 +370,7 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*
|
|||||||
|
|
||||||
RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
|
RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
|
||||||
: m_pool(pool)
|
: m_pool(pool)
|
||||||
|
, m_loop{}
|
||||||
, m_loopThread{}
|
, m_loopThread{}
|
||||||
{
|
{
|
||||||
int err = uv_loop_init(&m_loop);
|
int err = uv_loop_init(&m_loop);
|
||||||
@@ -380,6 +379,9 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
|
|||||||
panic();
|
panic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init loop user data before running it
|
||||||
|
GetLoopUserData(&m_loop);
|
||||||
|
|
||||||
uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
|
uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
|
||||||
uv_async_init(&m_loop, &m_kickTheLoopAsync, nullptr);
|
uv_async_init(&m_loop, &m_kickTheLoopAsync, nullptr);
|
||||||
m_shutdownAsync.data = this;
|
m_shutdownAsync.data = this;
|
||||||
@@ -387,11 +389,7 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
|
|||||||
uv_mutex_init_checked(&m_requestMutex);
|
uv_mutex_init_checked(&m_requestMutex);
|
||||||
uv_mutex_init_checked(&m_condMutex);
|
uv_mutex_init_checked(&m_condMutex);
|
||||||
|
|
||||||
err = uv_cond_init(&m_cond);
|
uv_cond_init_checked(&m_cond);
|
||||||
if (err) {
|
|
||||||
LOGERR(1, "failed to create cond, error " << uv_err_name(err));
|
|
||||||
panic();
|
|
||||||
}
|
|
||||||
|
|
||||||
err = uv_thread_create(&m_loopThread, loop, this);
|
err = uv_thread_create(&m_loopThread, loop, this);
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -415,9 +413,19 @@ RandomX_Hasher_RPC::~RandomX_Hasher_RPC()
|
|||||||
void RandomX_Hasher_RPC::loop(void* data)
|
void RandomX_Hasher_RPC::loop(void* data)
|
||||||
{
|
{
|
||||||
LOGINFO(1, "event loop started");
|
LOGINFO(1, "event loop started");
|
||||||
|
|
||||||
RandomX_Hasher_RPC* hasher = static_cast<RandomX_Hasher_RPC*>(data);
|
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");
|
LOGINFO(1, "event loop stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,17 +436,19 @@ bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t h
|
|||||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(data_ptr);
|
const uint8_t* data = reinterpret_cast<const uint8_t*>(data_ptr);
|
||||||
const uint8_t major_version = data[0];
|
const uint8_t major_version = data[0];
|
||||||
|
|
||||||
char buf[log::Stream::BUF_SIZE + 1];
|
char buf[log::Stream::BUF_SIZE + 1] = {};
|
||||||
log::Stream s(buf);
|
log::Stream s(buf);
|
||||||
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"calc_pow\",\"params\":{\"major_version\":" << major_version <<
|
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"calc_pow\",\"params\":{\"major_version\":" << major_version <<
|
||||||
",\"height\":" << height <<
|
",\"height\":" << height <<
|
||||||
",\"block_blob\":\"" << log::hex_buf(data, size) << '"' <<
|
",\"block_blob\":\"" << log::hex_buf(data, size) << '"' <<
|
||||||
",\"seed_hash\":\"\"}}";
|
",\"seed_hash\":\"\"}}\0";
|
||||||
|
|
||||||
volatile int result = 0;
|
volatile int result = 0;
|
||||||
volatile bool done = false;
|
volatile bool done = false;
|
||||||
|
|
||||||
JSONRPCRequest::call(m_pool->params().m_host.c_str(), m_pool->params().m_rpcPort, buf,
|
const Params& params = m_pool->params();
|
||||||
|
|
||||||
|
JSONRPCRequest::call(params.m_host, params.m_rpcPort, buf, params.m_rpcLogin, params.m_socks5Proxy,
|
||||||
[&result, &h](const char* data, size_t size)
|
[&result, &h](const char* data, size_t size)
|
||||||
{
|
{
|
||||||
rapidjson::Document doc;
|
rapidjson::Document doc;
|
||||||
|
|||||||
+3
-1
@@ -100,7 +100,7 @@ public:
|
|||||||
explicit RandomX_Hasher_RPC(p2pool* pool);
|
explicit RandomX_Hasher_RPC(p2pool* pool);
|
||||||
~RandomX_Hasher_RPC();
|
~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:
|
private:
|
||||||
static void loop(void* data);
|
static void loop(void* data);
|
||||||
@@ -122,6 +122,8 @@ private:
|
|||||||
RandomX_Hasher_RPC* server = reinterpret_cast<RandomX_Hasher_RPC*>(async->data);
|
RandomX_Hasher_RPC* server = reinterpret_cast<RandomX_Hasher_RPC*>(async->data);
|
||||||
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_shutdownAsync), nullptr);
|
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_shutdownAsync), nullptr);
|
||||||
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_kickTheLoopAsync), nullptr);
|
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_kickTheLoopAsync), nullptr);
|
||||||
|
|
||||||
|
delete GetLoopUserData(&server->m_loop, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+459
-119
@@ -32,26 +32,22 @@
|
|||||||
#include "stratum_server.h"
|
#include "stratum_server.h"
|
||||||
#include "params.h"
|
#include "params.h"
|
||||||
#include "json_parsers.h"
|
#include "json_parsers.h"
|
||||||
|
#include "crypto.h"
|
||||||
#include <rapidjson/document.h>
|
#include <rapidjson/document.h>
|
||||||
#include <rapidjson/istreamwrapper.h>
|
#include <rapidjson/istreamwrapper.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <numeric>
|
#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 char log_category_prefix[] = "SideChain ";
|
||||||
|
|
||||||
constexpr uint64_t MIN_DIFFICULTY = 100000;
|
static constexpr uint64_t MIN_DIFFICULTY = 100000;
|
||||||
constexpr size_t UNCLE_BLOCK_DEPTH = 3;
|
static constexpr size_t UNCLE_BLOCK_DEPTH = 3;
|
||||||
|
|
||||||
static_assert(1 <= UNCLE_BLOCK_DEPTH && UNCLE_BLOCK_DEPTH <= 10, "Invalid UNCLE_BLOCK_DEPTH");
|
static_assert(1 <= UNCLE_BLOCK_DEPTH && UNCLE_BLOCK_DEPTH <= 10, "Invalid UNCLE_BLOCK_DEPTH");
|
||||||
|
|
||||||
|
static constexpr uint64_t MONERO_BLOCK_TIME = 120;
|
||||||
|
|
||||||
namespace p2pool {
|
namespace p2pool {
|
||||||
|
|
||||||
static constexpr uint8_t default_consensus_id[HASH_SIZE] = { 34,175,126,231,181,11,104,146,227,153,218,107,44,108,68,39,178,81,4,212,169,4,142,0,177,110,157,240,68,7,249,24 };
|
static constexpr uint8_t default_consensus_id[HASH_SIZE] = { 34,175,126,231,181,11,104,146,227,153,218,107,44,108,68,39,178,81,4,212,169,4,142,0,177,110,157,240,68,7,249,24 };
|
||||||
@@ -61,12 +57,14 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
|
|||||||
: m_pool(pool)
|
: m_pool(pool)
|
||||||
, m_networkType(type)
|
, m_networkType(type)
|
||||||
, m_chainTip{ nullptr }
|
, m_chainTip{ nullptr }
|
||||||
|
, m_seenWalletsLastPruneTime(0)
|
||||||
, m_poolName(pool_name ? pool_name : "default")
|
, m_poolName(pool_name ? pool_name : "default")
|
||||||
, m_targetBlockTime(10)
|
, m_targetBlockTime(10)
|
||||||
, m_minDifficulty(MIN_DIFFICULTY, 0)
|
, m_minDifficulty(MIN_DIFFICULTY, 0)
|
||||||
, m_chainWindowSize(2160)
|
, m_chainWindowSize(2160)
|
||||||
, m_unclePenalty(20)
|
, m_unclePenalty(20)
|
||||||
, m_curDifficulty(m_minDifficulty)
|
, m_curDifficulty(m_minDifficulty)
|
||||||
|
, m_precalcFinished(false)
|
||||||
{
|
{
|
||||||
LOGINFO(1, log::LightCyan() << "network type = " << m_networkType);
|
LOGINFO(1, log::LightCyan() << "network type = " << m_networkType);
|
||||||
|
|
||||||
@@ -78,12 +76,12 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
|
|||||||
panic();
|
panic();
|
||||||
}
|
}
|
||||||
|
|
||||||
uv_mutex_init_checked(&m_sidechainLock);
|
uv_rwlock_init_checked(&m_sidechainLock);
|
||||||
|
uv_mutex_init_checked(&m_seenWalletsLock);
|
||||||
uv_mutex_init_checked(&m_seenBlocksLock);
|
uv_mutex_init_checked(&m_seenBlocksLock);
|
||||||
|
uv_rwlock_init_checked(&m_curDifficultyLock);
|
||||||
|
|
||||||
m_difficultyData.reserve(m_chainWindowSize);
|
m_difficultyData.reserve(m_chainWindowSize);
|
||||||
m_tmpShares.reserve(m_chainWindowSize * 2);
|
|
||||||
m_tmpRewards.reserve(m_chainWindowSize * 2);
|
|
||||||
|
|
||||||
LOGINFO(1, "generating consensus ID");
|
LOGINFO(1, "generating consensus ID");
|
||||||
|
|
||||||
@@ -158,20 +156,53 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
|
|||||||
memset(buf + 8, '*', HASH_SIZE * 2 - 16);
|
memset(buf + 8, '*', HASH_SIZE * 2 - 16);
|
||||||
m_consensusIdDisplayStr.assign(buf);
|
m_consensusIdDisplayStr.assign(buf);
|
||||||
LOGINFO(1, "consensus ID = " << log::LightCyan() << m_consensusIdDisplayStr.c_str());
|
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()
|
SideChain::~SideChain()
|
||||||
{
|
{
|
||||||
uv_mutex_destroy(&m_sidechainLock);
|
finish_precalc();
|
||||||
|
|
||||||
|
uv_rwlock_destroy(&m_sidechainLock);
|
||||||
|
uv_mutex_destroy(&m_seenWalletsLock);
|
||||||
uv_mutex_destroy(&m_seenBlocksLock);
|
uv_mutex_destroy(&m_seenBlocksLock);
|
||||||
for (auto& it : m_blocksById) {
|
uv_rwlock_destroy(&m_curDifficultyLock);
|
||||||
|
|
||||||
|
for (const auto& it : m_blocksById) {
|
||||||
delete it.second;
|
delete it.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares)
|
void SideChain::fill_sidechain_data(PoolBlock& block, const Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares) const
|
||||||
{
|
{
|
||||||
MutexLock lock(m_sidechainLock);
|
ReadLock lock(m_sidechainLock);
|
||||||
|
|
||||||
block.m_minerWallet = *w;
|
block.m_minerWallet = *w;
|
||||||
block.m_txkeySec = txkeySec;
|
block.m_txkeySec = txkeySec;
|
||||||
@@ -206,7 +237,11 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (uint64_t i = 0, n = std::min<uint64_t>(UNCLE_BLOCK_DEPTH, tip->m_sidechainHeight + 1); i < n; ++i) {
|
for (uint64_t i = 0, n = std::min<uint64_t>(UNCLE_BLOCK_DEPTH, tip->m_sidechainHeight + 1); i < n; ++i) {
|
||||||
for (PoolBlock* uncle : m_blocksByHeight[tip->m_sidechainHeight - i]) {
|
auto it = m_blocksByHeight.find(tip->m_sidechainHeight - i);
|
||||||
|
if (it == m_blocksByHeight.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const PoolBlock* uncle : it->second) {
|
||||||
// Only add verified and valid blocks
|
// Only add verified and valid blocks
|
||||||
if (!uncle || !uncle->m_verified || uncle->m_invalid) {
|
if (!uncle || !uncle->m_verified || uncle->m_invalid) {
|
||||||
continue;
|
continue;
|
||||||
@@ -221,16 +256,13 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
|
|||||||
bool same_chain = false;
|
bool same_chain = false;
|
||||||
do {
|
do {
|
||||||
tmp = tip;
|
tmp = tip;
|
||||||
while (tmp->m_sidechainHeight > uncle->m_sidechainHeight) {
|
while (tmp && (tmp->m_sidechainHeight > uncle->m_sidechainHeight)) {
|
||||||
tmp = get_parent(tmp);
|
tmp = get_parent(tmp);
|
||||||
if (!tmp) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!tmp || (tmp->m_sidechainHeight < uncle->m_sidechainHeight)) {
|
if (!tmp || (tmp->m_sidechainHeight < uncle->m_sidechainHeight)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
PoolBlock* tmp2 = uncle;
|
const PoolBlock* tmp2 = uncle;
|
||||||
for (size_t j = 0; (j < UNCLE_BLOCK_DEPTH) && tmp && tmp2 && (tmp->m_sidechainHeight + UNCLE_BLOCK_DEPTH >= block.m_sidechainHeight); ++j) {
|
for (size_t j = 0; (j < UNCLE_BLOCK_DEPTH) && tmp && tmp2 && (tmp->m_sidechainHeight + UNCLE_BLOCK_DEPTH >= block.m_sidechainHeight); ++j) {
|
||||||
if (tmp->m_parent == tmp2->m_parent) {
|
if (tmp->m_parent == tmp2->m_parent) {
|
||||||
same_chain = true;
|
same_chain = true;
|
||||||
@@ -263,7 +295,7 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
|
|||||||
block.m_uncles.erase(std::unique(block.m_uncles.begin(), block.m_uncles.end()), block.m_uncles.end());
|
block.m_uncles.erase(std::unique(block.m_uncles.begin(), block.m_uncles.end()), block.m_uncles.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
block.m_difficulty = m_curDifficulty;
|
block.m_difficulty = difficulty();
|
||||||
block.m_cumulativeDifficulty = tip->m_cumulativeDifficulty + block.m_difficulty;
|
block.m_cumulativeDifficulty = tip->m_cumulativeDifficulty + block.m_difficulty;
|
||||||
|
|
||||||
for (const hash& uncle_id : block.m_uncles) {
|
for (const hash& uncle_id : block.m_uncles) {
|
||||||
@@ -283,7 +315,7 @@ P2PServer* SideChain::p2pServer() const
|
|||||||
return m_pool ? m_pool->p2p_server() : nullptr;
|
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.clear();
|
||||||
shares.reserve(m_chainWindowSize * 2);
|
shares.reserve(m_chainWindowSize * 2);
|
||||||
@@ -291,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"
|
// Collect shares from each block in the PPLNS window, starting from the "tip"
|
||||||
|
|
||||||
uint64_t block_depth = 0;
|
uint64_t block_depth = 0;
|
||||||
PoolBlock* cur = tip;
|
const PoolBlock* cur = tip;
|
||||||
do {
|
do {
|
||||||
MinerShare cur_share{ cur->m_difficulty.lo, &cur->m_minerWallet };
|
MinerShare cur_share{ cur->m_difficulty.lo, &cur->m_minerWallet };
|
||||||
|
|
||||||
@@ -341,7 +373,7 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
cur = it->second;
|
cur = it->second;
|
||||||
} while (block_depth < m_chainWindowSize);
|
} while (true);
|
||||||
|
|
||||||
// Combine shares with the same wallet addresses
|
// Combine shares with the same wallet addresses
|
||||||
std::sort(shares.begin(), shares.end(), [](const auto& a, const auto& b) { return *a.m_wallet < *b.m_wallet; });
|
std::sort(shares.begin(), shares.end(), [](const auto& a, const auto& b) { return *a.m_wallet < *b.m_wallet; });
|
||||||
@@ -365,6 +397,50 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
|
|||||||
return true;
|
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)
|
bool SideChain::block_seen(const PoolBlock& block)
|
||||||
{
|
{
|
||||||
// Check if it's some old block
|
// Check if it's some old block
|
||||||
@@ -386,8 +462,6 @@ void SideChain::unsee_block(const PoolBlock& block)
|
|||||||
m_seenBlocks.erase(block.m_sidechainId);
|
m_seenBlocks.erase(block.m_sidechainId);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern const char* BLOCK_FOUND;
|
|
||||||
|
|
||||||
bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks)
|
bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks)
|
||||||
{
|
{
|
||||||
if (block.m_difficulty < m_minDifficulty) {
|
if (block.m_difficulty < m_minDifficulty) {
|
||||||
@@ -395,9 +469,10 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool too_low_diff = (block.m_difficulty < m_curDifficulty);
|
const difficulty_type expected_diff = difficulty();
|
||||||
|
bool too_low_diff = (block.m_difficulty < expected_diff);
|
||||||
{
|
{
|
||||||
MutexLock lock(m_sidechainLock);
|
ReadLock lock(m_sidechainLock);
|
||||||
if (m_blocksById.find(block.m_sidechainId) != m_blocksById.end()) {
|
if (m_blocksById.find(block.m_sidechainId) != m_blocksById.end()) {
|
||||||
LOGINFO(4, "add_external_block: block " << block.m_sidechainId << " is already added");
|
LOGINFO(4, "add_external_block: block " << block.m_sidechainId << " is already added");
|
||||||
return true;
|
return true;
|
||||||
@@ -423,7 +498,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
|||||||
LOGINFO(4, "add_external_block: height = " << block.m_sidechainHeight << ", id = " << block.m_sidechainId << ", mainchain height = " << block.m_txinGenHeight);
|
LOGINFO(4, "add_external_block: height = " << block.m_sidechainHeight << ", id = " << block.m_sidechainId << ", mainchain height = " << block.m_txinGenHeight);
|
||||||
|
|
||||||
if (too_low_diff) {
|
if (too_low_diff) {
|
||||||
LOGWARN(4, "add_external_block: block has too low difficulty " << block.m_difficulty << ", expected >= ~" << m_curDifficulty << ". Ignoring it.");
|
LOGWARN(4, "add_external_block: block has too low difficulty " << block.m_difficulty << ", expected >= ~" << expected_diff << ". Ignoring it.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +532,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
|||||||
MinerData miner_data = m_pool->miner_data();
|
MinerData miner_data = m_pool->miner_data();
|
||||||
if ((block.m_prevId == miner_data.prev_id) && miner_data.difficulty.check_pow(pow_hash)) {
|
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");
|
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 {
|
else {
|
||||||
difficulty_type diff;
|
difficulty_type diff;
|
||||||
@@ -466,7 +541,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
|||||||
}
|
}
|
||||||
else if (diff.check_pow(pow_hash)) {
|
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");
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,7 +554,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
|||||||
|
|
||||||
missing_blocks.clear();
|
missing_blocks.clear();
|
||||||
{
|
{
|
||||||
MutexLock lock(m_sidechainLock);
|
WriteLock lock(m_sidechainLock);
|
||||||
if (!block.m_parent.empty() && (m_blocksById.find(block.m_parent) == m_blocksById.end())) {
|
if (!block.m_parent.empty() && (m_blocksById.find(block.m_parent) == m_blocksById.end())) {
|
||||||
missing_blocks.push_back(block.m_parent);
|
missing_blocks.push_back(block.m_parent);
|
||||||
}
|
}
|
||||||
@@ -504,7 +579,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (block_found) {
|
if (block_found) {
|
||||||
m_pool->api_update_block_found(&data);
|
m_pool->api_update_block_found(&data, &block);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_block(block);
|
add_block(block);
|
||||||
@@ -520,8 +595,12 @@ void SideChain::add_block(const PoolBlock& block)
|
|||||||
);
|
);
|
||||||
|
|
||||||
PoolBlock* new_block = new PoolBlock(block);
|
PoolBlock* new_block = new PoolBlock(block);
|
||||||
|
{
|
||||||
|
MutexLock lock(m_seenWalletsLock);
|
||||||
|
m_seenWallets[new_block->m_minerWallet.spend_public_key()] = new_block->m_localTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
MutexLock lock(m_sidechainLock);
|
WriteLock lock(m_sidechainLock);
|
||||||
|
|
||||||
auto result = m_blocksById.insert({ new_block->m_sidechainId, new_block });
|
auto result = m_blocksById.insert({ new_block->m_sidechainId, new_block });
|
||||||
if (!result.second) {
|
if (!result.second) {
|
||||||
@@ -536,6 +615,9 @@ void SideChain::add_block(const PoolBlock& block)
|
|||||||
|
|
||||||
m_blocksByHeight[new_block->m_sidechainHeight].push_back(new_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);
|
update_depths(new_block);
|
||||||
|
|
||||||
if (new_block->m_verified) {
|
if (new_block->m_verified) {
|
||||||
@@ -551,13 +633,11 @@ void SideChain::add_block(const PoolBlock& block)
|
|||||||
else {
|
else {
|
||||||
verify_loop(new_block);
|
verify_loop(new_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_seenWallets[new_block->m_minerWallet.spend_public_key()] = new_block->m_localTimestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PoolBlock* SideChain::find_block(const hash& id)
|
PoolBlock* SideChain::find_block(const hash& id) const
|
||||||
{
|
{
|
||||||
MutexLock lock(m_sidechainLock);
|
ReadLock lock(m_sidechainLock);
|
||||||
|
|
||||||
auto it = m_blocksById.find(id);
|
auto it = m_blocksById.find(id);
|
||||||
if (it != m_blocksById.end()) {
|
if (it != m_blocksById.end()) {
|
||||||
@@ -569,20 +649,25 @@ PoolBlock* SideChain::find_block(const hash& id)
|
|||||||
|
|
||||||
void SideChain::watch_mainchain_block(const ChainMain& data, const hash& possible_id)
|
void SideChain::watch_mainchain_block(const ChainMain& data, const hash& possible_id)
|
||||||
{
|
{
|
||||||
MutexLock lock(m_sidechainLock);
|
WriteLock lock(m_sidechainLock);
|
||||||
m_watchBlock = data;
|
m_watchBlock = data;
|
||||||
m_watchBlockSidechainId = possible_id;
|
m_watchBlockSidechainId = possible_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob)
|
bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob) const
|
||||||
{
|
{
|
||||||
MutexLock lock(m_sidechainLock);
|
ReadLock lock(m_sidechainLock);
|
||||||
|
|
||||||
const PoolBlock* block = nullptr;
|
const PoolBlock* block = nullptr;
|
||||||
|
|
||||||
// Empty hash means we return current sidechain tip
|
// Empty hash means we return current sidechain tip
|
||||||
if (id == hash()) {
|
if (id.empty()) {
|
||||||
block = m_chainTip;
|
block = m_chainTip;
|
||||||
|
|
||||||
|
// Don't return stale chain tip
|
||||||
|
if (block && (block->m_txinGenHeight + 2 < m_pool->miner_data().height)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto it = m_blocksById.find(id);
|
auto it = m_blocksById.find(id);
|
||||||
@@ -595,18 +680,18 @@ bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob)
|
|||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob)
|
bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob, uv_loop_t* loop) const
|
||||||
{
|
{
|
||||||
blob.clear();
|
blob.clear();
|
||||||
|
|
||||||
MutexLock lock(m_sidechainLock);
|
ReadLock lock(m_sidechainLock);
|
||||||
|
|
||||||
auto it = m_blocksById.find(block->m_sidechainId);
|
auto it = m_blocksById.find(block->m_sidechainId);
|
||||||
if (it != m_blocksById.end()) {
|
if (it != m_blocksById.end()) {
|
||||||
@@ -616,13 +701,15 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
|
|||||||
blob.reserve(n * 39 + 64);
|
blob.reserve(n * 39 + 64);
|
||||||
writeVarint(n, blob);
|
writeVarint(n, blob);
|
||||||
|
|
||||||
|
const uint8_t tx_type = b->get_tx_type();
|
||||||
|
|
||||||
for (const PoolBlock::TxOutput& output : b->m_outputs) {
|
for (const PoolBlock::TxOutput& output : b->m_outputs) {
|
||||||
writeVarint(output.m_reward, blob);
|
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);
|
blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
|
||||||
|
|
||||||
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
|
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||||
blob.emplace_back(output.m_viewTag);
|
blob.emplace_back(static_cast<uint8_t>(output.m_viewTag));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -630,11 +717,71 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!get_shares(block, m_tmpShares) || !split_reward(total_reward, m_tmpShares, m_tmpRewards) || (m_tmpRewards.size() != m_tmpShares.size())) {
|
std::vector<MinerShare> tmpShares;
|
||||||
|
std::vector<uint64_t> tmpRewards;
|
||||||
|
|
||||||
|
if (!get_shares(block, tmpShares) || !split_reward(total_reward, tmpShares, tmpRewards) || (tmpRewards.size() != tmpShares.size())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t n = m_tmpShares.size();
|
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);
|
blob.reserve(n * 39 + 64);
|
||||||
|
|
||||||
@@ -647,12 +794,19 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
|
|||||||
|
|
||||||
hash eph_public_key;
|
hash eph_public_key;
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
writeVarint(m_tmpRewards[i], blob);
|
// 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);
|
blob.emplace_back(tx_type);
|
||||||
|
|
||||||
uint8_t view_tag;
|
uint8_t view_tag;
|
||||||
if (!m_tmpShares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key, view_tag)) {
|
if (!tmpShares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key, view_tag)) {
|
||||||
LOGWARN(6, "get_eph_public_key failed at index " << i);
|
LOGWARN(6, "get_eph_public_key failed at index " << i);
|
||||||
}
|
}
|
||||||
blob.insert(blob.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
|
blob.insert(blob.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
|
||||||
@@ -661,24 +815,38 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
|
|||||||
blob.emplace_back(view_tag);
|
blob.emplace_back(view_tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
block->m_outputs.emplace_back(m_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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SideChain::print_status()
|
void SideChain::print_status(bool obtain_sidechain_lock) const
|
||||||
{
|
{
|
||||||
std::vector<hash> blocks_in_window;
|
std::vector<hash> blocks_in_window;
|
||||||
blocks_in_window.reserve(m_chainWindowSize * 9 / 8);
|
blocks_in_window.reserve(m_chainWindowSize * 9 / 8);
|
||||||
|
|
||||||
MutexLock lock(m_sidechainLock);
|
const difficulty_type diff = difficulty();
|
||||||
|
|
||||||
|
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 rem;
|
||||||
uint64_t pool_hashrate = udiv128(m_curDifficulty.hi, m_curDifficulty.lo, m_targetBlockTime, &rem);
|
uint64_t pool_hashrate = udiv128(diff.hi, diff.lo, m_targetBlockTime, &rem);
|
||||||
|
|
||||||
difficulty_type network_diff = m_pool->miner_data().difficulty;
|
difficulty_type network_diff = m_pool->miner_data().difficulty;
|
||||||
uint64_t network_hashrate = udiv128(network_diff.hi, network_diff.lo, 120, &rem);
|
uint64_t network_hashrate = udiv128(network_diff.hi, network_diff.lo, MONERO_BLOCK_TIME, &rem);
|
||||||
|
|
||||||
const PoolBlock* tip = m_chainTip;
|
const PoolBlock* tip = m_chainTip;
|
||||||
|
|
||||||
@@ -736,7 +904,11 @@ void SideChain::print_status()
|
|||||||
if (tip) {
|
if (tip) {
|
||||||
std::sort(blocks_in_window.begin(), blocks_in_window.end());
|
std::sort(blocks_in_window.begin(), blocks_in_window.end());
|
||||||
for (uint64_t i = 0; (i < m_chainWindowSize) && (i <= tip_height); ++i) {
|
for (uint64_t i = 0; (i < m_chainWindowSize) && (i <= tip_height); ++i) {
|
||||||
for (PoolBlock* block : m_blocksByHeight[tip_height - i]) {
|
auto it = m_blocksByHeight.find(tip_height - i);
|
||||||
|
if (it == m_blocksByHeight.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const PoolBlock* block : it->second) {
|
||||||
if (!std::binary_search(blocks_in_window.begin(), blocks_in_window.end(), block->m_sidechainId)) {
|
if (!std::binary_search(blocks_in_window.begin(), blocks_in_window.end(), block->m_sidechainId)) {
|
||||||
LOGINFO(4, "orphan block at height " << log::Gray() << block->m_sidechainHeight << log::NoColor() << ": " << log::Gray() << block->m_sidechainId);
|
LOGINFO(4, "orphan block at height " << log::Gray() << block->m_sidechainHeight << log::NoColor() << ": " << log::Gray() << block->m_sidechainId);
|
||||||
++total_orphans;
|
++total_orphans;
|
||||||
@@ -747,13 +919,14 @@ void SideChain::print_status()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Wallet w = m_pool->params().m_wallet;
|
const Wallet& w = m_pool->params().m_wallet;
|
||||||
|
const uint8_t tx_type = tip->get_tx_type();
|
||||||
|
|
||||||
hash eph_public_key;
|
hash eph_public_key;
|
||||||
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
|
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
|
||||||
const PoolBlock::TxOutput& out = tip->m_outputs[i];
|
const PoolBlock::TxOutput& out = tip->m_outputs[i];
|
||||||
if (!your_reward) {
|
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)) {
|
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;
|
your_reward = out.m_reward;
|
||||||
}
|
}
|
||||||
@@ -811,6 +984,39 @@ void SideChain::print_status()
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double SideChain::get_reward_share(const Wallet& w) const
|
||||||
|
{
|
||||||
|
uint64_t reward = 0;
|
||||||
|
uint64_t total_reward = 0;
|
||||||
|
{
|
||||||
|
ReadLock lock(m_sidechainLock);
|
||||||
|
|
||||||
|
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 (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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uint8_t view_tag;
|
||||||
|
if (w.get_eph_public_key(tip->m_txkeySec, i, eph_public_key, view_tag) && (out.m_ephPublicKey == eph_public_key)) {
|
||||||
|
reward = out.m_reward;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total_reward += out.m_reward;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total_reward ? (static_cast<double>(reward) / static_cast<double>(total_reward)) : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
difficulty_type SideChain::total_hashes() const
|
difficulty_type SideChain::total_hashes() const
|
||||||
{
|
{
|
||||||
const PoolBlock* tip = m_chainTip;
|
const PoolBlock* tip = m_chainTip;
|
||||||
@@ -821,16 +1027,19 @@ uint64_t SideChain::miner_count()
|
|||||||
{
|
{
|
||||||
const uint64_t cur_time = seconds_since_epoch();
|
const uint64_t cur_time = seconds_since_epoch();
|
||||||
|
|
||||||
MutexLock lock(m_sidechainLock);
|
MutexLock lock(m_seenWalletsLock);
|
||||||
|
|
||||||
// Delete wallets that weren't seen for more than 72 hours and return how many remain
|
// Every 5 minutes, delete wallets that weren't seen for more than 72 hours
|
||||||
for (auto it = m_seenWallets.begin(); it != m_seenWallets.end();) {
|
if (m_seenWalletsLastPruneTime + 5 * 60 <= cur_time) {
|
||||||
if (it->second + 72 * 60 * 60 <= cur_time) {
|
for (auto it = m_seenWallets.begin(); it != m_seenWallets.end();) {
|
||||||
it = m_seenWallets.erase(it);
|
if (it->second + 72 * 60 * 60 < cur_time) {
|
||||||
}
|
it = m_seenWallets.erase(it);
|
||||||
else {
|
}
|
||||||
++it;
|
else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
m_seenWalletsLastPruneTime = cur_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_seenWallets.size();
|
return m_seenWallets.size();
|
||||||
@@ -890,11 +1099,11 @@ bool SideChain::split_reward(uint64_t reward, const std::vector<MinerShare>& sha
|
|||||||
return true;
|
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();
|
difficultyData.clear();
|
||||||
|
|
||||||
PoolBlock* cur = tip;
|
const PoolBlock* cur = tip;
|
||||||
uint64_t oldest_timestamp = std::numeric_limits<uint64_t>::max();
|
uint64_t oldest_timestamp = std::numeric_limits<uint64_t>::max();
|
||||||
|
|
||||||
uint64_t block_depth = 0;
|
uint64_t block_depth = 0;
|
||||||
@@ -1340,6 +1549,8 @@ void SideChain::verify(PoolBlock* block)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint8_t tx_type = block->get_tx_type();
|
||||||
|
|
||||||
for (size_t i = 0, n = rewards.size(); i < n; ++i) {
|
for (size_t i = 0, n = rewards.size(); i < n; ++i) {
|
||||||
const PoolBlock::TxOutput& out = block->m_outputs[i];
|
const PoolBlock::TxOutput& out = block->m_outputs[i];
|
||||||
|
|
||||||
@@ -1363,7 +1574,7 @@ void SideChain::verify(PoolBlock* block)
|
|||||||
return;
|
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 <<
|
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
|
||||||
", id = " << block->m_sidechainId <<
|
", id = " << block->m_sidechainId <<
|
||||||
", mainchain height = " << block->m_txinGenHeight <<
|
", mainchain height = " << block->m_txinGenHeight <<
|
||||||
@@ -1386,7 +1597,7 @@ void SideChain::verify(PoolBlock* block)
|
|||||||
block->m_invalid = false;
|
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) {
|
if (!block->m_verified || block->m_invalid) {
|
||||||
LOGERR(1, "trying to update chain tip to an unverified or invalid block, fix the code!");
|
LOGERR(1, "trying to update chain tip to an unverified or invalid block, fix the code!");
|
||||||
@@ -1404,21 +1615,29 @@ void SideChain::update_chain_tip(PoolBlock* block)
|
|||||||
if (is_longer_chain(tip, block, is_alternative)) {
|
if (is_longer_chain(tip, block, is_alternative)) {
|
||||||
difficulty_type diff;
|
difficulty_type diff;
|
||||||
if (get_difficulty(block, m_difficultyData, diff)) {
|
if (get_difficulty(block, m_difficultyData, diff)) {
|
||||||
m_chainTip = block;
|
m_chainTip = const_cast<PoolBlock*>(block);
|
||||||
m_curDifficulty = diff;
|
{
|
||||||
|
WriteLock lock(m_curDifficultyLock);
|
||||||
|
m_curDifficulty = diff;
|
||||||
|
}
|
||||||
|
|
||||||
LOGINFO(2, "new chain tip: next height = " << log::Gray() << block->m_sidechainHeight + 1 << log::NoColor() <<
|
LOGINFO(2, "new chain tip: next height = " << log::Gray() << block->m_sidechainHeight + 1 << log::NoColor() <<
|
||||||
", next difficulty = " << log::Gray() << m_curDifficulty << log::NoColor() <<
|
", next difficulty = " << log::Gray() << diff << log::NoColor() <<
|
||||||
", main chain height = " << log::Gray() << block->m_txinGenHeight);
|
", main chain height = " << log::Gray() << block->m_txinGenHeight);
|
||||||
|
|
||||||
block->m_wantBroadcast = true;
|
block->m_wantBroadcast = true;
|
||||||
if (m_pool) {
|
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
|
// Reset stratum share counters when switching to an alternative chain to avoid confusion
|
||||||
StratumServer* s = m_pool->stratum_server();
|
if (is_alternative) {
|
||||||
if (s && is_alternative) {
|
StratumServer* s = m_pool->stratum_server();
|
||||||
s->reset_share_counters();
|
if (s) {
|
||||||
|
s->reset_share_counters();
|
||||||
|
}
|
||||||
|
// Also clear cache because it has data from all old blocks now
|
||||||
|
clear_crypto_cache();
|
||||||
|
LOGINFO(0, log::LightCyan() << "SYNCHRONIZED");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prune_old_blocks();
|
prune_old_blocks();
|
||||||
@@ -1438,40 +1657,11 @@ void SideChain::update_chain_tip(PoolBlock* block)
|
|||||||
|
|
||||||
if (p2pServer() && block->m_wantBroadcast && !block->m_broadcasted) {
|
if (p2pServer() && block->m_wantBroadcast && !block->m_broadcasted) {
|
||||||
block->m_broadcasted = true;
|
block->m_broadcasted = true;
|
||||||
#ifdef DEBUG_BROADCAST_DELAY_MS
|
|
||||||
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);
|
p2pServer()->broadcast(*block);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PoolBlock* SideChain::get_parent(const PoolBlock* block)
|
PoolBlock* SideChain::get_parent(const PoolBlock* block) const
|
||||||
{
|
{
|
||||||
if (block) {
|
if (block) {
|
||||||
auto it = m_blocksById.find(block->m_parent);
|
auto it = m_blocksById.find(block->m_parent);
|
||||||
@@ -1491,13 +1681,15 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!block) {
|
if (!block) {
|
||||||
|
// Switching from an empty to a non-empty chain
|
||||||
|
is_alternative = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If these two blocks are on the same chain, they must have a common ancestor
|
// If these two blocks are on the same chain, they must have a common ancestor
|
||||||
|
|
||||||
const PoolBlock* block_ancestor = block;
|
const PoolBlock* block_ancestor = block;
|
||||||
while (block_ancestor->m_sidechainHeight > candidate->m_sidechainHeight) {
|
while (block_ancestor && (block_ancestor->m_sidechainHeight > candidate->m_sidechainHeight)) {
|
||||||
const hash& id = block_ancestor->m_parent;
|
const hash& id = block_ancestor->m_parent;
|
||||||
block_ancestor = get_parent(block_ancestor);
|
block_ancestor = get_parent(block_ancestor);
|
||||||
if (!block_ancestor) {
|
if (!block_ancestor) {
|
||||||
@@ -1517,6 +1709,7 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cppcheck-suppress knownConditionTrueFalse
|
||||||
while (block_ancestor && candidate_ancestor) {
|
while (block_ancestor && candidate_ancestor) {
|
||||||
if (block_ancestor->m_parent == candidate_ancestor->m_parent) {
|
if (block_ancestor->m_parent == candidate_ancestor->m_parent) {
|
||||||
// If they are really on the same chain, we can just compare cumulative difficulties
|
// If they are really on the same chain, we can just compare cumulative difficulties
|
||||||
@@ -1537,6 +1730,7 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
|
|||||||
const PoolBlock* new_chain = candidate;
|
const PoolBlock* new_chain = candidate;
|
||||||
|
|
||||||
uint64_t candidate_mainchain_height = 0;
|
uint64_t candidate_mainchain_height = 0;
|
||||||
|
uint64_t candidate_mainchain_min_height = 0;
|
||||||
hash mainchain_prev_id;
|
hash mainchain_prev_id;
|
||||||
|
|
||||||
for (uint64_t i = 0; (i < m_chainWindowSize) && (old_chain || new_chain); ++i) {
|
for (uint64_t i = 0; (i < m_chainWindowSize) && (old_chain || new_chain); ++i) {
|
||||||
@@ -1546,6 +1740,7 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (new_chain) {
|
if (new_chain) {
|
||||||
|
candidate_mainchain_min_height = candidate_mainchain_min_height ? std::min(candidate_mainchain_min_height, new_chain->m_txinGenHeight) : new_chain->m_txinGenHeight;
|
||||||
candidate_total_diff += new_chain->m_difficulty;
|
candidate_total_diff += new_chain->m_difficulty;
|
||||||
|
|
||||||
ChainMain data;
|
ChainMain data;
|
||||||
@@ -1569,6 +1764,12 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint64_t limit = m_chainWindowSize * 4 * m_targetBlockTime / MONERO_BLOCK_TIME;
|
||||||
|
if (candidate_mainchain_min_height + limit < data.height) {
|
||||||
|
LOGWARN(3, "received a longer alternative chain but it's stale: min height " << candidate_mainchain_min_height << ", must be >= " << (data.height - limit));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
LOGINFO(3, "received a longer alternative chain: height " <<
|
LOGINFO(3, "received a longer alternative chain: height " <<
|
||||||
log::Gray() << block->m_sidechainHeight << log::NoColor() << " -> " <<
|
log::Gray() << block->m_sidechainHeight << log::NoColor() << " -> " <<
|
||||||
log::Gray() << candidate->m_sidechainHeight << log::NoColor() << ", cumulative difficulty " <<
|
log::Gray() << candidate->m_sidechainHeight << log::NoColor() << ", cumulative difficulty " <<
|
||||||
@@ -1643,7 +1844,7 @@ void SideChain::update_depths(PoolBlock* block)
|
|||||||
void SideChain::prune_old_blocks()
|
void SideChain::prune_old_blocks()
|
||||||
{
|
{
|
||||||
// Leave 2 minutes worth of spare blocks in addition to 2xPPLNS window for lagging nodes which need to sync
|
// Leave 2 minutes worth of spare blocks in addition to 2xPPLNS window for lagging nodes which need to sync
|
||||||
const uint64_t prune_distance = m_chainWindowSize * 2 + 120 / m_targetBlockTime;
|
const uint64_t prune_distance = m_chainWindowSize * 2 + MONERO_BLOCK_TIME / m_targetBlockTime;
|
||||||
|
|
||||||
// Remove old blocks from alternative unconnected chains after long enough time
|
// Remove old blocks from alternative unconnected chains after long enough time
|
||||||
const uint64_t cur_time = seconds_since_epoch();
|
const uint64_t cur_time = seconds_since_epoch();
|
||||||
@@ -1698,14 +1899,17 @@ void SideChain::prune_old_blocks()
|
|||||||
if (p2pServer()) {
|
if (p2pServer()) {
|
||||||
p2pServer()->clear_cached_blocks();
|
p2pServer()->clear_cached_blocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pre-calc workers are not needed anymore
|
||||||
|
finish_precalc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SideChain::get_missing_blocks(std::vector<hash>& missing_blocks)
|
void SideChain::get_missing_blocks(std::vector<hash>& missing_blocks) const
|
||||||
{
|
{
|
||||||
missing_blocks.clear();
|
missing_blocks.clear();
|
||||||
|
|
||||||
MutexLock lock(m_sidechainLock);
|
ReadLock lock(m_sidechainLock);
|
||||||
|
|
||||||
for (auto& b : m_blocksById) {
|
for (auto& b : m_blocksById) {
|
||||||
if (b.second->m_verified) {
|
if (b.second->m_verified) {
|
||||||
@@ -1716,9 +1920,18 @@ void SideChain::get_missing_blocks(std::vector<hash>& missing_blocks)
|
|||||||
missing_blocks.push_back(b.second->m_parent);
|
missing_blocks.push_back(b.second->m_parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int num_missing_uncles = 0;
|
||||||
|
|
||||||
for (const hash& h : b.second->m_uncles) {
|
for (const hash& h : b.second->m_uncles) {
|
||||||
if (!h.empty() && (m_blocksById.find(h) == m_blocksById.end())) {
|
if (!h.empty() && (m_blocksById.find(h) == m_blocksById.end())) {
|
||||||
missing_blocks.push_back(h);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1783,8 +1996,8 @@ bool SideChain::check_config()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((m_targetBlockTime < 1) || (m_targetBlockTime > 120)) {
|
if ((m_targetBlockTime < 1) || (m_targetBlockTime > MONERO_BLOCK_TIME)) {
|
||||||
LOGERR(1, "block_time is invalid (must be between 1 and 120)");
|
LOGERR(1, "block_time is invalid (must be between 1 and " << MONERO_BLOCK_TIME << ")");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1815,4 +2028,131 @@ bool SideChain::check_config()
|
|||||||
return true;
|
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
|
} // namespace p2pool
|
||||||
|
|||||||
+42
-17
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "uv_util.h"
|
#include "uv_util.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace p2pool {
|
namespace p2pool {
|
||||||
|
|
||||||
@@ -31,33 +32,34 @@ class Wallet;
|
|||||||
struct MinerShare
|
struct MinerShare
|
||||||
{
|
{
|
||||||
FORCEINLINE MinerShare() : m_weight(0), m_wallet(nullptr) {}
|
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;
|
uint64_t m_weight;
|
||||||
Wallet* m_wallet;
|
const Wallet* m_wallet;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SideChain
|
class SideChain : public nocopy_nomove
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr);
|
SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr);
|
||||||
~SideChain();
|
~SideChain();
|
||||||
|
|
||||||
void fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares);
|
void fill_sidechain_data(PoolBlock& block, const Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares) const;
|
||||||
|
|
||||||
bool block_seen(const PoolBlock& block);
|
bool block_seen(const PoolBlock& block);
|
||||||
void unsee_block(const PoolBlock& block);
|
void unsee_block(const PoolBlock& block);
|
||||||
bool add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks);
|
bool add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks);
|
||||||
void add_block(const PoolBlock& block);
|
void add_block(const PoolBlock& block);
|
||||||
void get_missing_blocks(std::vector<hash>& missing_blocks);
|
void get_missing_blocks(std::vector<hash>& missing_blocks) const;
|
||||||
|
|
||||||
PoolBlock* find_block(const hash& id);
|
PoolBlock* find_block(const hash& id) const;
|
||||||
void watch_mainchain_block(const ChainMain& data, const hash& possible_id);
|
void watch_mainchain_block(const ChainMain& data, const hash& possible_id);
|
||||||
|
|
||||||
bool get_block_blob(const hash& id, std::vector<uint8_t>& blob);
|
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);
|
bool get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob, uv_loop_t* loop) const;
|
||||||
|
|
||||||
void print_status();
|
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
|
// Consensus ID can be used to spawn independent P2Pools with their own sidechains
|
||||||
// It's never sent over the network to avoid revealing it to the possible man in the middle
|
// It's never sent over the network to avoid revealing it to the possible man in the middle
|
||||||
@@ -65,7 +67,7 @@ public:
|
|||||||
const std::vector<uint8_t>& consensus_id() const { return m_consensusId; }
|
const std::vector<uint8_t>& consensus_id() const { return m_consensusId; }
|
||||||
uint64_t chain_window_size() const { return m_chainWindowSize; }
|
uint64_t chain_window_size() const { return m_chainWindowSize; }
|
||||||
NetworkType network_type() const { return m_networkType; }
|
NetworkType network_type() const { return m_networkType; }
|
||||||
const difficulty_type& difficulty() const { return m_curDifficulty; }
|
FORCEINLINE difficulty_type difficulty() const { ReadLock lock(m_curDifficultyLock); return m_curDifficulty; }
|
||||||
difficulty_type total_hashes() const;
|
difficulty_type total_hashes() const;
|
||||||
uint64_t block_time() const { return m_targetBlockTime; }
|
uint64_t block_time() const { return m_targetBlockTime; }
|
||||||
uint64_t miner_count();
|
uint64_t miner_count();
|
||||||
@@ -74,6 +76,7 @@ public:
|
|||||||
bool is_mini() const;
|
bool is_mini() const;
|
||||||
|
|
||||||
const PoolBlock* chainTip() const { return m_chainTip; }
|
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);
|
static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards);
|
||||||
|
|
||||||
@@ -83,12 +86,13 @@ private:
|
|||||||
NetworkType m_networkType;
|
NetworkType m_networkType;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) const;
|
bool get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares) const;
|
||||||
bool get_difficulty(PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) 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_loop(PoolBlock* block);
|
||||||
void verify(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);
|
PoolBlock* get_parent(const PoolBlock* block) const;
|
||||||
|
|
||||||
// Checks if "candidate" has longer (higher difficulty) chain than "block"
|
// Checks if "candidate" has longer (higher difficulty) chain than "block"
|
||||||
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative);
|
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative);
|
||||||
@@ -98,13 +102,14 @@ private:
|
|||||||
bool load_config(const std::string& filename);
|
bool load_config(const std::string& filename);
|
||||||
bool check_config();
|
bool check_config();
|
||||||
|
|
||||||
mutable uv_mutex_t m_sidechainLock;
|
mutable uv_rwlock_t m_sidechainLock;
|
||||||
std::atomic<PoolBlock*> m_chainTip;
|
std::atomic<PoolBlock*> m_chainTip;
|
||||||
std::map<uint64_t, std::vector<PoolBlock*>> m_blocksByHeight;
|
std::map<uint64_t, std::vector<PoolBlock*>> m_blocksByHeight;
|
||||||
unordered_map<hash, PoolBlock*> m_blocksById;
|
unordered_map<hash, PoolBlock*> m_blocksById;
|
||||||
|
|
||||||
|
uv_mutex_t m_seenWalletsLock;
|
||||||
unordered_map<hash, uint64_t> m_seenWallets;
|
unordered_map<hash, uint64_t> m_seenWallets;
|
||||||
std::vector<MinerShare> m_tmpShares;
|
uint64_t m_seenWalletsLastPruneTime;
|
||||||
std::vector<uint64_t> m_tmpRewards;
|
|
||||||
|
|
||||||
uv_mutex_t m_seenBlocksLock;
|
uv_mutex_t m_seenBlocksLock;
|
||||||
unordered_set<hash> m_seenBlocks;
|
unordered_set<hash> m_seenBlocks;
|
||||||
@@ -121,10 +126,30 @@ private:
|
|||||||
std::vector<uint8_t> m_consensusId;
|
std::vector<uint8_t> m_consensusId;
|
||||||
std::string m_consensusIdDisplayStr;
|
std::string m_consensusIdDisplayStr;
|
||||||
|
|
||||||
|
mutable uv_rwlock_t m_curDifficultyLock;
|
||||||
difficulty_type m_curDifficulty;
|
difficulty_type m_curDifficulty;
|
||||||
|
|
||||||
ChainMain m_watchBlock;
|
ChainMain m_watchBlock;
|
||||||
hash m_watchBlockSidechainId;
|
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
|
} // namespace p2pool
|
||||||
|
|||||||
+282
-108
@@ -27,9 +27,15 @@ static constexpr char log_category_prefix[] = "StratumServer ";
|
|||||||
|
|
||||||
static constexpr int DEFAULT_BACKLOG = 128;
|
static constexpr int DEFAULT_BACKLOG = 128;
|
||||||
static constexpr uint64_t DEFAULT_BAN_TIME = 600;
|
static constexpr uint64_t DEFAULT_BAN_TIME = 600;
|
||||||
|
static constexpr uint64_t MIN_DIFF = 1000;
|
||||||
|
static constexpr uint64_t AUTO_DIFF_TARGET_TIME = 30;
|
||||||
|
|
||||||
// Use short target format (4 bytes) for diff <= 4 million
|
// Use short target format (4 bytes) for diff <= 4 million
|
||||||
static constexpr uint64_t TARGET_4_BYTES_LIMIT = std::numeric_limits<uint64_t>::max() / 4000000;
|
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"
|
#include "tcp_server.inl"
|
||||||
|
|
||||||
@@ -38,7 +44,7 @@ namespace p2pool {
|
|||||||
StratumServer::StratumServer(p2pool* pool)
|
StratumServer::StratumServer(p2pool* pool)
|
||||||
: TCPServer(StratumClient::allocate)
|
: TCPServer(StratumClient::allocate)
|
||||||
, m_pool(pool)
|
, m_pool(pool)
|
||||||
, m_extraNonce(0)
|
, m_autoDiff(pool->params().m_autoDiff)
|
||||||
, m_rng(RandomDeviceSeed::instance)
|
, m_rng(RandomDeviceSeed::instance)
|
||||||
, m_cumulativeHashes(0)
|
, m_cumulativeHashes(0)
|
||||||
, m_cumulativeHashesAtLastShare(0)
|
, m_cumulativeHashesAtLastShare(0)
|
||||||
@@ -53,11 +59,12 @@ StratumServer::StratumServer(p2pool* pool)
|
|||||||
// Diffuse the initial state in case it has low quality
|
// Diffuse the initial state in case it has low quality
|
||||||
m_rng.discard(10000);
|
m_rng.discard(10000);
|
||||||
|
|
||||||
|
m_extraNonce = static_cast<uint32_t>(m_rng());
|
||||||
|
|
||||||
m_hashrateData[0] = { seconds_since_epoch(), 0 };
|
m_hashrateData[0] = { seconds_since_epoch(), 0 };
|
||||||
|
|
||||||
uv_mutex_init_checked(&m_blobsQueueLock);
|
uv_mutex_init_checked(&m_blobsQueueLock);
|
||||||
uv_mutex_init_checked(&m_rngLock);
|
uv_mutex_init_checked(&m_rngLock);
|
||||||
uv_mutex_init_checked(&m_submittedSharesPoolLock);
|
|
||||||
uv_rwlock_init_checked(&m_hashrateDataLock);
|
uv_rwlock_init_checked(&m_hashrateDataLock);
|
||||||
|
|
||||||
m_submittedSharesPool.resize(10);
|
m_submittedSharesPool.resize(10);
|
||||||
@@ -68,7 +75,7 @@ StratumServer::StratumServer(p2pool* pool)
|
|||||||
const int err = uv_async_init(&m_loop, &m_blobsAsync, on_blobs_ready);
|
const int err = uv_async_init(&m_loop, &m_blobsAsync, on_blobs_ready);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
||||||
return;
|
panic();
|
||||||
}
|
}
|
||||||
m_blobsAsync.data = this;
|
m_blobsAsync.data = this;
|
||||||
m_blobsQueue.reserve(2);
|
m_blobsQueue.reserve(2);
|
||||||
@@ -78,13 +85,10 @@ StratumServer::StratumServer(p2pool* pool)
|
|||||||
|
|
||||||
StratumServer::~StratumServer()
|
StratumServer::~StratumServer()
|
||||||
{
|
{
|
||||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_blobsAsync), nullptr);
|
|
||||||
|
|
||||||
shutdown_tcp();
|
shutdown_tcp();
|
||||||
|
|
||||||
uv_mutex_destroy(&m_blobsQueueLock);
|
uv_mutex_destroy(&m_blobsQueueLock);
|
||||||
uv_mutex_destroy(&m_rngLock);
|
uv_mutex_destroy(&m_rngLock);
|
||||||
uv_mutex_destroy(&m_submittedSharesPoolLock);
|
|
||||||
uv_rwlock_destroy(&m_hashrateDataLock);
|
uv_rwlock_destroy(&m_hashrateDataLock);
|
||||||
|
|
||||||
for (SubmittedShare* share : m_submittedSharesPool) {
|
for (SubmittedShare* share : m_submittedSharesPool) {
|
||||||
@@ -102,7 +106,11 @@ void StratumServer::on_block(const BlockTemplate& block)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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{};
|
BlobsData* blobs_data = new BlobsData{};
|
||||||
|
blobs_data->m_extraNonceStart = extra_nonce_start;
|
||||||
|
|
||||||
difficulty_type difficulty;
|
difficulty_type difficulty;
|
||||||
difficulty_type sidechain_difficulty;
|
difficulty_type sidechain_difficulty;
|
||||||
@@ -112,9 +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()
|
// 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
|
// 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_numClientsExpected = num_connections;
|
||||||
m_extraNonce.exchange(blobs_data->m_numClientsExpected);
|
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);
|
||||||
|
|
||||||
blobs_data->m_blobSize = block.get_hashing_blobs(0, blobs_data->m_numClientsExpected, blobs_data->m_blobs, blobs_data->m_height, difficulty, sidechain_difficulty, blobs_data->m_seedHash, nonce_offset, blobs_data->m_templateId);
|
|
||||||
|
|
||||||
// Integrity checks
|
// Integrity checks
|
||||||
if (blobs_data->m_blobSize < 76) {
|
if (blobs_data->m_blobSize < 76) {
|
||||||
@@ -132,7 +138,7 @@ void StratumServer::on_block(const BlockTemplate& block)
|
|||||||
|
|
||||||
// Get first 8 bytes of the Merkle root hash from each blob
|
// Get first 8 bytes of the Merkle root hash from each blob
|
||||||
for (size_t i = 0; i < num_connections; ++i) {
|
for (size_t i = 0; i < num_connections; ++i) {
|
||||||
blob_hashes.emplace_back(*reinterpret_cast<const uint64_t*>(data + i * size + 43));
|
blob_hashes.emplace_back(read_unaligned(reinterpret_cast<const uint64_t*>(data + i * size + 43)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find duplicates
|
// Find duplicates
|
||||||
@@ -178,11 +184,13 @@ void StratumServer::on_block(const BlockTemplate& block)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StratumServer::get_custom_user(const char* s, std::string& user)
|
template<size_t N>
|
||||||
|
static bool get_custom_user(const char* s, char (&user)[N])
|
||||||
{
|
{
|
||||||
user.clear();
|
size_t len = 0;
|
||||||
|
|
||||||
// Find first of '+' or '.', drop non-printable characters
|
// Find first of '+' or '.', drop non-printable characters
|
||||||
while (s && (user.length() < 64)) {
|
while (s && (len < N - 1)) {
|
||||||
const char c = *s;
|
const char c = *s;
|
||||||
if (!c) {
|
if (!c) {
|
||||||
break;
|
break;
|
||||||
@@ -192,15 +200,16 @@ bool StratumServer::get_custom_user(const char* s, std::string& user)
|
|||||||
}
|
}
|
||||||
// Limit to printable ASCII characters
|
// Limit to printable ASCII characters
|
||||||
if (c >= ' ' && c <= '~') {
|
if (c >= ' ' && c <= '~') {
|
||||||
user += c;
|
user[len++] = c;
|
||||||
}
|
}
|
||||||
++s;
|
++s;
|
||||||
}
|
}
|
||||||
|
user[len] = '\0';
|
||||||
|
|
||||||
return !user.empty();
|
return (len > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StratumServer::get_custom_diff(const char* s, difficulty_type& diff)
|
static bool get_custom_diff(const char* s, difficulty_type& diff)
|
||||||
{
|
{
|
||||||
const char* diff_str = nullptr;
|
const char* diff_str = nullptr;
|
||||||
|
|
||||||
@@ -219,8 +228,8 @@ bool StratumServer::get_custom_diff(const char* s, difficulty_type& diff)
|
|||||||
if (diff_str) {
|
if (diff_str) {
|
||||||
const uint64_t t = strtoull(diff_str + 1, nullptr, 10);
|
const uint64_t t = strtoull(diff_str + 1, nullptr, 10);
|
||||||
if (t) {
|
if (t) {
|
||||||
// Don't let clients set difficulty less than 1000
|
// Don't let clients set difficulty less than MIN_DIFF
|
||||||
diff = { std::max<uint64_t>(t + 1, 1000), 0 };
|
diff = { std::max<uint64_t>(t + 1, MIN_DIFF), 0 };
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,18 +257,21 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
|
|||||||
LOGINFO(5, "client " << log::Gray() << static_cast<char*>(client->m_addrString) << " set custom difficulty " << client->m_customDiff);
|
LOGINFO(5, "client " << log::Gray() << static_cast<char*>(client->m_addrString) << " set custom difficulty " << client->m_customDiff);
|
||||||
target = std::max(target, client->m_customDiff.target());
|
target = std::max(target, client->m_customDiff.target());
|
||||||
}
|
}
|
||||||
|
else if (m_autoDiff) {
|
||||||
|
// Limit autodiff to 4000000 for maximum compatibility
|
||||||
|
target = std::max(target, TARGET_4_BYTES_LIMIT);
|
||||||
|
}
|
||||||
|
|
||||||
if (get_custom_user(login, client->m_customUser)) {
|
if (get_custom_user(login, client->m_customUser)) {
|
||||||
LOGINFO(5, "client " << log::Gray() << static_cast<char*>(client->m_addrString) << " set custom user " << client->m_customUser);
|
const char* s = client->m_customUser;
|
||||||
|
LOGINFO(5, "client " << log::Gray() << static_cast<char*>(client->m_addrString) << " set custom user " << s);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t job_id;
|
uint32_t job_id;
|
||||||
{
|
{
|
||||||
MutexLock lock(client->m_jobsLock);
|
job_id = ++client->m_perConnectionJobId;
|
||||||
|
|
||||||
job_id = client->m_perConnectionJobId++;
|
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE];
|
||||||
|
|
||||||
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
|
|
||||||
saved_job.job_id = job_id;
|
saved_job.job_id = job_id;
|
||||||
saved_job.extra_nonce = extra_nonce;
|
saved_job.extra_nonce = extra_nonce;
|
||||||
saved_job.template_id = template_id;
|
saved_job.template_id = template_id;
|
||||||
@@ -267,7 +279,7 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bool result = send(client,
|
const bool result = send(client,
|
||||||
[client, id, &hashing_blob, job_id, blob_size, target, height, &seed_hash](void* buf)
|
[client, id, &hashing_blob, job_id, blob_size, target, height, &seed_hash](void* buf, size_t buf_size)
|
||||||
{
|
{
|
||||||
do {
|
do {
|
||||||
client->m_rpcId = static_cast<uint32_t>(static_cast<StratumServer*>(client->m_owner)->get_random64());
|
client->m_rpcId = static_cast<uint32_t>(static_cast<StratumServer*>(client->m_owner)->get_random64());
|
||||||
@@ -280,7 +292,7 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
|
|||||||
target_hex.m_size -= sizeof(uint32_t);
|
target_hex.m_size -= sizeof(uint32_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::Stream s(reinterpret_cast<char*>(buf));
|
log::Stream s(buf, buf_size);
|
||||||
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"result\":{\"id\":\"";
|
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"result\":{\"id\":\"";
|
||||||
s << log::Hex(client->m_rpcId) << "\",\"job\":{\"blob\":\"";
|
s << log::Hex(client->m_rpcId) << "\",\"job\":{\"blob\":\"";
|
||||||
s << log::hex_buf(hashing_blob, blob_size) << "\",\"job_id\":\"";
|
s << log::hex_buf(hashing_blob, blob_size) << "\",\"job_id\":\"";
|
||||||
@@ -307,6 +319,11 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
|||||||
job_id = (job_id << 4) + d;
|
job_id = (job_id << 4) + d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!job_id) {
|
||||||
|
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " invalid params ('job_id' can't be 0)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t nonce = 0;
|
uint32_t nonce = 0;
|
||||||
|
|
||||||
for (int i = static_cast<int>(sizeof(uint32_t)) - 1; i >= 0; --i) {
|
for (int i = static_cast<int>(sizeof(uint32_t)) - 1; i >= 0; --i) {
|
||||||
@@ -335,9 +352,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
|||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
{
|
{
|
||||||
MutexLock lock(client->m_jobsLock);
|
const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE];
|
||||||
|
|
||||||
const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
|
|
||||||
if (saved_job.job_id == job_id) {
|
if (saved_job.job_id == job_id) {
|
||||||
template_id = saved_job.template_id;
|
template_id = saved_job.template_id;
|
||||||
extra_nonce = saved_job.extra_nonce;
|
extra_nonce = saved_job.extra_nonce;
|
||||||
@@ -348,38 +363,39 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
|||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
BlockTemplate& block = m_pool->block_template();
|
BlockTemplate& block = m_pool->block_template();
|
||||||
|
uint64_t height;
|
||||||
difficulty_type mainchain_diff, sidechain_diff;
|
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");
|
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
|
||||||
return send(client,
|
return send(client,
|
||||||
[id](void* buf)
|
[id](void* buf, size_t buf_size)
|
||||||
{
|
{
|
||||||
log::Stream s(reinterpret_cast<char*>(buf));
|
log::Stream s(buf, buf_size);
|
||||||
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Stale share\"}}\n";
|
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Stale share\"}}\n";
|
||||||
return s.m_pos;
|
return s.m_pos;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mainchain_diff.check_pow(resultHash)) {
|
if (mainchain_diff.check_pow(resultHash)) {
|
||||||
const std::string& s = client->m_customUser;
|
const char* s = client->m_customUser;
|
||||||
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (!s.empty() ? " user " : "") << s << " found a mainchain block, submitting it");
|
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << " found a mainchain block, submitting it");
|
||||||
m_pool->submit_block_async(template_id, nonce, extra_nonce);
|
m_pool->submit_block_async(template_id, nonce, extra_nonce);
|
||||||
block.update_tx_keys();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SubmittedShare* share;
|
SubmittedShare* share;
|
||||||
|
|
||||||
{
|
if (!m_submittedSharesPool.empty()) {
|
||||||
MutexLock lock(m_submittedSharesPoolLock);
|
share = m_submittedSharesPool.back();
|
||||||
|
m_submittedSharesPool.pop_back();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
share = new SubmittedShare{};
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_submittedSharesPool.empty()) {
|
if (target >= TARGET_4_BYTES_LIMIT) {
|
||||||
share = m_submittedSharesPool.back();
|
// "Low diff share" fix: adjust target to the same value as XMRig would use
|
||||||
m_submittedSharesPool.pop_back();
|
target = std::numeric_limits<uint64_t>::max() / (std::numeric_limits<uint32_t>::max() / (target >> 32));
|
||||||
}
|
|
||||||
else {
|
|
||||||
share = new SubmittedShare{};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
share->m_req.data = share;
|
share->m_req.data = share;
|
||||||
@@ -395,9 +411,18 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
|||||||
share->m_target = target;
|
share->m_target = target;
|
||||||
share->m_resultHash = resultHash;
|
share->m_resultHash = resultHash;
|
||||||
share->m_sidechainDifficulty = sidechain_diff;
|
share->m_sidechainDifficulty = sidechain_diff;
|
||||||
|
share->m_mainchainHeight = height;
|
||||||
|
share->m_effort = -1.0;
|
||||||
|
share->m_timestamp = seconds_since_epoch();
|
||||||
|
|
||||||
|
uint64_t rem;
|
||||||
|
share->m_hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 1;
|
||||||
|
share->m_highEnoughDifficulty = sidechain_diff.check_pow(resultHash);
|
||||||
|
|
||||||
|
update_auto_diff(client, share->m_timestamp, share->m_hashes);
|
||||||
|
|
||||||
// If this share is below sidechain difficulty, process it in this thread because it'll be quick
|
// If this share is below sidechain difficulty, process it in this thread because it'll be quick
|
||||||
if (!share->m_sidechainDifficulty.check_pow(share->m_resultHash)) {
|
if (!share->m_highEnoughDifficulty) {
|
||||||
on_share_found(&share->m_req);
|
on_share_found(&share->m_req);
|
||||||
on_after_share_found(&share->m_req, 0);
|
on_after_share_found(&share->m_req, 0);
|
||||||
return true;
|
return true;
|
||||||
@@ -416,12 +441,12 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
|||||||
return true;
|
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,
|
const bool result = send(client,
|
||||||
[id](void* buf)
|
[id](void* buf, size_t buf_size)
|
||||||
{
|
{
|
||||||
log::Stream s(reinterpret_cast<char*>(buf));
|
log::Stream s(buf, buf_size);
|
||||||
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Invalid job id\"}}\n";
|
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Invalid job id\"}}\n";
|
||||||
return s.m_pos;
|
return s.m_pos;
|
||||||
});
|
});
|
||||||
@@ -441,8 +466,53 @@ void StratumServer::print_status()
|
|||||||
print_stratum_status();
|
print_stratum_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StratumServer::show_workers()
|
||||||
|
{
|
||||||
|
const uint64_t cur_time = seconds_since_epoch();
|
||||||
|
const difficulty_type pool_diff = m_pool->side_chain().difficulty();
|
||||||
|
|
||||||
|
MutexLock lock(m_clientsListLock);
|
||||||
|
|
||||||
|
int addr_len = 0;
|
||||||
|
for (const StratumClient* c = static_cast<StratumClient*>(m_connectedClientsList->m_next); c != m_connectedClientsList; c = static_cast<StratumClient*>(c->m_next)) {
|
||||||
|
addr_len = std::max(addr_len, static_cast<int>(strlen(c->m_addrString)));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t n = 0;
|
||||||
|
|
||||||
|
LOGINFO(0, log::pad_right("IP:port", addr_len + 8)
|
||||||
|
<< log::pad_right("uptime", 20)
|
||||||
|
<< log::pad_right("difficulty", 20)
|
||||||
|
<< log::pad_right("hashrate", 15)
|
||||||
|
<< "name"
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const StratumClient* c = static_cast<StratumClient*>(m_connectedClientsList->m_next); c != m_connectedClientsList; c = static_cast<StratumClient*>(c->m_next)) {
|
||||||
|
difficulty_type diff;
|
||||||
|
if (c->m_customDiff != 0) {
|
||||||
|
diff = c->m_customDiff;
|
||||||
|
}
|
||||||
|
else if (m_autoDiff && (c->m_autoDiff != 0)) {
|
||||||
|
diff = c->m_autoDiff;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
diff = pool_diff;
|
||||||
|
}
|
||||||
|
LOGINFO(0, log::pad_right(static_cast<const char*>(c->m_addrString), addr_len + 8)
|
||||||
|
<< log::pad_right(log::Duration(cur_time - c->m_connectedTime), 20)
|
||||||
|
<< log::pad_right(diff, 20)
|
||||||
|
<< log::pad_right(log::Hashrate(c->m_autoDiff.lo / AUTO_DIFF_TARGET_TIME, m_autoDiff && (c->m_autoDiff != 0)), 15)
|
||||||
|
<< (c->m_rpcId ? c->m_customUser : "not logged in")
|
||||||
|
);
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGINFO(0, "Total: " << n << " workers");
|
||||||
|
}
|
||||||
|
|
||||||
void StratumServer::reset_share_counters()
|
void StratumServer::reset_share_counters()
|
||||||
{
|
{
|
||||||
|
m_cumulativeHashes = 0;
|
||||||
m_cumulativeHashesAtLastShare = 0;
|
m_cumulativeHashesAtLastShare = 0;
|
||||||
m_cumulativeFoundSharesDiff = 0.0;
|
m_cumulativeFoundSharesDiff = 0.0;
|
||||||
m_totalFoundShares = 0;
|
m_totalFoundShares = 0;
|
||||||
@@ -495,10 +565,83 @@ void StratumServer::print_stratum_status() const
|
|||||||
"\nShares found = " << m_totalFoundShares <<
|
"\nShares found = " << m_totalFoundShares <<
|
||||||
"\nAverage effort = " << average_effort << '%' <<
|
"\nAverage effort = " << average_effort << '%' <<
|
||||||
"\nCurrent effort = " << static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double() << '%' <<
|
"\nCurrent effort = " << static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double() << '%' <<
|
||||||
"\nConnections = " << m_numConnections << " (" << m_numIncomingConnections << " incoming)"
|
"\nConnections = " << m_numConnections.load() << " (" << m_numIncomingConnections.load() << " incoming)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compresses 64-bit hashes value into 16-bit value (5 bits for shift, 11 bits for data)
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
enum HashValue : uint64_t {
|
||||||
|
bits = 11,
|
||||||
|
mask = (1 << bits) - 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr FORCEINLINE uint64_t hash_uncompress(uint64_t h)
|
||||||
|
{
|
||||||
|
return (h & HashValue::mask) << (h >> HashValue::bits);
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HashMaxValue : uint64_t {
|
||||||
|
value = hash_uncompress(std::numeric_limits<uint16_t>::max())
|
||||||
|
};
|
||||||
|
|
||||||
|
static FORCEINLINE uint16_t hash_compress(uint64_t h)
|
||||||
|
{
|
||||||
|
if (h <= HashValue::mask) {
|
||||||
|
return static_cast<uint16_t>(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (h >= HashMaxValue::value) {
|
||||||
|
return std::numeric_limits<uint16_t>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t shift = bsr(h) - (HashValue::bits - 1);
|
||||||
|
return static_cast<uint16_t>((shift << HashValue::bits) | (h >> shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void StratumServer::update_auto_diff(StratumClient* client, const uint64_t timestamp, const uint64_t hashes)
|
||||||
|
{
|
||||||
|
const uint16_t hashes_compressed = hash_compress(hashes);
|
||||||
|
client->m_autoDiffWindowHashes += hash_uncompress(hashes_compressed);
|
||||||
|
|
||||||
|
const uint32_t k = client->m_autoDiffIndex++;
|
||||||
|
constexpr uint32_t N = StratumClient::AUTO_DIFF_SIZE;
|
||||||
|
|
||||||
|
StratumClient::AutoDiffData& auto_diff_data = client->m_autoDiffData[k % N];
|
||||||
|
|
||||||
|
if (k >= N) {
|
||||||
|
client->m_autoDiffWindowHashes -= hash_uncompress(auto_diff_data.m_hashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t t1 = auto_diff_data.m_timestamp;
|
||||||
|
const uint16_t t2 = static_cast<uint16_t>(timestamp);
|
||||||
|
auto_diff_data.m_timestamp = t2;
|
||||||
|
auto_diff_data.m_hashes = hashes_compressed;
|
||||||
|
|
||||||
|
if (k >= N) {
|
||||||
|
// Full window
|
||||||
|
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 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;
|
||||||
|
}
|
||||||
|
else if (k == 0) {
|
||||||
|
// First share, fix auto diff to current difficulty until we have at least 10 shares
|
||||||
|
client->m_autoDiff.lo = hashes;
|
||||||
|
client->m_autoDiff.hi = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void StratumServer::on_blobs_ready()
|
void StratumServer::on_blobs_ready()
|
||||||
{
|
{
|
||||||
std::vector<BlobsData*> blobs_queue;
|
std::vector<BlobsData*> blobs_queue;
|
||||||
@@ -523,9 +666,10 @@ void StratumServer::on_blobs_ready()
|
|||||||
|
|
||||||
// Only send the latest blob
|
// Only send the latest blob
|
||||||
BlobsData* data = blobs_queue.back();
|
BlobsData* data = blobs_queue.back();
|
||||||
|
const uint32_t extra_nonce_start = data->m_extraNonceStart;
|
||||||
|
|
||||||
size_t numClientsProcessed = 0;
|
size_t numClientsProcessed = 0;
|
||||||
uint32_t extra_nonce = 0;
|
uint32_t num_sent = 0;
|
||||||
|
|
||||||
const uint64_t cur_time = seconds_since_epoch();
|
const uint64_t cur_time = seconds_since_epoch();
|
||||||
{
|
{
|
||||||
@@ -544,33 +688,54 @@ void StratumServer::on_blobs_ready()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extra_nonce >= data->m_numClientsExpected) {
|
if (num_sent >= data->m_numClientsExpected) {
|
||||||
// We don't have any more extra_nonce values available
|
// We don't have any more extra_nonce values available
|
||||||
continue;
|
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;
|
uint64_t target = data->m_target;
|
||||||
if (client->m_customDiff.lo) {
|
if (client->m_customDiff.lo) {
|
||||||
target = std::max(target, client->m_customDiff.target());
|
target = std::max(target, client->m_customDiff.target());
|
||||||
}
|
}
|
||||||
|
else if (m_autoDiff) {
|
||||||
|
// Limit autodiff to 4000000 for maximum compatibility
|
||||||
|
target = std::max(target, TARGET_4_BYTES_LIMIT);
|
||||||
|
|
||||||
|
if (client->m_autoDiff.lo) {
|
||||||
|
const uint32_t k = client->m_autoDiffIndex;
|
||||||
|
const uint16_t elapsed_time = static_cast<uint16_t>(cur_time) - client->m_autoDiffData[(k - 1) % StratumClient::AUTO_DIFF_SIZE].m_timestamp;
|
||||||
|
if (elapsed_time > AUTO_DIFF_TARGET_TIME * 5) {
|
||||||
|
// More than 500% effort, reduce the auto diff by 1/8 every time until the share is found
|
||||||
|
client->m_autoDiff.lo = std::max<uint64_t>(client->m_autoDiff.lo - client->m_autoDiff.lo / 8, MIN_DIFF);
|
||||||
|
}
|
||||||
|
target = std::max(target, client->m_autoDiff.target());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Not enough shares from the client yet, cut diff in half every 16 seconds
|
||||||
|
const uint64_t num_halvings = (cur_time - client->m_connectedTime) / 16;
|
||||||
|
constexpr uint64_t max_target = (std::numeric_limits<uint64_t>::max() / MIN_DIFF) + 1;
|
||||||
|
for (uint64_t i = 0; (i < num_halvings) && (target < max_target); ++i) {
|
||||||
|
target *= 2;
|
||||||
|
}
|
||||||
|
target = std::min<uint64_t>(target, max_target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t job_id;
|
uint32_t job_id;
|
||||||
{
|
{
|
||||||
MutexLock lock3(client->m_jobsLock);
|
job_id = ++client->m_perConnectionJobId;
|
||||||
|
|
||||||
job_id = client->m_perConnectionJobId++;
|
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE];
|
||||||
|
|
||||||
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
|
|
||||||
saved_job.job_id = job_id;
|
saved_job.job_id = job_id;
|
||||||
saved_job.extra_nonce = extra_nonce;
|
saved_job.extra_nonce = extra_nonce_start + num_sent;
|
||||||
saved_job.template_id = data->m_templateId;
|
saved_job.template_id = data->m_templateId;
|
||||||
saved_job.target = target;
|
saved_job.target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool result = send(client,
|
const bool result = send(client,
|
||||||
[data, target, hashing_blob, &job_id](void* buf)
|
[data, target, hashing_blob, &job_id](void* buf, size_t buf_size)
|
||||||
{
|
{
|
||||||
log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t));
|
log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t));
|
||||||
|
|
||||||
@@ -579,7 +744,7 @@ void StratumServer::on_blobs_ready()
|
|||||||
target_hex.m_size -= sizeof(uint32_t);
|
target_hex.m_size -= sizeof(uint32_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::Stream s(reinterpret_cast<char*>(buf));
|
log::Stream s(buf, buf_size);
|
||||||
s << "{\"jsonrpc\":\"2.0\",\"method\":\"job\",\"params\":{\"blob\":\"";
|
s << "{\"jsonrpc\":\"2.0\",\"method\":\"job\",\"params\":{\"blob\":\"";
|
||||||
s << log::hex_buf(hashing_blob, data->m_blobSize) << "\",\"job_id\":\"";
|
s << log::hex_buf(hashing_blob, data->m_blobSize) << "\",\"job_id\":\"";
|
||||||
s << log::Hex(job_id) << "\",\"target\":\"";
|
s << log::Hex(job_id) << "\",\"target\":\"";
|
||||||
@@ -590,19 +755,20 @@ void StratumServer::on_blobs_ready()
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
++extra_nonce;
|
++num_sent;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
client->close();
|
client->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numClientsProcessed != m_numConnections) {
|
const uint32_t num_connections = m_numConnections;
|
||||||
LOGWARN(1, "client list is broken, expected " << m_numConnections << ", got " << numClientsProcessed << " clients");
|
if (numClientsProcessed != num_connections) {
|
||||||
|
LOGWARN(1, "client list is broken, expected " << num_connections << ", got " << numClientsProcessed << " clients");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp)
|
||||||
@@ -638,26 +804,23 @@ void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp)
|
|||||||
|
|
||||||
void StratumServer::on_share_found(uv_work_t* req)
|
void StratumServer::on_share_found(uv_work_t* req)
|
||||||
{
|
{
|
||||||
bkg_jobs_tracker.start("StratumServer::on_share_found");
|
|
||||||
|
|
||||||
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
|
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
|
||||||
|
if (share->m_highEnoughDifficulty) {
|
||||||
|
bkg_jobs_tracker.start("StratumServer::on_share_found");
|
||||||
|
}
|
||||||
|
|
||||||
StratumClient* client = share->m_client;
|
StratumClient* client = share->m_client;
|
||||||
StratumServer* server = share->m_server;
|
StratumServer* server = share->m_server;
|
||||||
p2pool* pool = server->m_pool;
|
p2pool* pool = server->m_pool;
|
||||||
|
|
||||||
uint64_t target = share->m_target;
|
const uint64_t target = share->m_target;
|
||||||
if (target >= TARGET_4_BYTES_LIMIT) {
|
const uint64_t hashes = share->m_hashes;
|
||||||
target = (target >> 32) << 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t rem;
|
|
||||||
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
|
|
||||||
|
|
||||||
if (pool->stopped()) {
|
if (pool->stopped()) {
|
||||||
LOGWARN(0, "p2pool is shutting down, but a share was found. Trying to process it anyway!");
|
LOGWARN(0, "p2pool is shutting down, but a share was found. Trying to process it anyway!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (share->m_sidechainDifficulty.check_pow(share->m_resultHash)) {
|
if (share->m_highEnoughDifficulty) {
|
||||||
uint8_t blob[128];
|
uint8_t blob[128];
|
||||||
uint64_t height;
|
uint64_t height;
|
||||||
difficulty_type difficulty;
|
difficulty_type difficulty;
|
||||||
@@ -687,19 +850,20 @@ void StratumServer::on_share_found(uv_work_t* req)
|
|||||||
if (pow_hash != share->m_resultHash) {
|
if (pow_hash != share->m_resultHash) {
|
||||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " submitted a share with invalid PoW");
|
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " submitted a share with invalid PoW");
|
||||||
share->m_result = SubmittedShare::Result::INVALID_POW;
|
share->m_result = SubmittedShare::Result::INVALID_POW;
|
||||||
|
client->m_score += BAD_SHARE_POINTS;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client->m_score += GOOD_SHARE_POINTS;
|
||||||
|
|
||||||
const uint64_t n = server->m_cumulativeHashes + hashes;
|
const uint64_t n = server->m_cumulativeHashes + hashes;
|
||||||
const double diff = sidechain_difficulty.to_double();
|
const double diff = sidechain_difficulty.to_double();
|
||||||
const double effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
|
share->m_effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
|
||||||
server->m_cumulativeHashesAtLastShare = n;
|
server->m_cumulativeHashesAtLastShare = n;
|
||||||
|
|
||||||
server->m_cumulativeFoundSharesDiff += diff;
|
server->m_cumulativeFoundSharesDiff += diff;
|
||||||
++server->m_totalFoundShares;
|
++server->m_totalFoundShares;
|
||||||
|
|
||||||
const std::string& s = client->m_customUser;
|
|
||||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", client " << static_cast<char*>(client->m_addrString) << (!s.empty() ? " user " : "") << s << ", effort " << effort << '%');
|
|
||||||
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
|
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,7 +871,7 @@ void StratumServer::on_share_found(uv_work_t* req)
|
|||||||
const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t));
|
const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t));
|
||||||
|
|
||||||
if (LIKELY(value < target)) {
|
if (LIKELY(value < target)) {
|
||||||
const uint64_t timestamp = seconds_since_epoch();
|
const uint64_t timestamp = share->m_timestamp;
|
||||||
server->update_hashrate_data(hashes, timestamp);
|
server->update_hashrate_data(hashes, timestamp);
|
||||||
server->api_update_local_stats(timestamp);
|
server->api_update_local_stats(timestamp);
|
||||||
share->m_result = SubmittedShare::Result::OK;
|
share->m_result = SubmittedShare::Result::OK;
|
||||||
@@ -715,33 +879,32 @@ void StratumServer::on_share_found(uv_work_t* req)
|
|||||||
else {
|
else {
|
||||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a low diff share");
|
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a low diff share");
|
||||||
share->m_result = SubmittedShare::Result::LOW_DIFF;
|
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*/)
|
void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||||
{
|
{
|
||||||
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
|
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
|
||||||
|
|
||||||
ON_SCOPE_LEAVE(
|
|
||||||
[share]()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
MutexLock lock(share->m_server->m_submittedSharesPoolLock);
|
|
||||||
share->m_server->m_submittedSharesPool.push_back(share);
|
|
||||||
}
|
|
||||||
bkg_jobs_tracker.stop("StratumServer::on_share_found");
|
|
||||||
});
|
|
||||||
|
|
||||||
StratumClient* client = share->m_client;
|
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); });
|
||||||
|
|
||||||
StratumServer* server = share->m_server;
|
StratumServer* server = share->m_server;
|
||||||
|
|
||||||
const bool bad_share = (share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW);
|
const bool bad_share = (share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW);
|
||||||
|
|
||||||
if ((client->m_resetCounter.load() == share->m_clientResetCounter) && (client->m_rpcId == share->m_rpcId)) {
|
if ((client->m_resetCounter.load() == share->m_clientResetCounter) && (client->m_rpcId == share->m_rpcId)) {
|
||||||
const bool result = server->send(client,
|
const bool result = server->send(client,
|
||||||
[share](void* buf)
|
[share](void* buf, size_t buf_size)
|
||||||
{
|
{
|
||||||
log::Stream s(reinterpret_cast<char*>(buf));
|
log::Stream s(buf, buf_size);
|
||||||
switch (share->m_result) {
|
switch (share->m_result) {
|
||||||
case SubmittedShare::Result::STALE:
|
case SubmittedShare::Result::STALE:
|
||||||
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Stale share\"}}\n";
|
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Stale share\"}}\n";
|
||||||
@@ -762,7 +925,7 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
|||||||
return s.m_pos;
|
return s.m_pos;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (bad_share) {
|
if (bad_share && (client->m_score <= BAN_THRESHOLD_POINTS)) {
|
||||||
client->ban(DEFAULT_BAN_TIME);
|
client->ban(DEFAULT_BAN_TIME);
|
||||||
client->close();
|
client->close();
|
||||||
}
|
}
|
||||||
@@ -775,30 +938,44 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StratumServer::StratumClient::StratumClient()
|
void StratumServer::on_shutdown()
|
||||||
: m_rpcId(0)
|
|
||||||
, m_connectedTime(0)
|
|
||||||
, m_jobs{}
|
|
||||||
, m_perConnectionJobId(0)
|
|
||||||
, m_customDiff{}
|
|
||||||
{
|
{
|
||||||
uv_mutex_init_checked(&m_jobsLock);
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_blobsAsync), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
StratumServer::StratumClient::~StratumClient()
|
StratumServer::StratumClient::StratumClient()
|
||||||
|
: m_rpcId(0)
|
||||||
|
, m_perConnectionJobId(0)
|
||||||
|
, m_connectedTime(0)
|
||||||
|
, m_jobs{}
|
||||||
|
, m_autoDiffData{}
|
||||||
|
, m_autoDiffWindowHashes(0)
|
||||||
|
, m_autoDiffIndex(0)
|
||||||
|
, m_customDiff{}
|
||||||
|
, m_autoDiff{}
|
||||||
|
, m_customUser{}
|
||||||
|
, m_score(0)
|
||||||
{
|
{
|
||||||
uv_mutex_destroy(&m_jobsLock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StratumServer::StratumClient::reset()
|
void StratumServer::StratumClient::reset()
|
||||||
{
|
{
|
||||||
Client::reset();
|
Client::reset();
|
||||||
m_rpcId = 0;
|
m_rpcId = 0;
|
||||||
m_connectedTime = 0;
|
|
||||||
memset(m_jobs, 0, sizeof(m_jobs));
|
|
||||||
m_perConnectionJobId = 0;
|
m_perConnectionJobId = 0;
|
||||||
|
m_connectedTime = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < JOBS_SIZE; ++i) {
|
||||||
|
m_jobs[i].job_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_autoDiffWindowHashes = 0;
|
||||||
|
m_autoDiffIndex = 0;
|
||||||
m_customDiff = {};
|
m_customDiff = {};
|
||||||
m_customUser.clear();
|
m_autoDiff = {};
|
||||||
|
m_customUser[0] = '\0';
|
||||||
|
|
||||||
|
m_score = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StratumServer::StratumClient::on_connect()
|
bool StratumServer::StratumClient::on_connect()
|
||||||
@@ -888,12 +1065,9 @@ bool StratumServer::StratumClient::process_request(char* data, uint32_t /*size*/
|
|||||||
LOGINFO(6, "incoming keepalive from " << log::Gray() << static_cast<char*>(m_addrString));
|
LOGINFO(6, "incoming keepalive from " << log::Gray() << static_cast<char*>(m_addrString));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
LOGWARN(4, "client " << static_cast<char*>(m_addrString) << " invalid JSON request (unknown method)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
LOGWARN(4, "client " << static_cast<char*>(m_addrString) << " invalid JSON request (unknown method)");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StratumServer::StratumClient::process_login(rapidjson::Document& doc, uint32_t id)
|
bool StratumServer::StratumClient::process_login(rapidjson::Document& doc, uint32_t id)
|
||||||
|
|||||||
+30
-9
@@ -39,7 +39,7 @@ public:
|
|||||||
struct StratumClient : public Client
|
struct StratumClient : public Client
|
||||||
{
|
{
|
||||||
StratumClient();
|
StratumClient();
|
||||||
~StratumClient();
|
FORCEINLINE ~StratumClient() {}
|
||||||
|
|
||||||
static Client* allocate() { return new StratumClient(); }
|
static Client* allocate() { return new StratumClient(); }
|
||||||
|
|
||||||
@@ -52,20 +52,34 @@ public:
|
|||||||
bool process_submit(rapidjson::Document& doc, uint32_t id);
|
bool process_submit(rapidjson::Document& doc, uint32_t id);
|
||||||
|
|
||||||
uint32_t m_rpcId;
|
uint32_t m_rpcId;
|
||||||
|
uint32_t m_perConnectionJobId;
|
||||||
uint64_t m_connectedTime;
|
uint64_t m_connectedTime;
|
||||||
|
|
||||||
uv_mutex_t m_jobsLock;
|
enum {
|
||||||
|
JOBS_SIZE = 4,
|
||||||
|
AUTO_DIFF_SIZE = 64,
|
||||||
|
};
|
||||||
|
|
||||||
struct SavedJob {
|
struct SavedJob {
|
||||||
uint32_t job_id;
|
uint32_t job_id;
|
||||||
uint32_t extra_nonce;
|
uint32_t extra_nonce;
|
||||||
uint32_t template_id;
|
uint32_t template_id;
|
||||||
uint64_t target;
|
uint64_t target;
|
||||||
} m_jobs[4];
|
} m_jobs[JOBS_SIZE];
|
||||||
|
|
||||||
|
struct AutoDiffData {
|
||||||
|
uint16_t m_timestamp;
|
||||||
|
uint16_t m_hashes;
|
||||||
|
} m_autoDiffData[AUTO_DIFF_SIZE];
|
||||||
|
|
||||||
|
uint64_t m_autoDiffWindowHashes;
|
||||||
|
uint32_t m_autoDiffIndex;
|
||||||
|
|
||||||
uint32_t m_perConnectionJobId;
|
|
||||||
difficulty_type m_customDiff;
|
difficulty_type m_customDiff;
|
||||||
std::string m_customUser;
|
difficulty_type m_autoDiff;
|
||||||
|
char m_customUser[32];
|
||||||
|
|
||||||
|
int32_t m_score;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool on_login(StratumClient* client, uint32_t id, const char* login);
|
bool on_login(StratumClient* client, uint32_t id, const char* login);
|
||||||
@@ -73,22 +87,23 @@ public:
|
|||||||
uint64_t get_random64();
|
uint64_t get_random64();
|
||||||
|
|
||||||
void print_status() override;
|
void print_status() override;
|
||||||
|
void show_workers();
|
||||||
|
|
||||||
void reset_share_counters();
|
void reset_share_counters();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void print_stratum_status() const;
|
void print_stratum_status() const;
|
||||||
|
void update_auto_diff(StratumClient* client, const uint64_t timestamp, const uint64_t hashes);
|
||||||
static bool get_custom_diff(const char* s, difficulty_type& diff);
|
|
||||||
static bool get_custom_user(const char* s, std::string& user);
|
|
||||||
|
|
||||||
static void on_share_found(uv_work_t* req);
|
static void on_share_found(uv_work_t* req);
|
||||||
static void on_after_share_found(uv_work_t* req, int status);
|
static void on_after_share_found(uv_work_t* req, int status);
|
||||||
|
|
||||||
p2pool* m_pool;
|
p2pool* m_pool;
|
||||||
|
bool m_autoDiff;
|
||||||
|
|
||||||
struct BlobsData
|
struct BlobsData
|
||||||
{
|
{
|
||||||
|
uint32_t m_extraNonceStart;
|
||||||
std::vector<uint8_t> m_blobs;
|
std::vector<uint8_t> m_blobs;
|
||||||
size_t m_blobSize;
|
size_t m_blobSize;
|
||||||
uint64_t m_target;
|
uint64_t m_target;
|
||||||
@@ -125,6 +140,11 @@ private:
|
|||||||
uint64_t m_target;
|
uint64_t m_target;
|
||||||
hash m_resultHash;
|
hash m_resultHash;
|
||||||
difficulty_type m_sidechainDifficulty;
|
difficulty_type m_sidechainDifficulty;
|
||||||
|
uint64_t m_mainchainHeight;
|
||||||
|
double m_effort;
|
||||||
|
uint64_t m_timestamp;
|
||||||
|
uint64_t m_hashes;
|
||||||
|
bool m_highEnoughDifficulty;
|
||||||
|
|
||||||
enum class Result {
|
enum class Result {
|
||||||
STALE,
|
STALE,
|
||||||
@@ -135,7 +155,6 @@ private:
|
|||||||
} m_result;
|
} m_result;
|
||||||
};
|
};
|
||||||
|
|
||||||
uv_mutex_t m_submittedSharesPoolLock;
|
|
||||||
std::vector<SubmittedShare*> m_submittedSharesPool;
|
std::vector<SubmittedShare*> m_submittedSharesPool;
|
||||||
|
|
||||||
struct HashrateData
|
struct HashrateData
|
||||||
@@ -161,6 +180,8 @@ private:
|
|||||||
|
|
||||||
void update_hashrate_data(uint64_t hashes, uint64_t timestamp);
|
void update_hashrate_data(uint64_t hashes, uint64_t timestamp);
|
||||||
void api_update_local_stats(uint64_t timestamp);
|
void api_update_local_stats(uint64_t timestamp);
|
||||||
|
|
||||||
|
void on_shutdown() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace p2pool
|
} // namespace p2pool
|
||||||
|
|||||||
+39
-28
@@ -36,16 +36,16 @@ public:
|
|||||||
|
|
||||||
bool connect_to_peer(bool is_v6, const char* ip, int port);
|
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();
|
void shutdown_tcp();
|
||||||
virtual void print_status();
|
virtual void print_status();
|
||||||
|
|
||||||
uv_loop_t* get_loop() { return &m_loop; }
|
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);
|
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);
|
void ban(const raw_ip& ip, uint64_t seconds);
|
||||||
virtual void print_bans();
|
virtual void print_bans();
|
||||||
@@ -58,6 +58,7 @@ public:
|
|||||||
virtual void reset();
|
virtual void reset();
|
||||||
virtual bool on_connect() = 0;
|
virtual bool on_connect() = 0;
|
||||||
virtual bool on_read(char* data, uint32_t size) = 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_read_failed(int /*err*/) {}
|
||||||
virtual void on_disconnected() {}
|
virtual void on_disconnected() {}
|
||||||
|
|
||||||
@@ -68,7 +69,9 @@ public:
|
|||||||
void close();
|
void close();
|
||||||
void ban(uint64_t seconds);
|
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];
|
||||||
|
|
||||||
TCPServer* m_owner;
|
TCPServer* m_owner;
|
||||||
|
|
||||||
@@ -77,19 +80,24 @@ public:
|
|||||||
Client* m_next;
|
Client* m_next;
|
||||||
|
|
||||||
uv_tcp_t m_socket;
|
uv_tcp_t m_socket;
|
||||||
uv_connect_t m_connectRequest;
|
|
||||||
|
|
||||||
bool m_isV6;
|
bool m_isV6;
|
||||||
bool m_isIncoming;
|
bool m_isIncoming;
|
||||||
raw_ip m_addr;
|
|
||||||
int m_port;
|
|
||||||
char m_addrString[64];
|
|
||||||
|
|
||||||
bool m_readBufInUse;
|
bool m_readBufInUse;
|
||||||
char m_readBuf[READ_BUF_SIZE];
|
bool m_isClosing;
|
||||||
uint32_t m_numRead;
|
uint32_t m_numRead;
|
||||||
|
|
||||||
std::atomic<uint32_t> m_resetCounter{ 0 };
|
raw_ip m_addr;
|
||||||
|
int m_port;
|
||||||
|
char m_addrString[72];
|
||||||
|
|
||||||
|
enum class Socks5ProxyState {
|
||||||
|
Default,
|
||||||
|
MethodSelectionSent,
|
||||||
|
ConnectRequestSent,
|
||||||
|
} m_socks5ProxyState;
|
||||||
|
|
||||||
|
std::atomic<uint32_t> m_resetCounter;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WriteBuf
|
struct WriteBuf
|
||||||
@@ -99,20 +107,19 @@ public:
|
|||||||
std::vector<uint8_t> m_data;
|
std::vector<uint8_t> m_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
uv_mutex_t m_writeBuffersLock;
|
|
||||||
std::vector<WriteBuf*> m_writeBuffers;
|
std::vector<WriteBuf*> m_writeBuffers;
|
||||||
|
|
||||||
struct SendCallbackBase
|
struct SendCallbackBase
|
||||||
{
|
{
|
||||||
virtual ~SendCallbackBase() {}
|
virtual ~SendCallbackBase() {}
|
||||||
virtual size_t operator()(void*) = 0;
|
virtual size_t operator()(void*, size_t) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct SendCallback : public SendCallbackBase
|
struct SendCallback : public SendCallbackBase
|
||||||
{
|
{
|
||||||
explicit FORCEINLINE SendCallback(T&& callback) : m_callback(std::move(callback)) {}
|
explicit FORCEINLINE SendCallback(T&& callback) : m_callback(std::move(callback)) {}
|
||||||
size_t operator()(void* buf) override { return m_callback(buf); }
|
size_t operator()(void* buf, size_t buf_size) override { return m_callback(buf, buf_size); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SendCallback& operator=(SendCallback&&) = delete;
|
SendCallback& operator=(SendCallback&&) = delete;
|
||||||
@@ -127,11 +134,12 @@ private:
|
|||||||
static void loop(void* data);
|
static void loop(void* data);
|
||||||
static void on_new_connection(uv_stream_t* server, int status);
|
static void on_new_connection(uv_stream_t* server, int status);
|
||||||
static void on_connection_close(uv_handle_t* handle);
|
static void on_connection_close(uv_handle_t* handle);
|
||||||
|
static void on_connection_error(uv_handle_t* handle);
|
||||||
static void on_connect(uv_connect_t* req, int status);
|
static void on_connect(uv_connect_t* req, int status);
|
||||||
void on_new_client(uv_stream_t* server);
|
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);
|
bool send_internal(Client* client, SendCallbackBase&& callback);
|
||||||
|
|
||||||
@@ -146,38 +154,41 @@ private:
|
|||||||
protected:
|
protected:
|
||||||
void start_listening(const std::string& listen_addresses);
|
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;
|
std::atomic<int> m_finished;
|
||||||
int m_listenPort;
|
int m_listenPort;
|
||||||
|
|
||||||
uv_loop_t m_loop;
|
uv_loop_t m_loop;
|
||||||
std::atomic<bool> m_loopStopped;
|
|
||||||
|
|
||||||
uv_mutex_t m_clientsListLock;
|
uv_mutex_t m_clientsListLock;
|
||||||
std::vector<Client*> m_preallocatedClients;
|
std::vector<Client*> m_preallocatedClients;
|
||||||
Client* m_connectedClientsList;
|
Client* m_connectedClientsList;
|
||||||
uint32_t m_numConnections;
|
std::atomic<uint32_t> m_numConnections;
|
||||||
uint32_t m_numIncomingConnections;
|
std::atomic<uint32_t> m_numIncomingConnections;
|
||||||
|
|
||||||
uv_mutex_t m_bansLock;
|
uv_mutex_t m_bansLock;
|
||||||
unordered_map<raw_ip, std::chrono::steady_clock::time_point> m_bans;
|
unordered_map<raw_ip, std::chrono::steady_clock::time_point> m_bans;
|
||||||
|
|
||||||
bool is_banned(const raw_ip& ip);
|
bool is_banned(const raw_ip& ip);
|
||||||
|
|
||||||
uv_mutex_t m_pendingConnectionsLock;
|
|
||||||
unordered_set<raw_ip> m_pendingConnections;
|
unordered_set<raw_ip> m_pendingConnections;
|
||||||
|
|
||||||
uv_async_t m_dropConnectionsAsync;
|
uv_async_t m_dropConnectionsAsync;
|
||||||
static void on_drop_connections(uv_async_t* async) { reinterpret_cast<TCPServer*>(async->data)->close_sockets(false); }
|
static void on_drop_connections(uv_async_t* async) { reinterpret_cast<TCPServer*>(async->data)->close_sockets(false); }
|
||||||
|
|
||||||
uv_async_t m_shutdownAsync;
|
virtual void on_shutdown() = 0;
|
||||||
static void on_shutdown(uv_async_t* async)
|
|
||||||
{
|
|
||||||
TCPServer* server = reinterpret_cast<TCPServer*>(async->data);
|
|
||||||
server->close_sockets(true);
|
|
||||||
|
|
||||||
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_dropConnectionsAsync), nullptr);
|
uv_async_t m_shutdownAsync;
|
||||||
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_shutdownAsync), nullptr);
|
uv_prepare_t m_shutdownPrepare;
|
||||||
}
|
uv_timer_t m_shutdownTimer;
|
||||||
|
uint32_t m_shutdownCountdown;
|
||||||
|
uint32_t m_numHandles;
|
||||||
|
|
||||||
|
static void on_shutdown(uv_async_t* async);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace p2pool
|
} // namespace p2pool
|
||||||
|
|||||||
+454
-253
File diff suppressed because it is too large
Load Diff
+120
-2
@@ -32,7 +32,7 @@ namespace p2pool {
|
|||||||
#define STR2(X) STR(X)
|
#define STR2(X) STR(X)
|
||||||
#define STR(X) #X
|
#define STR(X) #X
|
||||||
|
|
||||||
const char* VERSION = "v2.0 (built"
|
const char* VERSION = "v2.5 (built"
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
" with clang/" __clang_version__
|
" with clang/" __clang_version__
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__)
|
||||||
@@ -205,6 +205,15 @@ std::istream& operator>>(std::istream& s, hash& h)
|
|||||||
return s;
|
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)
|
void uv_mutex_init_checked(uv_mutex_t* mutex)
|
||||||
{
|
{
|
||||||
const int result = uv_mutex_init(mutex);
|
const int result = uv_mutex_init(mutex);
|
||||||
@@ -344,12 +353,19 @@ static thread_local bool main_thread = false;
|
|||||||
void set_main_thread() { main_thread = true; }
|
void set_main_thread() { main_thread = true; }
|
||||||
bool is_main_thread() { return main_thread; }
|
bool is_main_thread() { return main_thread; }
|
||||||
|
|
||||||
|
bool disable_resolve_host = false;
|
||||||
|
|
||||||
bool resolve_host(std::string& host, bool& is_v6)
|
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{};
|
addrinfo hints{};
|
||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
|
hints.ai_flags = AI_ADDRCONFIG;
|
||||||
|
|
||||||
addrinfo* r = nullptr;
|
addrinfo* r = nullptr;
|
||||||
const int err = getaddrinfo(host.c_str(), nullptr, &hints, &r);
|
const int err = getaddrinfo(host.c_str(), nullptr, &hints, &r);
|
||||||
@@ -384,4 +400,106 @@ bool resolve_host(std::string& host, bool& is_v6)
|
|||||||
|
|
||||||
RandomDeviceSeed RandomDeviceSeed::instance;
|
RandomDeviceSeed RandomDeviceSeed::instance;
|
||||||
|
|
||||||
|
struct BSR8
|
||||||
|
{
|
||||||
|
uint8_t data[256];
|
||||||
|
|
||||||
|
static constexpr BSR8 init() {
|
||||||
|
BSR8 result = { 55 };
|
||||||
|
|
||||||
|
for (int i = 1; i < 256; ++i) {
|
||||||
|
int x = i;
|
||||||
|
result.data[i] = 63;
|
||||||
|
while (x < 0x80) {
|
||||||
|
--result.data[i];
|
||||||
|
x <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr BSR8 bsr8_table = BSR8::init();
|
||||||
|
|
||||||
|
NOINLINE uint64_t bsr_reference(uint64_t x)
|
||||||
|
{
|
||||||
|
uint32_t y = static_cast<uint32_t>(x);
|
||||||
|
|
||||||
|
uint64_t n0 = (x == y) ? 0 : 32;
|
||||||
|
y = static_cast<uint32_t>(x >> n0);
|
||||||
|
n0 ^= 32;
|
||||||
|
|
||||||
|
const uint64_t n1 = (y & 0xFFFF0000UL) ? 0 : 16;
|
||||||
|
y <<= n1;
|
||||||
|
|
||||||
|
const uint64_t n2 = (y & 0xFF000000UL) ? 0 : 8;
|
||||||
|
y <<= n2;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (!data && create) {
|
||||||
|
data = new UV_LoopUserData(loop);
|
||||||
|
loop->data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace p2pool
|
} // namespace p2pool
|
||||||
|
|||||||
+26
-2
@@ -68,7 +68,7 @@ private:
|
|||||||
|
|
||||||
template<typename T> FORCEINLINE ScopeGuard<T> on_scope_leave(T&& handler) { return ScopeGuard<T>(std::move(handler)); }
|
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
|
struct MinerCallbackHandler
|
||||||
{
|
{
|
||||||
@@ -136,7 +136,7 @@ const uint8_t* readVarint(const uint8_t* data, const uint8_t* data_end, T& b)
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
FORCEINLINE T read_unaligned(const T* p)
|
FORCEINLINE T read_unaligned(const T* p)
|
||||||
{
|
{
|
||||||
static_assert(std::is_integral<T>::value, "T must be an integer type");
|
static_assert(std::is_trivially_copyable<T>::value, "T must be a trivially copyable type");
|
||||||
|
|
||||||
T result;
|
T result;
|
||||||
memcpy(&result, p, sizeof(T));
|
memcpy(&result, p, sizeof(T));
|
||||||
@@ -171,6 +171,7 @@ extern BackgroundJobTracker bkg_jobs_tracker;
|
|||||||
void set_main_thread();
|
void set_main_thread();
|
||||||
bool is_main_thread();
|
bool is_main_thread();
|
||||||
|
|
||||||
|
extern bool disable_resolve_host;
|
||||||
bool resolve_host(std::string& host, bool& is_v6);
|
bool resolve_host(std::string& host, bool& is_v6);
|
||||||
|
|
||||||
template <typename Key, typename T>
|
template <typename Key, typename T>
|
||||||
@@ -203,8 +204,31 @@ FORCEINLINE uint64_t seconds_since_epoch()
|
|||||||
return duration_cast<seconds>(steady_clock::now().time_since_epoch()).count();
|
return duration_cast<seconds>(steady_clock::now().time_since_epoch()).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t bsr_reference(uint64_t x);
|
||||||
|
|
||||||
|
#ifdef HAVE_BUILTIN_CLZLL
|
||||||
|
#define bsr(x) (63 - __builtin_clzll(x))
|
||||||
|
#elif defined HAVE_BITSCANREVERSE64
|
||||||
|
#pragma intrinsic(_BitScanReverse64)
|
||||||
|
FORCEINLINE uint64_t bsr(uint64_t x)
|
||||||
|
{
|
||||||
|
unsigned long index;
|
||||||
|
_BitScanReverse64(&index, x);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#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
|
} // namespace p2pool
|
||||||
|
|
||||||
|
void memory_tracking_start();
|
||||||
|
void memory_tracking_stop();
|
||||||
|
void p2pool_usage();
|
||||||
|
|
||||||
namespace robin_hood {
|
namespace robin_hood {
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
|||||||
+113
@@ -57,8 +57,121 @@ private:
|
|||||||
|
|
||||||
typedef RWLock<true> WriteLock;
|
typedef RWLock<true> WriteLock;
|
||||||
|
|
||||||
|
void uv_cond_init_checked(uv_cond_t* cond);
|
||||||
void uv_mutex_init_checked(uv_mutex_t* mutex);
|
void uv_mutex_init_checked(uv_mutex_t* mutex);
|
||||||
void uv_rwlock_init_checked(uv_rwlock_t* lock);
|
void uv_rwlock_init_checked(uv_rwlock_t* lock);
|
||||||
uv_loop_t* uv_default_loop_checked();
|
uv_loop_t* uv_default_loop_checked();
|
||||||
|
|
||||||
|
struct UV_LoopCallbackBase
|
||||||
|
{
|
||||||
|
virtual ~UV_LoopCallbackBase() {}
|
||||||
|
virtual void operator()() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct UV_LoopCallback : public UV_LoopCallbackBase
|
||||||
|
{
|
||||||
|
explicit FORCEINLINE UV_LoopCallback(T&& cb) : m_cb(std::move(cb)) {}
|
||||||
|
void operator()() override { m_cb(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
UV_LoopCallback& operator=(UV_LoopCallback&&) = delete;
|
||||||
|
T m_cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UV_LoopUserData
|
||||||
|
{
|
||||||
|
uv_loop_t* m_loop;
|
||||||
|
uv_async_t* m_async;
|
||||||
|
|
||||||
|
uv_mutex_t m_callbacksLock;
|
||||||
|
std::vector<UV_LoopCallbackBase*> m_callbacks;
|
||||||
|
|
||||||
|
std::vector<UV_LoopCallbackBase*> m_callbacksToRun;
|
||||||
|
|
||||||
|
explicit UV_LoopUserData(uv_loop_t* loop)
|
||||||
|
: m_loop(loop)
|
||||||
|
, m_async(new uv_async_t{})
|
||||||
|
, m_callbacksLock{}
|
||||||
|
, m_callbacks{}
|
||||||
|
, m_callbacksToRun{}
|
||||||
|
{
|
||||||
|
uv_async_init(m_loop, m_async, async_cb);
|
||||||
|
m_async->data = this;
|
||||||
|
|
||||||
|
uv_mutex_init_checked(&m_callbacksLock);
|
||||||
|
|
||||||
|
m_callbacks.reserve(2);
|
||||||
|
m_callbacksToRun.reserve(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
~UV_LoopUserData()
|
||||||
|
{
|
||||||
|
m_loop->data = nullptr;
|
||||||
|
uv_mutex_destroy(&m_callbacksLock);
|
||||||
|
uv_close(reinterpret_cast<uv_handle_t*>(m_async), [](uv_handle_t* h) { delete reinterpret_cast<uv_async_t*>(h); });
|
||||||
|
for (const UV_LoopCallbackBase* cb : m_callbacks) {
|
||||||
|
delete cb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void async_cb(uv_async_t* h)
|
||||||
|
{
|
||||||
|
UV_LoopUserData* data = reinterpret_cast<UV_LoopUserData*>(h->data);
|
||||||
|
|
||||||
|
data->m_callbacksToRun.clear();
|
||||||
|
{
|
||||||
|
MutexLock lock(data->m_callbacksLock);
|
||||||
|
std::swap(data->m_callbacks, data->m_callbacksToRun);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UV_LoopCallbackBase* cb : data->m_callbacksToRun) {
|
||||||
|
(*cb)();
|
||||||
|
delete cb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UV_LoopUserData(const UV_LoopUserData&) = delete;
|
||||||
|
UV_LoopUserData& operator=(const UV_LoopUserData&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create = true);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
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));
|
||||||
|
{
|
||||||
|
MutexLock lock(data->m_callbacksLock);
|
||||||
|
data->m_callbacks.push_back(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
} // namespace p2pool
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user