Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9fee040ce0 | |||
| ca39b36337 | |||
| 7923f84233 | |||
| 7667f9d21e | |||
| 0741ad1c07 | |||
| f05ce6f697 | |||
| 23d3a24bc4 | |||
| 2cc5ba2fb9 | |||
| 93e3c07a18 | |||
| 7f0be0bcf5 | |||
| f8310f27b8 | |||
| 160ebd4289 | |||
| 6d6c12c1e8 | |||
| cf1c88b907 | |||
| 6449a78863 | |||
| d966088470 | |||
| 5faec12678 | |||
| 1bcddc8329 | |||
| 0905b23123 | |||
| 64914566c0 | |||
| ab80f55698 | |||
| 31860f2e65 | |||
| 6e7311a83c | |||
| e0a6daa5c7 | |||
| 313fb64b41 | |||
| 096e2e086b |
@@ -326,101 +326,6 @@ jobs:
|
||||
name: p2pool-${{ matrix.config.os }}-aarch64
|
||||
path: build/p2pool
|
||||
|
||||
build-ubuntu-riscv64:
|
||||
|
||||
timeout-minutes: 60
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {os: ubuntu-22.04, flags: "-fuse-linker-plugin -ffunction-sections"}
|
||||
|
||||
steps:
|
||||
- name: Setup Alpine Linux
|
||||
uses: jirutka/setup-alpine@v1
|
||||
with:
|
||||
arch: riscv64
|
||||
branch: latest-stable
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y git build-essential cmake gcc-riscv64-linux-gnu g++-riscv64-linux-gnu binutils-riscv64-linux-gnu xz-utils
|
||||
sudo cp /usr/riscv64-linux-gnu/lib/* /home/runner/rootfs/alpine-latest-riscv64/lib
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Build libcurl
|
||||
run: |
|
||||
cd external/src/curl
|
||||
cmake . -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../../../cmake/riscv64_toolchain.cmake -DCMAKE_C_FLAGS="-Os -flto=auto ${{ matrix.config.flags }}" -DBUILD_CURL_EXE=OFF -DBUILD_SHARED_LIBS=OFF -DCURL_DISABLE_INSTALL=ON -DCURL_ENABLE_EXPORT_TARGET=OFF -DCURL_DISABLE_HEADERS_API=ON -DCURL_DISABLE_BINDLOCAL=ON -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF -DCURL_ZLIB=OFF -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF -DCURL_DISABLE_ALTSVC=ON -DCURL_DISABLE_COOKIES=ON -DCURL_DISABLE_DOH=ON -DCURL_DISABLE_GETOPTIONS=ON -DCURL_DISABLE_HSTS=ON -DCURL_DISABLE_LIBCURL_OPTION=ON -DCURL_DISABLE_MIME=ON -DCURL_DISABLE_NETRC=ON -DCURL_DISABLE_NTLM=ON -DCURL_DISABLE_PARSEDATE=ON -DCURL_DISABLE_PROGRESS_METER=ON -DCURL_DISABLE_SHUFFLE_DNS=ON -DCURL_DISABLE_SOCKETPAIR=ON -DCURL_DISABLE_VERBOSE_STRINGS=ON -DCURL_DISABLE_WEBSOCKETS=ON -DHTTP_ONLY=ON -DCURL_ENABLE_SSL=OFF -DUSE_LIBIDN2=OFF -DCURL_USE_LIBPSL=OFF -DCURL_USE_LIBSSH2=OFF -DENABLE_UNIX_SOCKETS=OFF -DCURL_DISABLE_TESTS=ON -DUSE_NGHTTP2=OFF -DBUILD_EXAMPLES=OFF -DP2POOL_BORINGSSL=ON -DCURL_DISABLE_SRP=ON -DCURL_DISABLE_AWS=ON -DCURL_DISABLE_BASIC_AUTH=ON -DCURL_DISABLE_BEARER_AUTH=ON -DCURL_DISABLE_KERBEROS_AUTH=ON -DCURL_DISABLE_NEGOTIATE_AUTH=ON -DOPENSSL_INCLUDE_DIR=../grpc/third_party/boringssl-with-bazel/include
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Build libuv
|
||||
run: |
|
||||
cd external/src/libuv
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../../../cmake/riscv64_toolchain.cmake -DCMAKE_C_FLAGS='-Os -flto=auto ${{ matrix.config.flags }}' -DBUILD_TESTING=OFF -DLIBUV_BUILD_SHARED=OFF
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Build libzmq
|
||||
run: |
|
||||
cd external/src/libzmq
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../../../cmake/riscv64_toolchain.cmake -DCMAKE_C_FLAGS='-Os -flto=auto ${{ matrix.config.flags }}' -DCMAKE_CXX_FLAGS='-Os -flto=auto ${{ matrix.config.flags }}' -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF -DWITH_DOCS=OFF -DENABLE_DRAFTS=OFF -DBUILD_SHARED=OFF -DCMAKE_POLICY_VERSION_MINIMUM="3.5"
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Build p2pool
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../cmake/riscv64_toolchain.cmake -DCMAKE_C_FLAGS='${{ matrix.config.flags }} -Wl,-s -Wl,--gc-sections' -DCMAKE_CXX_FLAGS='${{ matrix.config.flags }} -Wl,-s -Wl,--gc-sections' -DSTATIC_LIBS=ON -DARCH_ID=riscv64 -DCMAKE_POLICY_VERSION_MINIMUM="3.5"
|
||||
make -j$(nproc) p2pool
|
||||
|
||||
- name: Run self-test
|
||||
shell: alpine.sh {0}
|
||||
run: build/p2pool --test
|
||||
|
||||
- name: Build RandomX tests
|
||||
run: |
|
||||
cd build
|
||||
make -j$(nproc) randomx-tests
|
||||
|
||||
- name: Run RandomX tests
|
||||
shell: alpine.sh {0}
|
||||
run: build/external/src/RandomX/randomx-tests
|
||||
|
||||
- name: Build tests
|
||||
run: |
|
||||
cd tests
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../../cmake/riscv64_toolchain.cmake -DCMAKE_C_FLAGS='${{ matrix.config.flags }} -Wl,-s -Wl,--gc-sections' -DCMAKE_CXX_FLAGS='${{ matrix.config.flags }} -Wl,-s -Wl,--gc-sections' -DSTATIC_LIBS=ON -DARCH_ID=riscv64 -DCMAKE_POLICY_VERSION_MINIMUM="3.5"
|
||||
make -j$(nproc) p2pool_tests
|
||||
|
||||
- name: Prepare test data
|
||||
run: |
|
||||
cd tests/build
|
||||
unxz *.xz
|
||||
|
||||
- name: Run tests
|
||||
shell: alpine.sh {0}
|
||||
run: |
|
||||
cd tests/build
|
||||
./p2pool_tests
|
||||
|
||||
- name: Archive binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: p2pool-${{ matrix.config.os }}-riscv64
|
||||
path: build/p2pool
|
||||
|
||||
build-windows-msys2:
|
||||
|
||||
timeout-minutes: 60
|
||||
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
|
||||
sync-test-ubuntu-msan:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
|
||||
@@ -99,13 +99,10 @@ In order to continue mining on P2Pool, you must update both Monero and P2Pool so
|
||||
- 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 can mine to a primary wallet address (the one starting with `4`) for mining. If you want to mine to a subaddress, you will need to provide both the main address (starting with 4) and the subaddress (starting with 8) using `--wallet` and `--subaddress` command line parameters.
|
||||
- You can add the `--mini` or `--nano` parameter to your P2Pool command to connect to the respective sidechain.
|
||||
> [!NOTE]
|
||||
>
|
||||
> You cannot use both `--mini` and `--nano` together
|
||||
>
|
||||
> Using `--mini` will change the p2p port to 37888.
|
||||
>
|
||||
> Using `--nano` will change the p2p port to 37890.
|
||||
> [!NOTE]
|
||||
> You cannot use both `--mini` and `--nano` together<br>
|
||||
> Using `--mini` will change the p2p port to 37888.<br>
|
||||
> Using `--nano` will change the p2p port to 37890.<br>
|
||||
- Check that ports 18080 (Monero p2p port) and 37889/37888/37890 (P2Pool default/mini/nano p2p port) are open in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine.
|
||||
- You can connect multiple miners to the same P2Pool node. The more the better!
|
||||
- The 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.
|
||||
@@ -117,7 +114,6 @@ In order to continue mining on P2Pool, you must update both Monero and P2Pool so
|
||||
- [Cake Wallet v4.4.5 and newer](https://cakewallet.com/)
|
||||
- [Monero.com by Cake Wallet](https://monero.com/)
|
||||
- [Feather Wallet v2.1.0 and newer](https://featherwallet.org/)
|
||||
- [MyMonero](https://mymonero.com/)
|
||||
|
||||
### GUI for P2Pool
|
||||
|
||||
@@ -129,7 +125,7 @@ Merge mining is available in P2Pool since the fork that happened on October 12th
|
||||
|
||||
Blockchains that will support [Merge mining RPC API](https://github.com/SChernykh/p2pool/blob/master/docs/MERGE_MINING.MD#proposed-rpc-api):
|
||||
- [Townforge](https://townforge.net/) supports it in their [tmp-mm branch](https://git.townforge.net/townforge/townforge/src/branch/tmp-mm) (not released yet)
|
||||
- [DarkFi](https://dark.fi/) is going to support it, but it's [not ready yet](https://github.com/darkrenaissance/darkfi/issues/244)
|
||||
- [DarkFi](https://dark.fi/) supports it in their [testnet](https://dark.fi/book/testnet/merge-mining.html)
|
||||
```
|
||||
p2pool.exe --wallet YOUR_MONERO_WALLET_ADDRESS --merge-mine IP:port YOUR_WALLET_ADDRESS_ON_ANOTHER_BLOCKCHAIN
|
||||
```
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum (WARNING: incompatible with Nicehash and MRR)
|
||||
--rpc-login Specify username[:password] required for Monero RPC server
|
||||
--socks5 Specify IP:port of a SOCKS5 proxy to use for outgoing connections
|
||||
--socks5-proxy-type The type of SOCKS5 proxy. Can be one of the following values: auto, plain, tor. Default is auto (auto-detect by the port number)
|
||||
--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
|
||||
--no-upnp Disable UPnP port forwarding
|
||||
@@ -40,6 +41,8 @@
|
||||
--tls-cert-key file Load TLS certificate private key from "file" in the PEM format
|
||||
--rpc-ssl Enable SSL on RPC connections to the Monero node
|
||||
--rpc-ssl-fingerprint base64-encoded fingerprint of the Monero node's certificate (optional, use it for certificate pinning)
|
||||
--stratum-proxy-protocol Enable HAProxy PROXY protocol v2 for incoming Stratum connections (use with HAProxy send-proxy-v2)
|
||||
--p2p-proxy-protocol Enable HAProxy PROXY protocol v2 for incoming P2P connections (use with HAProxy send-proxy-v2)
|
||||
--no-stratum-http Disable HTTP on Stratum ports
|
||||
--full-validation Enables full share validation / increases CPU usage
|
||||
--onion-address Tell other peers to use this .onion address to connect to this node through TOR
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@
|
||||
P2Pool has several command-line options that should be used for TOR setup:
|
||||
|
||||
- `--socks5 IP:port` to specify your TOR proxy address (usually `127.0.0.1:9050` if you installed TOR on the same PC)
|
||||
- `--socks5-proxy-type tor` to specify the type of SOCKS5 proxy being used
|
||||
- `--no-dns` to disable all DNS queries and prevent DNS leaks. P2Pool only ever makes DNS requests to get a list of seed nodes, to resolve your Monero node's domain (if it's not set as an IP address), and to resolve manually added peers
|
||||
- `--no-upnp` to disable UPnP requests (they are sent to your router, so use this option if you are not on your home network)
|
||||
- `--onion-address` your hidden service's address (without port number). This address will be broadcast to other peers when you mine a share in P2Pool. This is to prevent address spamming - you have to mine a real share to be able to broadcast your TOR address.
|
||||
@@ -27,5 +28,5 @@ P2Pool has several command-line options that should be used for TOR setup:
|
||||
|
||||
## Command line example
|
||||
```
|
||||
./p2pool --host MONERO_NODE_IP --wallet YOUR_WALLET --socks5 127.0.0.1:9050 --no-dns --no-upnp --onion-address ADDRESS_FROM_HOSTNAME_FILE
|
||||
./p2pool --host MONERO_NODE_IP --wallet YOUR_WALLET --socks5 127.0.0.1:9050 --socks5-proxy-type tor --no-dns --no-upnp --onion-address ADDRESS_FROM_HOSTNAME_FILE
|
||||
```
|
||||
|
||||
@@ -239,7 +239,6 @@ Wallets with confirmed P2Pool support<br>
|
||||
<li><a href="https://play.google.com/store/apps/details?id=com.m2049r.xmrwallet" target="_blank">Monerujo v3.0.2 "Fluorine Fermi"</a> and newer
|
||||
<li><a href="https://play.google.com/store/apps/details?id=com.cakewallet.cake_wallet" target="_blank">Cake Wallet v4.4.5</a> and newer
|
||||
<li><a href="https://featherwallet.org/" target="_blank">Feather Wallet v2.1.0</a> and newer
|
||||
<li><a href="https://mymonero.com/" target="_blank">MyMonero</a>
|
||||
</ul>
|
||||
It's highly recommended to create a new wallet for mining because wallet addresses are public on p2pool. You can mine to a primary wallet address (the one starting with `4`) for mining. If you want to mine to a subaddress, you will need to provide both the main address (starting with 4) and the subaddress (starting with 8) using "--wallet" and "--subaddress" command line parameters.
|
||||
</details>
|
||||
@@ -315,4 +314,4 @@ Donate to support the project:<br><span style="font-size:0.9rem;color:#b0b0b0;">
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -239,7 +239,6 @@ Wallets with confirmed P2Pool support<br>
|
||||
<li><a href="https://play.google.com/store/apps/details?id=com.m2049r.xmrwallet" target="_blank">Monerujo v3.0.2 "Fluorine Fermi"</a> and newer
|
||||
<li><a href="https://play.google.com/store/apps/details?id=com.cakewallet.cake_wallet" target="_blank">Cake Wallet v4.4.5</a> and newer
|
||||
<li><a href="https://featherwallet.org/" target="_blank">Feather Wallet v2.1.0</a> and newer
|
||||
<li><a href="https://mymonero.com/" target="_blank">MyMonero</a>
|
||||
</ul>
|
||||
It's highly recommended to create a new wallet for mining because wallet addresses are public on p2pool. You can mine to a primary wallet address (the one starting with `4`) for mining. If you want to mine to a subaddress, you will need to provide both the main address (starting with 4) and the subaddress (starting with 8) using "--wallet" and "--subaddress" command line parameters.
|
||||
</details>
|
||||
@@ -315,4 +314,4 @@ Donate to support the project:<br><span style="font-size:0.9rem;color:#b0b0b0;">
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -242,7 +242,6 @@ Wallets with confirmed P2Pool support<br>
|
||||
<li><a href="https://play.google.com/store/apps/details?id=com.m2049r.xmrwallet" target="_blank">Monerujo v3.0.2 "Fluorine Fermi"</a> and newer
|
||||
<li><a href="https://play.google.com/store/apps/details?id=com.cakewallet.cake_wallet" target="_blank">Cake Wallet v4.4.5</a> and newer
|
||||
<li><a href="https://featherwallet.org/" target="_blank">Feather Wallet v2.1.0</a> and newer
|
||||
<li><a href="https://mymonero.com/" target="_blank">MyMonero</a>
|
||||
</ul>
|
||||
It's highly recommended to create a new wallet for mining because wallet addresses are public on p2pool. You can mine to a primary wallet address (the one starting with `4`) for mining. If you want to mine to a subaddress, you will need to provide both the main address (starting with 4) and the subaddress (starting with 8) using "--wallet" and "--subaddress" command line parameters.
|
||||
</details>
|
||||
@@ -318,4 +317,4 @@ Donate to support the project:<br><span style="font-size:0.9rem;color:#b0b0b0;">
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
Vendored
+1
-1
Submodule external/src/RandomX updated: d341d6a61c...1343007c61
Vendored
+1
-1
Submodule external/src/cppzmq updated: 57cc98f8fc...041f755b79
Vendored
+1
-1
Submodule external/src/curl updated: efe7a9f403...a781b68efb
Vendored
+1
-1
Submodule external/src/grpc updated: b40fe4ffc4...f310102b02
Vendored
+1
-1
Submodule external/src/libuv updated: 012b1e9698...4ae2852bbb
Vendored
+1
-1
Submodule external/src/libzmq updated: 1fce1ef005...8d18911edf
Vendored
+1
-1
Submodule external/src/miniupnp updated: 88a1515ea8...bdbda466ed
@@ -1,5 +1,5 @@
|
||||
diff --git a/crypto/rand/windows.cc b/crypto/rand/windows.cc
|
||||
index 6a93a2d84..f75112b4f 100644
|
||||
index ebd0d4afd..65e877f0e 100644
|
||||
--- a/crypto/rand/windows.cc
|
||||
+++ b/crypto/rand/windows.cc
|
||||
@@ -24,6 +24,7 @@
|
||||
@@ -8,11 +8,11 @@ index 6a93a2d84..f75112b4f 100644
|
||||
#include <windows.h>
|
||||
+#include <wincrypt.h>
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
|
||||
!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
@@ -57,16 +58,39 @@ void CRYPTO_sysrand(uint8_t *out, size_t requested) {
|
||||
using namespace bssl;
|
||||
|
||||
@@ -59,16 +60,39 @@ void bssl::CRYPTO_sysrand(uint8_t *out, size_t requested) {
|
||||
// See: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
|
||||
typedef BOOL (WINAPI *ProcessPrngFunction)(PBYTE pbData, SIZE_T cbData);
|
||||
typedef BOOL(WINAPI *ProcessPrngFunction)(PBYTE pbData, SIZE_T cbData);
|
||||
static ProcessPrngFunction g_processprng_fn = nullptr;
|
||||
+static HCRYPTPROV g_hCryptProv = 0;
|
||||
+
|
||||
@@ -21,7 +21,7 @@ index 6a93a2d84..f75112b4f 100644
|
||||
+ return CryptGenRandom(g_hCryptProv, cbData, pbData);
|
||||
+}
|
||||
|
||||
static void init_processprng(void) {
|
||||
static void init_processprng() {
|
||||
HMODULE hmod = LoadLibraryW(L"bcryptprimitives");
|
||||
- if (hmod == nullptr) {
|
||||
- abort();
|
||||
@@ -54,4 +54,4 @@ index 6a93a2d84..f75112b4f 100644
|
||||
+ abort();
|
||||
}
|
||||
|
||||
void CRYPTO_init_sysrand(void) {
|
||||
void bssl::CRYPTO_init_sysrand() {
|
||||
|
||||
@@ -3,14 +3,14 @@ set -e
|
||||
|
||||
# Software versions to install
|
||||
|
||||
_7ZIP_VERSION=2501
|
||||
BINUTILS_VERSION=2_45_1
|
||||
_7ZIP_VERSION=2600
|
||||
BINUTILS_VERSION=2_46
|
||||
CLANG_VERSION=21.1.8
|
||||
CMAKE_VERSION=4.2.1
|
||||
CMAKE_VERSION=4.2.3
|
||||
FREEBSD_VERSION=12.4
|
||||
GCC_VERSION=15.2.0
|
||||
GLIBC_VERSION=2.42
|
||||
LINUX_HEADERS_VERSION=6.18.1
|
||||
GLIBC_VERSION=2.43
|
||||
LINUX_HEADERS_VERSION=6.19.3
|
||||
MACOSX_SDK_VERSION=26.1
|
||||
OSXCROSS_VERSION=2bc739ebe45db5d72e176d2a4e1c7dd95464e8e2
|
||||
MAKE_VERSION=4.4.1
|
||||
@@ -18,12 +18,12 @@ MINGW_VERSION=13.0.0
|
||||
XZ_VERSION=5.8.2
|
||||
TAR_VERSION=1.35
|
||||
|
||||
_7ZIP_SHA256="4ca3b7c6f2f67866b92622818b58233dc70367be2f36b498eb0bdeaaa44b53f4"
|
||||
CMAKE_SHA256="c059bff1e97a2b6b5b0c0872263627486345ad0ed083298cb21cff2eda883980"
|
||||
_7ZIP_SHA256="c74dc4a48492cde43f5fec10d53fb2a66f520e4a62a69d630c44cb22c477edc6"
|
||||
CMAKE_SHA256="5bb505d5e0cca0480a330f7f27ccf52c2b8b5214c5bba97df08899f5ef650c23"
|
||||
FREEBSD_AARCH64_SHA256="6c401819bfb93e810c9f9aa670a1e4685f924df5e7e0c9c6397dd6c16c954fa2"
|
||||
FREEBSD_X86_64_SHA256="581c7edacfd2fca2bdf5791f667402d22fccd8a5e184635e0cac075564d57aa8"
|
||||
GLIBC_SHA256="d1775e32e4628e64ef930f435b67bb63af7599acb6be2b335b9f19f16509f17f"
|
||||
HEADERS_SHA256="d0a78bf3f0d12aaa10af3b5adcaed5bc767b5b78705e5ef885d5e930b72e25d5"
|
||||
GLIBC_SHA256="d9c86c6b5dbddb43a3e08270c5844fc5177d19442cf5b8df4be7c07cd5fa3831"
|
||||
HEADERS_SHA256="0e474968adfcbee32916fd01a89d8ccfd1168d8d32569e76a5c664c793198ebe"
|
||||
MACOSX_SDK_SHA256="beee7212d265a6d2867d0236cc069314b38d5fb3486a6515734e76fa210c784c"
|
||||
MAKE_SHA256="dd16fb1d67bfab79a72f5e8390735c49e3e8e70b4945a15ab1f81ddb78658fb3"
|
||||
TAR_SHA256="4d62ff37342ec7aed748535323930c7cf94acf71c3591882b26a7ea50f3edc16"
|
||||
@@ -33,7 +33,7 @@ echo "Install prerequisites"
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
apt-get update && apt-get upgrade -yq --no-install-recommends
|
||||
apt-get install -yq --no-install-recommends ca-certificates curl bzip2 flex texinfo bison ninja-build python3 file rsync xz-utils gawk gettext patch git gcc g++
|
||||
apt-get install -yq --no-install-recommends ca-certificates curl bzip2 flex texinfo bison ninja-build python3 python3-yaml file rsync xz-utils gawk gettext patch git gcc g++
|
||||
|
||||
echo "Cloning the P2Pool repository in background"
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
* Portions Copyright (c) 2012-2013 The Cryptonote developers
|
||||
* Portions Copyright (c) 2014-2021 The Monero Project
|
||||
* Portions Copyright (c) 2021 XMRig <https://github.com/xmrig>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021 hyc <https://github.com/hyc>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@@ -35,7 +35,7 @@ static constexpr int DEFAULT_BACKLOG = 1;
|
||||
namespace p2pool {
|
||||
|
||||
ConsoleCommands::ConsoleCommands(p2pool* pool)
|
||||
: TCPServer(DEFAULT_BACKLOG, ConsoleClient::allocate, std::string())
|
||||
: TCPServer(DEFAULT_BACKLOG, ConsoleClient::allocate, std::string(), Params::ProxyType::INVALID)
|
||||
, m_pool(pool)
|
||||
, m_tty{}
|
||||
, m_stdin_pipe{}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+10
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -89,6 +89,7 @@ void p2pool_usage()
|
||||
"--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum (WARNING: incompatible with Nicehash and MRR)\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"
|
||||
"--socks5-proxy-type The type of SOCKS5 proxy. Can be one of the following values: auto, plain, tor. Default is auto (auto-detect by the port number)\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"
|
||||
#ifdef WITH_UPNP
|
||||
@@ -104,6 +105,8 @@ void p2pool_usage()
|
||||
"--rpc-ssl Enable SSL on RPC connections to the Monero node\n"
|
||||
"--rpc-ssl-fingerprint base64-encoded fingerprint of the Monero node's certificate (optional, use it for certificate pinning)\n"
|
||||
#endif
|
||||
"--stratum-proxy-protocol Enable HAProxy PROXY protocol v2 for incoming Stratum connections (use with HAProxy send-proxy-v2)\n"
|
||||
"--p2p-proxy-protocol Enable HAProxy PROXY protocol v2 for incoming P2P connections (use with HAProxy send-proxy-v2)\n"
|
||||
"--no-stratum-http Disable HTTP on Stratum ports\n"
|
||||
"--full-validation Enables full share validation / increases CPU usage\n"
|
||||
"--onion-address Tell other peers to use this .onion address to connect to this node through TOR\n"
|
||||
@@ -343,7 +346,9 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
#ifdef WITH_GRPC
|
||||
grpc_init();
|
||||
if (params.grpc_needed()) {
|
||||
grpc_init();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_TLS
|
||||
@@ -362,7 +367,9 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
#ifdef WITH_GRPC
|
||||
grpc_shutdown();
|
||||
if (params.grpc_needed()) {
|
||||
grpc_shutdown();
|
||||
}
|
||||
#endif
|
||||
|
||||
curl_global_cleanup();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -42,7 +42,7 @@ MergeMiningClientTari::MergeMiningClientTari(p2pool* pool, std::string host, con
|
||||
, m_auxWallet(wallet)
|
||||
, m_pool(pool)
|
||||
, m_tariJobParams{}
|
||||
, m_server(new TariServer(pool->params().m_socks5Proxy))
|
||||
, m_server(new TariServer(pool->params().m_socks5Proxy, pool->params().m_socks5ProxyType))
|
||||
, m_hostStr(host)
|
||||
, m_workerStop(0)
|
||||
, m_pushBlockStop(0)
|
||||
@@ -863,8 +863,8 @@ void MergeMiningClientTari::run()
|
||||
// TariServer and TariClient are simply a proxy from a localhost TCP port to the external Tari node
|
||||
// This is needed for SOCKS5 proxy support (gRPC library doesn't support it natively)
|
||||
|
||||
MergeMiningClientTari::TariServer::TariServer(const std::string& socks5Proxy)
|
||||
: TCPServer(1, MergeMiningClientTari::TariClient::allocate, socks5Proxy)
|
||||
MergeMiningClientTari::TariServer::TariServer(const std::string& socks5Proxy, Params::ProxyType socks5ProxyType)
|
||||
: TCPServer(1, MergeMiningClientTari::TariClient::allocate, socks5Proxy, socks5ProxyType)
|
||||
, m_TariNodeIsV6(false)
|
||||
, m_TariNodeHost()
|
||||
, m_TariNodePort(0)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -83,7 +83,7 @@ private:
|
||||
|
||||
struct TariServer : public TCPServer
|
||||
{
|
||||
explicit TariServer(const std::string& socks5Proxy);
|
||||
TariServer(const std::string& socks5Proxy, Params::ProxyType socks5ProxyType);
|
||||
~TariServer() {}
|
||||
|
||||
[[nodiscard]] bool start();
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+24
-9
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -64,7 +64,7 @@ static constexpr hash seed_onion_nodes[] = {
|
||||
};
|
||||
|
||||
P2PServer::P2PServer(p2pool* pool)
|
||||
: TCPServer(DEFAULT_BACKLOG, P2PClient::allocate, pool->params().m_socks5Proxy)
|
||||
: TCPServer(DEFAULT_BACKLOG, P2PClient::allocate, pool->params().m_socks5Proxy, pool->params().m_socks5ProxyType, pool->params().m_p2pProxyProtocol)
|
||||
, m_pool(pool)
|
||||
, m_cache(pool->params().m_blockCache ? new BlockCache(pool->params()) : nullptr)
|
||||
, m_cacheLoaded(false)
|
||||
@@ -426,14 +426,16 @@ void P2PServer::update_peer_connections()
|
||||
|
||||
uint32_t num_outgoing = m_numConnections - m_numIncomingConnections;
|
||||
|
||||
// Try to have at least N/2 outgoing onion connections
|
||||
if (!m_socks5Proxy.empty()) {
|
||||
// Try to have at least N/2 (or N if clearnet P2P is disabled) outgoing onion connections
|
||||
if (!m_socks5Proxy.empty() && (m_socks5ProxyType == Params::ProxyType::TOR)) {
|
||||
std::vector<hash> pubkeys = m_pool->side_chain().seen_onion_pubkeys();
|
||||
|
||||
// Add seed nodes
|
||||
pubkeys.insert(pubkeys.end(), seed_onion_nodes, seed_onion_nodes + array_size(seed_onion_nodes));
|
||||
|
||||
for (uint32_t i = m_numOnionConnections, n = N / 2; (i < n) && !pubkeys.empty();) {
|
||||
const uint32_t n = m_pool->params().m_noClearnetP2P ? N : (N / 2);
|
||||
|
||||
for (uint32_t i = m_numOnionConnections; (i < n) && !pubkeys.empty();) {
|
||||
const uint64_t k = get_random64() % pubkeys.size();
|
||||
const std::string addr = to_onion_v3(pubkeys[k]);
|
||||
|
||||
@@ -727,8 +729,21 @@ void P2PServer::load_peer_list()
|
||||
}
|
||||
|
||||
// Finally load peers from p2pool_peers.txt and p2pool_onion_peers.txt
|
||||
for (size_t i = 0, n = (m_socks5Proxy.empty() ? 1 : 2); i < n; ++i) {
|
||||
const std::string path = m_pool->params().m_dataDir + (i ? saved_onion_peer_list_file_name : saved_peer_list_file_name);
|
||||
std::vector<std::string> paths;
|
||||
paths.resize(Params::ProxyType::MAX);
|
||||
|
||||
paths[Params::ProxyType::PLAIN] = saved_peer_list_file_name;
|
||||
|
||||
if (!m_socks5Proxy.empty() && (m_socks5ProxyType == Params::ProxyType::TOR)) {
|
||||
paths[Params::ProxyType::TOR] = saved_onion_peer_list_file_name;
|
||||
}
|
||||
|
||||
for (int i = Params::ProxyType::PLAIN; i < Params::ProxyType::MAX; ++i) {
|
||||
const std::string& path = paths[i];
|
||||
|
||||
if (path.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::ifstream f(path);
|
||||
|
||||
@@ -740,7 +755,7 @@ void P2PServer::load_peer_list()
|
||||
std::getline(f, address);
|
||||
|
||||
if (!address.empty()) {
|
||||
if (i > 0) {
|
||||
if (i == Params::ProxyType::TOR) {
|
||||
const hash h = from_onion_v3(address);
|
||||
if (!h.empty()) {
|
||||
pubkeys.emplace_back(h);
|
||||
@@ -756,7 +771,7 @@ void P2PServer::load_peer_list()
|
||||
}
|
||||
f.close();
|
||||
|
||||
if (i > 0) {
|
||||
if (i == Params::ProxyType::TOR) {
|
||||
s.add_onion_pubkeys(pubkeys);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+338
-264
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,6 +21,11 @@
|
||||
#include "p2p_server.h"
|
||||
#include <fstream>
|
||||
|
||||
#ifdef WITH_GRPC
|
||||
#include "merge_mining_client.h"
|
||||
#include "merge_mining_client_tari.h"
|
||||
#endif
|
||||
|
||||
LOG_CATEGORY(Params)
|
||||
|
||||
void p2pool_usage();
|
||||
@@ -32,275 +37,13 @@ static constexpr uint64_t MAX_STRATUM_BAN_TIME = (UINT64_C(1) << 34) - 1;
|
||||
|
||||
Params::Params(const std::vector<std::vector<std::string>>& args)
|
||||
{
|
||||
auto has1 = [](const auto& v) { return (v.size() > 1) && !v[1].empty();};
|
||||
auto has2 = [](const auto& v) { return (v.size() > 2) && !v[2].empty();};
|
||||
|
||||
for (const auto& arg : args) {
|
||||
// Ignore empty parameters
|
||||
if (arg.empty() || arg[0].empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
|
||||
if ((arg[0] == "host") && has1(arg)) {
|
||||
const char* address = arg[1].c_str();
|
||||
|
||||
if (m_hosts.empty()) {
|
||||
m_hosts.emplace_back();
|
||||
m_hosts.back().m_address = address;
|
||||
}
|
||||
else {
|
||||
const Host& h = m_hosts.back();
|
||||
m_hosts.emplace_back(address, h.m_rpcPort, h.m_zmqPort, "");
|
||||
}
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "rpc-port") && has1(arg)) {
|
||||
if (m_hosts.empty()) {
|
||||
m_hosts.emplace_back();
|
||||
}
|
||||
|
||||
m_hosts.back().m_rpcPort = static_cast<int32_t>(std::min(std::max(strtoul(arg[1].c_str(), nullptr, 10), 1UL), 65535UL));
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "zmq-port") && has1(arg)) {
|
||||
if (m_hosts.empty()) {
|
||||
m_hosts.emplace_back();
|
||||
}
|
||||
|
||||
m_hosts.back().m_zmqPort = static_cast<int32_t>(std::min(std::max(strtoul(arg[1].c_str(), nullptr, 10), 1UL), 65535UL));
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (arg[0] == "light-mode") {
|
||||
m_lightMode = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "wallet") && has1(arg)) {
|
||||
const char* s = arg[1].c_str();
|
||||
|
||||
if (!m_mainWallet.decode(s)) {
|
||||
LOGERR(1, "Wallet " << s << " failed to decode");
|
||||
}
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "subaddress") && has1(arg)) {
|
||||
const char* s = arg[1].c_str();
|
||||
|
||||
if (!m_subaddress.decode(s)) {
|
||||
LOGERR(1, "Subaddress " << s << " failed to decode");
|
||||
}
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "stratum") && has1(arg)) {
|
||||
m_stratumAddresses = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "stratum-ban-time") && has1(arg)) {
|
||||
m_stratumBanTime = strtoull(arg[1].c_str(), nullptr, 10);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "p2p") && has1(arg)) {
|
||||
m_p2pAddresses = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "addpeers") && has1(arg)) {
|
||||
m_p2pPeerList = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "loglevel") && has1(arg)) {
|
||||
const int level = std::min(std::max<int>(static_cast<int>(strtol(arg[1].c_str(), nullptr, 10)), 0), log::MAX_GLOBAL_LOG_LEVEL);
|
||||
log::GLOBAL_LOG_LEVEL = level;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "data-dir") && has1(arg)) {
|
||||
m_dataDir = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "log-file") && has1(arg)) {
|
||||
m_logFilePath = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "sidechain-config") && has1(arg)) {
|
||||
m_sidechainConfig = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "data-api") && has1(arg)) {
|
||||
m_apiPath = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "local-api") || (arg[0] == "stratum-api")) {
|
||||
m_localStats = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (arg[0] == "no-cache") {
|
||||
m_blockCache = false;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (arg[0] == "no-color") {
|
||||
log::CONSOLE_COLORS = false;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
if (arg[0] == "no-randomx") {
|
||||
m_disableRandomX = true;
|
||||
ok = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (((arg[0] == "out-peers") || (arg[0] == "outpeers")) && has1(arg)) {
|
||||
m_maxOutgoingPeers = std::min(std::max(strtoul(arg[1].c_str(), nullptr, 10), 10UL), 450UL);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (((arg[0] == "in-peers") || (arg[0] == "inpeers")) && has1(arg)) {
|
||||
m_maxIncomingPeers = std::min(std::max(strtoul(arg[1].c_str(), nullptr, 10), 10UL), 450UL);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "start-mining") && has1(arg)) {
|
||||
m_minerThreads = std::min(std::max(strtoul(arg[1].c_str(), nullptr, 10), 1UL), 64UL);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (arg[0] == "mini") {
|
||||
m_mini = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (arg[0] == "nano") {
|
||||
m_nano = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (arg[0] == "no-autodiff") {
|
||||
m_autoDiff = false;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "rpc-login") && has1(arg)) {
|
||||
if (m_hosts.empty()) {
|
||||
m_hosts.emplace_back();
|
||||
}
|
||||
|
||||
m_hosts.back().m_rpcLogin = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
#ifdef WITH_TLS
|
||||
if (arg[0] == "rpc-ssl") {
|
||||
if (m_hosts.empty()) {
|
||||
m_hosts.emplace_back();
|
||||
}
|
||||
|
||||
m_hosts.back().m_rpcSSL = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "rpc-ssl-fingerprint") && has1(arg)) {
|
||||
if (m_hosts.empty()) {
|
||||
m_hosts.emplace_back();
|
||||
}
|
||||
|
||||
m_hosts.back().m_rpcSSL_Fingerprint = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((arg[0] == "socks5") && has1(arg)) {
|
||||
m_socks5Proxy = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (arg[0] == "no-dns") {
|
||||
m_dns = false;
|
||||
disable_resolve_host = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "p2p-external-port") && has1(arg)) {
|
||||
m_p2pExternalPort = static_cast<int32_t>(std::min(std::max(strtoul(arg[1].c_str(), nullptr, 10), 1UL), 65535UL));
|
||||
ok = true;
|
||||
}
|
||||
|
||||
#ifdef WITH_UPNP
|
||||
if ((arg[0] == "no-upnp") || (arg[0] == "no-igd")) {
|
||||
m_upnp = false;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (arg[0] == "upnp-stratum") {
|
||||
m_upnpStratum = true;
|
||||
ok = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((arg[0] == "merge-mine") && has1(arg) && has2(arg)) {
|
||||
m_mergeMiningHosts.emplace_back(arg[1], arg[2]);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
#ifdef WITH_TLS
|
||||
if ((arg[0] == "tls-cert") && has1(arg)) {
|
||||
m_tlsCert = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "tls-cert-key") && has1(arg)) {
|
||||
m_tlsCertKey = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (arg[0] == "no-stratum-http") {
|
||||
m_enableStratumHTTP = false;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
#ifdef WITH_MERGE_MINING_DONATION
|
||||
if ((arg[0] == "adkf") && has1(arg)) {
|
||||
m_authorKeyFile = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (arg[0] == "full-validation") {
|
||||
m_enableFullValidation = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "onion-address") && has1(arg)) {
|
||||
m_onionAddress = arg[1];
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (arg[0] == "no-clearnet-p2p") {
|
||||
m_noClearnetP2P = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
if (!process_arg(arg)) {
|
||||
fprintf(stderr, "Unknown or invalid command line parameter \"%s\"\n\n", arg[0].c_str());
|
||||
p2pool_usage();
|
||||
throw std::exception();
|
||||
@@ -421,6 +164,318 @@ Params::Params(const std::vector<std::vector<std::string>>& args)
|
||||
else {
|
||||
fixup_path(m_dataDir);
|
||||
}
|
||||
|
||||
// Detect the proxy type
|
||||
if (!m_socks5Proxy.empty() && (m_socks5ProxyType == ProxyType::AUTO)) {
|
||||
m_socks5ProxyType = ProxyType::INVALID;
|
||||
|
||||
const size_t k = m_socks5Proxy.find_last_of(':');
|
||||
|
||||
if ((k != std::string::npos) && (k < m_socks5Proxy.length() - 1)) {
|
||||
const unsigned long port = strtoul(m_socks5Proxy.substr(k + 1).c_str(), nullptr, 10);
|
||||
|
||||
if ((port > 0) && (port < 65536)) {
|
||||
switch (port) {
|
||||
case 9050:
|
||||
m_socks5ProxyType = ProxyType::TOR;
|
||||
break;
|
||||
|
||||
default:
|
||||
m_socks5ProxyType = ProxyType::PLAIN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Params::process_arg(const std::vector<std::string>& arg)
|
||||
{
|
||||
if ((arg[0] == "host") && has1(arg)) {
|
||||
const std::string& address = arg[1];
|
||||
|
||||
if (m_hosts.empty()) {
|
||||
m_hosts.emplace_back();
|
||||
m_hosts.back().m_address = address;
|
||||
}
|
||||
else {
|
||||
const Host& h = m_hosts.back();
|
||||
|
||||
int32_t rpc_port = h.m_rpcPort;
|
||||
int32_t zmq_port = h.m_zmqPort;
|
||||
|
||||
// Better not to use "h" here because emplace_back can reallocate
|
||||
m_hosts.emplace_back(address, rpc_port, zmq_port, std::string());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "rpc-port") && has1(arg)) {
|
||||
if (m_hosts.empty()) {
|
||||
m_hosts.emplace_back();
|
||||
}
|
||||
|
||||
m_hosts.back().m_rpcPort = static_cast<int32_t>(std::min(std::max(strtoul(arg[1].c_str(), nullptr, 10), 1UL), 65535UL));
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "zmq-port") && has1(arg)) {
|
||||
if (m_hosts.empty()) {
|
||||
m_hosts.emplace_back();
|
||||
}
|
||||
|
||||
m_hosts.back().m_zmqPort = static_cast<int32_t>(std::min(std::max(strtoul(arg[1].c_str(), nullptr, 10), 1UL), 65535UL));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arg[0] == "light-mode") {
|
||||
m_lightMode = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "wallet") && has1(arg)) {
|
||||
const char* s = arg[1].c_str();
|
||||
|
||||
if (!m_mainWallet.decode(s)) {
|
||||
LOGERR(1, "Wallet " << s << " failed to decode");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "subaddress") && has1(arg)) {
|
||||
const char* s = arg[1].c_str();
|
||||
|
||||
if (!m_subaddress.decode(s)) {
|
||||
LOGERR(1, "Subaddress " << s << " failed to decode");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "stratum") && has1(arg)) {
|
||||
m_stratumAddresses = arg[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "stratum-ban-time") && has1(arg)) {
|
||||
m_stratumBanTime = strtoull(arg[1].c_str(), nullptr, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "p2p") && has1(arg)) {
|
||||
m_p2pAddresses = arg[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "addpeers") && has1(arg)) {
|
||||
m_p2pPeerList = arg[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "loglevel") && has1(arg)) {
|
||||
const int level = std::min(std::max<int>(static_cast<int>(strtol(arg[1].c_str(), nullptr, 10)), 0), log::MAX_GLOBAL_LOG_LEVEL);
|
||||
log::GLOBAL_LOG_LEVEL = level;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "data-dir") && has1(arg)) {
|
||||
m_dataDir = arg[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "log-file") && has1(arg)) {
|
||||
m_logFilePath = arg[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "sidechain-config") && has1(arg)) {
|
||||
m_sidechainConfig = arg[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "data-api") && has1(arg)) {
|
||||
m_apiPath = arg[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "local-api") || (arg[0] == "stratum-api")) {
|
||||
m_localStats = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arg[0] == "no-cache") {
|
||||
m_blockCache = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arg[0] == "no-color") {
|
||||
log::CONSOLE_COLORS = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WITH_RANDOMX
|
||||
if (arg[0] == "no-randomx") {
|
||||
m_disableRandomX = true;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (((arg[0] == "out-peers") || (arg[0] == "outpeers")) && has1(arg)) {
|
||||
m_maxOutgoingPeers = std::min(std::max(strtoul(arg[1].c_str(), nullptr, 10), 10UL), 450UL);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (((arg[0] == "in-peers") || (arg[0] == "inpeers")) && has1(arg)) {
|
||||
m_maxIncomingPeers = std::min(std::max(strtoul(arg[1].c_str(), nullptr, 10), 10UL), 450UL);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "start-mining") && has1(arg)) {
|
||||
m_minerThreads = std::min(std::max(strtoul(arg[1].c_str(), nullptr, 10), 1UL), 64UL);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arg[0] == "mini") {
|
||||
m_mini = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arg[0] == "nano") {
|
||||
m_nano = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arg[0] == "no-autodiff") {
|
||||
m_autoDiff = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "rpc-login") && has1(arg)) {
|
||||
if (m_hosts.empty()) {
|
||||
m_hosts.emplace_back();
|
||||
}
|
||||
|
||||
m_hosts.back().m_rpcLogin = arg[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WITH_TLS
|
||||
if (arg[0] == "rpc-ssl") {
|
||||
if (m_hosts.empty()) {
|
||||
m_hosts.emplace_back();
|
||||
}
|
||||
|
||||
m_hosts.back().m_rpcSSL = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "rpc-ssl-fingerprint") && has1(arg)) {
|
||||
if (m_hosts.empty()) {
|
||||
m_hosts.emplace_back();
|
||||
}
|
||||
|
||||
m_hosts.back().m_rpcSSL_Fingerprint = arg[1];
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((arg[0] == "socks5") && has1(arg)) {
|
||||
m_socks5Proxy = arg[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "socks5-proxy-type") && has1(arg)) {
|
||||
const std::string s = tolower(arg[1]);
|
||||
|
||||
m_socks5ProxyType = ProxyType::INVALID;
|
||||
|
||||
if (s == "auto") m_socks5ProxyType = ProxyType::AUTO;
|
||||
if (s == "plain") m_socks5ProxyType = ProxyType::PLAIN;
|
||||
if (s == "tor") m_socks5ProxyType = ProxyType::TOR;
|
||||
|
||||
return m_socks5ProxyType != ProxyType::INVALID;
|
||||
}
|
||||
|
||||
if (arg[0] == "no-dns") {
|
||||
m_dns = false;
|
||||
disable_resolve_host = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "p2p-external-port") && has1(arg)) {
|
||||
m_p2pExternalPort = static_cast<int32_t>(std::min(std::max(strtoul(arg[1].c_str(), nullptr, 10), 1UL), 65535UL));
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WITH_UPNP
|
||||
if ((arg[0] == "no-upnp") || (arg[0] == "no-igd")) {
|
||||
m_upnp = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arg[0] == "upnp-stratum") {
|
||||
m_upnpStratum = true;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((arg[0] == "merge-mine") && has1(arg) && has2(arg)) {
|
||||
m_mergeMiningHosts.emplace_back(arg[1], arg[2]);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WITH_TLS
|
||||
if ((arg[0] == "tls-cert") && has1(arg)) {
|
||||
m_tlsCert = arg[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "tls-cert-key") && has1(arg)) {
|
||||
m_tlsCertKey = arg[1];
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (arg[0] == "no-stratum-http") {
|
||||
m_enableStratumHTTP = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WITH_MERGE_MINING_DONATION
|
||||
if ((arg[0] == "adkf") && has1(arg)) {
|
||||
m_authorKeyFile = arg[1];
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (arg[0] == "full-validation") {
|
||||
m_enableFullValidation = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((arg[0] == "onion-address") && has1(arg)) {
|
||||
m_onionAddress = arg[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arg[0] == "no-clearnet-p2p") {
|
||||
m_noClearnetP2P = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arg[0] == "stratum-proxy-protocol") {
|
||||
m_stratumProxyProtocol = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arg[0] == "p2p-proxy-protocol") {
|
||||
m_p2pProxyProtocol = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Params::valid() const
|
||||
@@ -473,9 +528,28 @@ bool Params::valid() const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_socks5ProxyType == ProxyType::INVALID) {
|
||||
LOGERR(1, "Invalid SOCKS5 proxy type. Check the --socks5 and --socks5-proxy-type parameters.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WITH_GRPC
|
||||
bool Params::grpc_needed() const
|
||||
{
|
||||
for (const MergeMiningHost& h : m_mergeMiningHosts) {
|
||||
if (h.m_host.find(MergeMiningClientTari::TARI_PREFIX) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool Params::Host::init_display_name(const Params& p)
|
||||
{
|
||||
m_displayName = m_address;
|
||||
|
||||
+27
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -31,13 +31,17 @@ struct Params
|
||||
|
||||
explicit Params(const std::vector<std::vector<std::string>>& args);
|
||||
|
||||
bool valid() const;
|
||||
[[nodiscard]] bool valid() const;
|
||||
|
||||
#ifdef WITH_GRPC
|
||||
[[nodiscard]] bool grpc_needed() const;
|
||||
#endif
|
||||
|
||||
struct Host
|
||||
{
|
||||
Host() : m_address("127.0.0.1"), m_rpcPort(18081), m_zmqPort(18083), m_rpcSSL(false) {}
|
||||
|
||||
Host(const char* address, int32_t rpcPort, int32_t zmqPort, const char* rpcLogin)
|
||||
Host(const std::string& address, int32_t rpcPort, int32_t zmqPort, const std::string& rpcLogin)
|
||||
: m_address(address)
|
||||
, m_rpcPort(rpcPort)
|
||||
, m_zmqPort(zmqPort)
|
||||
@@ -45,9 +49,9 @@ struct Params
|
||||
, m_rpcSSL(false)
|
||||
{}
|
||||
|
||||
bool valid() const { return !m_address.empty() && m_rpcPort && m_zmqPort && (m_rpcPort != m_zmqPort); }
|
||||
[[nodiscard]] bool valid() const { return !m_address.empty() && m_rpcPort && m_zmqPort && (m_rpcPort != m_zmqPort); }
|
||||
|
||||
bool init_display_name(const Params& p);
|
||||
[[nodiscard]] bool init_display_name(const Params& p);
|
||||
|
||||
std::string m_address;
|
||||
int32_t m_rpcPort;
|
||||
@@ -104,7 +108,17 @@ struct Params
|
||||
bool m_mini = false;
|
||||
bool m_nano = false;
|
||||
bool m_autoDiff = true;
|
||||
|
||||
std::string m_socks5Proxy;
|
||||
|
||||
enum ProxyType {
|
||||
INVALID = -1,
|
||||
AUTO = 0,
|
||||
PLAIN = 1,
|
||||
TOR = 2,
|
||||
MAX = 3,
|
||||
} m_socks5ProxyType = ProxyType::AUTO;
|
||||
|
||||
bool m_dns = true;
|
||||
int32_t m_p2pExternalPort = 0;
|
||||
#ifdef WITH_UPNP
|
||||
@@ -135,6 +149,14 @@ struct Params
|
||||
std::string m_onionAddress;
|
||||
hash m_onionPubkey;
|
||||
bool m_noClearnetP2P = false;
|
||||
bool m_stratumProxyProtocol = false;
|
||||
bool m_p2pProxyProtocol = false;
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool process_arg(const std::vector<std::string>& arg);
|
||||
|
||||
[[nodiscard]] static bool has1(const std::vector<std::string>& arg) { return (arg.size() > 1) && !arg[1].empty();}
|
||||
[[nodiscard]] static bool has2(const std::vector<std::string>& arg) { return (arg.size() > 2) && !arg[2].empty();}
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && defined(__cppcheck__)
|
||||
// cppcheck chokes on rapidjson headers in Windows
|
||||
#else
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+47
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -44,7 +44,7 @@ static constexpr int32_t BAN_THRESHOLD_POINTS = -15;
|
||||
namespace p2pool {
|
||||
|
||||
StratumServer::StratumServer(p2pool* pool)
|
||||
: TCPServer(DEFAULT_BACKLOG, StratumClient::allocate, std::string())
|
||||
: TCPServer(DEFAULT_BACKLOG, StratumClient::allocate, std::string(), Params::ProxyType::INVALID, pool->params().m_stratumProxyProtocol)
|
||||
, m_pool(pool)
|
||||
, m_autoDiff(pool->params().m_autoDiff)
|
||||
, m_enableFullValidation(pool->params().m_enableFullValidation)
|
||||
@@ -70,6 +70,7 @@ StratumServer::StratumServer(p2pool* pool)
|
||||
|
||||
m_hashrateData[0] = { seconds_since_epoch(), 0 };
|
||||
|
||||
uv_mutex_init_checked(&m_resetShareCountersLock);
|
||||
uv_mutex_init_checked(&m_blobsQueueLock);
|
||||
uv_mutex_init_checked(&m_showWorkersLock);
|
||||
uv_mutex_init_checked(&m_rngLock);
|
||||
@@ -77,6 +78,9 @@ StratumServer::StratumServer(p2pool* pool)
|
||||
|
||||
m_extraNonce = get_random32();
|
||||
|
||||
uv_async_init_checked(&m_loop, &m_resetShareCountersAsync, on_reset_share_counters);
|
||||
m_resetShareCountersAsync.data = this;
|
||||
|
||||
uv_async_init_checked(&m_loop, &m_blobsAsync, on_blobs_ready);
|
||||
m_blobsAsync.data = this;
|
||||
m_blobsQueue.reserve(2);
|
||||
@@ -101,6 +105,7 @@ StratumServer::~StratumServer()
|
||||
}
|
||||
}
|
||||
|
||||
uv_mutex_destroy(&m_resetShareCountersLock);
|
||||
uv_mutex_destroy(&m_blobsQueueLock);
|
||||
uv_mutex_destroy(&m_showWorkersLock);
|
||||
uv_mutex_destroy(&m_rngLock);
|
||||
@@ -480,6 +485,8 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
|
||||
update_auto_diff(client, share.m_timestamp, share.m_hashes);
|
||||
|
||||
++client->m_stratumShares;
|
||||
|
||||
// If this share is below sidechain difficulty, process it in this thread because it'll be quick
|
||||
if (!share.m_highEnoughDifficulty && !m_enableFullValidation) {
|
||||
on_share_found(&share.m_req);
|
||||
@@ -565,6 +572,7 @@ void StratumServer::show_workers()
|
||||
<< log::pad_right("uptime", 20)
|
||||
<< log::pad_right("difficulty", 20)
|
||||
<< log::pad_right("hashrate", 15)
|
||||
<< log::pad_right("shares", 12)
|
||||
<< "name"
|
||||
);
|
||||
|
||||
@@ -585,11 +593,16 @@ void StratumServer::show_workers()
|
||||
constexpr bool is_tls = false;
|
||||
#endif
|
||||
|
||||
char shares_buf[16] = {};
|
||||
log::Stream s(shares_buf);
|
||||
s << c->m_sidechainShares << '/' << c->m_stratumShares << '\0';
|
||||
|
||||
LOGINFO(0, log::pad_right(static_cast<const char*>(c->m_addrString), addr_len + 8)
|
||||
<< (is_tls ? "yes " : "no ")
|
||||
<< 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)
|
||||
<< log::pad_right(static_cast<const char*>(shares_buf), 12)
|
||||
<< (c->m_rpcId ? c->m_customUser : "not logged in")
|
||||
);
|
||||
++n;
|
||||
@@ -600,6 +613,22 @@ void StratumServer::show_workers()
|
||||
|
||||
void StratumServer::reset_share_counters()
|
||||
{
|
||||
MutexLock lock(m_resetShareCountersLock);
|
||||
|
||||
if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_resetShareCountersAsync))) {
|
||||
uv_async_send(&m_resetShareCountersAsync);
|
||||
}
|
||||
}
|
||||
|
||||
void StratumServer::on_reset_share_counters()
|
||||
{
|
||||
check_event_loop_thread(__func__);
|
||||
|
||||
for (StratumClient* c = static_cast<StratumClient*>(m_connectedClientsList->m_next); c != m_connectedClientsList; c = static_cast<StratumClient*>(c->m_next)) {
|
||||
c->m_stratumShares = 0;
|
||||
c->m_sidechainShares = 0;
|
||||
}
|
||||
|
||||
WriteLock lock(m_hashrateDataLock);
|
||||
|
||||
m_cumulativeHashesAtLastShare = m_cumulativeHashes;
|
||||
@@ -1058,10 +1087,13 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
|
||||
server->check_event_loop_thread(__func__);
|
||||
|
||||
bool share_found = false;
|
||||
|
||||
if (share->m_highEnoughDifficulty) {
|
||||
const char* s = share->m_clientCustomUser;
|
||||
if (share->m_result == SubmittedShare::Result::OK) {
|
||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << share->m_mainchainHeight << ", sidechain height " << share->m_sidechainHeight << ", diff " << share->m_sidechainDifficulty << ", client " << static_cast<char*>(share->m_clientAddrString) << (*s ? ", user " : "") << s << ", effort " << share->m_effort << '%');
|
||||
share_found = true;
|
||||
}
|
||||
else {
|
||||
static const char* reason_list[] = {
|
||||
@@ -1117,6 +1149,10 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
|
||||
client->m_score += share->m_score;
|
||||
|
||||
if (share_found) {
|
||||
++client->m_sidechainShares;
|
||||
}
|
||||
|
||||
if (bad_share && (client->m_score <= BAN_THRESHOLD_POINTS)) {
|
||||
client->ban(server->m_banTime);
|
||||
client->close();
|
||||
@@ -1156,6 +1192,10 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
|
||||
void StratumServer::on_shutdown()
|
||||
{
|
||||
{
|
||||
MutexLock lock(m_resetShareCountersLock);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_resetShareCountersAsync), nullptr);
|
||||
}
|
||||
{
|
||||
MutexLock lock(m_blobsQueueLock);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_blobsAsync), nullptr);
|
||||
@@ -1181,6 +1221,8 @@ StratumServer::StratumClient::StratumClient()
|
||||
, m_customUser{}
|
||||
, m_lastJobTarget(0)
|
||||
, m_score(0)
|
||||
, m_stratumShares(0)
|
||||
, m_sidechainShares(0)
|
||||
{
|
||||
m_rawReadBuf[0] = '\0';
|
||||
m_stratumReadBuf[0] = '\0';
|
||||
@@ -1210,6 +1252,9 @@ void StratumServer::StratumClient::reset()
|
||||
m_lastJobTarget = 0;
|
||||
|
||||
m_score = 0;
|
||||
|
||||
m_stratumShares = 0;
|
||||
m_sidechainShares = 0;
|
||||
}
|
||||
|
||||
bool StratumServer::StratumClient::on_connect()
|
||||
|
||||
+10
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -91,6 +91,9 @@ public:
|
||||
uint64_t m_lastJobTarget;
|
||||
|
||||
int32_t m_score;
|
||||
|
||||
uint32_t m_stratumShares;
|
||||
uint32_t m_sidechainShares;
|
||||
};
|
||||
|
||||
[[nodiscard]] bool on_login(StratumClient* client, uint32_t id, const char* login);
|
||||
@@ -128,6 +131,12 @@ private:
|
||||
hash m_seedHash;
|
||||
};
|
||||
|
||||
uv_mutex_t m_resetShareCountersLock;
|
||||
uv_async_t m_resetShareCountersAsync;
|
||||
|
||||
static void on_reset_share_counters(uv_async_t* handle) { reinterpret_cast<StratumServer*>(handle->data)->on_reset_share_counters(); }
|
||||
void on_reset_share_counters();
|
||||
|
||||
uv_mutex_t m_blobsQueueLock;
|
||||
uv_async_t m_blobsAsync;
|
||||
std::vector<BlobsData*> m_blobsQueue;
|
||||
|
||||
+180
-37
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -23,7 +23,7 @@ static thread_local const char* log_category_prefix = "TCPServer ";
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
TCPServer::TCPServer(int default_backlog, allocate_client_callback allocate_new_client, const std::string& socks5Proxy)
|
||||
TCPServer::TCPServer(int default_backlog, allocate_client_callback allocate_new_client, const std::string& socks5Proxy, Params::ProxyType socks5ProxyType, bool proxyProtocol)
|
||||
: m_allocateNewClient(allocate_new_client)
|
||||
, m_defaultBacklog(default_backlog)
|
||||
, m_loopThread{}
|
||||
@@ -32,9 +32,11 @@ TCPServer::TCPServer(int default_backlog, allocate_client_callback allocate_new_
|
||||
, m_portMapping(0)
|
||||
#endif
|
||||
, m_socks5Proxy(socks5Proxy)
|
||||
, m_socks5ProxyType(socks5ProxyType)
|
||||
, m_socks5ProxyV6(false)
|
||||
, m_socks5ProxyIP{}
|
||||
, m_socks5ProxyPort(-1)
|
||||
, m_proxyProtocol(proxyProtocol)
|
||||
, m_finished(0)
|
||||
, m_listenPort(-1)
|
||||
, m_loop{}
|
||||
@@ -319,9 +321,9 @@ bool TCPServer::connect_to_peer(bool is_v6, const raw_ip& ip, int port)
|
||||
bool TCPServer::connect_to_peer(const std::string& domain, int port)
|
||||
{
|
||||
if (m_socks5Proxy.empty()) {
|
||||
LOGERR(1, "Can't connect to " << domain << ": SOCKS5 proxy is required");
|
||||
return false;
|
||||
}
|
||||
LOGERR(1, "Can't connect to " << domain << ": SOCKS5 proxy is required");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_finished.load()) {
|
||||
return false;
|
||||
@@ -337,7 +339,7 @@ bool TCPServer::connect_to_peer(const std::string& domain, int port)
|
||||
s << domain << ':' << port << '\0';
|
||||
|
||||
if (s.m_spilled) {
|
||||
LOGERR(1, "Can't connect to " << domain << ": too long domain name");
|
||||
LOGERR(1, "Can't connect to " << domain << ": too long domain name");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -891,45 +893,51 @@ void TCPServer::on_new_client(uv_stream_t* server, Client* client)
|
||||
|
||||
LOGINFO(5, "new connection " << (client->m_isIncoming ? "from " : "to ") << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
|
||||
if (is_banned(client->isV6(), client->m_addr)) {
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(client->m_addrString) << log::NoColor() << " is banned, disconnecting");
|
||||
client->close();
|
||||
return;
|
||||
if (m_proxyProtocol && client->m_isIncoming) {
|
||||
// Defer ban check and on_connect until real IP is extracted from PROXY protocol header
|
||||
client->m_proxyProtocolState = Client::ProxyProtocolState::ExpectingHeader;
|
||||
}
|
||||
|
||||
TCPServer* owner = client->m_owner;
|
||||
|
||||
if (owner->m_finished.load()) {
|
||||
client->close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->m_isIncoming || owner->m_socks5Proxy.empty()) {
|
||||
if (!client->on_connect()) {
|
||||
else {
|
||||
if (is_banned(client->isV6(), client->m_addr)) {
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(client->m_addrString) << log::NoColor() << " is banned, disconnecting");
|
||||
client->close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const bool result = owner->send(client,
|
||||
[](uint8_t* buf, size_t buf_size) -> size_t
|
||||
{
|
||||
if (buf_size < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf[0] = 5; // Protocol version (SOCKS5)
|
||||
buf[1] = 1; // NMETHODS
|
||||
buf[2] = 0; // Method 0 (no authentication)
|
||||
TCPServer* owner = client->m_owner;
|
||||
|
||||
return 3;
|
||||
});
|
||||
if (owner->m_finished.load()) {
|
||||
client->close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
client->m_socks5ProxyState = Client::Socks5ProxyState::MethodSelectionSent;
|
||||
if (client->m_isIncoming || owner->m_socks5Proxy.empty()) {
|
||||
if (!client->on_connect()) {
|
||||
client->close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
client->close();
|
||||
const bool result = owner->send(client,
|
||||
[](uint8_t* buf, size_t buf_size) -> size_t
|
||||
{
|
||||
if (buf_size < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf[0] = 5; // Protocol version (SOCKS5)
|
||||
buf[1] = 1; // NMETHODS
|
||||
buf[2] = 0; // Method 0 (no authentication)
|
||||
|
||||
return 3;
|
||||
});
|
||||
|
||||
if (result) {
|
||||
client->m_socks5ProxyState = Client::Socks5ProxyState::MethodSelectionSent;
|
||||
}
|
||||
else {
|
||||
client->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1084,6 +1092,7 @@ TCPServer::Client::Client(char* read_buf, size_t size)
|
||||
, m_port(0)
|
||||
, m_addrString{}
|
||||
, m_socks5ProxyState(Socks5ProxyState::Default)
|
||||
, m_proxyProtocolState(ProxyProtocolState::None)
|
||||
, m_resetCounter{ 0 }
|
||||
#ifdef WITH_TLS
|
||||
, m_tlsChecked(false)
|
||||
@@ -1110,6 +1119,7 @@ void TCPServer::Client::reset()
|
||||
m_port = -1;
|
||||
m_addrString[0] = '\0';
|
||||
m_socks5ProxyState = Socks5ProxyState::Default;
|
||||
m_proxyProtocolState = ProxyProtocolState::None;
|
||||
m_readBuf[0] = '\0';
|
||||
m_readBuf[m_readBufSize - 1] = '\0';
|
||||
|
||||
@@ -1154,7 +1164,12 @@ void TCPServer::Client::on_read(uv_stream_t* stream, ssize_t nread, const uv_buf
|
||||
|
||||
if (nread > 0) {
|
||||
if (client->m_owner && !client->m_owner->m_finished.load()) {
|
||||
if (client->m_socks5ProxyState == Socks5ProxyState::Default) {
|
||||
if (client->m_proxyProtocolState == ProxyProtocolState::ExpectingHeader) {
|
||||
if (!client->on_proxy_protocol(buf->base, static_cast<uint32_t>(nread))) {
|
||||
client->close();
|
||||
}
|
||||
}
|
||||
else if (client->m_socks5ProxyState == Socks5ProxyState::Default) {
|
||||
if (!client->on_read(buf->base, static_cast<uint32_t>(nread))) {
|
||||
client->close();
|
||||
}
|
||||
@@ -1314,6 +1329,134 @@ bool TCPServer::Client::on_proxy_handshake(const char* data, uint32_t size)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TCPServer::Client::on_proxy_protocol(const char* data, uint32_t size)
|
||||
{
|
||||
// PROXY protocol v2 signature
|
||||
static constexpr uint8_t ppv2_signature[12] = { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A };
|
||||
|
||||
if ((data != m_readBuf + m_numRead) || (data + size > m_readBuf + m_readBufSize)) {
|
||||
LOGERR(1, "peer " << static_cast<char*>(m_addrString) << " invalid data pointer or size in on_proxy_protocol()");
|
||||
return false;
|
||||
}
|
||||
m_numRead += size;
|
||||
|
||||
// Need at least 16 bytes for the fixed header
|
||||
if (m_numRead < 16) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint8_t* p = reinterpret_cast<const uint8_t*>(m_readBuf);
|
||||
|
||||
// Validate 12-byte signature
|
||||
if (memcmp(p, ppv2_signature, 12) != 0) {
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " invalid PROXY protocol v2 signature");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Byte 12: version (high nibble) and command (low nibble)
|
||||
const uint8_t ver_cmd = p[12];
|
||||
const uint8_t version = (ver_cmd >> 4) & 0x0F;
|
||||
const uint8_t command = ver_cmd & 0x0F;
|
||||
|
||||
if (version != 2) {
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " unsupported PROXY protocol version " << version);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (command > 1) {
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " invalid PROXY protocol command " << command);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Byte 13: address family (high nibble) and protocol (low nibble)
|
||||
const uint8_t family_proto = p[13];
|
||||
|
||||
// Bytes 14-15: address data length (big-endian), includes address block + optional TLV extensions
|
||||
const uint16_t addr_len = (static_cast<uint16_t>(p[14]) << 8) | static_cast<uint16_t>(p[15]);
|
||||
|
||||
const uint32_t total_header_len = 16 + static_cast<uint32_t>(addr_len);
|
||||
|
||||
if (total_header_len > m_readBufSize) {
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " PROXY protocol v2 header too large (" << total_header_len << " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for complete header
|
||||
if (m_numRead < total_header_len) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// PROXY command: extract real client address
|
||||
if (command == 1) {
|
||||
const uint8_t family = (family_proto >> 4) & 0x0F;
|
||||
|
||||
if (family == 1) {
|
||||
// AF_INET: src(4) + dst(4) + sport(2) + dport(2) = 12 bytes minimum, may be larger with TLV extensions
|
||||
if (addr_len < 12) {
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " PROXY protocol v2 IPv4 address data too short (" << addr_len << " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(m_addr.data, raw_ip::ipv4_prefix, sizeof(raw_ip::ipv4_prefix));
|
||||
memcpy(m_addr.data + sizeof(raw_ip::ipv4_prefix), p + 16, 4);
|
||||
m_port = (static_cast<int>(p[24]) << 8) | static_cast<int>(p[25]);
|
||||
m_addressType = AddressType::IPv4;
|
||||
}
|
||||
else if (family == 2) {
|
||||
// AF_INET6: src(16) + dst(16) + sport(2) + dport(2) = 36 bytes minimum, may be larger with TLV extensions
|
||||
if (addr_len < 36) {
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " PROXY protocol v2 IPv6 address data too short (" << addr_len << " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(m_addr.data, p + 16, 16);
|
||||
m_port = (static_cast<int>(p[48]) << 8) | static_cast<int>(p[49]);
|
||||
m_addressType = AddressType::IPv6;
|
||||
}
|
||||
else {
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " PROXY protocol v2 unsupported address family " << family);
|
||||
return false;
|
||||
}
|
||||
|
||||
init_addr_string();
|
||||
LOGINFO(5, "PROXY protocol v2: real address is " << log::Gray() << static_cast<char*>(m_addrString));
|
||||
}
|
||||
// LOCAL command (health checks): keep original address
|
||||
|
||||
// Move remaining data to beginning of buffer
|
||||
m_numRead -= total_header_len;
|
||||
if (m_numRead > 0) {
|
||||
memmove(m_readBuf, m_readBuf + total_header_len, m_numRead);
|
||||
}
|
||||
|
||||
m_proxyProtocolState = ProxyProtocolState::HeaderReceived;
|
||||
|
||||
// Deferred ban check with real IP
|
||||
if (m_owner->is_banned(isV6(), m_addr)) {
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " is banned, disconnecting");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_owner->m_finished.load()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!on_connect()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pass remaining data to the protocol handler
|
||||
if (m_numRead > 0) {
|
||||
const uint32_t nread = m_numRead;
|
||||
m_numRead = 0;
|
||||
if (!on_read(m_readBuf, nread)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TCPServer::Client::on_write(uv_write_t* req, int status)
|
||||
{
|
||||
WriteBuf* buf = static_cast<WriteBuf*>(req->data);
|
||||
|
||||
+13
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,6 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "uv_util.h"
|
||||
#include "params.h"
|
||||
|
||||
#ifdef WITH_TLS
|
||||
#include "tls.h"
|
||||
@@ -33,7 +34,7 @@ public:
|
||||
struct Client;
|
||||
typedef Client* (*allocate_client_callback)();
|
||||
|
||||
TCPServer(int default_backlog, allocate_client_callback allocate_new_client, const std::string& socks5Proxy);
|
||||
TCPServer(int default_backlog, allocate_client_callback allocate_new_client, const std::string& socks5Proxy, Params::ProxyType socks5ProxyType, bool proxyProtocol = false);
|
||||
virtual ~TCPServer();
|
||||
|
||||
[[nodiscard]] bool connect_to_peer(bool is_v6, const char* ip, int port);
|
||||
@@ -66,6 +67,7 @@ public:
|
||||
[[nodiscard]] virtual bool on_connect() = 0;
|
||||
[[nodiscard]] virtual bool on_read(const char* data, uint32_t size) = 0;
|
||||
[[nodiscard]] bool on_proxy_handshake(const char* data, uint32_t size);
|
||||
[[nodiscard]] bool on_proxy_protocol(const char* data, uint32_t size);
|
||||
virtual void on_connect_failed(int /*err*/) {}
|
||||
virtual void on_read_failed(int /*err*/) {}
|
||||
virtual void on_disconnected() {}
|
||||
@@ -120,6 +122,12 @@ public:
|
||||
ConnectRequestSent,
|
||||
} m_socks5ProxyState;
|
||||
|
||||
enum class ProxyProtocolState : uint8_t {
|
||||
None,
|
||||
ExpectingHeader,
|
||||
HeaderReceived,
|
||||
} m_proxyProtocolState;
|
||||
|
||||
std::atomic<uint32_t> m_resetCounter;
|
||||
|
||||
#ifdef WITH_TLS
|
||||
@@ -191,10 +199,13 @@ protected:
|
||||
#endif
|
||||
|
||||
std::string m_socks5Proxy;
|
||||
Params::ProxyType m_socks5ProxyType;
|
||||
bool m_socks5ProxyV6;
|
||||
raw_ip m_socks5ProxyIP;
|
||||
int m_socks5ProxyPort;
|
||||
|
||||
bool m_proxyProtocol;
|
||||
|
||||
std::atomic<int> m_finished;
|
||||
int m_listenPort;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+2
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -1014,12 +1014,8 @@ hash from_onion_v3(const std::string& address)
|
||||
return {};
|
||||
}
|
||||
|
||||
// Convert address to lowercase
|
||||
std::string s = address;
|
||||
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
|
||||
// Checksum validation
|
||||
if (to_onion_v3(result) != s) {
|
||||
if (to_onion_v3(result) != tolower(address)) {
|
||||
LOGWARN(3, "Invalid onion address \"" << address << "\": checksum failed");
|
||||
return {};
|
||||
}
|
||||
|
||||
+14
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -35,7 +35,7 @@
|
||||
namespace p2pool {
|
||||
|
||||
#define P2POOL_VERSION_MAJOR 4
|
||||
#define P2POOL_VERSION_MINOR 13
|
||||
#define P2POOL_VERSION_MINOR 14
|
||||
#define P2POOL_VERSION_PATCH 0
|
||||
|
||||
constexpr uint32_t P2POOL_VERSION = (P2POOL_VERSION_MAJOR << 16) | (P2POOL_VERSION_MINOR << 8) | P2POOL_VERSION_PATCH;
|
||||
@@ -395,6 +395,18 @@ FORCEINLINE void secure_zero_memory(T& value)
|
||||
secure_zero_memory(&value, sizeof(T));
|
||||
}
|
||||
|
||||
FORCEINLINE std::string tolower(const std::string& s)
|
||||
{
|
||||
std::string result;
|
||||
result.reserve(s.size() + 1);
|
||||
|
||||
for (char c : s) {
|
||||
result.push_back(static_cast<char>(std::tolower(c)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string to_onion_v3(const hash& pubkey);
|
||||
hash from_onion_v3(const std::string& address);
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
* Portions Copyright (c) 2012-2013 The Cryptonote developers
|
||||
* Portions Copyright (c) 2014-2021 The Monero Project
|
||||
* Portions Copyright (c) 2021 XMRig <https://github.com/xmrig>
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
+11
-4
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -38,6 +38,13 @@ ZMQReader::ZMQReader(const std::string& address, uint32_t zmq_port, const std::s
|
||||
m_proxy.clear();
|
||||
}
|
||||
|
||||
const bool is_v6 = address.find_first_of(':') != std::string::npos;
|
||||
|
||||
if (is_v6) {
|
||||
m_publisher.set(zmq::sockopt::ipv6, 1);
|
||||
m_subscriber.set(zmq::sockopt::ipv6, 1);
|
||||
}
|
||||
|
||||
char addr_buf[log::Stream::BUF_SIZE + 1];
|
||||
addr_buf[0] = '\0';
|
||||
|
||||
@@ -49,7 +56,7 @@ ZMQReader::ZMQReader(const std::string& address, uint32_t zmq_port, const std::s
|
||||
|
||||
try {
|
||||
log::Stream s(addr_buf);
|
||||
s << "tcp://127.0.0.1:" << port << '\0';
|
||||
s << (is_v6 ? "tcp://[::1]:" : "tcp://127.0.0.1:") << port << '\0';
|
||||
m_publisher.bind(addr_buf);
|
||||
m_publisherPort = static_cast<uint16_t>(port);
|
||||
break;
|
||||
@@ -65,11 +72,11 @@ ZMQReader::ZMQReader(const std::string& address, uint32_t zmq_port, const std::s
|
||||
throw zmq::error_t(EFSM);
|
||||
}
|
||||
|
||||
LOGINFO(5, "listening on tcp://127.0.0.1:" << m_publisherPort << " for internal communications");
|
||||
std::string addr(addr_buf);
|
||||
LOGINFO(5, "listening on " << addr << " for internal communications");
|
||||
|
||||
m_subscriber.set(zmq::sockopt::connect_timeout, 1000);
|
||||
|
||||
std::string addr(addr_buf);
|
||||
if (!connect(addr, false)) {
|
||||
throw zmq::error_t(EFSM);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021-2025 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2021-2026 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
Reference in New Issue
Block a user