Compare commits

...

125 Commits

Author SHA1 Message Date
SChernykh 92827035e0 p2pool v3.2
C/C++ CI / build-ubuntu (map[c:gcc-11 cpp:g++-11 os:ubuntu-20.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-12 cpp:g++-12 os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-static-libs (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (map[os:ubuntu-20.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (map[os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-windows-msys2 (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:OFF upnp:OFF vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:OFF upnp:ON vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:ON upnp:ON vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-macos (macos-11) (push) Has been cancelled
C/C++ CI / build-macos (macos-12) (push) Has been cancelled
C/C++ CI / build-freebsd (map[architecture:x86-64 host:ubuntu-22.04 name:freebsd version:12.4]) (push) Has been cancelled
C/C++ CI / build-openbsd (map[architecture:x86-64 host:ubuntu-22.04 name:openbsd version:7.2]) (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
cppcheck / cppcheck-ubuntu (push) Has been cancelled
cppcheck / cppcheck-windows (push) Has been cancelled
Microsoft C++ Code Analysis / Analyze (push) Has been cancelled
Sync test / sync-test-ubuntu (push) Has been cancelled
Sync test / sync-test-macos (push) Has been cancelled
Sync test / sync-test-windows (push) Has been cancelled
2023-03-31 18:30:34 +02:00
SChernykh 5f7b8c3371 Fixed cppcheck false positives 2023-03-31 13:35:23 +02:00
SChernykh 03eddddc0e Cache: save data from previous Monero block too
To improve caching of stale/outdated sidechain blocks.
2023-03-31 12:59:11 +02:00
SChernykh 080fbaa26c Util: adder PerfTimer 2023-03-30 19:09:15 +02:00
SChernykh f6a285de87 API: added PPLNS window size to pool stats 2023-03-28 13:53:52 +02:00
SChernykh 55e4ea0277 API: added sidechain diff and height to pool stats 2023-03-28 11:40:43 +02:00
SChernykh bb80fe12a0 Added a check for unstable hardware 2023-03-28 10:27:27 +02:00
SChernykh 5c67f0df20 P2PServer: truncate too long peers list in api 2023-03-27 12:15:33 +02:00
SChernykh 3697c079fa Fixed cppcheck error 2023-03-27 11:47:48 +02:00
SChernykh 2e6041dcf5 P2PServer: add local api data 2023-03-27 11:16:57 +02:00
SChernykh 645de31fe3 SideChain: stricter checks in get_difficulty 2023-03-24 13:26:30 +01:00
SChernykh eef90da7ae SideChain: stricter checks of alternative chains 2023-03-24 11:50:39 +01:00
SChernykh c0586a5089 Added --no-igd as an alias for --no-upnp
For compatibility with monerod command line parameters
2023-03-21 19:31:18 +01:00
SChernykh b30018bb38 SideChain::update_depths() - reduced log level 2023-03-21 16:22:11 +01:00
SChernykh 6c45573976 Faster block verification 2023-03-21 14:13:47 +01:00
SChernykh b6c1b1a6d7 P2PServer: log time it took to relay a block 2023-03-21 11:22:48 +01:00
SChernykh d41a441e98 UPnP: remove port mapping on shutdown 2023-03-21 10:23:17 +01:00
SChernykh 86b6cf2d65 UPnP: handle port mapping conflicts 2023-03-20 22:13:12 +01:00
SChernykh cf11993cb1 Update miniupnp 2023-03-20 15:56:21 +01:00
SChernykh fe7ef30650 Don't port forward Stratum port by default
Added a command line parameter to enable it
2023-03-20 14:04:19 +01:00
SChernykh bb4c7f0298 Run UPnP discovery in the background 2023-03-20 11:56:41 +01:00
SChernykh a7db46d69b Fixed command line help output 2023-03-20 10:04:43 +01:00
SChernykh ce81c2769c Added get_hashing_blobs test with full mempool 2023-03-20 09:46:09 +01:00
SChernykh d4a57370ef Disable cmd params depending on compile flags 2023-03-20 09:17:59 +01:00
SChernykh 634039e551 Fixed static build 2023-03-19 23:55:22 +01:00
SChernykh a83edceae3 Merge pull request #243 from hinto-janai/master
README: update gupax link
2023-03-19 23:04:11 +01:00
SChernykh 19cc044cab Added UPnP port forwarding 2023-03-19 23:02:09 +01:00
SChernykh 79ad29e99a Fixed pool_block.deserialize test 2023-03-19 20:54:09 +01:00
hinto.janai f96e291681 README: update gupax link 2023-03-19 15:05:02 -04:00
SChernykh 6685a22118 CI test-sync: removed unnecessary dependencies 2023-03-19 19:32:20 +01:00
SChernykh a84a0bd8dc Added miniupnp submodule 2023-03-19 19:13:29 +01:00
SChernykh c2d8c806d2 Removed fork code, only v2 is left 2023-03-19 17:19:25 +01:00
SChernykh 387860aa4a Post-fork test data 2023-03-19 11:38:43 +01:00
SChernykh de7bdba95f TCPServer: don't spam logs with 127.0.0.1 bans 2023-03-17 16:59:01 +01:00
SChernykh 6baf5247cb Fixed parallel_run logic on single CPU systems 2023-03-17 16:01:52 +01:00
SChernykh 146d29b627 Don't let user connect to a node without ZMQ 2023-03-10 15:02:07 +01:00
SChernykh c1a1249be1 Code cleanup 2023-03-09 10:59:52 +01:00
SChernykh 8e7a797586 Merge pull request #238 from twlee79/feature/namedpipe
Allow named pipe as stdin
2023-03-09 10:44:54 +01:00
SChernykh 14a1918fda Merge pull request #239 from hx507/master
Disable LTO on NixOS builds
2023-03-09 09:29:45 +01:00
git_unspecified 54e755efdd Fix nix build 2023-03-09 02:12:37 -06:00
T W Lee 3df7430663 allow named pipe as stdin, fixes after review 2023-03-09 14:38:20 +13:00
T W Lee c71d89c95a allow named pipe as stdin 2023-03-08 02:35:49 +13:00
SChernykh 1b5860eec8 Fixed cppcheck errors 2023-03-06 14:52:47 +01:00
SChernykh 4741880044 StratumServer: added address sanitizer checks 2023-03-06 14:30:13 +01:00
SChernykh 02a0cb174a StratumServer: show a warning for invalid shares 2023-03-02 13:19:25 +01:00
SChernykh e53fd7a062 Tests: fixed block_template test volatility
Hashes should stay the same from now on.
2023-03-01 09:20:09 +01:00
SChernykh a76d8049c0 p2pool v3.1
C/C++ CI / build-ubuntu (map[c:gcc-11 cpp:g++-11 os:ubuntu-20.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-12 cpp:g++-12 os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-static-libs (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (map[os:ubuntu-20.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (map[os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-windows-msys2 (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:OFF vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:ON vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-macos (macos-11) (push) Has been cancelled
C/C++ CI / build-macos (macos-12) (push) Has been cancelled
C/C++ CI / build-freebsd (map[architecture:x86-64 host:ubuntu-22.04 name:freebsd version:12.4]) (push) Has been cancelled
C/C++ CI / build-openbsd (map[architecture:x86-64 host:ubuntu-22.04 name:openbsd version:7.2]) (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
cppcheck / cppcheck-ubuntu (push) Has been cancelled
Microsoft C++ Code Analysis / Analyze (push) Has been cancelled
cppcheck / cppcheck-windows (push) Has been cancelled
Sync test / sync-test-ubuntu (push) Has been cancelled
Sync test / sync-test-macos (push) Has been cancelled
Sync test / sync-test-windows (push) Has been cancelled
2023-02-28 18:11:54 +01:00
SChernykh 0ce1558f54 Fixed data races 2023-02-27 19:35:28 +01:00
SChernykh 2807f9a51c TCPServer: disabled debug check in release builds 2023-02-27 19:14:03 +01:00
SChernykh 03a3423da0 StratumServer: fixed data race 2023-02-27 18:58:56 +01:00
SChernykh 12a011a9ff TCPServer: removed unnecessary mutex 2023-02-27 15:59:42 +01:00
SChernykh 7a1afc7a95 Fixed cppcheck error 2023-02-26 20:12:00 +01:00
SChernykh 262b4f2a2e Updated dependencies 2023-02-26 19:16:13 +01:00
SChernykh fadc5055b2 Updated --no-autodiff description 2023-02-21 09:17:42 +01:00
SChernykh 1c908c261d TCPServer: refactored memory allocation 2023-02-17 08:47:52 +01:00
SChernykh 39216df8eb CI: added openbsd build 2023-02-16 15:16:24 +01:00
SChernykh ed59579a9d Don't reset m_cumulativeFoundSharesDiff 2023-02-13 09:41:11 +01:00
SChernykh 2aa7389c3f Fix: never reset m_cumulativeHashes
Hashrates in stratum status can be wrong if `m_cumulativeHashes` decreases.
2023-02-10 17:55:26 +01:00
SChernykh 02970e2f8f Update RandomX 2023-02-07 14:14:36 +01:00
SChernykh 2e00ac402a Github CI: added timeouts to all checks 2023-02-07 11:13:13 +01:00
SChernykh c94dd46294 Increased timeout for FreeBSD build 2023-02-07 11:00:14 +01:00
SChernykh df802c3b26 Added an option to compile without LTO 2023-02-07 10:04:37 +01:00
SChernykh 8430a506cc Update RandomX 2023-02-06 10:01:28 +01:00
SChernykh 46a6196c92 Disabled -fassociative-math on old GCC 2023-02-02 22:22:49 +01:00
SChernykh e64a0acfb3 p2pool v3.0
C/C++ CI / build-ubuntu (map[c:gcc-11 cpp:g++-11 os:ubuntu-20.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-12 cpp:g++-12 os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-static-libs (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (map[os:ubuntu-20.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (map[os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-windows-msys2 (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:ON vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\amd64\ os:2022 rx:OFF vs:Visual Studio 17 2022]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\amd64\ os:2022 rx:ON vs:Visual Studio 17 2022]) (push) Has been cancelled
C/C++ CI / build-macos (macos-11) (push) Has been cancelled
C/C++ CI / build-macos (macos-12) (push) Has been cancelled
C/C++ CI / build-freebsd (map[architecture:x86-64 host:ubuntu-22.04 name:freebsd version:12.4]) (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
cppcheck / cppcheck-ubuntu (push) Has been cancelled
cppcheck / cppcheck-windows (push) Has been cancelled
Microsoft C++ Code Analysis / Analyze (push) Has been cancelled
Sync test / sync-test-ubuntu (push) Has been cancelled
Sync test / sync-test-macos (push) Has been cancelled
Sync test / sync-test-windows (push) Has been cancelled
2023-01-31 18:17:18 +01:00
SChernykh 14f7a1cb61 Merge branch 'hardfork' 2023-01-31 18:01:06 +01:00
SChernykh 1cfacf4118 Code cleanup and fixes 2023-01-28 09:58:32 +01:00
SChernykh 975679ef87 Show actual worker difficulty in workers command 2023-01-27 22:09:17 +01:00
SChernykh dfa27016fd Update RandomX 2023-01-27 18:50:21 +01:00
SChernykh 25d083ef25 Show Monero node in status 2023-01-27 17:42:00 +01:00
SChernykh c989bdb0ea Tweaked compiler flags to reduce binary size 2023-01-27 15:49:20 +01:00
SChernykh cb9e98dc88 Update c-cpp.yml 2023-01-27 11:30:34 +01:00
SChernykh c7ba11c607 Retry getaddrinfo with IPv4 only after an error 2023-01-27 10:29:04 +01:00
SChernykh 8876af53d8 SideChain: fixed potential division by 0 2023-01-26 21:42:15 +01:00
SChernykh 70f78ebfd1 cmake required version fixes 2023-01-26 20:19:57 +01:00
SChernykh be434329d1 Removed dl dependency 2023-01-26 15:46:43 +01:00
SChernykh 67f091b843 CI: added FreeBSD build 2023-01-26 12:56:46 +01:00
SChernykh 33a592ee52 Fixed v2 signaling 2023-01-25 14:43:55 +01:00
SChernykh a574e68bb7 StratumServer: check for bans before checking shares 2023-01-25 11:36:52 +01:00
SChernykh 001c9e937d Fixed use of destroyed object 2023-01-24 08:19:13 +01:00
SChernykh f01bd88edd Fixed PPLNS window calculation 2023-01-24 07:59:36 +01:00
SChernykh 8700edb330 Miner: added m_sharesFailed to api data 2023-01-23 16:17:17 +01:00
SChernykh 9b686673ae Fixed m_sharesFailed initialization 2023-01-23 13:13:08 +01:00
SChernykh c841a2d2a9 Set new testnet hardfork date 2023-01-22 16:24:36 +01:00
SChernykh c8248b2d33 Merge pull request #230 from hinto-janaiyo/master
README: Update Gupax info
2023-01-22 09:57:34 +01:00
SChernykh 5e373b5304 Miner: count failed shares 2023-01-22 09:28:23 +01:00
hinto-janaiyo d73b506d09 readme: update gupax info 2023-01-22 02:44:47 +00:00
SChernykh d332f6cf23 Fixed PPLNS window display in status 2023-01-21 23:09:57 +01:00
SChernykh 1c4806e0d7 Fixed m_txkeySecSeed in block template update 2023-01-21 22:48:51 +01:00
SChernykh f5ac485ac8 TCPServer: optimized internal write buffers 2023-01-20 17:03:59 +01:00
SChernykh 595196b5ec Reduced transaction time-to-include delay
Based on data from https://rucknium.me/posts/monero-pool-transaction-delay/
> 95 percent all transactions arrived at all five Monero nodes within a five-second interval.
2023-01-20 13:14:58 +01:00
SChernykh 85d177d394 Miner: update extra_nonce on each new block 2023-01-19 11:22:22 +01:00
SChernykh 4ee8890166 Fixed status command output 2023-01-19 00:04:35 +01:00
SChernykh 5e8858c40e Fixed warning level 2023-01-18 20:57:45 +01:00
SChernykh 420cb71990 Fixed min difficulty setting 2023-01-18 16:16:35 +01:00
SChernykh 451778d704 Optimized RNG usage 2023-01-17 16:24:15 +01:00
SChernykh c855bb769a PoolBlock: removed unnecessary mutex 2023-01-17 13:20:09 +01:00
SChernykh f95d56d1eb Fixed wrong type in uv_walk 2023-01-17 08:15:53 +01:00
SChernykh fe5e209eb1 Don't limit min difficulty on testnet 2023-01-16 18:34:48 +01:00
SChernykh f206e4b334 Testnet fork on January 21st 2023-01-16 15:36:21 +01:00
SChernykh 5bd518da4b Optimized BlockTemplate::update() 2023-01-16 11:17:41 +01:00
SChernykh ec8e40907d Fixed a crash in status command 2023-01-15 22:20:06 +01:00
SChernykh e828709090 Optimized SideChain::get_outputs_blob() 2023-01-15 22:00:44 +01:00
SChernykh d4329ae594 Show file:line where panic() was called from 2023-01-14 12:37:08 +01:00
SChernykh 2c4c2b8469 Fixed a rare crash on exit 2023-01-13 18:25:04 +01:00
SChernykh 14f667de74 Support dynamic PPLNS window in status 2023-01-13 17:43:56 +01:00
SChernykh 578baeae96 API: added pplnsWeight to pool stats 2023-01-13 16:01:35 +01:00
SChernykh 1ad3fdfd46 Added more errors checks to p2pool_api 2023-01-13 15:16:06 +01:00
SChernykh 5eaf5d1f51 Update README.md 2023-01-13 10:29:12 +01:00
SChernykh 8e7df958b8 Use extra_nonce to signal hardfork readiness 2023-01-12 10:19:29 +01:00
SChernykh 4f2ad9a93c Optimized get_shares 2023-01-10 22:17:17 +01:00
SChernykh 850eb8e6e0 Fixed submit_block 2023-01-09 22:27:06 +01:00
SChernykh 47f551c046 Randomize output order for P2Pool payouts 2023-01-09 20:07:11 +01:00
SChernykh ae6747c82d Initialize tx key from previous blocks 2023-01-09 16:14:33 +01:00
SChernykh 1bd04c2840 Initialize tx key from random seed 2023-01-09 11:28:47 +01:00
SChernykh 11e4c459c9 pub/sec tx key cleanup 2023-01-09 01:15:06 +01:00
SChernykh 647319b731 Added missing check to update_chain_tip 2023-01-09 00:33:37 +01:00
SChernykh e0913f9068 Speed up calc_sidechain_hash 2023-01-08 23:29:54 +01:00
SChernykh af0fa872ae Set sidechain extra (duplicate shares fix) 2023-01-08 21:12:09 +01:00
SChernykh 1ce8f28105 Added sidechain extra 2023-01-08 17:07:28 +01:00
SChernykh a525f34fec Speed up calc_miner_tx_hash a lot
Cache keccak state and update it for new extra_nonce in O(1) time instead of O(N). It reduces the number `keccakf` calls from 30-150 to just 2-3. Time to generate 10,000 hashing blobs is reduced from 0.75s to 0.03s.
2023-01-08 14:17:44 +01:00
SChernykh 632f3faac5 Refactored keccak interface
- Allow calculating keccak hash in steps
- Only allow 32 and 200 byte final hashes
2023-01-08 13:06:20 +01:00
SChernykh 4f34c4466a Dynamic PPLNS window 2023-01-08 00:47:03 +01:00
SChernykh ccc5117172 Track shares that failed to be added 2023-01-07 15:50:02 +01:00
SChernykh b346b93285 Updated copyright 2023-01-04 13:07:55 +01:00
82 changed files with 2421 additions and 1147 deletions
+126 -17
View File
@@ -5,6 +5,7 @@ on: [push, pull_request]
jobs:
build-ubuntu:
timeout-minutes: 10
runs-on: ${{ matrix.config.os }}
strategy:
@@ -51,6 +52,7 @@ jobs:
build-ubuntu-static-libs:
timeout-minutes: 15
runs-on: ubuntu-22.04
steps:
@@ -70,7 +72,7 @@ jobs:
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
./configure CFLAGS='-Os' --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j$(nproc)
- name: Build libuv
@@ -78,7 +80,7 @@ jobs:
cd external/src/libuv
mkdir build
cd build
cmake .. -DBUILD_TESTING=OFF
cmake .. -DCMAKE_C_FLAGS='-Os' -DBUILD_TESTING=OFF
make -j$(nproc)
- name: Build libzmq
@@ -86,7 +88,7 @@ jobs:
cd external/src/libzmq
mkdir build
cd build
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
cmake .. -DCMAKE_C_FLAGS='-Os' -DCMAKE_CXX_FLAGS='-Os' -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j$(nproc)
- name: Build p2pool
@@ -115,6 +117,7 @@ jobs:
build-ubuntu-aarch64:
timeout-minutes: 10
runs-on: ${{ matrix.config.os }}
strategy:
@@ -138,7 +141,7 @@ jobs:
run: |
cd external/src/curl
autoreconf -fi
./configure --host=aarch64-linux-gnu --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
./configure --host=aarch64-linux-gnu CFLAGS='-Os' --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j$(nproc)
- name: Build libuv
@@ -146,7 +149,7 @@ jobs:
cd external/src/libuv
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DBUILD_TESTING=OFF
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DCMAKE_C_FLAGS='-Os' -DBUILD_TESTING=OFF
make -j$(nproc)
- name: Build libzmq
@@ -154,7 +157,7 @@ jobs:
cd external/src/libzmq
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DCMAKE_C_FLAGS='-Os' -DCMAKE_CXX_FLAGS='-Os' -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j$(nproc)
- name: Build p2pool
@@ -172,6 +175,7 @@ jobs:
build-windows-msys2:
timeout-minutes: 45
runs-on: windows-latest
defaults:
@@ -194,7 +198,7 @@ jobs:
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
./configure CFLAGS='-Os' --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j$(nproc)
- name: Build libuv
@@ -202,7 +206,7 @@ jobs:
cd external/src/libuv
mkdir build
cd build
cmake .. -G "Unix Makefiles" -DBUILD_TESTING=OFF
cmake .. -G "Unix Makefiles" -DCMAKE_C_FLAGS='-Os' -DBUILD_TESTING=OFF
make -j$(nproc)
- name: Build libzmq
@@ -210,7 +214,7 @@ jobs:
cd external/src/libzmq
mkdir build
cd build
cmake .. -G "Unix Makefiles" -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF -DZMQ_HAVE_IPC=OFF
cmake .. -G "Unix Makefiles" -DCMAKE_C_FLAGS='-Os' -DCMAKE_CXX_FLAGS='-Os' -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF -DZMQ_HAVE_IPC=OFF
make -j$(nproc)
- name: Build p2pool
@@ -241,14 +245,15 @@ jobs:
build-windows-msbuild:
timeout-minutes: 20
runs-on: windows-${{ matrix.config.os }}
strategy:
matrix:
config:
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\", rx: "ON"}
- {vs: Visual Studio 17 2022, os: 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\", rx: "ON"}
- {vs: Visual Studio 17 2022, os: 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\", rx: "OFF"}
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\", rx: "ON", upnp: "ON"}
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\", rx: "OFF", upnp: "ON"}
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\", rx: "OFF", upnp: "OFF"}
steps:
- name: Checkout repository
@@ -263,7 +268,7 @@ jobs:
run: |
mkdir build
cd build
cmake .. -G "${{ matrix.config.vs }}" -DWITH_RANDOMX=${{ matrix.config.rx }}
cmake .. -G "${{ matrix.config.vs }}" -DWITH_RANDOMX=${{ matrix.config.rx }} -DWITH_UPNP=${{ matrix.config.upnp }}
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool.vcxproj
- name: Build tests
@@ -282,11 +287,12 @@ jobs:
- name: Archive binary
uses: actions/upload-artifact@v3
with:
name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}.exe
name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}-upnp-${{ matrix.config.upnp }}.exe
path: build/Release/p2pool.exe
build-macos:
timeout-minutes: 15
runs-on: ${{ matrix.os }}
strategy:
@@ -306,7 +312,7 @@ jobs:
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
./configure CFLAGS='-Os' --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j3
- name: Build libuv
@@ -314,7 +320,7 @@ jobs:
cd external/src/libuv
mkdir build
cd build
cmake .. -DBUILD_TESTING=OFF
cmake .. -DCMAKE_C_FLAGS='-Os' -DBUILD_TESTING=OFF
make -j3
- name: Build libzmq
@@ -322,7 +328,7 @@ jobs:
cd external/src/libzmq
mkdir build
cd build
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
cmake .. -DCMAKE_C_FLAGS='-Os' -DCMAKE_CXX_FLAGS='-Os' -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j3
- name: Build p2pool
@@ -350,3 +356,106 @@ jobs:
with:
name: p2pool-${{ matrix.os }}
path: build/p2pool
build-freebsd:
timeout-minutes: 75
runs-on: ${{ matrix.os.host }}
strategy:
matrix:
os:
- name: freebsd
architecture: x86-64
version: '12.4'
host: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build p2pool
uses: cross-platform-actions/action@v0.10.0
with:
operating_system: ${{ matrix.os.name }}
architecture: ${{ matrix.os.architecture }}
version: ${{ matrix.os.version }}
shell: bash
run: |
sudo pkg install -y cmake autoconf automake libtool
cd external/src/curl
autoreconf -fi
./configure CFLAGS='-Os' --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j2
cd ../libuv
mkdir build
cd build
cmake .. -DCMAKE_C_FLAGS='-Os' -DBUILD_TESTING=OFF
make -j2
cd ../../libzmq
mkdir build
cd build
cmake .. -DCMAKE_C_FLAGS='-Os' -DCMAKE_CXX_FLAGS='-Os' -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j2
cd ../../../..
mkdir build
cd build
cmake .. -DSTATIC_BINARY=ON
make -j2
cd ../tests
mkdir build
cd build
cmake .. -DSTATIC_LIBS=ON
make -j2
./p2pool_tests
- name: Archive binary
uses: actions/upload-artifact@v3
with:
name: p2pool-${{ matrix.os.name }}-${{ matrix.os.version }}
path: build/p2pool
build-openbsd:
timeout-minutes: 75
runs-on: ${{ matrix.os.host }}
strategy:
matrix:
os:
- name: openbsd
architecture: x86-64
version: '7.2'
host: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build p2pool
uses: cross-platform-actions/action@v0.10.0
with:
operating_system: ${{ matrix.os.name }}
architecture: ${{ matrix.os.architecture }}
version: ${{ matrix.os.version }}
shell: bash
run: |
sudo pkg_add -I cmake libuv zeromq curl
mkdir build && cd build
cmake ..
make -j2
cd ../tests
mkdir build && cd build
cmake ..
make -j2
./p2pool_tests
- name: Archive binary
uses: actions/upload-artifact@v3
with:
name: p2pool-${{ matrix.os.name }}-${{ matrix.os.version }}
path: build/p2pool
+1
View File
@@ -20,6 +20,7 @@ on:
jobs:
analyze:
name: Analyze
timeout-minutes: 20
runs-on: ubuntu-latest
permissions:
actions: read
+2
View File
@@ -5,6 +5,7 @@ on: [push, pull_request]
jobs:
cppcheck-ubuntu:
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
@@ -31,6 +32,7 @@ jobs:
cppcheck-windows:
timeout-minutes: 20
runs-on: windows-latest
steps:
+1
View File
@@ -22,6 +22,7 @@ env:
jobs:
analyze:
name: Analyze
timeout-minutes: 15
runs-on: windows-latest
steps:
+22 -10
View File
@@ -5,6 +5,7 @@ on: [push, pull_request]
jobs:
sync-test-ubuntu:
timeout-minutes: 30
runs-on: ubuntu-22.04
steps:
@@ -30,16 +31,20 @@ jobs:
timeout-minutes: 15
run: |
cd build
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 6
mkdir data
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --data-api data --local-api --loglevel 6
- name: Archive p2pool.log
uses: actions/upload-artifact@v3
with:
name: p2pool_ubuntu.log
path: build/p2pool.log
name: p2pool_ubuntu_data
path: |
build/p2pool.log
build/data/
sync-test-macos:
timeout-minutes: 30
runs-on: macos-12
steps:
@@ -49,7 +54,7 @@ jobs:
submodules: recursive
- name: Install dependencies
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake autoconf libtool automake libuv zmq libpgm curl
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake libuv zmq libpgm curl
- name: Build p2pool
run: |
@@ -62,16 +67,20 @@ jobs:
timeout-minutes: 15
run: |
cd build
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 6
mkdir data
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --data-api data --local-api --loglevel 6
- name: Archive p2pool.log
uses: actions/upload-artifact@v3
with:
name: p2pool_macos.log
path: build/p2pool.log
name: p2pool_macos_data
path: |
build/p2pool.log
build/data/
sync-test-windows:
timeout-minutes: 30
runs-on: windows-2022
steps:
@@ -94,10 +103,13 @@ jobs:
timeout-minutes: 15
run: |
cd build/Debug
./p2pool.exe --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 6
mkdir data
./p2pool.exe --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --data-api data --local-api --loglevel 6
- name: Archive p2pool.log
uses: actions/upload-artifact@v3
with:
name: p2pool_windows.log
path: build/Debug/p2pool.log
name: p2pool_windows_data
path: |
build/Debug/p2pool.log
build/Debug/data/
+3
View File
@@ -22,3 +22,6 @@
[submodule "external/src/curl"]
path = external/src/curl
url = https://github.com/SChernykh/curl
[submodule "external/src/miniupnp"]
path = external/src/miniupnp
url = https://github.com/SChernykh/miniupnp
+22 -5
View File
@@ -1,20 +1,30 @@
cmake_minimum_required(VERSION 3.6)
cmake_minimum_required(VERSION 2.8.12)
project(p2pool)
option(STATIC_BINARY "Build static binary" OFF)
option(STATIC_LIBS "Link libuv and libzmq statically" OFF)
option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned off, p2pool will rely on monerod for verifying RandomX hashes" ON)
option(WITH_LTO "Use link-time compiler optimization (if linking fails for you, run cmake with -DWITH_LTO=OFF)" ON)
option(WITH_UPNP "Include UPnP support. If this is turned off, p2pool will not be able to configure port forwarding on UPnP-enabled routers." ON)
option(DEV_TEST_SYNC "[Developer only] Sync test, stop p2pool after sync is complete" OFF)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
if (${CMAKE_VERSION} VERSION_GREATER "3.5.2")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
endif()
if (WITH_RANDOMX)
add_definitions(-DWITH_RANDOMX)
add_subdirectory(external/src/RandomX)
set(LIBS randomx)
set(LIBS ${LIBS} randomx)
endif()
if (WITH_UPNP)
add_definitions(-DWITH_UPNP)
add_subdirectory(external/src/miniupnp/miniupnpc)
set(LIBS ${LIBS} libminiupnpc-static)
endif()
if (DEV_TEST_SYNC)
@@ -114,6 +124,9 @@ if (WITH_RANDOMX)
endif()
include_directories(external/src/rapidjson/include)
include_directories(external/src/robin-hood-hashing/src/include)
if (WITH_UPNP)
include_directories(external/src/miniupnp/miniupnpc/include)
endif()
if (WIN32)
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
@@ -124,7 +137,7 @@ if (WIN32)
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(LIBS ${LIBS} pthread)
elseif (NOT APPLE)
set(LIBS ${LIBS} pthread dl)
set(LIBS ${LIBS} pthread)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
@@ -213,6 +226,10 @@ if (STATIC_BINARY OR STATIC_LIBS)
set(STATIC_LIBS randomx)
endif()
if (WITH_UPNP)
set(STATIC_LIBS ${STATIC_LIBS} libminiupnpc-static)
endif()
if (WIN32)
set(STATIC_LIBS ${STATIC_LIBS} ws2_32 iphlpapi userenv psapi wldap32)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
@@ -226,7 +243,7 @@ if (STATIC_BINARY OR STATIC_LIBS)
find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration)
set(STATIC_LIBS ${STATIC_LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB})
else()
set(STATIC_LIBS ${STATIC_LIBS} pthread dl)
set(STATIC_LIBS ${STATIC_LIBS} pthread)
endif()
target_link_libraries(${CMAKE_PROJECT_NAME}
+9 -2
View File
@@ -2,7 +2,13 @@
Decentralized pool for Monero mining.
Pool status and monitoring pages can be found at https://p2pool.io/, https://p2pool.io/mini/ and https://p2pool.observer/
Pool status and monitoring pages can be found at https://p2pool.io/, https://p2pool.io/mini/ and https://p2pool.observer/, https://mini.p2pool.observer/
# P2Pool hardfork on March 18, 2023
P2Pool (not Monero!) will hardfork to new consensus rules on March 18th at 21:00 UTC (use [this link](https://dateful.com/convert/utc?t=9pm&d=2023-03-18) to convert to your local time). The hardfork is necessary to improve P2Pool scalability and reduce the impact on Monero blockchain. You'll need to update to **P2Pool v3.0** or newer version before this time.
Reddit discussion: https://www.reddit.com/r/MoneroMining/comments/1095730/psa_p2pool_network_upgrade_aka_hardfork_on_march/
### Build Status
@@ -24,6 +30,7 @@ Pool status and monitoring pages can be found at https://p2pool.io/, https://p2p
- [Monero version support](#monero-version-support)
- [How to mine on P2Pool](#how-to-mine-on-p2pool)
- [General Considerations](#general-considerations)
- [GUI for P2Pool](#gui-for-p2pool)
- [GNU/Linux](#gnulinux)
- [Windows](#windows)
- [Build instructions](#build-instructions)
@@ -97,7 +104,7 @@ Monero network upgrade happened on August 13th, 2022 (block 2,688,888). In order
### GUI for P2Pool
- [Gupax](https://github.com/hinto-janaiyo/gupax) project (currently in development) aims to provide an easy to use GUI to configure and run monerod/p2pool/xmrig
- [Gupax](https://github.com/hinto-janai/gupax) project aims to provide an easy to use cross-platform GUI to configure and run P2Pool & [XMRig](https://github.com/xmrig/xmrig).
### GNU/Linux
+14
View File
@@ -10,6 +10,14 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(WARNING_FLAGS "-Wall -Wextra -Wcast-align -Wcast-qual -Wlogical-op -Wstrict-overflow=2 -Wundef -Wformat=2 -Wpointer-arith -Werror")
set(OPTIMIZATION_FLAGS "-Ofast -s")
if (WITH_LTO)
set(OPTIMIZATION_FLAGS "${OPTIMIZATION_FLAGS} -flto -fuse-linker-plugin")
endif()
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9)
set(OPTIMIZATION_FLAGS "${OPTIMIZATION_FLAGS} -fno-associative-math")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
@@ -39,11 +47,17 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG")
elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
set(GENERAL_FLAGS "-pthread")
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal -Wunreachable-code-aggressive -Wmissing-prototypes -Wmissing-variable-declarations -Werror")
set(OPTIMIZATION_FLAGS "-Ofast -funroll-loops -fmerge-all-constants")
if (WITH_LTO)
set(OPTIMIZATION_FLAGS "${OPTIMIZATION_FLAGS} -flto")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
+6 -3
View File
@@ -21,11 +21,14 @@
--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 450)
--start-mining N Start built-in miner using N threads (any value between 1 and 64)
--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from 37889 to 37888
--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum
--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
--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-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
--no-igd An alias for --no-upnp
--upnp-stratum Port forward Stratum port (it's not forwarded by default)
```
### Example command line
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Vendored Submodule
+1
Submodule external/src/miniupnp added at e17579e4de
Generated
+6 -6
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1631368560,
"narHash": "sha256-JUx+2uf1C2dZ1n56hcy5gjPGh4UP0nQwIr+WbjaWaP4=",
"lastModified": 1678349056,
"narHash": "sha256-kcL7Ap6IfUQRmGFeE2F0SkzIbQaPvZiylIuL2dD6mL0=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "91333fe5c9212e1a0c587c8ce70b0571bf0b18bd",
"rev": "6a231a6eb788a345b265e1f26e04b96d8a402bf5",
"type": "github"
},
"original": {
@@ -23,11 +23,11 @@
},
"utils": {
"locked": {
"lastModified": 1629481132,
"narHash": "sha256-JHgasjPR0/J1J3DRm4KxM4zTyAj4IOJY8vIl75v/kPI=",
"lastModified": 1676283394,
"narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "997f7efcb746a9c140ce1f13c72263189225f482",
"rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
"type": "github"
},
"original": {
+2
View File
@@ -26,6 +26,8 @@
inherit (pkgs) libuv zeromq libsodium gss curl;
};
cmakeFlags = ["-DWITH_LTO=OFF"];
installPhase = ''
mkdir -p $out/bin
cp -r ./p2pool $out/bin/
+1 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
+276 -100
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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>
@@ -55,10 +55,12 @@ BlockTemplate::BlockTemplate(SideChain* sidechain, RandomX_Hasher_Base* hasher)
, m_difficulty{}
, m_seedHash{}
, m_timestamp(0)
, m_txkeyPub{}
, m_txkeySec{}
, m_poolBlockTemplate(new PoolBlock())
, m_finalReward(0)
, m_minerTxKeccakState{}
, m_minerTxKeccakStateInputLength(0)
, m_sidechainHashKeccakState{}
, m_sidechainHashInputLength(0)
, m_rng(RandomDeviceSeed::instance)
{
// Diffuse the initial state in case it has low quality
@@ -72,6 +74,8 @@ BlockTemplate::BlockTemplate(SideChain* sidechain, RandomX_Hasher_Base* hasher)
m_transactionHashes.reserve(8192);
m_rewards.reserve(100);
m_blockTemplateBlob.reserve(65536);
m_fullDataBlob.reserve(65536);
m_sidechainHashBlob.reserve(65536);
m_merkleTreeMainBranch.reserve(HASH_SIZE * 10);
m_mempoolTxs.reserve(1024);
m_mempoolTxsOrder.reserve(1024);
@@ -119,6 +123,8 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
m_templateId = b.m_templateId;
m_lastUpdated = b.m_lastUpdated.load();
m_blockTemplateBlob = b.m_blockTemplateBlob;
m_fullDataBlob = b.m_fullDataBlob;
m_sidechainHashBlob = b.m_sidechainHashBlob;
m_merkleTreeMainBranch = b.m_merkleTreeMainBranch;
m_blockHeaderSize = b.m_blockHeaderSize;
m_minerTxOffsetInTemplate = b.m_minerTxOffsetInTemplate;
@@ -131,10 +137,14 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
m_difficulty = b.m_difficulty;
m_seedHash = b.m_seedHash;
m_timestamp = b.m_timestamp;
m_txkeyPub = b.m_txkeyPub;
m_txkeySec = b.m_txkeySec;
*m_poolBlockTemplate = *b.m_poolBlockTemplate;
m_finalReward = b.m_finalReward;
m_finalReward = b.m_finalReward.load();
memcpy(m_minerTxKeccakState, b.m_minerTxKeccakState, sizeof(m_minerTxKeccakState));
m_minerTxKeccakStateInputLength = b.m_minerTxKeccakStateInputLength;
memcpy(m_sidechainHashKeccakState, b.m_sidechainHashKeccakState, sizeof(m_sidechainHashKeccakState));
m_sidechainHashInputLength = b.m_sidechainHashInputLength;
m_minerTx.clear();
m_blockHeader.clear();
@@ -188,13 +198,16 @@ static FORCEINLINE uint64_t get_block_reward(uint64_t base_reward, uint64_t medi
void BlockTemplate::shuffle_tx_order()
{
const int64_t n = static_cast<int64_t>(m_mempoolTxsOrder.size());
for (int64_t i = n - 1; i > 0; --i) {
std::swap(m_mempoolTxsOrder[i], m_mempoolTxsOrder[m_rng() % (i + 1)]);
const uint64_t n = m_mempoolTxsOrder.size();
if (n > 1) {
for (uint64_t i = 0, k; i < n - 1; ++i) {
umul128(m_rng(), n - i, &k);
std::swap(m_mempoolTxsOrder[i], m_mempoolTxsOrder[i + k]);
}
}
}
void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet)
void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const Wallet* miner_wallet)
{
if (data.major_version > HARDFORK_SUPPORTED_VERSION) {
LOGERR(1, "got hardfork version " << data.major_version << ", expected <= " << HARDFORK_SUPPORTED_VERSION);
@@ -221,8 +234,6 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
*this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)];
};
get_tx_keys(m_txkeyPub, m_txkeySec, miner_wallet->spend_public_key(), data.prev_id);
m_height = data.height;
m_difficulty = data.difficulty;
m_seedHash = data.seed_hash;
@@ -256,11 +267,66 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_blockHeader.insert(m_blockHeader.end(), NONCE_SIZE, 0);
m_poolBlockTemplate->m_nonce = 0;
// Fill in m_txinGenHeight here so get_shares() can use it to calculate the correct PPLNS window
m_poolBlockTemplate->m_txinGenHeight = data.height;
m_blockHeaderSize = m_blockHeader.size();
m_sidechain->fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares);
m_poolBlockTemplate->m_minerWallet = *miner_wallet;
// Only choose transactions that were received 10 or more seconds ago, or high fee (>= 0.006 XMR) transactions
m_sidechain->fill_sidechain_data(*m_poolBlockTemplate, m_shares);
// Pre-calculate outputs to speed up miner tx generation
if (!m_shares.empty()) {
struct Precalc
{
FORCEINLINE Precalc(const std::vector<MinerShare>& s, const hash& k) : txKeySec(k)
{
const size_t N = s.size();
counter = static_cast<int>(N) - 1;
shares = reinterpret_cast<std::pair<hash, hash>*>(malloc_hook(sizeof(std::pair<hash, hash>) * N));
if (shares) {
const MinerShare* src = &s[0];
std::pair<hash, hash>* dst = shares;
std::pair<hash, hash>* e = shares + N;
for (; dst < e; ++src, ++dst) {
const Wallet* w = src->m_wallet;
dst->first = w->view_public_key();
dst->second = w->spend_public_key();
}
}
}
FORCEINLINE Precalc(Precalc&& rhs) noexcept : txKeySec(rhs.txKeySec), counter(rhs.counter.load()), shares(rhs.shares) { rhs.shares = nullptr; }
FORCEINLINE ~Precalc() { free_hook(shares); }
// Disable any other way of copying/moving Precalc
Precalc(const Precalc&) = delete;
Precalc& operator=(const Precalc&) = delete;
Precalc& operator=(Precalc&&) = delete;
FORCEINLINE void operator()()
{
if (shares) {
hash derivation, eph_public_key;
int i;
while ((i = counter.fetch_sub(1)) >= 0) {
uint8_t view_tag;
generate_key_derivation(shares[i].first, txKeySec, i, derivation, view_tag);
derive_public_key(derivation, i, shares[i].second, eph_public_key);
}
}
}
hash txKeySec;
std::atomic<int> counter;
std::pair<hash, hash>* shares;
};
parallel_run(uv_default_loop_checked(), Precalc(m_shares, m_poolBlockTemplate->m_txkeySec));
}
// Only choose transactions that were received 5 or more seconds ago, or high fee (>= 0.006 XMR) transactions
size_t total_mempool_transactions;
{
m_mempoolTxs.clear();
@@ -272,7 +338,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
const uint64_t cur_time = seconds_since_epoch();
for (auto& it : mempool.m_transactions) {
if ((cur_time >= it.second.time_received + 10) || (it.second.fee >= HIGH_FEE_VALUE)) {
if ((cur_time > it.second.time_received + 5) || (it.second.fee >= HIGH_FEE_VALUE)) {
m_mempoolTxs.emplace_back(it.second);
}
}
@@ -570,43 +636,95 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_poolBlockTemplate->m_minerWallet = *miner_wallet;
m_poolBlockTemplate->serialize_sidechain_data();
m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash();
const int sidechain_hash_offset = static_cast<int>(m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize) + 2;
// Layout: [software id, version, random number, sidechain extra_nonce]
uint32_t* sidechain_extra = m_poolBlockTemplate->m_sidechainExtraBuf;
sidechain_extra[0] = 0;
#ifdef P2POOL_SIDECHAIN_EXTRA_1
sidechain_extra[1] = P2POOL_SIDECHAIN_EXTRA_1;
#else
sidechain_extra[1] = (P2POOL_VERSION_MAJOR << 16) | P2POOL_VERSION_MINOR;
#endif
sidechain_extra[2] = static_cast<uint32_t>(m_rng() >> 32);
sidechain_extra[3] = 0;
memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
m_poolBlockTemplate->m_nonce = 0;
m_poolBlockTemplate->m_extraNonce = 0;
m_poolBlockTemplate->m_sidechainId = {};
#if POOL_BLOCK_DEBUG
const std::vector<uint8_t> mainchain_data = m_poolBlockTemplate->serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
const std::vector<uint8_t>& consensus_id = m_sidechain->consensus_id();
if (mainchain_data != m_blockTemplateBlob) {
LOGERR(1, "serialize_mainchain_data() has a bug, fix it! ");
LOGERR(1, "m_poolBlockTemplate->m_mainChainData.size() = " << mainchain_data.size());
LOGERR(1, "m_blockTemplateBlob.size() = " << m_blockTemplateBlob.size());
for (size_t i = 0, n = std::min(mainchain_data.size(), m_blockTemplateBlob.size()); i < n; ++i) {
if (mainchain_data[i] != m_blockTemplateBlob[i]) {
LOGERR(1, "m_poolBlockTemplate->m_mainChainData is different at offset " << i);
break;
}
m_sidechainHashBlob = m_poolBlockTemplate->serialize_mainchain_data();
m_sidechainHashBlob.insert(m_sidechainHashBlob.end(), sidechain_data.begin(), sidechain_data.end());
m_sidechainHashBlob.insert(m_sidechainHashBlob.end(), consensus_id.begin(), consensus_id.end());
{
memset(m_sidechainHashKeccakState, 0, sizeof(m_sidechainHashKeccakState));
const size_t extra_nonce_offset = m_sidechainHashBlob.size() - HASH_SIZE - EXTRA_NONCE_SIZE;
if (extra_nonce_offset >= KeccakParams::HASH_DATA_AREA) {
// Sidechain data is big enough to cache keccak state up to extra_nonce
m_sidechainHashInputLength = (extra_nonce_offset / KeccakParams::HASH_DATA_AREA) * KeccakParams::HASH_DATA_AREA;
const uint8_t* in = m_sidechainHashBlob.data();
int inlen = static_cast<int>(m_sidechainHashInputLength);
keccak_step(in, inlen, m_sidechainHashKeccakState);
}
else {
m_sidechainHashInputLength = 0;
}
}
{
std::vector<uint8_t> buf = m_blockTemplateBlob;
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
m_fullDataBlob = m_blockTemplateBlob;
m_fullDataBlob.insert(m_fullDataBlob.end(), sidechain_data.begin(), sidechain_data.end());
m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(0);
if (pool_block_debug()) {
const size_t sidechain_hash_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 2;
memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
memcpy(m_fullDataBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
const std::vector<uint8_t> mainchain_data = m_poolBlockTemplate->serialize_mainchain_data();
if (mainchain_data != m_blockTemplateBlob) {
LOGERR(1, "serialize_mainchain_data() has a bug, fix it! ");
LOGERR(1, "mainchain_data.size() = " << mainchain_data.size());
LOGERR(1, "m_blockTemplateBlob.size() = " << m_blockTemplateBlob.size());
for (size_t i = 0, n = std::min(mainchain_data.size(), m_blockTemplateBlob.size()); i < n; ++i) {
if (mainchain_data[i] != m_blockTemplateBlob[i]) {
LOGERR(1, "mainchain_data is different at offset " << i);
break;
}
}
}
PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), *m_sidechain, nullptr, false);
const int result = check.deserialize(m_fullDataBlob.data(), m_fullDataBlob.size(), *m_sidechain, nullptr, false);
if (result != 0) {
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
}
else {
LOGINFO(6, "blob size = " << buf.size());
LOGINFO(6, "blob size = " << m_fullDataBlob.size());
}
}
#endif
memset(m_minerTxKeccakState, 0, sizeof(m_minerTxKeccakState));
const size_t extra_nonce_offset = m_extraNonceOffsetInTemplate - m_minerTxOffsetInTemplate;
if (extra_nonce_offset >= KeccakParams::HASH_DATA_AREA) {
// Miner transaction is big enough to cache keccak state up to extra_nonce
m_minerTxKeccakStateInputLength = (extra_nonce_offset / KeccakParams::HASH_DATA_AREA) * KeccakParams::HASH_DATA_AREA;
const uint8_t* in = m_blockTemplateBlob.data() + m_minerTxOffsetInTemplate;
int inlen = static_cast<int>(m_minerTxKeccakStateInputLength);
keccak_step(in, inlen, m_minerTxKeccakState);
}
else {
m_minerTxKeccakStateInputLength = 0;
}
const hash minerTx_hash = calc_miner_tx_hash(0);
@@ -760,7 +878,7 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
}
else {
hash eph_public_key;
if (!shares[i].m_wallet->get_eph_public_key(m_txkeySec, i, eph_public_key, view_tag)) {
if (!shares[i].m_wallet->get_eph_public_key(m_poolBlockTemplate->m_txkeySec, i, eph_public_key, view_tag)) {
LOGERR(1, "get_eph_public_key failed at index " << i);
}
m_minerTx.insert(m_minerTx.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
@@ -783,14 +901,11 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
return -2;
}
m_poolBlockTemplate->m_txkeyPub = m_txkeyPub;
m_poolBlockTemplate->m_txkeySec = m_txkeySec;
// TX_EXTRA begin
m_minerTxExtra.clear();
m_minerTxExtra.push_back(TX_EXTRA_TAG_PUBKEY);
m_minerTxExtra.insert(m_minerTxExtra.end(), m_txkeyPub.h, m_txkeyPub.h + HASH_SIZE);
m_minerTxExtra.insert(m_minerTxExtra.end(), m_poolBlockTemplate->m_txkeyPub.h, m_poolBlockTemplate->m_txkeyPub.h + HASH_SIZE);
m_minerTxExtra.push_back(TX_EXTRA_NONCE);
@@ -828,48 +943,55 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
return 1;
}
hash BlockTemplate::calc_sidechain_hash() const
hash BlockTemplate::calc_sidechain_hash(uint32_t sidechain_extra_nonce) const
{
// Calculate side-chain hash (all block template bytes + all side-chain bytes + consensus ID, replacing NONCE, EXTRA_NONCE and HASH itself with 0's)
hash sidechain_hash;
const int sidechain_hash_offset = static_cast<int>(m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize) + 2;
const int blob_size = static_cast<int>(m_blockTemplateBlob.size());
const size_t size = m_sidechainHashBlob.size();
const size_t N = m_sidechainHashInputLength;
const std::vector<uint8_t>& consensus_id = m_sidechain->consensus_id();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
const size_t sidechain_extra_nonce_offset = size - HASH_SIZE - EXTRA_NONCE_SIZE;
const uint8_t sidechain_extra_nonce_buf[EXTRA_NONCE_SIZE] = {
static_cast<uint8_t>(sidechain_extra_nonce >> 0),
static_cast<uint8_t>(sidechain_extra_nonce >> 8),
static_cast<uint8_t>(sidechain_extra_nonce >> 16),
static_cast<uint8_t>(sidechain_extra_nonce >> 24)
};
keccak_custom([this, sidechain_hash_offset, blob_size, consensus_id, &sidechain_data](int offset) -> uint8_t {
uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(m_nonceOffset));
if (k < NONCE_SIZE) {
return 0;
}
hash result;
uint8_t buf[288];
k = static_cast<uint32_t>(offset - static_cast<int>(m_extraNonceOffsetInTemplate));
const bool b = N && (N <= sidechain_extra_nonce_offset) && (N < size) && (size - N <= sizeof(buf));
// Slow path: O(N)
if (!b || pool_block_debug()) {
keccak_custom([this, sidechain_extra_nonce_offset, &sidechain_extra_nonce_buf](int offset) -> uint8_t {
const uint32_t k = static_cast<uint32_t>(offset - sidechain_extra_nonce_offset);
if (k < EXTRA_NONCE_SIZE) {
return 0;
return sidechain_extra_nonce_buf[k];
}
return m_sidechainHashBlob[offset];
}, static_cast<int>(size), result.h, HASH_SIZE);
}
k = static_cast<uint32_t>(offset - sidechain_hash_offset);
if (k < HASH_SIZE) {
return 0;
}
// Fast path: O(1)
if (b) {
const int inlen = static_cast<int>(size - N);
if (offset < blob_size) {
return m_blockTemplateBlob[offset];
}
memcpy(buf, m_sidechainHashBlob.data() + N, size - N);
memcpy(buf + sidechain_extra_nonce_offset - N, sidechain_extra_nonce_buf, EXTRA_NONCE_SIZE);
const int side_chain_data_offsset = offset - blob_size;
const int side_chain_data_size = static_cast<int>(sidechain_data.size());
if (side_chain_data_offsset < side_chain_data_size) {
return sidechain_data[side_chain_data_offsset];
}
uint64_t st[25];
memcpy(st, m_sidechainHashKeccakState, sizeof(st));
keccak_finish(buf, inlen, st);
const int consensus_id_offset = side_chain_data_offsset - side_chain_data_size;
return consensus_id[consensus_id_offset];
},
static_cast<int>(m_blockTemplateBlob.size() + sidechain_data.size() + consensus_id.size()), sidechain_hash.h, HASH_SIZE);
if (pool_block_debug() && (memcmp(st, result.h, HASH_SIZE) != 0)) {
LOGERR(1, "calc_sidechain_hash fast path is broken. Fix the code!");
}
return sidechain_hash;
memcpy(result.h, st, HASH_SIZE);
}
return result;
}
hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
@@ -879,7 +1001,7 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
const uint8_t* data = m_blockTemplateBlob.data() + m_minerTxOffsetInTemplate;
const int extra_nonce_offset = static_cast<int>(m_extraNonceOffsetInTemplate - m_minerTxOffsetInTemplate);
const size_t extra_nonce_offset = m_extraNonceOffsetInTemplate - m_minerTxOffsetInTemplate;
const uint8_t extra_nonce_buf[EXTRA_NONCE_SIZE] = {
static_cast<uint8_t>(extra_nonce >> 0),
static_cast<uint8_t>(extra_nonce >> 8),
@@ -887,17 +1009,57 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
static_cast<uint8_t>(extra_nonce >> 24)
};
// Calculate sidechain id with this extra_nonce
const hash sidechain_id = calc_sidechain_hash(extra_nonce);
const size_t sidechain_hash_offset = extra_nonce_offset + m_poolBlockTemplate->m_extraNonceSize + 2;
// 1. Prefix (everything except vin_rct_type byte in the end)
// Apply extra_nonce in-place because we can't write to the block template here
keccak_custom([data, extra_nonce_offset, &extra_nonce_buf](int offset)
{
const uint32_t k = static_cast<uint32_t>(offset - extra_nonce_offset);
const size_t tx_size = m_minerTxSize - 1;
hash full_hash;
uint8_t tx_buf[288];
const size_t N = m_minerTxKeccakStateInputLength;
const bool b = N && (N <= extra_nonce_offset) && (N < tx_size) && (tx_size - N <= sizeof(tx_buf));
// Slow path: O(N)
if (!b || pool_block_debug())
{
keccak_custom([data, extra_nonce_offset, &extra_nonce_buf, sidechain_hash_offset, &sidechain_id](int offset) {
uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(extra_nonce_offset));
if (k < EXTRA_NONCE_SIZE) {
return extra_nonce_buf[k];
}
k = static_cast<uint32_t>(offset - static_cast<int>(sidechain_hash_offset));
if (k < HASH_SIZE) {
return sidechain_id.h[k];
}
return data[offset];
},
static_cast<int>(m_minerTxSize) - 1, hashes, HASH_SIZE);
}, static_cast<int>(tx_size), full_hash.h, HASH_SIZE);
memcpy(hashes, full_hash.h, HASH_SIZE);
}
// Fast path: O(1)
if (b) {
const int inlen = static_cast<int>(tx_size - N);
memcpy(tx_buf, data + N, inlen);
memcpy(tx_buf + extra_nonce_offset - N, extra_nonce_buf, EXTRA_NONCE_SIZE);
memcpy(tx_buf + sidechain_hash_offset - N, sidechain_id.h, HASH_SIZE);
uint64_t st[25];
memcpy(st, m_minerTxKeccakState, sizeof(st));
keccak_finish(tx_buf, inlen, st);
if (pool_block_debug() && (memcmp(st, full_hash.h, HASH_SIZE) != 0)) {
LOGERR(1, "calc_miner_tx_hash fast path is broken. Fix the code!");
}
memcpy(hashes, st, HASH_SIZE);
}
// 2. Base RCT, single 0 byte in miner tx
static constexpr uint8_t known_second_hash[HASH_SIZE] = {
@@ -910,7 +1072,7 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
// Calculate miner transaction hash
hash result;
keccak(hashes, sizeof(hashes), result.h, HASH_SIZE);
keccak(hashes, sizeof(hashes), result.h);
return result;
}
@@ -920,16 +1082,14 @@ void BlockTemplate::calc_merkle_tree_main_branch()
m_merkleTreeMainBranch.clear();
const uint64_t count = m_numTransactionHashes + 1;
if (count == 1) {
return;
}
const uint8_t* h = m_transactionHashes.data();
hash root_hash;
if (count == 1) {
memcpy(root_hash.h, h, HASH_SIZE);
}
else if (count == 2) {
if (count == 2) {
m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), h + HASH_SIZE, h + HASH_SIZE * 2);
keccak(h, HASH_SIZE * 2, root_hash.h, HASH_SIZE);
}
else {
size_t i, j, cnt;
@@ -941,11 +1101,14 @@ void BlockTemplate::calc_merkle_tree_main_branch()
std::vector<uint8_t> ints(cnt * HASH_SIZE);
memcpy(ints.data(), h, (cnt * 2 - count) * HASH_SIZE);
hash tmp;
for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
if (i == 0) {
m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), h + HASH_SIZE, h + HASH_SIZE * 2);
}
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, ints.data() + j * HASH_SIZE, HASH_SIZE);
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, tmp.h);
memcpy(ints.data() + j * HASH_SIZE, tmp.h, HASH_SIZE);
}
while (cnt > 2) {
@@ -954,12 +1117,12 @@ void BlockTemplate::calc_merkle_tree_main_branch()
if (i == 0) {
m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2);
}
keccak(ints.data() + i * HASH_SIZE, HASH_SIZE * 2, ints.data() + j * HASH_SIZE, HASH_SIZE);
keccak(ints.data() + i * HASH_SIZE, HASH_SIZE * 2, tmp.h);
memcpy(ints.data() + j * HASH_SIZE, tmp.h, HASH_SIZE);
}
}
m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2);
keccak(ints.data(), HASH_SIZE * 2, root_hash.h, HASH_SIZE);
}
}
@@ -1039,7 +1202,7 @@ uint32_t BlockTemplate::get_hashing_blob_nolock(uint32_t extra_nonce, uint8_t* b
memcpy(h, root_hash.h, HASH_SIZE);
memcpy(h + HASH_SIZE, m_merkleTreeMainBranch.data() + i, HASH_SIZE);
keccak(h, HASH_SIZE * 2, root_hash.h, HASH_SIZE);
keccak(h, HASH_SIZE * 2, root_hash.h);
}
memcpy(p, root_hash.h, HASH_SIZE);
@@ -1095,36 +1258,41 @@ uint32_t BlockTemplate::get_hashing_blobs(uint32_t extra_nonce_start, uint32_t c
return blob_size;
}
std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const
std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id, uint32_t sidechain_extra_nonce, size_t& nonce_offset, size_t& extra_nonce_offset, size_t& sidechain_id_offset, hash& sidechain_id) const
{
ReadLock lock(m_lock);
if (template_id != m_templateId) {
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) {
return old->get_block_template_blob(template_id, nonce_offset, extra_nonce_offset);
return old->get_block_template_blob(template_id, sidechain_extra_nonce, nonce_offset, extra_nonce_offset, sidechain_id_offset, sidechain_id);
}
nonce_offset = 0;
extra_nonce_offset = 0;
sidechain_id_offset = 0;
sidechain_id = {};
return std::vector<uint8_t>();
}
nonce_offset = m_nonceOffset;
extra_nonce_offset = m_extraNonceOffsetInTemplate;
sidechain_id_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 2;
sidechain_id = calc_sidechain_hash(sidechain_extra_nonce);
return m_blockTemplateBlob;
}
void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
bool BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
{
WriteLock lock(m_lock);
if (template_id == m_templateId) {
m_poolBlockTemplate->m_nonce = nonce;
m_poolBlockTemplate->m_extraNonce = extra_nonce;
m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(extra_nonce);
m_poolBlockTemplate->m_sidechainExtraBuf[3] = extra_nonce;
#if POOL_BLOCK_DEBUG
{
if (pool_block_debug()) {
std::vector<uint8_t> buf = m_poolBlockTemplate->serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
@@ -1149,22 +1317,30 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
}
}
}
#endif
m_poolBlockTemplate->m_verified = true;
if (!m_sidechain->block_seen(*m_poolBlockTemplate)) {
m_poolBlockTemplate->m_wantBroadcast = true;
m_sidechain->add_block(*m_poolBlockTemplate);
const bool result = m_sidechain->add_block(*m_poolBlockTemplate);
if (!result) {
LOGWARN(3, "failed to submit a share: add_block failed for template id " << template_id);
}
return result;
}
return;
const PoolBlock* b = m_poolBlockTemplate;
LOGWARN(3, "failed to submit a share: template id " << template_id << ", block " << b->m_sidechainId << ", nonce = " << b->m_nonce << ", extra_nonce = " << b->m_extraNonce << " was already added before");
return false;
}
BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) {
old->submit_sidechain_block(template_id, nonce, extra_nonce);
return;
return old->submit_sidechain_block(template_id, nonce, extra_nonce);
}
LOGWARN(3, "failed to submit a share: template id " << template_id << " is too old/out of range, current template id is " << m_templateId);
return false;
}
} // namespace p2pool
+19 -9
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -39,7 +39,7 @@ public:
BlockTemplate(const BlockTemplate& b);
BlockTemplate& operator=(const BlockTemplate& b);
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet);
void update(const MinerData& data, const Mempool& mempool, const Wallet* miner_wallet);
uint64_t last_updated() const { return m_lastUpdated.load(); }
bool get_difficulties(const uint32_t template_id, uint64_t& height, uint64_t& sidechain_height, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
@@ -48,15 +48,20 @@ public:
uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, uint64_t& sidechain_height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
uint32_t get_hashing_blobs(uint32_t extra_nonce_start, uint32_t count, std::vector<uint8_t>& blobs, uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
std::vector<uint8_t> get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const;
std::vector<uint8_t> get_block_template_blob(uint32_t template_id, uint32_t sidechain_extra_nonce, size_t& nonce_offset, size_t& extra_nonce_offset, size_t& sidechain_id_offset, hash& sidechain_id) const;
FORCEINLINE uint64_t height() const { return m_height; }
FORCEINLINE difficulty_type difficulty() const { return m_difficulty; }
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
bool submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
FORCEINLINE const std::vector<MinerShare>& shares() const { return m_shares; }
FORCEINLINE uint64_t get_reward() const { return m_finalReward; }
#ifdef P2POOL_UNIT_TESTS
FORCEINLINE const PoolBlock* pool_block_template() const { return m_poolBlockTemplate; }
FORCEINLINE std::mt19937_64& rng() { return m_rng; }
#endif
private:
SideChain* m_sidechain;
@@ -64,7 +69,7 @@ private:
private:
int create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run);
hash calc_sidechain_hash() const;
hash calc_sidechain_hash(uint32_t sidechain_extra_nonce) const;
hash calc_miner_tx_hash(uint32_t extra_nonce) const;
void calc_merkle_tree_main_branch();
@@ -76,6 +81,7 @@ private:
std::atomic<uint64_t> m_lastUpdated;
std::vector<uint8_t> m_blockTemplateBlob;
std::vector<uint8_t> m_fullDataBlob;
std::vector<uint8_t> m_merkleTreeMainBranch;
size_t m_blockHeaderSize;
@@ -92,17 +98,21 @@ private:
uint64_t m_timestamp;
hash m_txkeyPub;
hash m_txkeySec;
PoolBlock* m_poolBlockTemplate;
BlockTemplate* m_oldTemplates[4] = {};
uint64_t m_finalReward;
std::atomic<uint64_t> m_finalReward;
// Temp vectors, will be cleaned up after use and skipped in copy constructor/assignment operators
std::vector<uint8_t> m_minerTx;
uint64_t m_minerTxKeccakState[25];
size_t m_minerTxKeccakStateInputLength;
std::vector<uint8_t> m_sidechainHashBlob;
uint64_t m_sidechainHashKeccakState[25];
size_t m_sidechainHashInputLength;
std::vector<uint8_t> m_blockHeader;
std::vector<uint8_t> m_minerTxExtra;
std::vector<uint8_t> m_transactionHashes;
+18 -3
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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,16 @@
#define P2POOL_DEBUGGING 1
#endif
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
#define ASAN_POISON_MEMORY_REGION(addr, size) __asan_poison_memory_region((addr), (size))
#define ASAN_UNPOISON_MEMORY_REGION(addr, size) __asan_unpoison_memory_region((addr), (size))
extern "C" void __asan_poison_memory_region(void const volatile* addr, size_t size);
extern "C" void __asan_unpoison_memory_region(void const volatile* addr, size_t size);
#else
#define ASAN_POISON_MEMORY_REGION(addr, size)
#define ASAN_UNPOISON_MEMORY_REGION(addr, size)
#endif
namespace p2pool {
constexpr size_t HASH_SIZE = 32;
@@ -185,8 +195,8 @@ struct
#endif
difficulty_type
{
FORCEINLINE difficulty_type() : lo(0), hi(0) {}
FORCEINLINE difficulty_type(uint64_t a, uint64_t b) : lo(a), hi(b) {}
FORCEINLINE constexpr difficulty_type() : lo(0), hi(0) {}
FORCEINLINE constexpr difficulty_type(uint64_t a, uint64_t b) : lo(a), hi(b) {}
uint64_t lo;
uint64_t hi;
@@ -254,7 +264,10 @@ struct
return (lo < other.lo);
}
FORCEINLINE bool operator>(const difficulty_type& other) const { return other.operator<(*this); }
FORCEINLINE bool operator>=(const difficulty_type& other) const { return !operator<(other); }
FORCEINLINE bool operator<=(const difficulty_type& other) const { return !operator>(other); }
FORCEINLINE bool operator==(const difficulty_type& other) const { return (lo == other.lo) && (hi == other.hi); }
FORCEINLINE bool operator!=(const difficulty_type& other) const { return (lo != other.lo) || (hi != other.hi); }
@@ -294,6 +307,8 @@ struct
static_assert(sizeof(difficulty_type) == sizeof(uint64_t) * 2, "struct difficulty_type has invalid size, check your compiler options");
static_assert(std::is_standard_layout<difficulty_type>::value, "struct difficulty_type is not a POD, check your compiler options");
static constexpr difficulty_type diff_max = { std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max() };
template<typename T>
FORCEINLINE difficulty_type operator+(const difficulty_type& a, const T& b)
{
+32 -10
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -36,12 +36,16 @@ ConsoleCommands::ConsoleCommands(p2pool* pool)
, m_loop{}
, m_shutdownAsync{}
, m_tty{}
, m_stdin_pipe{}
, m_stdin_handle(nullptr)
, m_loopThread{}
, m_readBuf{}
, m_readBufInUse(false)
{
if (uv_guess_handle(0) != UV_TTY) {
LOGERR(1, "tty is not available");
const uv_handle_type stdin_type = uv_guess_handle(0);
LOGINFO(3, "uv_guess_handle returned " << static_cast<int>(stdin_type));
if (stdin_type != UV_TTY && stdin_type != UV_NAMED_PIPE) {
LOGERR(1, "tty or named pipe is not available");
throw std::exception();
}
@@ -58,14 +62,32 @@ ConsoleCommands::ConsoleCommands(p2pool* pool)
}
m_shutdownAsync.data = this;
err = uv_tty_init(&m_loop, &m_tty, 0, 1);
if (err) {
LOGERR(1, "uv_tty_init failed, error " << uv_err_name(err));
throw std::exception();
if (stdin_type == UV_TTY) {
LOGINFO(3, "processing stdin as UV_TTY");
err = uv_tty_init(&m_loop, &m_tty, 0, 1);
if (err) {
LOGERR(1, "uv_tty_init failed, error " << uv_err_name(err));
throw std::exception();
}
m_stdin_handle = reinterpret_cast<uv_stream_t*>(&m_tty);
}
m_tty.data = this;
else {
LOGINFO(3, "processing stdin as UV_NAMED_PIPE");
err = uv_pipe_init(&m_loop, &m_stdin_pipe, 0);
if (err) {
LOGERR(1, "uv_pipe_init failed, error " << uv_err_name(err));
throw std::exception();
}
m_stdin_handle = reinterpret_cast<uv_stream_t*>(&m_stdin_pipe);
err = uv_pipe_open(&m_stdin_pipe, 0);
if (err) {
LOGERR(1, "uv_pipe_open failed, error " << uv_err_name(err));
throw std::exception();
}
}
m_stdin_handle->data = this;
err = uv_read_start(reinterpret_cast<uv_stream_t*>(&m_tty), allocCallback, stdinReadCallback);
err = uv_read_start(m_stdin_handle, allocCallback, stdinReadCallback);
if (err) {
LOGERR(1, "uv_read_start failed, error " << uv_err_name(err));
throw std::exception();
@@ -182,7 +204,7 @@ static void do_showpeers(p2pool* m_pool, const char* /* args */)
static void do_showworkers(p2pool* m_pool, const char* /* args */)
{
if (m_pool->stratum_server()) {
m_pool->stratum_server()->show_workers();
m_pool->stratum_server()->show_workers_async();
}
}
+4 -2
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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,6 +35,8 @@ private:
uv_loop_t m_loop;
uv_async_t m_shutdownAsync;
uv_tty_t m_tty;
uv_pipe_t m_stdin_pipe;
uv_stream_t* m_stdin_handle;
uv_thread_t m_loopThread;
char m_readBuf[64];
@@ -48,7 +50,7 @@ private:
{
ConsoleCommands* pThis = reinterpret_cast<ConsoleCommands*>(async->data);
uv_close(reinterpret_cast<uv_handle_t*>(&pThis->m_shutdownAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pThis->m_tty), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(pThis->m_stdin_handle), nullptr);
}
static void allocCallback(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
+65 -19
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -134,7 +134,7 @@ bool check_keys(const hash& pub, const hash& sec)
static FORCEINLINE void hash_to_scalar(const uint8_t* data, int length, uint8_t (&res)[HASH_SIZE])
{
keccak(data, length, res, HASH_SIZE);
keccak(data, length, res);
sc_reduce32(res);
}
@@ -214,10 +214,11 @@ public:
derive_view_tag(derivation, output_index, view_tag);
const uint64_t t = seconds_since_epoch();
{
WriteLock lock(derivations_lock);
DerivationEntry& entry = derivations->emplace(index, DerivationEntry{ derivation, { 0xFFFFFFFFUL, 0xFFFFFFFFUL }, {} }).first->second;
DerivationEntry& entry = derivations->emplace(index, DerivationEntry{ derivation, { 0xFFFFFFFFUL, 0xFFFFFFFFUL }, {}, t }).first->second;
entry.add_view_tag(static_cast<uint32_t>(output_index << 8) | view_tag);
}
@@ -235,7 +236,7 @@ public:
ReadLock lock(public_keys_lock);
auto it = public_keys->find(index);
if (it != public_keys->end()) {
derived_key = it->second;
derived_key = it->second.m_key;
return true;
}
}
@@ -258,26 +259,27 @@ public:
ge_p1p1_to_p2(&point5, &point4);
ge_tobytes(derived_key.h, &point5);
const uint64_t t = seconds_since_epoch();
{
WriteLock lock(public_keys_lock);
public_keys->emplace(index, derived_key);
public_keys->emplace(index, PublicKeyEntry{ derived_key, t });
}
return true;
}
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id)
void get_tx_keys(hash& pub, hash& sec, const hash& seed, const hash& monero_block_id)
{
std::array<uint8_t, HASH_SIZE * 2> index;
memcpy(index.data(), wallet_spend_key.h, HASH_SIZE);
memcpy(index.data(), seed.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE);
{
ReadLock lock(tx_keys_lock);
auto it = tx_keys->find(index);
if (it != tx_keys->end()) {
pub = it->second.first;
sec = it->second.second;
pub = it->second.m_pub;
sec = it->second.m_sec;
return;
}
}
@@ -287,19 +289,47 @@ public:
uint8_t entropy[N + HASH_SIZE * 2];
memcpy(entropy, domain, N);
memcpy(entropy + N, wallet_spend_key.h, HASH_SIZE);
memcpy(entropy + N, seed.h, HASH_SIZE);
memcpy(entropy + N + HASH_SIZE, monero_block_id.h, HASH_SIZE);
generate_keys_deterministic(pub, sec, entropy, sizeof(entropy));
const uint64_t t = seconds_since_epoch();
{
WriteLock lock(tx_keys_lock);
tx_keys->emplace(index, std::pair<hash, hash>(pub, sec));
tx_keys->emplace(index, TxKeyEntry{ pub, sec, t });
}
}
void clear()
void clear(uint64_t timestamp)
{
if (timestamp) {
auto clean_old = [timestamp](auto* table) {
for (auto it = table->begin(); it != table->end();) {
if (it->second.m_timestamp < timestamp) {
it = table->erase(it);
}
else {
++it;
}
}
};
{
WriteLock lock(derivations_lock);
clean_old(derivations);
}
{
WriteLock lock(public_keys_lock);
clean_old(public_keys);
}
{
WriteLock lock(tx_keys_lock);
clean_old(tx_keys);
}
return;
}
{
WriteLock lock(derivations_lock);
delete derivations;
@@ -326,6 +356,7 @@ private:
hash m_derivation;
uint32_t m_viewTags1[2] = { 0xFFFFFFFFUL, 0xFFFFFFFFUL };
std::vector<uint32_t> m_viewTags2;
uint64_t m_timestamp;
FORCEINLINE bool find_view_tag(size_t output_index, uint8_t& view_tag) const
{
@@ -373,9 +404,24 @@ private:
}
};
struct PublicKeyEntry
{
hash m_key;
// cppcheck-suppress unusedStructMember
uint64_t m_timestamp;
};
struct TxKeyEntry
{
hash m_pub;
hash m_sec;
// cppcheck-suppress unusedStructMember
uint64_t m_timestamp;
};
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, DerivationEntry> DerivationsMap;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> PublicKeysMap;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> TxKeysMap;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, PublicKeyEntry> PublicKeysMap;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, TxKeyEntry> TxKeysMap;
uv_rwlock_t derivations_lock;
DerivationsMap* derivations;
@@ -399,9 +445,9 @@ bool derive_public_key(const hash& derivation, size_t output_index, const hash&
return cache->get_public_key(derivation, output_index, base, derived_key);
}
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id)
void get_tx_keys(hash& pub, hash& sec, const hash& seed, const hash& monero_block_id)
{
cache->get_tx_keys(pub, sec, wallet_spend_key, monero_block_id);
cache->get_tx_keys(pub, sec, seed, monero_block_id);
}
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag)
@@ -416,7 +462,7 @@ void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_
writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; });
hash view_tag_full;
keccak(buf, static_cast<int>(p - buf), view_tag_full.h, HASH_SIZE);
keccak(buf, static_cast<int>(p - buf), view_tag_full.h);
view_tag = view_tag_full.h[0];
}
@@ -435,10 +481,10 @@ void destroy_crypto_cache()
}
}
void clear_crypto_cache()
void clear_crypto_cache(uint64_t timestamp)
{
if (cache) {
cache->clear();
cache->clear(timestamp);
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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,7 +21,7 @@ namespace p2pool {
void generate_keys(hash& pub, hash& sec);
void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len);
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id);
void get_tx_keys(hash& pub, hash& sec, const hash& seed, const hash& monero_block_id);
bool check_keys(const hash& pub, const hash& sec);
bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag);
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key);
@@ -29,6 +29,6 @@ void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_
void init_crypto_cache();
void destroy_crypto_cache();
void clear_crypto_cache();
void clear_crypto_cache(uint64_t timestamp = 0);
} // namespace p2pool
+1 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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 -2
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -315,7 +315,7 @@ int CurlContext::on_timer(CURLM* /*multi*/, long timeout_ms)
return 0;
}
if (timeout_ms == 0) {
if ((timeout_ms == 0) && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_async))) {
// 0 ms timeout, but we can't just call on_timeout() here - we have to kick the UV loop
const int result = uv_async_send(&m_async);
if (result < 0) {
+1 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
+14 -17
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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,7 +24,7 @@ namespace p2pool {
#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y))))
#endif
const uint64_t keccakf_rndc[24] =
static const uint64_t keccakf_rndc[24] =
{
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
@@ -36,7 +36,7 @@ const uint64_t keccakf_rndc[24] =
0x8000000000008080, 0x0000000080000001, 0x8000000080008008
};
NOINLINE void keccakf(uint64_t* st)
NOINLINE void keccakf(uint64_t (&st)[25])
{
for (int round = 0; round < KeccakParams::ROUNDS; ++round) {
uint64_t bc[5];
@@ -115,14 +115,10 @@ NOINLINE void keccakf(uint64_t* st)
}
}
NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen)
NOINLINE void keccak_step(const uint8_t* &in, int &inlen, uint64_t (&st)[25])
{
uint64_t st[25];
const int rsiz = sizeof(st) == mdlen ? KeccakParams::HASH_DATA_AREA : 200 - 2 * mdlen;
const int rsizw = rsiz / 8;
memset(st, 0, sizeof(st));
constexpr int rsiz = KeccakParams::HASH_DATA_AREA;
constexpr int rsizw = rsiz / 8;
for (; inlen >= rsiz; inlen -= rsiz, in += rsiz) {
for (int i = 0; i < rsizw; i++) {
@@ -130,6 +126,14 @@ NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen)
}
keccakf(st);
}
}
NOINLINE void keccak_finish(const uint8_t* in, int inlen, uint64_t (&st)[25])
{
constexpr int rsiz = KeccakParams::HASH_DATA_AREA;
constexpr int rsizw = rsiz / 8;
keccak_step(in, inlen, st);
// last block and padding
alignas(8) uint8_t temp[144];
@@ -144,13 +148,6 @@ NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen)
}
keccakf(st);
memcpy(md, st, mdlen);
}
void keccak(const uint8_t *in, int inlen, uint8_t (&md)[200])
{
keccak(in, inlen, md, 200);
}
} // namespace p2pool
+16 -7
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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,20 +24,29 @@ enum KeccakParams {
ROUNDS = 24,
};
void keccakf(uint64_t* st);
void keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen);
void keccak(const uint8_t* in, int inlen, uint8_t (&md)[200]);
void keccakf(uint64_t (&st)[25]);
void keccak_step(const uint8_t* &in, int &inlen, uint64_t (&st)[25]);
void keccak_finish(const uint8_t* in, int inlen, uint64_t (&st)[25]);
template<size_t N>
FORCEINLINE void keccak(const uint8_t* in, int inlen, uint8_t (&md)[N])
{
static_assert((N == 32) || (N == 200), "invalid size");
uint64_t st[25] = {};
keccak_step(in, inlen, st);
keccak_finish(in, inlen, st);
memcpy(md, st, N);
}
template<typename T>
FORCEINLINE void keccak_custom(T&& in, int inlen, uint8_t* md, int mdlen)
{
uint64_t st[25];
uint64_t st[25] = {};
const int rsiz = sizeof(st) == mdlen ? KeccakParams::HASH_DATA_AREA : 200 - 2 * mdlen;
const int rsizw = rsiz / 8;
memset(st, 0, sizeof(st));
int offset = 0;
for (; inlen >= rsiz; inlen -= rsiz, offset += rsiz) {
+1 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
+4 -4
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -176,7 +176,7 @@ INT_ENTRY(uint16_t)
INT_ENTRY(uint32_t)
INT_ENTRY(uint64_t)
#ifdef __APPLE__
#if defined(__APPLE__) || defined(__OpenBSD__)
INT_ENTRY(long)
INT_ENTRY(unsigned long)
#endif
@@ -492,7 +492,7 @@ struct DummyStream
MSVC_PRAGMA(warning(suppress:26444)) \
[=]() { \
log::DummyStream x; \
x << level << __VA_ARGS__; \
x << (level) << __VA_ARGS__; \
}; \
} \
} while (0)
@@ -508,7 +508,7 @@ struct DummyStream
#define LOG(level, severity, ...) \
do { \
SIDE_EFFECT_CHECK(level, __VA_ARGS__); \
if (level <= log::GLOBAL_LOG_LEVEL) { \
if ((level) <= log::GLOBAL_LOG_LEVEL) { \
log::Writer CONCAT(log_wrapper_, __LINE__)(severity); \
CONCAT(log_wrapper_, __LINE__) << log::Gray() << log_category_prefix; \
log::apply_severity<severity>(CONCAT(log_wrapper_, __LINE__)); \
+11 -4
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -41,16 +41,23 @@ void p2pool_usage()
"--stratum-api An alias for --local-api\n"
"--no-cache Disable p2pool.cache\n"
"--no-color Disable colors in console output\n"
#ifdef WITH_RANDOMX
"--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes\n"
#endif
"--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 450)\n"
"--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 450)\n"
"--start-mining N Start built-in miner using N threads (any value between 1 and 64)\n"
"--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d\n"
"--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum\n"
"--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"
"--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"
"--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
"--no-upnp Disable UPnP port forwarding\n"
"--no-igd An alias for --no-upnp\n"
"--upnp-stratum Port forward Stratum port (it's not forwarded by default)\n"
#endif
"--help Show this help message\n\n"
"Example command line:\n\n"
"%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",
+1 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
+39 -13
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -39,15 +39,17 @@ Miner::Miner(p2pool* pool, uint32_t threads)
, m_threads(threads)
, m_stopped{ false }
, m_startTimestamp(high_resolution_clock::now())
, m_nonce(0)
, m_rng(RandomDeviceSeed::instance)
, m_fullNonce(std::numeric_limits<uint64_t>::max())
, m_nonceTimestamp(m_startTimestamp)
, m_totalHashes(0)
, m_sharesFound(0)
, m_sharesFailed(0)
, m_job{}
, m_jobIndex{ 0 }
{
std::random_device rd;
m_extraNonce = static_cast<uint32_t>(rd());
// Diffuse the initial state in case it has low quality
m_rng.discard(10000);
on_block(m_pool->block_template());
@@ -77,15 +79,24 @@ Miner::~Miner()
void Miner::print_status()
{
const uint32_t hash_count = 0 - m_nonce.load();
const uint32_t hash_count = std::numeric_limits<uint32_t>::max() - static_cast<uint32_t>(m_fullNonce.load());
const double dt = static_cast<double>(duration_cast<nanoseconds>(high_resolution_clock::now() - m_nonceTimestamp).count()) / 1e9;
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
char shares_failed_buf[64] = {};
log::Stream s(shares_failed_buf);
const uint32_t shares_found = m_sharesFound;
const uint32_t shares_failed = m_sharesFailed;
if (shares_failed) {
s << log::Yellow() << "\nShares failed = " << shares_failed << log::NoColor();
}
LOGINFO(0, "status" <<
"\nThreads = " << m_threads <<
"\nHashrate = " << log::Hashrate(hr) <<
"\nShares found = " << m_sharesFound.load()
"\nThreads = " << m_threads <<
"\nHashrate = " << log::Hashrate(hr) <<
"\nShares found = " << shares_found << static_cast<const char*>(shares_failed_buf)
);
}
@@ -94,9 +105,12 @@ void Miner::on_block(const BlockTemplate& block)
const uint32_t next_index = m_jobIndex ^ 1;
Job& j = m_job[next_index];
hash seed;
j.m_blobSize = block.get_hashing_blob(m_extraNonce, j.m_blob, j.m_height, j.m_sidechainHeight, j.m_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
const uint32_t hash_count = 0 - m_nonce.exchange(0);
const uint32_t extra_nonce = static_cast<uint32_t>(m_rng() >> 32);
j.m_blobSize = block.get_hashing_blob(extra_nonce, j.m_blob, j.m_height, j.m_sidechainHeight, j.m_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
const uint64_t next_full_nonce = (static_cast<uint64_t>(extra_nonce) << 32) | std::numeric_limits<uint32_t>::max();
const uint32_t hash_count = std::numeric_limits<uint32_t>::max() - static_cast<uint32_t>(m_fullNonce.exchange(next_full_nonce));
m_jobIndex = next_index;
const auto cur_ts = high_resolution_clock::now();
@@ -118,6 +132,7 @@ void Miner::on_block(const BlockTemplate& block)
<< ",\"total_hashes\":" << m_totalHashes.load()
<< ",\"time_running\":" << time_running
<< ",\"shares_found\":" << m_sharesFound.load()
<< ",\"shares_failed\":" << m_sharesFailed.load()
<< ",\"block_reward_share_percent\":" << block_reward_share_percent
<< ",\"threads\":" << m_threads
<< "}";
@@ -129,6 +144,7 @@ void Miner::reset_share_counters()
{
m_totalHashes = 0;
m_sharesFound = 0;
m_sharesFailed = 0;
}
void Miner::run(void* data)
@@ -193,14 +209,19 @@ void Miner::run(WorkerData* data)
if (first) {
first = false;
memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job));
job[index].set_nonce(miner->m_nonce.fetch_sub(1), miner->m_extraNonce);
const uint64_t full_nonce = miner->m_fullNonce.fetch_sub(1);
job[index].set_nonce(static_cast<uint32_t>(full_nonce), static_cast<uint32_t>(full_nonce >> 32));
randomx_calculate_hash_first(vm, job[index].m_blob, job[index].m_blobSize);
}
const Job& j = job[index];
index ^= 1;
memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job));
job[index].set_nonce(miner->m_nonce.fetch_sub(1), miner->m_extraNonce);
const uint64_t full_nonce = miner->m_fullNonce.fetch_sub(1);
job[index].set_nonce(static_cast<uint32_t>(full_nonce), static_cast<uint32_t>(full_nonce >> 32));
hash h;
randomx_calculate_hash_next(vm, job[index].m_blob, job[index].m_blobSize, &h);
@@ -213,7 +234,12 @@ void Miner::run(WorkerData* data)
if (j.m_sidechainDiff.check_pow(h)) {
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << j.m_height << ", sidechain height " << j.m_sidechainHeight << ", diff " << j.m_sidechainDiff << ", worker thread " << data->m_index << '/' << data->m_count);
++m_sharesFound;
m_pool->submit_sidechain_block(j.m_templateId, j.m_nonce, j.m_extraNonce);
if (!m_pool->submit_sidechain_block(j.m_templateId, j.m_nonce, j.m_extraNonce)) {
if (m_sharesFound > 0) {
--m_sharesFound;
}
++m_sharesFailed;
}
}
std::this_thread::yield();
+5 -3
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -54,12 +54,14 @@ private:
std::chrono::high_resolution_clock::time_point m_startTimestamp;
std::atomic<uint32_t> m_nonce;
std::mt19937_64 m_rng;
std::atomic<uint64_t> m_fullNonce;
std::chrono::high_resolution_clock::time_point m_nonceTimestamp;
uint32_t m_extraNonce;
std::atomic<uint64_t> m_totalHashes;
std::atomic<uint32_t> m_sharesFound;
std::atomic<uint32_t> m_sharesFailed;
struct Job
{
+176 -116
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -26,6 +26,7 @@
#include "json_rpc_request.h"
#include "json_parsers.h"
#include "block_template.h"
#include "p2pool_api.h"
#include <rapidjson/document.h>
#include <fstream>
#include <numeric>
@@ -75,7 +76,7 @@ P2PServer::P2PServer(p2pool* pool)
[this](bool is_v6, const std::string& /*address*/, const std::string& ip, int port)
{
if (!str_to_ip(is_v6, ip.c_str(), m_socks5ProxyIP)) {
panic();
PANIC_STOP();
}
m_socks5ProxyV6 = is_v6;
m_socks5ProxyPort = port;
@@ -95,7 +96,7 @@ P2PServer::P2PServer(p2pool* pool)
int err = uv_async_init(&m_loop, &m_broadcastAsync, on_broadcast);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
m_broadcastAsync.data = this;
m_broadcastQueue.reserve(2);
@@ -103,21 +104,21 @@ P2PServer::P2PServer(p2pool* pool)
err = uv_async_init(&m_loop, &m_connectToPeersAsync, on_connect_to_peers);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
m_connectToPeersAsync.data = this;
err = uv_async_init(&m_loop, &m_showPeersAsync, on_show_peers);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
m_showPeersAsync.data = this;
err = uv_timer_init(&m_loop, &m_timer);
if (err) {
LOGERR(1, "failed to create timer, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
if (m_cache) {
@@ -130,11 +131,11 @@ P2PServer::P2PServer(p2pool* pool)
err = uv_timer_start(&m_timer, on_timer, 1000, m_timerInterval * 1000);
if (err) {
LOGERR(1, "failed to start timer, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
load_peer_list();
start_listening(params.m_p2pAddresses);
start_listening(params.m_p2pAddresses, params.m_upnp);
}
P2PServer::~P2PServer()
@@ -261,6 +262,8 @@ void P2PServer::on_connect_failed(bool is_v6, const raw_ip& ip, int port)
void P2PServer::update_peer_connections()
{
check_event_loop_thread(__func__);
const uint64_t cur_time = seconds_since_epoch();
const uint64_t last_updated = m_pool->side_chain().last_updated();
@@ -268,40 +271,37 @@ void P2PServer::update_peer_connections()
m_fastestPeer = nullptr;
unordered_set<raw_ip> connected_clients;
{
MutexLock lock(m_clientsListLock);
connected_clients.reserve(m_numConnections);
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
const int timeout = client->m_handshakeComplete ? 300 : 10;
if ((cur_time >= client->m_lastAlive + timeout) && (client->m_socks5ProxyState == Client::Socks5ProxyState::Default)) {
const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive);
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting");
connected_clients.reserve(m_numConnections);
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
const int timeout = client->m_handshakeComplete ? 300 : 10;
if ((cur_time >= client->m_lastAlive + timeout) && (client->m_socks5ProxyState == Client::Socks5ProxyState::Default)) {
const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive);
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting");
client->close();
continue;
}
if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) {
// - Side chain is at least 15 minutes newer (last_updated >= client->m_lastBroadcastTimestamp + 900)
// - It's been at least 10 seconds since side chain updated (cur_time >= last_updated + 10)
// - It's been at least 10 seconds since the last block request (peer is not syncing)
// - Peer should have sent a broadcast by now
if (last_updated && (cur_time >= std::max(last_updated, client->m_lastBlockrequestTimestamp) + 10) && (last_updated >= client->m_lastBroadcastTimestamp + 900)) {
const uint64_t dt = last_updated - client->m_lastBroadcastTimestamp;
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " is not broadcasting blocks (last update " << dt << " seconds ago)");
client->ban(DEFAULT_BAN_TIME);
remove_peer_from_list(client);
client->close();
continue;
}
}
if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) {
// - Side chain is at least 15 minutes newer (last_updated >= client->m_lastBroadcastTimestamp + 900)
// - It's been at least 10 seconds since side chain updated (cur_time >= last_updated + 10)
// - It's been at least 10 seconds since the last block request (peer is not syncing)
// - Peer should have sent a broadcast by now
if (last_updated && (cur_time >= std::max(last_updated, client->m_lastBlockrequestTimestamp) + 10) && (last_updated >= client->m_lastBroadcastTimestamp + 900)) {
const uint64_t dt = last_updated - client->m_lastBroadcastTimestamp;
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " is not broadcasting blocks (last update " << dt << " seconds ago)");
client->ban(DEFAULT_BAN_TIME);
remove_peer_from_list(client);
client->close();
continue;
}
}
connected_clients.insert(client->m_addr);
if (client->is_good()) {
has_good_peers = true;
if ((client->m_pingTime >= 0) && (!m_fastestPeer || (m_fastestPeer->m_pingTime > client->m_pingTime))) {
m_fastestPeer = client;
}
connected_clients.insert(client->m_addr);
if (client->is_good()) {
has_good_peers = true;
if ((client->m_pingTime >= 0) && (!m_fastestPeer || (m_fastestPeer->m_pingTime > client->m_pingTime))) {
m_fastestPeer = client;
}
}
}
@@ -363,7 +363,7 @@ void P2PServer::update_peer_connections()
void P2PServer::update_peer_list()
{
MutexLock lock(m_clientsListLock);
check_event_loop_thread(__func__);
const uint64_t cur_time = seconds_since_epoch();
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
@@ -497,7 +497,12 @@ void P2PServer::load_peer_list()
hints.ai_flags = AI_ADDRCONFIG;
addrinfo* result;
const int err = getaddrinfo(nodes[i], nullptr, &hints, &result);
int err = getaddrinfo(nodes[i], nullptr, &hints, &result);
if (err) {
LOGWARN(4, "getaddrinfo failed for " << nodes[i] << ": " << gai_strerror(err) << ", retrying with IPv4 only");
hints.ai_family = AF_INET;
err = getaddrinfo(nodes[i], nullptr, &hints, &result);
}
if (err == 0) {
for (addrinfo* r = result; r != NULL; r = r->ai_next) {
const char* addr_str;
@@ -737,7 +742,10 @@ void P2PServer::broadcast(const PoolBlock& block, const PoolBlock* parent)
return;
}
Broadcast* data = new Broadcast();
Broadcast* data = new Broadcast{};
data->id = block.m_sidechainId;
data->received_timestamp = block.m_receivedTimestamp;
int outputs_offset, outputs_blob_size;
const std::vector<uint8_t> mainchain_data = block.serialize_mainchain_data(nullptr, nullptr, &outputs_offset, &outputs_blob_size);
@@ -838,6 +846,8 @@ void P2PServer::broadcast(const PoolBlock& block, const PoolBlock* parent)
void P2PServer::on_broadcast()
{
check_event_loop_thread(__func__);
std::vector<Broadcast*> broadcast_queue;
broadcast_queue.reserve(2);
@@ -851,22 +861,13 @@ void P2PServer::on_broadcast()
return;
}
ON_SCOPE_LEAVE([&broadcast_queue]()
{
for (const Broadcast* data : broadcast_queue) {
delete data;
}
});
MutexLock lock(m_clientsListLock);
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if (!client->is_good()) {
continue;
}
for (Broadcast* data : broadcast_queue) {
send(client, [client, data](void* buf, size_t buf_size) -> size_t
const bool result = send(client, [client, data](void* buf, size_t buf_size) -> size_t
{
uint8_t* p0 = reinterpret_cast<uint8_t*>(buf);
uint8_t* p = p0;
@@ -925,15 +926,24 @@ void P2PServer::on_broadcast()
return p - p0;
});
if (!result) {
LOGWARN(5, "failed to broadcast to " << static_cast<char*>(client->m_addrString) << ", disconnecting");
client->close();
break;
}
}
}
for (const Broadcast* data : broadcast_queue) {
const double t = static_cast<double>(microseconds_since_epoch() - data->received_timestamp) * 1e-3;
LOGINFO(5, "Block " << data->id << " took " << t << " ms to process and relay to other peers");
delete data;
}
}
uint64_t P2PServer::get_random64()
{
if (!server_event_loop_thread) {
LOGERR(1, "get_random64() was called from another thread, this is not thread safe");
}
check_event_loop_thread(__func__);
return m_rng();
}
@@ -955,9 +965,9 @@ void P2PServer::show_peers_async()
}
}
void P2PServer::show_peers()
void P2PServer::show_peers() const
{
MutexLock lock(m_clientsListLock);
check_event_loop_thread(__func__);
const uint64_t cur_time = seconds_since_epoch();
size_t n = 0;
@@ -982,13 +992,13 @@ void P2PServer::show_peers()
LOGINFO(0, "Total: " << n << " peers");
}
int P2PServer::listen_port() const
int P2PServer::external_listen_port() const
{
const Params& params = m_pool->params();
return params.m_p2pExternalPort ? params.m_p2pExternalPort : m_listenPort;
}
int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size, bool compact)
int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size, bool compact, uint64_t received_timestamp)
{
int result;
@@ -1003,6 +1013,7 @@ int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size, bool compact
m_lookForMissingBlocks = true;
}
m_block->m_receivedTimestamp = received_timestamp;
return result;
}
@@ -1022,6 +1033,7 @@ void P2PServer::on_timer()
update_peer_connections();
check_zmq();
check_block_template();
api_update_local_stats();
}
void P2PServer::flush_cache()
@@ -1060,6 +1072,8 @@ void P2PServer::flush_cache()
void P2PServer::download_missing_blocks()
{
check_event_loop_thread(__func__);
if (!m_lookForMissingBlocks) {
return;
}
@@ -1073,8 +1087,6 @@ void P2PServer::download_missing_blocks()
return;
}
MutexLock lock(m_clientsListLock);
if (m_numConnections == 0) {
return;
}
@@ -1146,6 +1158,12 @@ void P2PServer::check_zmq()
return;
}
if (!m_pool->zmq_running()) {
LOGERR(1, "ZMQ is not running, restarting it");
m_pool->restart_zmq();
return;
}
const uint64_t cur_time = seconds_since_epoch();
const uint64_t last_active = m_pool->zmq_last_active();
@@ -1162,9 +1180,9 @@ void P2PServer::check_block_template()
return;
}
// Force update block template every 30 seconds after the initial sync is done
if (seconds_since_epoch() >= m_pool->block_template().last_updated() + 30) {
LOGINFO(4, "block template is 30 seconds old, updating it");
// Force update block template every 20 seconds after the initial sync is done
if (seconds_since_epoch() >= m_pool->block_template().last_updated() + 20) {
LOGINFO(4, "block template is 20 seconds old, updating it");
m_pool->update_block_template_async();
}
}
@@ -1204,6 +1222,57 @@ void P2PServer::on_shutdown()
uv_close(reinterpret_cast<uv_handle_t*>(&m_showPeersAsync), nullptr);
}
void P2PServer::api_update_local_stats()
{
if (!m_pool->api() || !m_pool->params().m_localStats || ((m_timerCounter % 30) != 5)) {
return;
}
m_pool->api()->set(p2pool_api::Category::LOCAL, "p2p",
[this](log::Stream& s)
{
const uint64_t cur_time = seconds_since_epoch();
s << "{\"connections\":" << m_numConnections.load()
<< ",\"incoming_connections\":" << m_numIncomingConnections.load()
<< ",\"peer_list_size\":" << m_peerList.size()
<< ",\"peers\":[";
bool first = true;
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if (client->m_listenPort >= 0) {
char buf[32] = {};
log::Stream s1(buf);
if (client->m_SoftwareVersion) {
s1 << client->software_name() << " v" << (client->m_SoftwareVersion >> 16) << '.' << (client->m_SoftwareVersion & 0xFFFF);
}
if (!first) {
s << ',';
}
s << '"'
<< (client->m_isIncoming ? "I," : "O,")
<< (cur_time - client->m_connectedTime) << ','
<< client->m_pingTime << ','
<< static_cast<const char*>(buf) << ','
<< client->m_broadcastMaxHeight << ','
<< static_cast<char*>(client->m_addrString)
<< '"';
if (s.m_pos + 128 >= s.m_bufSize) {
break;
}
first = false;
}
}
s << "],\"uptime\":" << cur_time - m_pool->start_time() << '}';
});
}
P2PServer::P2PClient::~P2PClient()
{
}
@@ -1261,7 +1330,6 @@ bool P2PServer::P2PClient::on_connect()
}
// Don't allow multiple connections to/from the same IP (except localhost)
// server->m_clientsListLock is already locked here
if (!m_addr.is_localhost()) {
for (P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if ((client != this) && (client->m_addr == m_addr)) {
@@ -1316,7 +1384,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
{
case MessageId::HANDSHAKE_CHALLENGE:
if (m_handshakeComplete) {
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected HANDSHAKE_CHALLENGE");
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " sent an unexpected HANDSHAKE_CHALLENGE");
ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this);
return false;
@@ -1337,7 +1405,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
case MessageId::HANDSHAKE_SOLUTION:
if (m_handshakeComplete) {
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected HANDSHAKE_SOLUTION");
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " sent an unexpected HANDSHAKE_SOLUTION");
ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this);
return false;
@@ -1378,7 +1446,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
case MessageId::BLOCK_REQUEST:
++num_block_requests;
if (num_block_requests > 100) {
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent too many BLOCK_REQUEST messages at once");
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " sent too many BLOCK_REQUEST messages at once");
ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this);
return false;
@@ -1398,7 +1466,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
case MessageId::BLOCK_RESPONSE:
if (m_blockPendingRequests.empty()) {
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected BLOCK_RESPONSE");
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " sent an unexpected BLOCK_RESPONSE");
ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this);
return false;
@@ -1458,7 +1526,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
case MessageId::PEER_LIST_RESPONSE:
if (m_peerListPendingRequests <= 0) {
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected PEER_LIST_RESPONSE");
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " sent an unexpected PEER_LIST_RESPONSE");
ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this);
return false;
@@ -1469,7 +1537,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
if (bytes_left >= 2) {
const uint32_t num_peers = buf[1];
if (num_peers > PEER_LIST_RESPONSE_MAX_PEERS) {
LOGWARN(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent too long peer list (" << num_peers << ')');
LOGWARN(5, "peer " << static_cast<char*>(m_addrString) << " sent too long peer list (" << num_peers << ')');
ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this);
return false;
@@ -1482,11 +1550,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
m_pingTime = std::max<int64_t>(duration_cast<milliseconds>(high_resolution_clock::now() - m_lastPeerListRequestTime).count(), 0);
--m_peerListPendingRequests;
if (!on_peer_list_response(buf + 1)) {
ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this);
return false;
}
on_peer_list_response(buf + 1);
}
}
break;
@@ -1578,6 +1642,7 @@ void P2PServer::P2PClient::send_handshake_solution(const uint8_t (&challenge)[CH
P2PClient* client;
P2PServer* server;
uint32_t reset_counter;
bool is_incoming;
uint8_t challenge[CHALLENGE_SIZE];
uint64_t salt;
@@ -1590,6 +1655,7 @@ void P2PServer::P2PClient::send_handshake_solution(const uint8_t (&challenge)[CH
work->client = this;
work->server = server;
work->reset_counter = m_resetCounter.load();
work->is_incoming = m_isIncoming;
memcpy(work->challenge, challenge, CHALLENGE_SIZE);
work->salt = server->get_random64();
@@ -1630,7 +1696,7 @@ void P2PServer::P2PClient::send_handshake_solution(const uint8_t (&challenge)[CH
return;
}
if (work->client->m_isIncoming) {
if (work->is_incoming) {
// This is an incoming connection, so it must do PoW, not us
return;
}
@@ -1745,6 +1811,8 @@ bool P2PServer::P2PClient::check_handshake_solution(const hash& solution, const
bool P2PServer::P2PClient::on_handshake_challenge(const uint8_t* buf)
{
check_event_loop_thread(__func__);
P2PServer* server = static_cast<P2PServer*>(m_owner);
uint8_t challenge[CHALLENGE_SIZE];
@@ -1760,23 +1828,14 @@ bool P2PServer::P2PClient::on_handshake_challenge(const uint8_t* buf)
m_peerId = peer_id;
bool same_peer = false;
{
MutexLock lock(server->m_clientsListLock);
for (const P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if ((client != this) && (client->m_peerId == peer_id)) {
LOGWARN(5, "tried to connect to the same peer twice: current connection " << static_cast<const char*>(client->m_addrString) << ", new connection " << static_cast<const char*>(m_addrString));
same_peer = true;
break;
}
for (const P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if ((client != this) && (client->m_peerId == peer_id)) {
LOGWARN(5, "tried to connect to the same peer twice: current connection " << static_cast<const char*>(client->m_addrString) << ", new connection " << static_cast<const char*>(m_addrString));
close();
return true;
}
}
if (same_peer) {
close();
return true;
}
send_handshake_solution(challenge);
return true;
}
@@ -1841,7 +1900,7 @@ void P2PServer::P2PClient::on_after_handshake(uint8_t* &p)
LOGINFO(5, "sending LISTEN_PORT to " << static_cast<char*>(m_addrString));
*(p++) = static_cast<uint8_t>(MessageId::LISTEN_PORT);
const int32_t port = m_owner->listen_port();
const int32_t port = m_owner->external_listen_port();
memcpy(p, &port, sizeof(port));
p += sizeof(port);
@@ -1921,11 +1980,13 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size,
return true;
}
const uint64_t received_timestamp = microseconds_since_epoch();
P2PServer* server = static_cast<P2PServer*>(m_owner);
MutexLock lock(server->m_blockLock);
const int result = server->deserialize_block(buf, size, false);
const int result = server->deserialize_block(buf, size, false, received_timestamp);
if (result != 0) {
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
return false;
@@ -1966,11 +2027,13 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size,
return false;
}
const uint64_t received_timestamp = microseconds_since_epoch();
P2PServer* server = static_cast<P2PServer*>(m_owner);
MutexLock lock(server->m_blockLock);
const int result = server->deserialize_block(buf, size, compact);
const int result = server->deserialize_block(buf, size, compact, received_timestamp);
if (result != 0) {
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
return false;
@@ -2021,6 +2084,8 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size,
bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
{
check_event_loop_thread(__func__);
P2PServer* server = static_cast<P2PServer*>(m_owner);
const uint64_t cur_time = seconds_since_epoch();
const bool first = (m_prevIncomingPeerListRequest == 0);
@@ -2038,33 +2103,30 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
Peer peers[PEER_LIST_RESPONSE_MAX_PEERS];
uint32_t num_selected_peers = 0;
{
MutexLock lock(server->m_clientsListLock);
// Send every 4th peer on average, selected at random
const uint32_t peers_to_send_target = std::min<uint32_t>(PEER_LIST_RESPONSE_MAX_PEERS, std::max<uint32_t>(1, server->m_numConnections / 4));
uint32_t n = 0;
// Send every 4th peer on average, selected at random
const uint32_t peers_to_send_target = std::min<uint32_t>(PEER_LIST_RESPONSE_MAX_PEERS, std::max<uint32_t>(1, server->m_numConnections / 4));
uint32_t n = 0;
for (P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if (!client->is_good() || (client->m_addr == m_addr)) {
continue;
}
for (P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if (!client->is_good() || (client->m_addr == m_addr)) {
continue;
}
const Peer p{ client->m_isV6, client->m_addr, client->m_listenPort, 0, 0 };
++n;
const Peer p{ client->m_isV6, client->m_addr, client->m_listenPort, 0, 0 };
++n;
// Use https://en.wikipedia.org/wiki/Reservoir_sampling algorithm
if (num_selected_peers < peers_to_send_target) {
peers[num_selected_peers++] = p;
continue;
}
// Use https://en.wikipedia.org/wiki/Reservoir_sampling algorithm
if (num_selected_peers < peers_to_send_target) {
peers[num_selected_peers++] = p;
continue;
}
uint64_t k;
umul128(server->get_random64(), n, &k);
uint64_t k;
umul128(server->get_random64(), n, &k);
if (k < peers_to_send_target) {
peers[k] = p;
}
if (k < peers_to_send_target) {
peers[k] = p;
}
}
@@ -2120,7 +2182,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
});
}
bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf)
void P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf)
{
P2PServer* server = static_cast<P2PServer*>(m_owner);
const uint64_t cur_time = seconds_since_epoch();
@@ -2181,8 +2243,6 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf)
server->m_peerList.emplace_back(Peer{ is_v6, ip, port, 0, cur_time });
}
}
return true;
}
bool P2PServer::P2PClient::handle_incoming_block_async(const PoolBlock* block, uint64_t max_time_delta)
+11 -5
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -68,6 +68,7 @@ public:
~P2PClient();
static Client* allocate() { return new P2PClient(); }
virtual size_t size() const override { return sizeof(P2PClient); }
void reset() override;
bool on_connect() override;
@@ -103,7 +104,7 @@ public:
bool on_block_response(const uint8_t* buf, uint32_t size, const hash& expected_id);
bool on_block_broadcast(const uint8_t* buf, uint32_t size, bool compact);
bool on_peer_list_request(const uint8_t* buf);
bool on_peer_list_response(const uint8_t* buf);
void on_peer_list_response(const uint8_t* buf);
bool handle_incoming_block_async(const PoolBlock* block, uint64_t max_time_delta = 0);
void handle_incoming_block(p2pool* pool, PoolBlock& block, const uint32_t reset_counter, const raw_ip& addr, std::vector<hash>& missing_blocks);
@@ -154,7 +155,7 @@ public:
void show_peers_async();
size_t peer_list_size() const { MutexLock lock(m_peerListLock); return m_peerList.size(); }
int listen_port() const override;
int external_listen_port() const override;
uint32_t max_outgoing_peers() const { return m_maxOutgoingPeers; }
uint32_t max_incoming_peers() const { return m_maxIncomingPeers; }
@@ -162,7 +163,7 @@ public:
void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 450U); }
void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 450U); }
int deserialize_block(const uint8_t* buf, uint32_t size, bool compact);
int deserialize_block(const uint8_t* buf, uint32_t size, bool compact, uint64_t received_timestamp);
const PoolBlock* get_block() const { return m_block; }
private:
@@ -225,6 +226,9 @@ private:
struct Broadcast
{
hash id;
uint64_t received_timestamp;
std::vector<uint8_t> blob;
std::vector<uint8_t> pruned_blob;
std::vector<uint8_t> compact_blob;
@@ -252,9 +256,11 @@ private:
uv_async_t m_showPeersAsync;
static void on_show_peers(uv_async_t* handle) { reinterpret_cast<P2PServer*>(handle->data)->show_peers(); }
void show_peers();
void show_peers() const;
void on_shutdown() override;
void api_update_local_stats();
};
} // namespace p2pool
+110 -55
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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,7 @@
#include "keccak.h"
#include <thread>
#include <fstream>
#include <numeric>
constexpr char log_category_prefix[] = "P2Pool ";
constexpr int BLOCK_HEADERS_REQUIRED = 720;
@@ -51,43 +52,64 @@ namespace p2pool {
p2pool::p2pool(int argc, char* argv[])
: m_stopped(false)
, m_params(new Params(argc, argv))
, m_updateSeed(true)
, m_submitBlockData{}
, m_zmqLastActive(0)
, m_startTime(seconds_since_epoch())
, m_lastMinerDataReceived(0)
{
LOGINFO(1, log::LightCyan() << VERSION);
if (!m_params->m_wallet.valid()) {
Params* p = new Params(argc, argv);
m_params = p;
#ifdef WITH_UPNP
if (p->m_upnp) {
init_upnp();
}
#endif
if (!p->m_wallet.valid()) {
LOGERR(1, "Invalid wallet address. Try \"p2pool --help\".");
throw std::exception();
}
if (m_params->m_socks5Proxy.empty()) {
if (m_params->m_dns) {
m_hostStr = p->m_host;
if (p->m_socks5Proxy.empty()) {
if (p->m_dns) {
bool is_v6;
if (!resolve_host(m_params->m_host, is_v6)) {
LOGERR(1, "resolve_host failed for " << m_params->m_host);
if (!resolve_host(p->m_host, is_v6)) {
LOGERR(1, "resolve_host failed for " << p->m_host);
throw std::exception();
}
}
else if (m_params->m_host.find_first_not_of("0123456789.:") != std::string::npos) {
LOGERR(1, "Can't resolve hostname " << m_params->m_host << " with DNS disabled");
else if (p->m_host.find_first_not_of("0123456789.:") != std::string::npos) {
LOGERR(1, "Can't resolve hostname " << p->m_host << " with DNS disabled");
throw std::exception();
}
}
{
const bool changed = (p->m_host != m_hostStr);
const std::string rpc_port = ':' + std::to_string(p->m_rpcPort);
const std::string zmq_port = ":ZMQ:" + std::to_string(p->m_zmqPort);
m_hostStr += rpc_port + zmq_port;
if (changed) {
m_hostStr += " (" + p->m_host + ')';
}
}
hash pub, sec, eph_public_key;
generate_keys(pub, sec);
uint8_t view_tag;
if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key, view_tag)) {
if (!p->m_wallet.get_eph_public_key(sec, 0, eph_public_key, view_tag)) {
LOGERR(1, "Invalid wallet address: get_eph_public_key failed");
throw std::exception();
}
const NetworkType type = m_params->m_wallet.type();
const NetworkType type = p->m_wallet.type();
if (type == NetworkType::Testnet) {
LOGWARN(1, "Mining to a testnet wallet address");
@@ -132,27 +154,27 @@ p2pool::p2pool(int argc, char* argv[])
#endif
uv_mutex_init_checked(&m_submitBlockDataLock);
m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath, m_params->m_localStats);
m_api = p->m_apiPath.empty() ? nullptr : new p2pool_api(p->m_apiPath, p->m_localStats);
if (m_params->m_localStats && !m_api) {
if (p->m_localStats && !m_api) {
LOGERR(1, "--local-api and --stratum-api command line parameters can't be used without --data-api");
throw std::exception();
}
m_sideChain = new SideChain(this, type, m_params->m_mini ? "mini" : nullptr);
m_sideChain = new SideChain(this, type, p->m_mini ? "mini" : nullptr);
if (m_params->m_p2pAddresses.empty()) {
if (p->m_p2pAddresses.empty()) {
const int p2p_port = m_sideChain->is_mini() ? DEFAULT_P2P_PORT_MINI : DEFAULT_P2P_PORT;
char buf[log::Stream::BUF_SIZE + 1];
char buf[48] = {};
log::Stream s(buf);
s << "[::]:" << p2p_port << ",0.0.0.0:" << p2p_port << '\0';
s << "[::]:" << p2p_port << ",0.0.0.0:" << p2p_port;
m_params->m_p2pAddresses = buf;
p->m_p2pAddresses = buf;
}
#ifdef WITH_RANDOMX
if (m_params->m_disableRandomX) {
if (p->m_disableRandomX) {
m_hasher = new RandomX_Hasher_RPC(this);
}
else {
@@ -176,6 +198,12 @@ p2pool::p2pool(int argc, char* argv[])
p2pool::~p2pool()
{
#ifdef WITH_UPNP
if (m_params->m_upnp) {
destroy_upnp();
}
#endif
uv_rwlock_destroy(&m_mainchainLock);
uv_rwlock_destroy(&m_minerDataLock);
uv_mutex_destroy(&m_foundBlocksLock);
@@ -192,9 +220,9 @@ p2pool::~p2pool()
delete m_params;
}
bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result)
bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result, bool force_light_mode)
{
return m_hasher->calculate(data, size, height, seed, result);
return m_hasher->calculate(data, size, height, seed, result, force_light_mode);
}
uint64_t p2pool::get_seed_height(uint64_t height)
@@ -308,9 +336,12 @@ void p2pool::handle_miner_data(MinerData& data)
// Tx secret keys from all miners change every block, so cache can be cleared here
if (m_sideChain->precalcFinished()) {
clear_crypto_cache();
// Clear all cache entries older than the previous miner data
clear_crypto_cache(m_lastMinerDataReceived);
}
m_lastMinerDataReceived = seconds_since_epoch();
if (!is_main_thread()) {
update_block_template_async();
}
@@ -517,19 +548,26 @@ void p2pool::submit_block() const
size_t nonce_offset = 0;
size_t extra_nonce_offset = 0;
size_t sidechain_id_offset = 0;
hash sidechain_id;
bool is_external = false;
if (submit_data.blob.empty()) {
LOGINFO(0, "submit_block: height = " << height << ", template id = " << submit_data.template_id << ", nonce = " << submit_data.nonce << ", extra_nonce = " << submit_data.extra_nonce);
submit_data.blob = m_blockTemplate->get_block_template_blob(submit_data.template_id, submit_data.extra_nonce, nonce_offset, extra_nonce_offset, sidechain_id_offset, sidechain_id);
LOGINFO(0, log::LightGreen() << "submit_block: height = " << height
<< ", template id = " << submit_data.template_id
<< ", nonce = " << submit_data.nonce
<< ", extra_nonce = " << submit_data.extra_nonce
<< ", id = " << sidechain_id);
submit_data.blob = m_blockTemplate->get_block_template_blob(submit_data.template_id, nonce_offset, extra_nonce_offset);
if (submit_data.blob.empty()) {
LOGERR(0, "submit_block: couldn't find block template with id " << submit_data.template_id);
return;
}
}
else {
LOGINFO(0, "submit_block: height = " << height << ", external blob (" << submit_data.blob.size() << " bytes)");
LOGINFO(0, log::LightGreen() << "submit_block: height = " << height << ", external blob (" << submit_data.blob.size() << " bytes)");
is_external = true;
}
@@ -543,26 +581,28 @@ void p2pool::submit_block() const
const uint32_t extra_nonce = submit_data.extra_nonce;
for (size_t i = 0; i < submit_data.blob.size(); ++i) {
char buf[16];
uint8_t b;
if (nonce_offset && nonce_offset <= i && i < nonce_offset + sizeof(submit_data.nonce)) {
snprintf(buf, sizeof(buf), "%02x", submit_data.nonce & 255);
b = submit_data.nonce & 255;
submit_data.nonce >>= 8;
}
else if (extra_nonce_offset && extra_nonce_offset <= i && i < extra_nonce_offset + sizeof(submit_data.extra_nonce)) {
snprintf(buf, sizeof(buf), "%02x", submit_data.extra_nonce & 255);
b = submit_data.extra_nonce & 255;
submit_data.extra_nonce >>= 8;
}
else {
snprintf(buf, sizeof(buf), "%02x", submit_data.blob[i]);
else if (sidechain_id_offset && sidechain_id_offset <= i && i < sidechain_id_offset + HASH_SIZE) {
b = sidechain_id.h[i - sidechain_id_offset];
}
request.append(buf);
else {
b = submit_data.blob[i];
}
request.append(1, "0123456789abcdef"[b >> 4]);
request.append(1, "0123456789abcdef"[b & 15]);
}
request.append("\"]}");
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, request, m_params->m_rpcLogin, m_params->m_socks5Proxy,
[height, diff, template_id, nonce, extra_nonce, is_external](const char* data, size_t size)
[height, diff, template_id, nonce, extra_nonce, sidechain_id, is_external](const char* data, size_t size)
{
rapidjson::Document doc;
if (doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size).HasParseError() || !doc.IsObject()) {
@@ -589,7 +629,7 @@ void p2pool::submit_block() const
LOGWARN(3, "submit_block (external blob): daemon returned error: " << (error_msg ? error_msg : "unknown error"));
}
else {
LOGERR(0, "submit_block: daemon returned error: '" << (error_msg ? error_msg : "unknown error") << "', template id = " << template_id << ", nonce = " << nonce << ", extra_nonce = " << extra_nonce);
LOGERR(0, "submit_block: daemon returned error: '" << (error_msg ? error_msg : "unknown error") << "', template id = " << template_id << ", nonce = " << nonce << ", extra_nonce = " << extra_nonce << ", id = " << sidechain_id);
}
return;
}
@@ -619,10 +659,10 @@ void p2pool::submit_block() const
});
}
void p2pool::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
bool p2pool::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
{
LOGINFO(3, "submit_sidechain_block: template id = " << template_id << ", nonce = " << nonce << ", extra_nonce = " << extra_nonce);
m_blockTemplate->submit_sidechain_block(template_id, nonce, extra_nonce);
return m_blockTemplate->submit_sidechain_block(template_id, nonce, extra_nonce);
}
void p2pool::update_block_template_async(bool is_alternative_block)
@@ -646,9 +686,8 @@ void p2pool::update_block_template()
{
MinerData data = miner_data();
if (m_updateSeed) {
if (m_updateSeed.exchange(false)) {
m_hasher->set_seed_async(data.seed_hash);
m_updateSeed = false;
}
m_blockTemplate->update(data, *m_mempool, &m_params->m_wallet);
stratum_on_block();
@@ -691,14 +730,14 @@ void p2pool::download_block_headers(uint64_t current_height)
}
else {
LOGERR(1, "fatal error: couldn't download block header for seed height " << height);
panic();
PANIC_STOP();
}
},
[height](const char* data, size_t size)
{
if (size > 0) {
LOGERR(1, "fatal error: couldn't download block header for seed height " << height << ", error " << log::const_buf(data, size));
panic();
PANIC_STOP();
}
});
}
@@ -714,14 +753,6 @@ void p2pool::download_block_headers(uint64_t current_height)
if (parse_block_headers_range(data, size) == current_height - start_height) {
update_median_timestamp();
if (m_serversStarted.exchange(1) == 0) {
try {
m_ZMQReader = new ZMQReader(m_params->m_host, m_params->m_zmqPort, m_params->m_socks5Proxy, this);
}
catch (const std::exception& e) {
LOGERR(1, "Couldn't start ZMQ reader: exception " << e.what());
panic();
}
m_stratumServer = new StratumServer(this);
m_p2pServer = new P2PServer(this);
#ifdef WITH_RANDOMX
@@ -729,6 +760,13 @@ void p2pool::download_block_headers(uint64_t current_height)
start_mining(m_params->m_minerThreads);
}
#endif
try {
m_ZMQReader = new ZMQReader(m_params->m_host, m_params->m_zmqPort, m_params->m_socks5Proxy, this);
}
catch (const std::exception& e) {
LOGERR(1, "Couldn't start ZMQ reader: exception " << e.what());
PANIC_STOP();
}
api_update_network_stats();
get_miner_data();
}
@@ -922,7 +960,7 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
if (monero_network != sidechain_network) {
LOGERR(1, "monerod is on " << monero_network << ", but you're mining to a " << sidechain_network << " sidechain");
panic();
PANIC_STOP();
}
get_version();
@@ -992,7 +1030,7 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
const uint64_t required_version_hi = required >> 16;
const uint64_t required_version_lo = required & 65535;
LOGERR(1, "monerod RPC v" << version_hi << '.' << version_lo << " is incompatible, update to RPC >= v" << required_version_hi << '.' << required_version_lo << " (Monero v0.18.0.0 or newer)");
panic();
PANIC_STOP();
}
get_miner_data();
@@ -1032,7 +1070,7 @@ void p2pool::parse_get_miner_data_rpc(const char* data, size_t size)
}
hash h;
keccak(reinterpret_cast<const uint8_t*>(data), static_cast<int>(size), h.h, HASH_SIZE);
keccak(reinterpret_cast<const uint8_t*>(data), static_cast<int>(size), h.h);
if (h == m_getMinerDataHash) {
LOGWARN(4, "Received a duplicate get_miner_data RPC response, ignoring it");
return;
@@ -1217,12 +1255,20 @@ void p2pool::api_update_pool_stats()
return;
}
const PoolBlock* tip = m_sideChain->chainTip();
const uint64_t bottom_height = m_sideChain->bottom_height(tip);
const uint64_t pplns_window_size = (tip && bottom_height) ? (tip->m_sidechainHeight - bottom_height + 1U) : m_sideChain->chain_window_size();
uint64_t t;
const difficulty_type& diff = m_sideChain->difficulty();
const difficulty_type diff = m_sideChain->difficulty();
const uint64_t height = tip ? tip->m_sidechainHeight : 0;
const uint64_t hashrate = udiv128(diff.hi, diff.lo, m_sideChain->block_time(), &t);
const uint64_t miners = std::max<uint64_t>(m_sideChain->miner_count(), m_p2pServer ? m_p2pServer->peer_list_size() : 0U);
const difficulty_type total_hashes = m_sideChain->total_hashes();
const auto& s = m_blockTemplate->shares();
const difficulty_type pplns_weight = std::accumulate(s.begin(), s.end(), difficulty_type(), [](const auto& a, const auto& b) { return a + b.m_weight; });
time_t last_block_found_time = 0;
uint64_t last_block_found_height = 0;
uint64_t total_blocks_found = 0;
@@ -1237,7 +1283,7 @@ void p2pool::api_update_pool_stats()
}
m_api->set(p2pool_api::Category::POOL, "stats",
[hashrate, miners, &total_hashes, last_block_found_time, last_block_found_height, total_blocks_found](log::Stream& s)
[hashrate, miners, &total_hashes, last_block_found_time, last_block_found_height, total_blocks_found, &pplns_weight, pplns_window_size, diff, height](log::Stream& s)
{
s << "{\"pool_list\":[\"pplns\"],\"pool_statistics\":{\"hashRate\":" << hashrate
<< ",\"miners\":" << miners
@@ -1245,6 +1291,10 @@ void p2pool::api_update_pool_stats()
<< ",\"lastBlockFoundTime\":" << last_block_found_time
<< ",\"lastBlockFound\":" << last_block_found_height
<< ",\"totalBlocksFound\":" << total_blocks_found
<< ",\"pplnsWeight\":" << pplns_weight
<< ",\"pplnsWindowSize\":" << pplns_window_size
<< ",\"sidechainDifficulty\":" << diff
<< ",\"sidechainHeight\":" << height
<< "}}";
});
@@ -1510,6 +1560,11 @@ void p2pool::stop()
}
}
bool p2pool::zmq_running() const
{
return m_ZMQReader && m_ZMQReader->is_running();
}
void p2pool::restart_zmq()
{
// If p2pool is stopped, m_restartZMQAsync is most likely already closed
@@ -1563,7 +1618,7 @@ int p2pool::run()
catch (const std::exception& e) {
const char* s = e.what();
LOGERR(1, "exception " << s);
panic();
PANIC_STOP();
}
m_stopped = true;
+10 -5
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -46,6 +46,7 @@ public:
bool stopped() const { return m_stopped; }
void stop();
const std::string& host_str() const { return m_hostStr; }
const Params& params() const { return *m_params; }
BlockTemplate& block_template() { return *m_blockTemplate; }
SideChain& side_chain() { return *m_sideChain; }
@@ -59,7 +60,7 @@ public:
p2pool_api* api() const { return m_api; }
RandomX_Hasher_Base* hasher() const { return m_hasher; }
bool calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result);
bool calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result, bool force_light_mode);
static uint64_t get_seed_height(uint64_t height);
bool get_seed(uint64_t height, hash& seed) const;
@@ -76,7 +77,7 @@ public:
void submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
void submit_block_async(std::vector<uint8_t>&& blob);
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
bool submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
void update_block_template_async(bool is_alternative_block = false);
void update_block_template();
@@ -94,6 +95,7 @@ public:
void stop_mining();
#endif
bool zmq_running() const;
uint64_t zmq_last_active() const { return m_zmqLastActive; }
uint64_t start_time() const { return m_startTime; }
void restart_zmq();
@@ -111,13 +113,14 @@ private:
std::atomic<bool> m_stopped;
Params* m_params;
std::string m_hostStr;
const Params* m_params;
p2pool_api* m_api;
SideChain* m_sideChain;
RandomX_Hasher_Base* m_hasher;
BlockTemplate* m_blockTemplate;
bool m_updateSeed;
std::atomic<bool> m_updateSeed;
Mempool* m_mempool;
mutable uv_rwlock_t m_mainchainLock;
@@ -208,6 +211,8 @@ private:
hash m_getMinerDataHash;
bool m_getMinerDataPending = false;
std::atomic<uint64_t> m_lastMinerDataReceived;
};
} // namespace p2pool
+77 -26
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -34,7 +34,7 @@ p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats)
{
if (m_apiPath.empty()) {
LOGERR(1, "api path is empty");
panic();
PANIC_STOP();
}
if ((m_apiPath.back() != '/')
@@ -48,13 +48,13 @@ p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats)
struct stat buf;
if (stat(m_apiPath.c_str(), &buf) != 0) {
LOGERR(1, "path " << m_apiPath << " doesn't exist");
panic();
PANIC_STOP();
}
int result = uv_async_init(uv_default_loop_checked(), &m_dumpToFileAsync, on_dump_to_file);
if (result) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(result));
panic();
PANIC_STOP();
}
m_dumpToFileAsync.data = this;
@@ -93,7 +93,7 @@ void p2pool_api::create_dir(const std::string& path)
result = errno;
if (result != EEXIST) {
LOGERR(1, "mkdir(" << path << ") failed, error " << result);
panic();
PANIC_STOP();
}
}
}
@@ -124,7 +124,9 @@ void p2pool_api::dump_to_file_async_internal(Category category, const char* file
m_dumpData[path] = std::move(buf);
}
uv_async_send(&m_dumpToFileAsync);
if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_dumpToFileAsync))) {
uv_async_send(&m_dumpToFileAsync);
}
}
void p2pool_api::dump_to_file()
@@ -170,59 +172,108 @@ void p2pool_api::on_fs_open(uv_fs_t* req)
buf[0].base = work->buf.data();
buf[0].len = static_cast<uint32_t>(work->buf.size());
const int result = uv_fs_write(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), buf, 1, 0, on_fs_write);
int result = uv_fs_write(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), buf, 1, -1, on_fs_write);
if (result < 0) {
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
return;
result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_error_cleanup);
if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
}
}
}
void p2pool_api::on_fs_write(uv_fs_t* req)
{
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
if (req->result < 0) {
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(static_cast<int>(req->result)));
}
int result = static_cast<int>(req->result);
uv_fs_req_cleanup(req);
const int result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_close);
if (result < 0) {
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
}
else if (result && (static_cast<size_t>(result) < work->buf.size())) {
work->buf.erase(work->buf.begin(), work->buf.begin() + result);
uv_buf_t buf[1];
buf[0].base = work->buf.data();
buf[0].len = static_cast<uint32_t>(work->buf.size());
result = uv_fs_write(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), buf, 1, -1, on_fs_write);
if (result < 0) {
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_error_cleanup);
if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
}
}
return;
}
result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_close);
if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
return;
}
}
void p2pool_api::on_fs_close(uv_fs_t* req)
{
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
if (req->result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(static_cast<int>(req->result)));
}
int result = static_cast<int>(req->result);
uv_fs_req_cleanup(req);
const int result = uv_fs_rename(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), work->name.c_str(), on_fs_rename);
if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
}
result = uv_fs_rename(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), work->name.c_str(), on_fs_rename);
if (result < 0) {
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(result));
delete work;
return;
result = uv_fs_unlink(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), on_fs_error_cleanup);
if (result < 0) {
LOGWARN(4, "failed to delete " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
}
}
}
void p2pool_api::on_fs_rename(uv_fs_t* req)
{
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
int result = static_cast<int>(req->result);
uv_fs_req_cleanup(req);
if (req->result < 0) {
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(static_cast<int>(req->result)));
if (result < 0) {
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(result));
result = uv_fs_unlink(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), on_fs_error_cleanup);
if (result < 0) {
LOGWARN(4, "failed to delete " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
}
return;
}
delete work;
}
void p2pool_api::on_fs_error_cleanup(uv_fs_t* req)
{
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
int result = static_cast<int>(req->result);
uv_fs_req_cleanup(req);
if (result < 0) {
LOGWARN(4, "failed to cleanup after previous errors " << work->tmp_name << ", error " << uv_err_name(result));
}
delete work;
}
+2 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -78,6 +78,7 @@ private:
static void on_fs_write(uv_fs_t* req);
static void on_fs_close(uv_fs_t* req);
static void on_fs_rename(uv_fs_t* req);
static void on_fs_error_cleanup(uv_fs_t* req);
std::string m_apiPath;
std::string m_networkPath;
+17 -3
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -100,10 +100,12 @@ Params::Params(int argc, char* argv[])
ok = true;
}
#ifdef WITH_RANDOMX
if (strcmp(argv[i], "--no-randomx") == 0) {
m_disableRandomX = true;
ok = true;
}
#endif
if ((!strcmp(argv[i], "--out-peers") || !strcmp(argv[i], "--outpeers")) && (i + 1 < argc)) {
m_maxOutgoingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 450UL);
@@ -151,6 +153,18 @@ Params::Params(int argc, char* argv[])
ok = true;
}
#ifdef WITH_UPNP
if ((strcmp(argv[i], "--no-upnp") == 0) || (strcmp(argv[i], "--no-igd") == 0)) {
m_upnp = false;
ok = true;
}
if (strcmp(argv[i], "--upnp-stratum") == 0) {
m_upnpStratum = true;
ok = true;
}
#endif
if (!ok) {
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
p2pool_usage();
@@ -161,9 +175,9 @@ Params::Params(int argc, char* argv[])
if (m_stratumAddresses.empty()) {
const int stratum_port = DEFAULT_STRATUM_PORT;
char buf[log::Stream::BUF_SIZE + 1];
char buf[48] = {};
log::Stream s(buf);
s << "[::]:" << stratum_port << ",0.0.0.0:" << stratum_port << '\0';
s << "[::]:" << stratum_port << ",0.0.0.0:" << stratum_port;
m_stratumAddresses = buf;
}
+8 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -53,6 +53,13 @@ struct Params
std::string m_socks5Proxy;
bool m_dns = true;
uint32_t m_p2pExternalPort = 0;
#ifdef WITH_UPNP
bool m_upnp = true;
bool m_upnpStratum = false;
#else
bool m_upnp = false;
bool m_upnpStratum = false;
#endif
};
} // namespace p2pool
+66 -44
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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,11 +38,13 @@ PoolBlock::PoolBlock()
, m_txkeyPub{}
, m_extraNonceSize(0)
, m_extraNonce(0)
, m_txkeySecSeed{}
, m_txkeySec{}
, m_parent{}
, m_sidechainHeight(0)
, m_difficulty{}
, m_cumulativeDifficulty{}
, m_sidechainExtraBuf{}
, m_sidechainId{}
, m_depth(0)
, m_verified(false)
@@ -51,13 +53,12 @@ PoolBlock::PoolBlock()
, m_wantBroadcast(false)
, m_precalculated(false)
, m_localTimestamp(seconds_since_epoch())
, m_receivedTimestamp(0)
{
uv_mutex_init_checked(&m_lock);
}
PoolBlock::PoolBlock(const PoolBlock& b)
{
uv_mutex_init_checked(&m_lock);
operator=(b);
}
@@ -68,11 +69,6 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
return *this;
}
const int lock_result = uv_mutex_trylock(&b.m_lock);
if (lock_result) {
LOGERR(1, "operator= uv_mutex_trylock failed. Fix the code!");
}
#if POOL_BLOCK_DEBUG
m_mainChainDataDebug = b.m_mainChainDataDebug;
m_sideChainDataDebug = b.m_sideChainDataDebug;
@@ -90,12 +86,14 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_extraNonce = b.m_extraNonce;
m_transactions = b.m_transactions;
m_minerWallet = b.m_minerWallet;
m_txkeySecSeed = b.m_txkeySecSeed;
m_txkeySec = b.m_txkeySec;
m_parent = b.m_parent;
m_uncles = b.m_uncles;
m_sidechainHeight = b.m_sidechainHeight;
m_difficulty = b.m_difficulty;
m_cumulativeDifficulty = b.m_cumulativeDifficulty;
memcpy(m_sidechainExtraBuf, b.m_sidechainExtraBuf, sizeof(m_sidechainExtraBuf));
m_sidechainId = b.m_sidechainId;
m_depth = b.m_depth;
m_verified = b.m_verified;
@@ -105,26 +103,12 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_precalculated = b.m_precalculated;
m_localTimestamp = seconds_since_epoch();
if (lock_result == 0) {
uv_mutex_unlock(&b.m_lock);
}
m_receivedTimestamp = b.m_receivedTimestamp;
return *this;
}
PoolBlock::~PoolBlock()
{
uv_mutex_destroy(&m_lock);
}
std::vector<uint8_t> PoolBlock::serialize_mainchain_data(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const
{
MutexLock lock(m_lock);
return serialize_mainchain_data_nolock(header_size, miner_tx_size, outputs_offset, outputs_blob_size);
}
std::vector<uint8_t> PoolBlock::serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const
std::vector<uint8_t> PoolBlock::serialize_mainchain_data(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size, const uint32_t* nonce, const uint32_t* extra_nonce) const
{
std::vector<uint8_t> data;
data.reserve(128 + m_outputs.size() * 39 + m_transactions.size() * HASH_SIZE);
@@ -134,7 +118,11 @@ std::vector<uint8_t> PoolBlock::serialize_mainchain_data_nolock(size_t* header_s
data.push_back(m_minorVersion);
writeVarint(m_timestamp, data);
data.insert(data.end(), m_prevId.h, m_prevId.h + HASH_SIZE);
data.insert(data.end(), reinterpret_cast<const uint8_t*>(&m_nonce), reinterpret_cast<const uint8_t*>(&m_nonce) + NONCE_SIZE);
if (!nonce) {
nonce = &m_nonce;
}
data.insert(data.end(), reinterpret_cast<const uint8_t*>(nonce), reinterpret_cast<const uint8_t*>(nonce) + NONCE_SIZE);
const size_t header_size0 = data.size();
if (header_size) {
@@ -187,7 +175,10 @@ std::vector<uint8_t> PoolBlock::serialize_mainchain_data_nolock(size_t* header_s
*(p++) = TX_EXTRA_NONCE;
*(p++) = static_cast<uint8_t>(extra_nonce_size);
memcpy(p, &m_extraNonce, EXTRA_NONCE_SIZE);
if (!extra_nonce) {
extra_nonce = &m_extraNonce;
}
memcpy(p, extra_nonce, EXTRA_NONCE_SIZE);
p += EXTRA_NONCE_SIZE;
if (extra_nonce_size > EXTRA_NONCE_SIZE) {
memset(p, 0, extra_nonce_size - EXTRA_NONCE_SIZE);
@@ -213,9 +204,9 @@ std::vector<uint8_t> PoolBlock::serialize_mainchain_data_nolock(size_t* header_s
data.insert(data.end(), t + HASH_SIZE, t + m_transactions.size() * HASH_SIZE);
#if POOL_BLOCK_DEBUG
if (!m_mainChainDataDebug.empty() && (data != m_mainChainDataDebug)) {
if ((nonce == &m_nonce) && (extra_nonce == &m_extraNonce) && !m_mainChainDataDebug.empty() && (data != m_mainChainDataDebug)) {
LOGERR(1, "serialize_mainchain_data() has a bug, fix it!");
panic();
PANIC_STOP();
}
#endif
@@ -226,16 +217,14 @@ std::vector<uint8_t> PoolBlock::serialize_sidechain_data() const
{
std::vector<uint8_t> data;
MutexLock lock(m_lock);
data.reserve((m_uncles.size() + 4) * HASH_SIZE + 20);
data.reserve((m_uncles.size() + 4) * HASH_SIZE + 36);
const hash& spend = m_minerWallet.spend_public_key();
const hash& view = m_minerWallet.view_public_key();
data.insert(data.end(), spend.h, spend.h + HASH_SIZE);
data.insert(data.end(), view.h, view.h + HASH_SIZE);
data.insert(data.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE);
data.insert(data.end(), m_txkeySecSeed.h, m_txkeySecSeed.h + HASH_SIZE);
data.insert(data.end(), m_parent.h, m_parent.h + HASH_SIZE);
writeVarint(m_uncles.size(), data);
@@ -252,10 +241,13 @@ std::vector<uint8_t> PoolBlock::serialize_sidechain_data() const
writeVarint(m_cumulativeDifficulty.lo, data);
writeVarint(m_cumulativeDifficulty.hi, data);
const uint8_t* p = reinterpret_cast<const uint8_t*>(m_sidechainExtraBuf);
data.insert(data.end(), p, p + sizeof(m_sidechainExtraBuf));
#if POOL_BLOCK_DEBUG
if (!m_sideChainDataDebug.empty() && (data != m_sideChainDataDebug)) {
LOGERR(1, "serialize_sidechain_data() has a bug, fix it!");
panic();
PANIC_STOP();
}
#endif
@@ -276,9 +268,10 @@ void PoolBlock::reset_offchain_data()
m_precalculated = false;
m_localTimestamp = seconds_since_epoch();
m_receivedTimestamp = 0;
}
bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash)
bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash, bool force_light_mode)
{
alignas(8) uint8_t hashes[HASH_SIZE * 3];
@@ -296,10 +289,8 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
size_t blob_size = 0;
{
MutexLock lock(m_lock);
size_t header_size, miner_tx_size;
const std::vector<uint8_t> mainchain_data = serialize_mainchain_data_nolock(&header_size, &miner_tx_size, nullptr, nullptr);
const std::vector<uint8_t> mainchain_data = serialize_mainchain_data(&header_size, &miner_tx_size, nullptr, nullptr, nullptr, nullptr);
if (!header_size || !miner_tx_size || (mainchain_data.size() < header_size + miner_tx_size)) {
LOGERR(1, "tried to calculate PoW of uninitialized block");
@@ -310,18 +301,22 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
memcpy(blob, mainchain_data.data(), blob_size);
const uint8_t* miner_tx = mainchain_data.data() + header_size;
keccak(miner_tx, static_cast<int>(miner_tx_size) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE);
hash tmp;
keccak(miner_tx, static_cast<int>(miner_tx_size) - 1, tmp.h);
memcpy(hashes, tmp.h, HASH_SIZE);
count = m_transactions.size();
uint8_t* h = reinterpret_cast<uint8_t*>(m_transactions.data());
keccak(reinterpret_cast<uint8_t*>(hashes), HASH_SIZE * 3, h, HASH_SIZE);
keccak(reinterpret_cast<uint8_t*>(hashes), HASH_SIZE * 3, tmp.h);
memcpy(h, tmp.h, HASH_SIZE);
if (count == 1) {
memcpy(blob + blob_size, h, HASH_SIZE);
}
else if (count == 2) {
keccak(h, HASH_SIZE * 2, blob + blob_size, HASH_SIZE);
keccak(h, HASH_SIZE * 2, tmp.h);
memcpy(blob + blob_size, tmp.h, HASH_SIZE);
}
else {
size_t i, j, cnt;
@@ -334,24 +329,27 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
memcpy(tmp_ints.data(), h, (cnt * 2 - count) * HASH_SIZE);
for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, tmp_ints.data() + j * HASH_SIZE, HASH_SIZE);
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, tmp.h);
memcpy(tmp_ints.data() + j * HASH_SIZE, tmp.h, HASH_SIZE);
}
while (cnt > 2) {
cnt >>= 1;
for (i = 0, j = 0; j < cnt; i += 2, ++j) {
keccak(tmp_ints.data() + i * HASH_SIZE, HASH_SIZE * 2, tmp_ints.data() + j * HASH_SIZE, HASH_SIZE);
keccak(tmp_ints.data() + i * HASH_SIZE, HASH_SIZE * 2, tmp.h);
memcpy(tmp_ints.data() + j * HASH_SIZE, tmp.h, HASH_SIZE);
}
}
keccak(tmp_ints.data(), HASH_SIZE * 2, blob + blob_size, HASH_SIZE);
keccak(tmp_ints.data(), HASH_SIZE * 2, tmp.h);
memcpy(blob + blob_size, tmp.h, HASH_SIZE);
}
}
blob_size += HASH_SIZE;
writeVarint(count, [&blob, &blob_size](uint8_t b) { blob[blob_size++] = b; });
return hasher->calculate(blob, blob_size, height, seed_hash, pow_hash);
return hasher->calculate(blob, blob_size, height, seed_hash, pow_hash, force_light_mode);
}
uint64_t PoolBlock::get_payout(const Wallet& w) const
@@ -380,4 +378,28 @@ uint64_t PoolBlock::get_payout(const Wallet& w) const
return 0;
}
hash PoolBlock::calculate_tx_key_seed() const
{
const char domain[] = "tx_key_seed";
const uint32_t zero = 0;
const std::vector<uint8_t> mainchain_data = serialize_mainchain_data(nullptr, nullptr, nullptr, nullptr, &zero, &zero);
const std::vector<uint8_t> sidechain_data = serialize_sidechain_data();
hash result;
keccak_custom([&domain, &mainchain_data, &sidechain_data](int offset) -> uint8_t {
size_t k = offset;
if (k < sizeof(domain)) return domain[k];
k -= sizeof(domain);
if (k < mainchain_data.size()) return mainchain_data[k];
k -= mainchain_data.size();
return sidechain_data[k];
}, static_cast<int>(sizeof(domain) + mainchain_data.size() + sidechain_data.size()), result.h, HASH_SIZE);
return result;
}
} // namespace p2pool
+12 -7
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -28,6 +28,8 @@
namespace p2pool {
static FORCEINLINE constexpr int pool_block_debug() { return POOL_BLOCK_DEBUG; }
class RandomX_Hasher_Base;
class SideChain;
@@ -56,13 +58,10 @@ struct DifficultyData
struct PoolBlock
{
PoolBlock();
~PoolBlock();
PoolBlock(const PoolBlock& b);
PoolBlock& operator=(const PoolBlock& b);
mutable uv_mutex_t m_lock;
#if POOL_BLOCK_DEBUG
std::vector<uint8_t> m_mainChainDataDebug;
std::vector<uint8_t> m_sideChainDataDebug;
@@ -104,6 +103,7 @@ struct PoolBlock
// Transaction secret key
// Required to check that pub keys in the miner transaction pay out to correct miner wallet addresses
hash m_txkeySecSeed;
hash m_txkeySec;
// Side-chain parent and uncle blocks
@@ -115,6 +115,9 @@ struct PoolBlock
difficulty_type m_difficulty;
difficulty_type m_cumulativeDifficulty;
// Arbitrary extra data
uint32_t m_sidechainExtraBuf[4];
// HASH (see diagram in the comment above)
hash m_sidechainId;
@@ -130,15 +133,15 @@ struct PoolBlock
bool m_precalculated;
uint64_t m_localTimestamp;
uint64_t m_receivedTimestamp;
std::vector<uint8_t> serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr) const;
std::vector<uint8_t> serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const;
std::vector<uint8_t> serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr, const uint32_t* nonce = nullptr, const uint32_t* extra_nonce = nullptr) const;
std::vector<uint8_t> serialize_sidechain_data() const;
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact);
void reset_offchain_data();
bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash);
bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash, bool force_light_mode = false);
uint64_t get_payout(const Wallet& w) const;
@@ -157,6 +160,8 @@ struct PoolBlock
memcpy(p + HASH_SIZE + NONCE_SIZE, &m_extraNonce, EXTRA_NONCE_SIZE);
return key;
}
hash calculate_tx_key_seed() const;
};
} // namespace p2pool
+13 -16
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -61,8 +61,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
#define READ_BUF(buf, size) do { if (!read_buf((buf), (size))) return __LINE__; } while(0)
MutexLock lock(m_lock);
READ_BYTE(m_majorVersion);
if (m_majorVersion > HARDFORK_SUPPORTED_VERSION) return __LINE__;
@@ -260,19 +258,16 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__;
}
READ_BUF(m_txkeySec.h, HASH_SIZE);
READ_BUF(m_txkeySecSeed.h, HASH_SIZE);
if (!check_keys(m_txkeyPub, m_txkeySec)) {
hash pub;
get_tx_keys(pub, m_txkeySec, m_txkeySecSeed, m_prevId);
if (pub != m_txkeyPub) {
return __LINE__;
}
// Enforce deterministic tx keys starting from v15
if (m_majorVersion >= HARDFORK_VIEW_TAGS_VERSION) {
hash pub, sec;
get_tx_keys(pub, sec, spend_pub_key, m_prevId);
if ((pub != m_txkeyPub) || (sec != m_txkeySec)) {
return __LINE__;
}
if (!check_keys(m_txkeyPub, m_txkeySec)) {
return __LINE__;
}
READ_BUF(m_parent.h, HASH_SIZE);
@@ -317,6 +312,8 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
READ_VARINT(m_cumulativeDifficulty.lo);
READ_VARINT(m_cumulativeDifficulty.hi);
READ_BUF(m_sidechainExtraBuf, sizeof(m_sidechainExtraBuf));
#undef READ_BYTE
#undef EXPECT_BYTE
#undef READ_VARINT
@@ -382,13 +379,13 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
},
static_cast<int>(size + outputs_blob_size_diff + transactions_blob_size_diff + consensus_id.size()), check.h, HASH_SIZE);
if (check != m_sidechainId) {
return __LINE__;
}
#if POOL_BLOCK_DEBUG
m_sideChainDataDebug.assign(sidechain_data_begin, data_end);
#endif
if (check != m_sidechainId) {
return __LINE__;
}
}
catch (std::exception& e) {
const char* msg = e.what();
+10 -10
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -68,7 +68,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
m_cache[i] = randomx_alloc_cache(flags);
if (!m_cache[i]) {
LOGERR(1, "couldn't allocate RandomX cache, aborting");
panic();
PANIC_STOP();
}
}
memory_allocated += RANDOMX_ARGON_MEMORY * 1024;
@@ -206,7 +206,7 @@ void RandomX_Hasher::set_seed(const hash& seed)
m_vm[m_index].vm = randomx_create_vm(flags, m_cache[m_index], nullptr);
if (!m_vm[m_index].vm) {
LOGERR(1, "couldn't allocate RandomX light VM, aborting");
panic();
PANIC_STOP();
}
}
}
@@ -308,7 +308,7 @@ void RandomX_Hasher::set_old_seed(const hash& seed)
m_vm[old_index].vm = randomx_create_vm(flags, m_cache[old_index], nullptr);
if (!m_vm[old_index].vm) {
LOGERR(1, "couldn't allocate RandomX light VM, aborting");
panic();
PANIC_STOP();
}
}
}
@@ -322,10 +322,10 @@ void RandomX_Hasher::sync_wait()
ReadLock lock2(m_cacheLock);
}
bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*/, const hash& seed, hash& result)
bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*/, const hash& seed, hash& result, bool force_light_mode)
{
// First try to use the dataset if it's ready
if (uv_rwlock_tryrdlock(&m_datasetLock) == 0) {
if (!force_light_mode && (uv_rwlock_tryrdlock(&m_datasetLock) == 0)) {
ON_SCOPE_LEAVE([this]() { uv_rwlock_rdunlock(&m_datasetLock); });
if (m_stopped.load()) {
@@ -340,7 +340,7 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*
}
}
// If dataset is not ready, use the cache and wait if necessary
// If dataset is not ready, or force_light_mode = true, use the cache and wait if necessary
ReadLock lock(m_cacheLock);
if (m_stopped.load()) {
@@ -376,7 +376,7 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
int err = uv_loop_init(&m_loop);
if (err) {
LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
// Init loop user data before running it
@@ -394,7 +394,7 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
err = uv_thread_create(&m_loopThread, loop, this);
if (err) {
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
}
@@ -429,7 +429,7 @@ void RandomX_Hasher_RPC::loop(void* data)
LOGINFO(1, "event loop stopped");
}
bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t height, const hash& /*seed*/, hash& h)
bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t height, const hash& /*seed*/, hash& h, bool /*force_light_mode*/)
{
MutexLock lock(m_requestMutex);
+4 -4
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -40,7 +40,7 @@ public:
virtual uint32_t seed_counter() const { return 0; }
virtual void sync_wait() {}
virtual bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) = 0;
virtual bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result, bool force_light_mode) = 0;
};
#ifdef WITH_RANDOMX
@@ -60,7 +60,7 @@ public:
uint32_t seed_counter() const override { return m_seedCounter.load(); }
void sync_wait() override;
bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) override;
bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result, bool force_light_mode) override;
private:
@@ -100,7 +100,7 @@ public:
explicit RandomX_Hasher_RPC(p2pool* pool);
~RandomX_Hasher_RPC();
bool calculate(const void* data_ptr, size_t size, uint64_t height, const hash& seed, hash& h) override;
bool calculate(const void* data_ptr, size_t size, uint64_t height, const hash& seed, hash& h, bool force_light_mode) override;
private:
static void loop(void* data);
+334 -257
View File
File diff suppressed because it is too large Load Diff
+31 -10
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -32,6 +32,8 @@ struct MinerShare
FORCEINLINE MinerShare() : m_weight(), m_wallet(nullptr) {}
FORCEINLINE MinerShare(const difficulty_type& w, const Wallet* x) : m_weight(w), m_wallet(x) {}
FORCEINLINE bool operator==(const MinerShare& s) const { return *m_wallet == *s.m_wallet; }
difficulty_type m_weight;
const Wallet* m_wallet;
};
@@ -42,12 +44,12 @@ public:
SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr);
~SideChain();
void fill_sidechain_data(PoolBlock& block, const Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares) const;
void fill_sidechain_data(PoolBlock& block, std::vector<MinerShare>& shares) const;
bool block_seen(const PoolBlock& block);
void unsee_block(const PoolBlock& block);
bool add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks);
void add_block(const PoolBlock& block);
bool add_block(const PoolBlock& block);
void get_missing_blocks(std::vector<hash>& missing_blocks) const;
PoolBlock* find_block(const hash& id) const;
@@ -64,8 +66,8 @@ public:
// Consensus ID can therefore be used as a password to create private P2Pools
const std::vector<uint8_t>& consensus_id() const { return m_consensusId; }
uint64_t chain_window_size() const { return m_chainWindowSize; }
NetworkType network_type() const { return m_networkType; }
uint64_t network_major_version(uint64_t height) const;
static NetworkType network_type() { return s_networkType; }
static uint64_t network_major_version(uint64_t height);
FORCEINLINE difficulty_type difficulty() const { ReadLock lock(m_curDifficultyLock); return m_curDifficulty; }
difficulty_type total_hashes() const;
uint64_t block_time() const { return m_targetBlockTime; }
@@ -73,28 +75,32 @@ public:
uint64_t last_updated() const;
bool is_default() const;
bool is_mini() const;
uint64_t bottom_height(const PoolBlock* tip) const;
const PoolBlock* chainTip() const { return m_chainTip; }
bool precalcFinished() const { return m_precalcFinished.load(); }
#ifdef P2POOL_UNIT_TESTS
difficulty_type m_testMainChainDiff;
#endif
static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards);
private:
p2pool* m_pool;
P2PServer* p2pServer() const;
NetworkType m_networkType;
static NetworkType s_networkType;
private:
bool get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares) const;
bool get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares, uint64_t* bottom_height = nullptr, bool quiet = false) const;
bool get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const;
bool get_wallets(const PoolBlock* tip, std::vector<const Wallet*>& wallets) const;
void verify_loop(PoolBlock* block);
void verify(PoolBlock* block);
void update_chain_tip(const PoolBlock* block);
PoolBlock* get_parent(const PoolBlock* block) const;
// Checks if "candidate" has longer (higher difficulty) chain than "block"
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative);
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative) const;
void update_depths(PoolBlock* block);
void prune_old_blocks();
@@ -134,7 +140,7 @@ private:
struct PrecalcJob
{
const PoolBlock* b;
std::vector<const Wallet*> wallets;
std::vector<MinerShare> shares;
};
uv_cond_t m_precalcJobsCond;
@@ -146,9 +152,24 @@ private:
std::atomic<bool> m_precalcFinished;
hash m_consensusHash;
void launch_precalc(const PoolBlock* block);
void precalc_worker();
void finish_precalc();
};
} // namespace p2pool
namespace robin_hood {
template<>
struct hash<p2pool::MinerShare>
{
FORCEINLINE size_t operator()(const p2pool::MinerShare& value) const noexcept
{
return hash_bytes(value.m_wallet->spend_public_key().h, p2pool::HASH_SIZE);
}
};
} // namespace robin_hood
+205 -132
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -54,33 +54,36 @@ StratumServer::StratumServer(p2pool* pool)
, m_hashrateDataTail_24h(0)
, m_cumulativeFoundSharesDiff(0.0)
, m_totalFoundShares(0)
, m_totalFailedShares(0)
, m_apiLastUpdateTime(0)
{
// Diffuse the initial state in case it has low quality
m_rng.discard(10000);
m_extraNonce = static_cast<uint32_t>(m_rng());
m_hashrateData[0] = { seconds_since_epoch(), 0 };
uv_mutex_init_checked(&m_blobsQueueLock);
uv_mutex_init_checked(&m_rngLock);
uv_rwlock_init_checked(&m_hashrateDataLock);
m_extraNonce = get_random32();
m_submittedSharesPool.resize(10);
for (size_t i = 0; i < m_submittedSharesPool.size(); ++i) {
m_submittedSharesPool[i] = new SubmittedShare{};
SubmittedShare* share = new SubmittedShare{};
ASAN_POISON_MEMORY_REGION(share, sizeof(SubmittedShare));
m_submittedSharesPool[i] = share;
}
const int err = uv_async_init(&m_loop, &m_blobsAsync, on_blobs_ready);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
}
uv_async_init_checked(&m_loop, &m_blobsAsync, on_blobs_ready);
m_blobsAsync.data = this;
m_blobsQueue.reserve(2);
start_listening(pool->params().m_stratumAddresses);
uv_async_init_checked(&m_loop, &m_showWorkersAsync, on_show_workers);
m_showWorkersAsync.data = this;
const Params& params = pool->params();
start_listening(params.m_stratumAddresses, params.m_upnp && params.m_upnpStratum);
}
StratumServer::~StratumServer()
@@ -92,6 +95,7 @@ StratumServer::~StratumServer()
uv_rwlock_destroy(&m_hashrateDataLock);
for (SubmittedShare* share : m_submittedSharesPool) {
ASAN_UNPOISON_MEMORY_REGION(share, sizeof(SubmittedShare));
delete share;
}
}
@@ -106,7 +110,7 @@ void StratumServer::on_block(const BlockTemplate& block)
return;
}
const uint32_t extra_nonce_start = static_cast<uint32_t>(get_random64());
const uint32_t extra_nonce_start = get_random32();
m_extraNonce.exchange(extra_nonce_start + num_connections);
BlobsData* blobs_data = new BlobsData{};
@@ -277,12 +281,13 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
saved_job.template_id = template_id;
saved_job.target = target;
}
client->m_lastJobTarget = target;
const bool result = send(client,
[client, id, &hashing_blob, job_id, blob_size, target, height, &seed_hash](void* buf, size_t buf_size)
{
do {
client->m_rpcId = static_cast<uint32_t>(static_cast<StratumServer*>(client->m_owner)->get_random64());
client->m_rpcId = static_cast<StratumServer*>(client->m_owner)->get_random32();
} while (!client->m_rpcId);
log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t));
@@ -388,6 +393,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
if (!m_submittedSharesPool.empty()) {
share = m_submittedSharesPool.back();
m_submittedSharesPool.pop_back();
ASAN_UNPOISON_MEMORY_REGION(share, sizeof(SubmittedShare));
}
else {
share = new SubmittedShare{};
@@ -419,6 +425,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
uint64_t rem;
share->m_hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 1;
share->m_highEnoughDifficulty = sidechain_diff.check_pow(resultHash);
share->m_score = 0;
// Don't count shares that were found during sync
const SideChain& side_chain = m_pool->side_chain();
@@ -462,10 +469,10 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
return result;
}
uint64_t StratumServer::get_random64()
uint32_t StratumServer::get_random32()
{
MutexLock lock(m_rngLock);
return m_rng();
return static_cast<uint32_t>(m_rng() >> 32);
}
void StratumServer::print_status()
@@ -474,13 +481,20 @@ void StratumServer::print_status()
print_stratum_status();
}
void StratumServer::show_workers_async()
{
if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_showWorkersAsync))) {
uv_async_send(&m_showWorkersAsync);
}
}
void StratumServer::show_workers()
{
check_event_loop_thread(__func__);
const uint64_t cur_time = seconds_since_epoch();
const difficulty_type pool_diff = m_pool->side_chain().difficulty();
MutexLock lock(m_clientsListLock);
int addr_len = 0;
for (const StratumClient* c = static_cast<StratumClient*>(m_connectedClientsList->m_next); c != m_connectedClientsList; c = static_cast<StratumClient*>(c->m_next)) {
addr_len = std::max(addr_len, static_cast<int>(strlen(c->m_addrString)));
@@ -496,15 +510,14 @@ void StratumServer::show_workers()
);
for (const StratumClient* c = static_cast<StratumClient*>(m_connectedClientsList->m_next); c != m_connectedClientsList; c = static_cast<StratumClient*>(c->m_next)) {
difficulty_type diff;
if (c->m_customDiff != 0) {
diff = c->m_customDiff;
}
else if (m_autoDiff && (c->m_autoDiff != 0)) {
diff = c->m_autoDiff;
}
else {
diff = pool_diff;
difficulty_type diff = pool_diff;
if (c->m_lastJobTarget > 1) {
uint64_t r;
diff.lo = udiv128(1, 0, c->m_lastJobTarget, &r);
diff.hi = 0;
if (r) {
++diff.lo;
}
}
LOGINFO(0, log::pad_right(static_cast<const char*>(c->m_addrString), addr_len + 8)
<< log::pad_right(log::Duration(cur_time - c->m_connectedTime), 20)
@@ -522,10 +535,9 @@ void StratumServer::reset_share_counters()
{
WriteLock lock(m_hashrateDataLock);
m_cumulativeHashes = 0;
m_cumulativeHashesAtLastShare = 0;
m_cumulativeFoundSharesDiff = 0.0;
m_cumulativeHashesAtLastShare = m_cumulativeHashes;
m_totalFoundShares = 0;
m_totalFailedShares = 0;
}
void StratumServer::print_stratum_status() const
@@ -535,7 +547,7 @@ void StratumServer::print_stratum_status() const
uint64_t hashes_since_last_share;
double average_effort;
int shares_found;
uint32_t shares_found, shares_failed;
{
ReadLock lock(m_hashrateDataLock);
@@ -565,18 +577,25 @@ void StratumServer::print_stratum_status() const
}
shares_found = m_totalFoundShares;
shares_failed = m_totalFailedShares;
}
const uint64_t hashrate_15m = (dt_15m > 0) ? (hashes_15m / dt_15m) : 0;
const uint64_t hashrate_1h = (dt_1h > 0) ? (hashes_1h / dt_1h ) : 0;
const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0;
char shares_failed_buf[64] = {};
log::Stream s(shares_failed_buf);
if (shares_failed) {
s << log::Yellow() << "\nShares failed = " << shares_failed << log::NoColor();
}
LOGINFO(0, "status" <<
"\nHashrate (15m est) = " << log::Hashrate(hashrate_15m) <<
"\nHashrate (1h est) = " << log::Hashrate(hashrate_1h) <<
"\nHashrate (24h est) = " << log::Hashrate(hashrate_24h) <<
"\nTotal hashes = " << total_hashes <<
"\nShares found = " << shares_found <<
"\nShares found = " << shares_found << static_cast<const char*>(shares_failed_buf) <<
"\nAverage effort = " << average_effort << '%' <<
"\nCurrent effort = " << static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double() << '%' <<
"\nConnections = " << m_numConnections.load() << " (" << m_numIncomingConnections.load() << " incoming)"
@@ -658,6 +677,8 @@ void StratumServer::update_auto_diff(StratumClient* client, const uint64_t times
void StratumServer::on_blobs_ready()
{
check_event_loop_thread(__func__);
std::vector<BlobsData*> blobs_queue;
blobs_queue.reserve(2);
@@ -686,100 +707,98 @@ void StratumServer::on_blobs_ready()
uint32_t num_sent = 0;
const uint64_t cur_time = seconds_since_epoch();
{
MutexLock lock2(m_clientsListLock);
for (StratumClient* client = static_cast<StratumClient*>(m_connectedClientsList->m_prev); client != m_connectedClientsList; client = static_cast<StratumClient*>(client->m_prev)) {
++numClientsProcessed;
for (StratumClient* client = static_cast<StratumClient*>(m_connectedClientsList->m_prev); client != m_connectedClientsList; client = static_cast<StratumClient*>(client->m_prev)) {
++numClientsProcessed;
if (!client->m_rpcId) {
// Not logged in yet, on_login() will send the job to this client. Also close inactive connections.
if (cur_time >= client->m_connectedTime + 10) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " didn't send login data");
client->ban(DEFAULT_BAN_TIME);
client->close();
}
continue;
}
if (num_sent >= data->m_numClientsExpected) {
// We don't have any more extra_nonce values available
continue;
}
uint8_t* hashing_blob = data->m_blobs.data() + num_sent * data->m_blobSize;
uint64_t target = data->m_target;
if (client->m_customDiff.lo) {
target = std::max(target, client->m_customDiff.target());
}
else if (m_autoDiff) {
// Limit autodiff to 4000000 for maximum compatibility
target = std::max(target, TARGET_4_BYTES_LIMIT);
if (client->m_autoDiff.lo) {
const uint32_t k = client->m_autoDiffIndex;
const uint16_t elapsed_time = static_cast<uint16_t>(cur_time) - client->m_autoDiffData[(k - 1) % StratumClient::AUTO_DIFF_SIZE].m_timestamp;
if (elapsed_time > AUTO_DIFF_TARGET_TIME * 5) {
// More than 500% effort, reduce the auto diff by 1/8 every time until the share is found
client->m_autoDiff.lo = std::max<uint64_t>(client->m_autoDiff.lo - client->m_autoDiff.lo / 8, MIN_DIFF);
}
target = std::max(target, client->m_autoDiff.target());
}
else {
// Not enough shares from the client yet, cut diff in half every 16 seconds
const uint64_t num_halvings = (cur_time - client->m_connectedTime) / 16;
constexpr uint64_t max_target = (std::numeric_limits<uint64_t>::max() / MIN_DIFF) + 1;
for (uint64_t i = 0; (i < num_halvings) && (target < max_target); ++i) {
target *= 2;
}
target = std::min<uint64_t>(target, max_target);
}
}
uint32_t job_id;
{
job_id = ++client->m_perConnectionJobId;
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE];
saved_job.job_id = job_id;
saved_job.extra_nonce = extra_nonce_start + num_sent;
saved_job.template_id = data->m_templateId;
saved_job.target = target;
}
const bool result = send(client,
[data, target, hashing_blob, &job_id](void* buf, size_t buf_size)
{
log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t));
if (target >= TARGET_4_BYTES_LIMIT) {
target_hex.m_data += sizeof(uint32_t);
target_hex.m_size -= sizeof(uint32_t);
}
log::Stream s(buf, buf_size);
s << "{\"jsonrpc\":\"2.0\",\"method\":\"job\",\"params\":{\"blob\":\"";
s << log::hex_buf(hashing_blob, data->m_blobSize) << "\",\"job_id\":\"";
s << log::Hex(job_id) << "\",\"target\":\"";
s << target_hex << "\",\"algo\":\"rx/0\",\"height\":";
s << data->m_height << ",\"seed_hash\":\"";
s << data->m_seedHash << "\"}}\n";
return s.m_pos;
});
if (result) {
++num_sent;
}
else {
if (!client->m_rpcId) {
// Not logged in yet, on_login() will send the job to this client. Also close inactive connections.
if (cur_time >= client->m_connectedTime + 10) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " didn't send login data");
client->ban(DEFAULT_BAN_TIME);
client->close();
}
continue;
}
const uint32_t num_connections = m_numConnections;
if (numClientsProcessed != num_connections) {
LOGWARN(1, "client list is broken, expected " << num_connections << ", got " << numClientsProcessed << " clients");
if (num_sent >= data->m_numClientsExpected) {
// We don't have any more extra_nonce values available
continue;
}
uint8_t* hashing_blob = data->m_blobs.data() + num_sent * data->m_blobSize;
uint64_t target = data->m_target;
if (client->m_customDiff.lo) {
target = std::max(target, client->m_customDiff.target());
}
else if (m_autoDiff) {
// Limit autodiff to 4000000 for maximum compatibility
target = std::max(target, TARGET_4_BYTES_LIMIT);
if (client->m_autoDiff.lo) {
const uint32_t k = client->m_autoDiffIndex;
const uint16_t elapsed_time = static_cast<uint16_t>(cur_time) - client->m_autoDiffData[(k - 1) % StratumClient::AUTO_DIFF_SIZE].m_timestamp;
if (elapsed_time > AUTO_DIFF_TARGET_TIME * 5) {
// More than 500% effort, reduce the auto diff by 1/8 every time until the share is found
client->m_autoDiff.lo = std::max<uint64_t>(client->m_autoDiff.lo - client->m_autoDiff.lo / 8, MIN_DIFF);
}
target = std::max(target, client->m_autoDiff.target());
}
else {
// Not enough shares from the client yet, cut diff in half every 16 seconds
const uint64_t num_halvings = (cur_time - client->m_connectedTime) / 16;
constexpr uint64_t max_target = (std::numeric_limits<uint64_t>::max() / MIN_DIFF) + 1;
for (uint64_t i = 0; (i < num_halvings) && (target < max_target); ++i) {
target *= 2;
}
target = std::min<uint64_t>(target, max_target);
}
}
uint32_t job_id;
{
job_id = ++client->m_perConnectionJobId;
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE];
saved_job.job_id = job_id;
saved_job.extra_nonce = extra_nonce_start + num_sent;
saved_job.template_id = data->m_templateId;
saved_job.target = target;
}
client->m_lastJobTarget = target;
const bool result = send(client,
[data, target, hashing_blob, job_id](void* buf, size_t buf_size)
{
log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t));
if (target >= TARGET_4_BYTES_LIMIT) {
target_hex.m_data += sizeof(uint32_t);
target_hex.m_size -= sizeof(uint32_t);
}
log::Stream s(buf, buf_size);
s << "{\"jsonrpc\":\"2.0\",\"method\":\"job\",\"params\":{\"blob\":\"";
s << log::hex_buf(hashing_blob, data->m_blobSize) << "\",\"job_id\":\"";
s << log::Hex(job_id) << "\",\"target\":\"";
s << target_hex << "\",\"algo\":\"rx/0\",\"height\":";
s << data->m_height << ",\"seed_hash\":\"";
s << data->m_seedHash << "\"}}\n";
return s.m_pos;
});
if (result) {
++num_sent;
}
else {
client->close();
}
}
const uint32_t num_connections = m_numConnections;
if (numClientsProcessed != num_connections) {
LOGWARN(1, "client list is broken, expected " << num_connections << ", got " << numClientsProcessed << " clients");
}
LOGINFO(3, "sent new job to " << num_sent << '/' << numClientsProcessed << " clients");
@@ -819,12 +838,19 @@ void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp)
void StratumServer::on_share_found(uv_work_t* req)
{
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
StratumServer* server = share->m_server;
if (server->is_banned(share->m_clientAddr)) {
share->m_highEnoughDifficulty = false;
share->m_result = SubmittedShare::Result::BANNED;
return;
}
if (share->m_highEnoughDifficulty) {
BACKGROUND_JOB_START(StratumServer::on_share_found);
}
StratumClient* client = share->m_client;
StratumServer* server = share->m_server;
p2pool* pool = server->m_pool;
const uint64_t target = share->m_target;
@@ -855,7 +881,7 @@ void StratumServer::on_share_found(uv_work_t* req)
}
hash pow_hash;
if (!pool->calculate_hash(blob, blob_size, height, seed_hash, pow_hash)) {
if (!pool->calculate_hash(blob, blob_size, height, seed_hash, pow_hash, false)) {
LOGWARN(3, "client " << static_cast<char*>(client->m_addrString) << " couldn't check share PoW");
share->m_result = SubmittedShare::Result::COULDNT_CHECK_POW;
return;
@@ -864,11 +890,18 @@ void StratumServer::on_share_found(uv_work_t* req)
if (pow_hash != share->m_resultHash) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " submitted a share with invalid PoW");
share->m_result = SubmittedShare::Result::INVALID_POW;
client->m_score += BAD_SHARE_POINTS;
share->m_score = BAD_SHARE_POINTS;
// Calculate the same hash second time to check if it's an unstable hardware that caused this
hash pow_hash2;
if (pool->calculate_hash(blob, blob_size, height, seed_hash, pow_hash2, true) && (pow_hash2 != pow_hash)) {
LOGERR(0, "UNSTABLE HARDWARE DETECTED: Calculated the same hash twice, got different results: " << pow_hash << " != " << pow_hash2);
}
return;
}
client->m_score += GOOD_SHARE_POINTS;
share->m_score = GOOD_SHARE_POINTS;
const double diff = sidechain_difficulty.to_double();
{
@@ -882,7 +915,14 @@ void StratumServer::on_share_found(uv_work_t* req)
++server->m_totalFoundShares;
}
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
if (!pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce)) {
WriteLock lock(server->m_hashrateDataLock);
if (server->m_totalFoundShares > 0) {
--server->m_totalFoundShares;
++server->m_totalFailedShares;
}
}
}
// Send the response to miner
@@ -897,7 +937,7 @@ void StratumServer::on_share_found(uv_work_t* req)
else {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a low diff share");
share->m_result = SubmittedShare::Result::LOW_DIFF;
client->m_score += BAD_SHARE_POINTS;
share->m_score = BAD_SHARE_POINTS;
}
}
@@ -905,14 +945,35 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
{
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
StratumClient* client = share->m_client;
client->m_score += share->m_score;
if (share->m_highEnoughDifficulty) {
const char* s = client->m_customUser;
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << share->m_mainchainHeight << ", sidechain height " << share->m_sidechainHeight << ", diff " << share->m_sidechainDifficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << ", effort " << share->m_effort << '%');
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*>(client->m_addrString) << (*s ? ", user " : "") << s << ", effort " << share->m_effort << '%');
}
else {
static const char* reason_list[] = {
"stale share",
"couldn't check PoW",
"low difficulty",
"invalid PoW",
"worker banned",
};
static_assert(array_size(reason_list) == static_cast<size_t>(SubmittedShare::Result::OK), "Update reason_list to match SubmittedShare::Result enum");
const size_t k = static_cast<size_t>(share->m_result);
const char* reason = (k < array_size(reason_list)) ? reason_list[k] : "unknown";
LOGWARN(0, log::Green() << "INVALID SHARE: mainchain height " << share->m_mainchainHeight << ", sidechain height " << share->m_sidechainHeight << ", diff " << share->m_sidechainDifficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? ", user " : "") << s << ", reason: " << reason);
}
BACKGROUND_JOB_STOP(StratumServer::on_share_found);
}
ON_SCOPE_LEAVE([share]() { share->m_server->m_submittedSharesPool.push_back(share); });
ON_SCOPE_LEAVE([share]()
{
ASAN_POISON_MEMORY_REGION(share, sizeof(SubmittedShare));
share->m_server->m_submittedSharesPool.push_back(share);
});
StratumServer* server = share->m_server;
@@ -936,6 +997,9 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
case SubmittedShare::Result::INVALID_POW:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Invalid PoW\"}}\n";
break;
case SubmittedShare::Result::BANNED:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Banned\"}}\n";
break;
case SubmittedShare::Result::OK:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":null,\"result\":{\"status\":\"OK\"}}\n";
break;
@@ -959,6 +1023,7 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
void StratumServer::on_shutdown()
{
uv_close(reinterpret_cast<uv_handle_t*>(&m_blobsAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_showWorkersAsync), nullptr);
}
StratumServer::StratumClient::StratumClient()
@@ -972,6 +1037,7 @@ StratumServer::StratumClient::StratumClient()
, m_customDiff{}
, m_autoDiff{}
, m_customUser{}
, m_lastJobTarget(0)
, m_score(0)
{
}
@@ -993,6 +1059,8 @@ void StratumServer::StratumClient::reset()
m_autoDiff = {};
m_customUser[0] = '\0';
m_lastJobTarget = 0;
m_score = 0;
}
@@ -1192,18 +1260,21 @@ void StratumServer::api_update_local_stats(uint64_t timestamp)
}
// Rate limit to no more than once in 60 seconds.
if (timestamp < m_apiLastUpdateTime + 60) {
uint64_t t = m_apiLastUpdateTime.load();
if (timestamp < t + 60) {
return;
}
m_apiLastUpdateTime = timestamp;
if (!m_apiLastUpdateTime.compare_exchange_strong(t, timestamp)) {
return;
}
uint64_t hashes_15m, hashes_1h, hashes_24h, total_hashes;
int64_t dt_15m, dt_1h, dt_24h;
uint64_t hashes_since_last_share;
double average_effort;
int shares_found;
uint32_t shares_found, shares_failed;
{
ReadLock lock(m_hashrateDataLock);
@@ -1233,6 +1304,7 @@ void StratumServer::api_update_local_stats(uint64_t timestamp)
}
shares_found = m_totalFoundShares;
shares_failed = m_totalFailedShares;
}
const uint64_t hashrate_15m = (dt_15m > 0) ? (hashes_15m / dt_15m) : 0;
@@ -1241,17 +1313,18 @@ void StratumServer::api_update_local_stats(uint64_t timestamp)
double current_effort = static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double();
int connections = m_numConnections;
int incoming_connections = m_numIncomingConnections;
uint32_t connections = m_numConnections;
uint32_t incoming_connections = m_numIncomingConnections;
m_pool->api()->set(p2pool_api::Category::LOCAL, "stats",
[hashrate_15m, hashrate_1h, hashrate_24h, total_hashes, shares_found, average_effort, current_effort, connections, incoming_connections](log::Stream& s)
m_pool->api()->set(p2pool_api::Category::LOCAL, "stratum",
[hashrate_15m, hashrate_1h, hashrate_24h, total_hashes, shares_found, shares_failed, average_effort, current_effort, connections, incoming_connections](log::Stream& s)
{
s << "{\"hashrate_15m\":" << hashrate_15m
<< ",\"hashrate_1h\":" << hashrate_1h
<< ",\"hashrate_24h\":" << hashrate_24h
<< ",\"total_hashes\":" << total_hashes
<< ",\"shares_found\":" << shares_found
<< ",\"shares_failed\":" << shares_failed
<< ",\"average_effort\":" << average_effort
<< ",\"current_effort\":" << current_effort
<< ",\"connections\":" << connections
+15 -4
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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,6 +42,7 @@ public:
FORCEINLINE ~StratumClient() {}
static Client* allocate() { return new StratumClient(); }
virtual size_t size() const override { return sizeof(StratumClient); }
void reset() override;
bool on_connect() override;
@@ -79,15 +80,17 @@ public:
difficulty_type m_autoDiff;
char m_customUser[32];
uint64_t m_lastJobTarget;
int32_t m_score;
};
bool on_login(StratumClient* client, uint32_t id, const char* login);
bool on_submit(StratumClient* client, uint32_t id, const char* job_id_str, const char* nonce_str, const char* result_str);
uint64_t get_random64();
uint32_t get_random32();
void print_status() override;
void show_workers();
void show_workers_async();
void reset_share_counters();
@@ -120,6 +123,11 @@ private:
static void on_blobs_ready(uv_async_t* handle) { reinterpret_cast<StratumServer*>(handle->data)->on_blobs_ready(); }
void on_blobs_ready();
uv_async_t m_showWorkersAsync;
static void on_show_workers(uv_async_t* handle) { reinterpret_cast<StratumServer*>(handle->data)->show_workers(); }
void show_workers();
std::atomic<uint32_t> m_extraNonce;
uv_mutex_t m_rngLock;
@@ -146,12 +154,14 @@ private:
uint64_t m_timestamp;
uint64_t m_hashes;
bool m_highEnoughDifficulty;
int32_t m_score;
enum class Result {
STALE,
COULDNT_CHECK_POW,
LOW_DIFF,
INVALID_POW,
BANNED,
OK
} m_result;
};
@@ -176,8 +186,9 @@ private:
double m_cumulativeFoundSharesDiff;
uint32_t m_totalFoundShares;
uint32_t m_totalFailedShares;
uint64_t m_apiLastUpdateTime;
std::atomic<uint64_t> m_apiLastUpdateTime;
void update_hashrate_data(uint64_t hashes, uint64_t timestamp);
void api_update_local_stats(uint64_t timestamp);
+25 -6
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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 @@ public:
uv_loop_t* get_loop() { return &m_loop; }
virtual int listen_port() const { return m_listenPort; }
virtual int external_listen_port() const { return m_listenPort; }
bool connect_to_peer(bool is_v6, const raw_ip& ip, int port);
virtual void on_connect_failed(bool /*is_v6*/, const raw_ip& /*ip*/, int /*port*/) {}
@@ -55,6 +55,8 @@ public:
Client();
virtual ~Client() {}
virtual size_t size() const = 0;
virtual void reset();
virtual bool on_connect() = 0;
virtual bool on_read(char* data, uint32_t size) = 0;
@@ -102,13 +104,17 @@ public:
struct WriteBuf
{
Client* m_client = nullptr;
uv_write_t m_write = {};
std::vector<uint8_t> m_data;
Client* m_client = nullptr;
void* m_data = nullptr;
size_t m_dataCapacity = 0;
};
std::vector<WriteBuf*> m_writeBuffers;
WriteBuf* get_write_buffer();
void return_write_buffer(WriteBuf* buf);
struct SendCallbackBase
{
virtual ~SendCallbackBase() {}
@@ -152,7 +158,11 @@ private:
uv_thread_t m_loopThread;
protected:
void start_listening(const std::string& listen_addresses);
void start_listening(const std::string& listen_addresses, bool upnp);
#ifdef WITH_UPNP
int m_portMapping;
#endif
std::string m_socks5Proxy;
bool m_socks5ProxyV6;
@@ -164,8 +174,17 @@ protected:
uv_loop_t m_loop;
uv_mutex_t m_clientsListLock;
#ifdef P2POOL_DEBUGGING
static void check_event_loop_thread(const char *func);
#else
static FORCEINLINE void check_event_loop_thread(const char*) {}
#endif
std::vector<Client*> m_preallocatedClients;
Client* get_client();
void return_client(Client* c);
Client* m_connectedClientsList;
std::atomic<uint32_t> m_numConnections;
std::atomic<uint32_t> m_numIncomingConnections;
+159 -95
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -25,6 +25,9 @@ template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback allocate_new_client)
: m_allocateNewClient(allocate_new_client)
, m_loopThread{}
#ifdef WITH_UPNP
, m_portMapping(0)
#endif
, m_socks5ProxyV6(false)
, m_socks5ProxyIP{}
, m_socks5ProxyPort(-1)
@@ -41,7 +44,7 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
int err = uv_loop_init(&m_loop);
if (err) {
LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
// Init loop user data before running it
@@ -50,18 +53,17 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
err = uv_async_init(&m_loop, &m_dropConnectionsAsync, on_drop_connections);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
m_dropConnectionsAsync.data = this;
err = uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
m_shutdownAsync.data = this;
uv_mutex_init_checked(&m_clientsListLock);
uv_mutex_init_checked(&m_bansLock);
m_connectedClientsList = m_allocateNewClient();
@@ -129,11 +131,11 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::parse_address_list(const std::str
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string& listen_addresses)
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string& listen_addresses, bool upnp)
{
if (listen_addresses.empty()) {
LOGERR(1, "listen address not set");
panic();
PANIC_STOP();
}
parse_address_list(listen_addresses,
@@ -144,7 +146,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
}
else if (m_listenPort != port) {
LOGERR(1, "all sockets must be listening on the same port number, fix the command line");
panic();
PANIC_STOP();
}
uv_tcp_t* socket = new uv_tcp_t();
@@ -159,14 +161,14 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
int err = uv_tcp_init(&m_loop, socket);
if (err) {
LOGERR(1, "failed to create tcp server handle, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
socket->data = this;
err = uv_tcp_nodelay(socket, 1);
if (err) {
LOGERR(1, "failed to set tcp_nodelay on tcp server handle, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
if (is_v6) {
@@ -174,13 +176,13 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
err = uv_ip6_addr(ip.c_str(), port, &addr6);
if (err) {
LOGERR(1, "failed to parse IPv6 address " << ip << ", error " << uv_err_name(err));
panic();
PANIC_STOP();
}
err = uv_tcp_bind(socket, reinterpret_cast<sockaddr*>(&addr6), UV_TCP_IPV6ONLY);
if (err) {
LOGERR(1, "failed to bind tcp server IPv6 socket " << address << ", error " << uv_err_name(err));
panic();
PANIC_STOP();
}
}
else {
@@ -188,29 +190,37 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
err = uv_ip4_addr(ip.c_str(), port, &addr);
if (err) {
LOGERR(1, "failed to parse IPv4 address " << ip << ", error " << uv_err_name(err));
panic();
PANIC_STOP();
}
err = uv_tcp_bind(socket, reinterpret_cast<sockaddr*>(&addr), 0);
if (err) {
LOGERR(1, "failed to bind tcp server IPv4 socket " << address << ", error " << uv_err_name(err));
panic();
PANIC_STOP();
}
}
err = uv_listen(reinterpret_cast<uv_stream_t*>(socket), DEFAULT_BACKLOG, on_new_connection);
if (err) {
LOGERR(1, "failed to listen on tcp server socket " << address << ", error " << uv_err_name(err));
panic();
PANIC_STOP();
}
LOGINFO(1, "listening on " << log::Gray() << address);
});
#ifdef WITH_UPNP
if (upnp) {
m_portMapping = add_portmapping(external_listen_port(), m_listenPort);
}
#else
(void)upnp;
#endif
const int err = uv_thread_create(&m_loopThread, loop, this);
if (err) {
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
}
@@ -226,23 +236,13 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(bool is_v6, const
return false;
}
Client* client;
if (!m_preallocatedClients.empty()) {
client = m_preallocatedClients.back();
m_preallocatedClients.pop_back();
client->reset();
}
else {
client = m_allocateNewClient();
}
Client* client = get_client();
client->m_owner = this;
client->m_port = port;
client->m_isV6 = is_v6;
if (!str_to_ip(is_v6, ip, client->m_addr)) {
m_preallocatedClients.push_back(client);
return_client(client);
return false;
}
@@ -264,17 +264,7 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(bool is_v6, const
return false;
}
Client* client;
if (!m_preallocatedClients.empty()) {
client = m_preallocatedClients.back();
m_preallocatedClients.pop_back();
client->reset();
}
else {
client = m_allocateNewClient();
}
Client* client = get_client();
client->m_owner = this;
client->m_addr = ip;
client->m_port = port;
@@ -312,20 +302,20 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(Client* client)
{
if (is_banned(client->m_addr)) {
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(client->m_addrString) << log::NoColor() << " is banned, not connecting to it");
m_preallocatedClients.push_back(client);
return_client(client);
return false;
}
if (!m_pendingConnections.insert(client->m_addr).second) {
LOGINFO(6, "there is already a pending connection to this IP, not connecting to " << log::Gray() << static_cast<char*>(client->m_addrString));
m_preallocatedClients.push_back(client);
return_client(client);
return false;
}
int err = uv_tcp_init(&m_loop, &client->m_socket);
if (err) {
LOGERR(1, "failed to create tcp client handle, error " << uv_err_name(err));
m_preallocatedClients.push_back(client);
return_client(client);
return false;
}
client->m_socket.data = client;
@@ -376,7 +366,7 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(Client* client)
err = uv_tcp_connect(connect_request, &client->m_socket, reinterpret_cast<sockaddr*>(&addr), on_connect);
if (err) {
LOGERR(1, "failed to initiate tcp connection to " << static_cast<const char*>(client->m_addrString) << ", error " << uv_err_name(err));
LOGWARN(5, "failed to initiate tcp connection to " << static_cast<const char*>(client->m_addrString) << ", error " << uv_err_name(err));
m_pendingConnections.erase(client->m_addr);
uv_close(reinterpret_cast<uv_handle_t*>(&client->m_socket), on_connection_error);
return false;
@@ -388,12 +378,20 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(Client* client)
return true;
}
#ifdef P2POOL_DEBUGGING
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::check_event_loop_thread(const char* func)
{
if (!server_event_loop_thread) {
LOGERR(1, func << " called from another thread, this is not thread safe");
}
}
#endif
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::close_sockets(bool listen_sockets)
{
if (!server_event_loop_thread) {
LOGERR(1, "closing sockets from another thread, this is not thread safe");
}
check_event_loop_thread(__func__);
if (listen_sockets) {
for (uv_tcp_t* s : m_listenSockets6) {
@@ -411,15 +409,12 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::close_sockets(bool listen_sockets
}
size_t numClosed = 0;
{
MutexLock lock(m_clientsListLock);
for (Client* c = m_connectedClientsList->m_next; c != m_connectedClientsList; c = c->m_next) {
uv_handle_t* h = reinterpret_cast<uv_handle_t*>(&c->m_socket);
if (!uv_is_closing(h)) {
uv_close(h, on_connection_close);
++numClosed;
}
for (Client* c = m_connectedClientsList->m_next; c != m_connectedClientsList; c = c->m_next) {
uv_handle_t* h = reinterpret_cast<uv_handle_t*>(&c->m_socket);
if (!uv_is_closing(h)) {
uv_close(h, on_connection_close);
++numClosed;
}
}
@@ -436,9 +431,15 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::shutdown_tcp()
}
uv_async_send(&m_shutdownAsync);
#ifdef WITH_UPNP
if (m_portMapping) {
remove_portmapping(m_portMapping);
}
#endif
uv_thread_join(&m_loopThread);
uv_mutex_destroy(&m_clientsListLock);
uv_mutex_destroy(&m_bansLock);
LOGINFO(1, "stopped");
@@ -484,24 +485,14 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::print_bans()
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, SendCallbackBase&& callback)
{
if (!server_event_loop_thread) {
LOGERR(1, "sending data from another thread, this is not thread safe");
}
check_event_loop_thread(__func__);
if (client->m_isClosing) {
LOGWARN(5, "client " << static_cast<const char*>(client->m_addrString) << " is being disconnected, can't send any more data");
return true;
}
WriteBuf* buf;
if (!m_writeBuffers.empty()) {
buf = m_writeBuffers.back();
m_writeBuffers.pop_back();
}
else {
buf = new WriteBuf();
}
WriteBuf* buf = get_write_buffer();
// callback_buf is used in only 1 thread, so it's safe
static uint8_t callback_buf[WRITE_BUF_SIZE];
@@ -509,28 +500,37 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
if (bytes_written > WRITE_BUF_SIZE) {
LOGERR(0, "send callback wrote " << bytes_written << " bytes, expected no more than " << WRITE_BUF_SIZE << " bytes");
panic();
PANIC_STOP();
}
if (bytes_written == 0) {
LOGWARN(1, "send callback wrote 0 bytes, nothing to do");
m_writeBuffers.push_back(buf);
return_write_buffer(buf);
return true;
}
buf->m_client = client;
buf->m_write.data = buf;
buf->m_data.reserve(round_up(bytes_written, 64));
buf->m_data.assign(callback_buf, callback_buf + bytes_written);
buf->m_client = client;
if (buf->m_dataCapacity < bytes_written) {
buf->m_dataCapacity = round_up(bytes_written, 64);
buf->m_data = realloc_hook(buf->m_data, buf->m_dataCapacity);
if (!buf->m_data) {
LOGERR(0, "failed to allocate " << buf->m_dataCapacity << " bytes to send data");
PANIC_STOP();
}
}
memcpy(buf->m_data, callback_buf, bytes_written);
uv_buf_t bufs[1];
bufs[0].base = reinterpret_cast<char*>(buf->m_data.data());
bufs[0].base = reinterpret_cast<char*>(buf->m_data);
bufs[0].len = static_cast<int>(bytes_written);
const int err = uv_write(&buf->m_write, reinterpret_cast<uv_stream_t*>(&client->m_socket), bufs, 1, Client::on_write);
if (err) {
LOGWARN(1, "failed to start writing data to client connection " << static_cast<const char*>(client->m_addrString) << ", error " << uv_err_name(err));
m_writeBuffers.push_back(buf);
return_write_buffer(buf);
return false;
}
@@ -547,8 +547,12 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::loop(void* data)
server->m_writeBuffers.resize(DEFAULT_BACKLOG);
server->m_preallocatedClients.reserve(DEFAULT_BACKLOG);
for (size_t i = 0; i < DEFAULT_BACKLOG; ++i) {
server->m_writeBuffers[i] = new WriteBuf();
server->m_preallocatedClients.emplace_back(server->m_allocateNewClient());
WriteBuf* wb = new WriteBuf();
Client* c = server->m_allocateNewClient();
ASAN_POISON_MEMORY_REGION(wb, sizeof(WriteBuf));
ASAN_POISON_MEMORY_REGION(c, c->size());
server->m_writeBuffers[i] = wb;
server->m_preallocatedClients.emplace_back(c);
}
int err = uv_run(&server->m_loop, UV_RUN_DEFAULT);
@@ -562,11 +566,18 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::loop(void* data)
}
for (WriteBuf* buf : server->m_writeBuffers) {
ASAN_UNPOISON_MEMORY_REGION(buf, sizeof(WriteBuf));
if (buf->m_data) {
ASAN_UNPOISON_MEMORY_REGION(buf->m_data, buf->m_dataCapacity);
free_hook(buf->m_data);
}
delete buf;
}
server->m_writeBuffers.clear();
for (Client* c : server->m_preallocatedClients) {
ASAN_UNPOISON_MEMORY_REGION(c, sizeof(Client));
ASAN_UNPOISON_MEMORY_REGION(c, c->size());
delete c;
}
server->m_preallocatedClients.clear();
@@ -594,14 +605,14 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_connection(uv_stream_t* se
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connection_close(uv_handle_t* handle)
{
check_event_loop_thread(__func__);
Client* client = static_cast<Client*>(handle->data);
TCPServer* owner = client->m_owner;
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(client->m_addrString) << log::NoColor() << " disconnected");
if (owner) {
MutexLock lock(owner->m_clientsListLock);
Client* prev_in_list = client->m_prev;
Client* next_in_list = client->m_next;
@@ -612,7 +623,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connection_close(uv_handle_t*
prev_in_list->m_next = next_in_list;
next_in_list->m_prev = prev_in_list;
owner->m_preallocatedClients.push_back(client);
owner->return_client(client);
--owner->m_numConnections;
if (is_incoming) {
@@ -628,7 +639,7 @@ template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connection_error(uv_handle_t* handle)
{
Client* client = reinterpret_cast<Client*>(handle->data);
client->m_owner->m_preallocatedClients.push_back(client);
client->m_owner->return_client(client);
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
@@ -665,21 +676,12 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_client(uv_stream_t* server
return;
}
Client* client;
if (!m_preallocatedClients.empty()) {
client = m_preallocatedClients.back();
m_preallocatedClients.pop_back();
client->reset();
}
else {
client = m_allocateNewClient();
}
Client* client = get_client();
int err = uv_tcp_init(&m_loop, &client->m_socket);
if (err) {
LOGERR(1, "failed to create tcp client handle, error " << uv_err_name(err));
m_preallocatedClients.push_back(client);
return_client(client);
return;
}
client->m_socket.data = client;
@@ -705,7 +707,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_client(uv_stream_t* server
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_client(uv_stream_t* server, Client* client)
{
MutexLock lock(m_clientsListLock);
check_event_loop_thread(__func__);
client->m_prev = m_connectedClientsList;
client->m_next = m_connectedClientsList->m_next;
@@ -810,7 +812,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_shutdown(uv_async_t* async)
delete GetLoopUserData(&s->m_loop, false);
s->m_numHandles = 0;
uv_walk(&s->m_loop, [](uv_handle_t*, void* n) { (*reinterpret_cast<size_t*>(n))++; }, &s->m_numHandles);
uv_walk(&s->m_loop, [](uv_handle_t*, void* n) { (*reinterpret_cast<uint32_t*>(n))++; }, &s->m_numHandles);
uv_prepare_init(&s->m_loop, &s->m_shutdownPrepare);
s->m_shutdownPrepare.data = s;
@@ -843,7 +845,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_shutdown(uv_async_t* async)
TCPServer* s = reinterpret_cast<TCPServer*>(h->data);
s->m_numHandles = 0;
uv_walk(&s->m_loop, [](uv_handle_t*, void* n) { (*reinterpret_cast<size_t*>(n))++; }, &s->m_numHandles);
uv_walk(&s->m_loop, [](uv_handle_t*, void* n) { (*reinterpret_cast<uint32_t*>(n))++; }, &s->m_numHandles);
if (s->m_numHandles > 2) {
// Don't count m_shutdownTimer and m_shutdownPrepare
@@ -858,6 +860,64 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_shutdown(uv_async_t* async)
});
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
typename TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::WriteBuf* TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::get_write_buffer()
{
WriteBuf* buf;
if (!m_writeBuffers.empty()) {
buf = m_writeBuffers.back();
m_writeBuffers.pop_back();
ASAN_UNPOISON_MEMORY_REGION(buf, sizeof(WriteBuf));
if (buf->m_data) {
ASAN_UNPOISON_MEMORY_REGION(buf->m_data, buf->m_dataCapacity);
}
}
else {
buf = new WriteBuf();
}
return buf;
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::return_write_buffer(WriteBuf* buf)
{
if (buf->m_data) {
ASAN_POISON_MEMORY_REGION(buf->m_data, buf->m_dataCapacity);
}
ASAN_POISON_MEMORY_REGION(buf, sizeof(WriteBuf));
m_writeBuffers.push_back(buf);
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
typename TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client* TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::get_client()
{
Client* c;
if (!m_preallocatedClients.empty()) {
c = m_preallocatedClients.back();
m_preallocatedClients.pop_back();
ASAN_UNPOISON_MEMORY_REGION(c, sizeof(Client));
ASAN_UNPOISON_MEMORY_REGION(c, c->size());
c->reset();
}
else {
c = m_allocateNewClient();
}
return c;
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::return_client(Client* c)
{
ASAN_POISON_MEMORY_REGION(c, c->size());
m_preallocatedClients.push_back(c);
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::Client()
: m_owner(nullptr)
@@ -1091,7 +1151,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::on_write(uv_write_t* req,
TCPServer* server = client->m_owner;
if (server) {
server->m_writeBuffers.push_back(buf);
server->return_write_buffer(buf);
}
if (status != 0) {
@@ -1122,6 +1182,10 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::close()
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::ban(uint64_t seconds)
{
if (m_addr.is_localhost()) {
return;
}
if (m_owner) {
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " banned for " << seconds << " seconds");
m_owner->ban(m_addr, seconds);
+169 -13
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -25,13 +25,15 @@
#include <sched.h>
#endif
#ifdef WITH_UPNP
#include "miniupnpc.h"
#include "upnpcommands.h"
#endif
static constexpr char log_category_prefix[] = "Util ";
namespace p2pool {
#define STR2(X) STR(X)
#define STR(X) #X
const char* VERSION = "v" STR2(P2POOL_VERSION_MAJOR) "." STR2(P2POOL_VERSION_MINOR) " (built"
#if defined(__clang__)
" with clang/" __clang_version__
@@ -42,13 +44,12 @@ const char* VERSION = "v" STR2(P2POOL_VERSION_MAJOR) "." STR2(P2POOL_VERSION_MIN
#endif
" on " __DATE__ ")";
#undef STR2
#undef STR
MinerCallbackHandler::~MinerCallbackHandler() {}
void panic()
void panic_stop(const char* message)
{
fprintf(stderr, "P2Pool can't continue execution: panic at %s\n", message);
p2pool::log::stop();
do {
#ifdef _WIN32
@@ -254,7 +255,7 @@ void uv_cond_init_checked(uv_cond_t* cond)
const int result = uv_cond_init(cond);
if (result) {
LOGERR(1, "failed to create conditional variable, error " << uv_err_name(result));
panic();
PANIC_STOP();
}
}
@@ -263,7 +264,7 @@ void uv_mutex_init_checked(uv_mutex_t* mutex)
const int result = uv_mutex_init(mutex);
if (result) {
LOGERR(1, "failed to create mutex, error " << uv_err_name(result));
panic();
PANIC_STOP();
}
}
@@ -272,7 +273,7 @@ void uv_rwlock_init_checked(uv_rwlock_t* lock)
const int result = uv_rwlock_init(lock);
if (result) {
LOGERR(1, "failed to create rwlock, error " << uv_err_name(result));
panic();
PANIC_STOP();
}
}
@@ -281,7 +282,7 @@ void uv_async_init_checked(uv_loop_t* loop, uv_async_t* async, uv_async_cb async
const int err = uv_async_init(loop, async, async_cb);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
PANIC_STOP();
}
}
@@ -335,6 +336,7 @@ struct BackgroundJobTracker::Impl
do {
{
MutexLock lock(m_lock);
// cppcheck-suppress knownConditionTrueFalse
if (m_jobs.empty()) {
return;
}
@@ -424,7 +426,12 @@ bool resolve_host(std::string& host, bool& is_v6)
hints.ai_flags = AI_ADDRCONFIG;
addrinfo* r = nullptr;
const int err = getaddrinfo(host.c_str(), nullptr, &hints, &r);
int err = getaddrinfo(host.c_str(), nullptr, &hints, &r);
if (err) {
LOGWARN(4, "getaddrinfo failed for " << host << ": " << gai_strerror(err) << ", retrying with IPv4 only");
hints.ai_family = AF_INET;
err = getaddrinfo(host.c_str(), nullptr, &hints, &r);
}
if ((err == 0) && r) {
const char* addr_str = nullptr;
char addr_str_buf[64];
@@ -558,4 +565,153 @@ UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create)
return data;
}
#ifdef WITH_UPNP
static struct UPnP_Discover
{
uv_mutex_t lock;
int error;
UPNPDev* devlist;
} upnp_discover;
void init_upnp()
{
uv_mutex_init_checked(&upnp_discover.lock);
uv_work_t* req = new uv_work_t{};
const int err = uv_queue_work(uv_default_loop_checked(), req,
[](uv_work_t* /*req*/)
{
BACKGROUND_JOB_START(init_upnp);
LOGINFO(1, "UPnP: Started scanning for UPnP IGD devices");
{
MutexLock lock(upnp_discover.lock);
upnp_discover.devlist = upnpDiscover(1000, nullptr, nullptr, UPNP_LOCAL_PORT_ANY, 0, 2, &upnp_discover.error);
}
LOGINFO(1, "UPnP: Finished scanning for UPnP IGD devices");
},
[](uv_work_t* req, int /*status*/)
{
delete req;
BACKGROUND_JOB_STOP(init_upnp);
}
);
if (err) {
LOGERR(0, "init_upnp: uv_queue_work failed, error " << uv_err_name(err));
delete req;
}
}
void destroy_upnp()
{
{
MutexLock lock(upnp_discover.lock);
freeUPNPDevlist(upnp_discover.devlist);
upnp_discover.devlist = nullptr;
}
uv_mutex_destroy(&upnp_discover.lock);
}
int add_portmapping(int external_port, int internal_port)
{
LOGINFO(1, "UPnP: trying to map WAN:" << external_port << " to LAN:" << internal_port);
MutexLock lock(upnp_discover.lock);
if (!upnp_discover.devlist) {
LOGWARN(1, "upnpDiscover: no UPnP IGD devices found, error " << upnp_discover.error);
return 0;
}
UPNPUrls urls;
IGDdatas data;
char local_addr[64] = {};
int result = UPNP_GetValidIGD(upnp_discover.devlist, &urls, &data, local_addr, sizeof(local_addr));
if (result != 1) {
LOGWARN(1, "UPNP_GetValidIGD returned " << result << ", no valid UPnP IGD devices found");
return 0;
}
LOGINFO(1, "UPnP: LAN IP address " << log::Gray() << static_cast<const char*>(local_addr));
char ext_addr[64] = {};
result = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, ext_addr);
if ((result != UPNPCOMMAND_SUCCESS) || !ext_addr[0]) {
LOGWARN(1, "UPNP_GetExternalIPAddress: failed to query external IP address, error " << result);
}
else {
LOGINFO(1, "UPnP: WAN IP address " << log::Gray() << static_cast<const char*>(ext_addr));
}
const std::string eport = std::to_string(external_port);
const std::string iport = std::to_string(internal_port);
result = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, eport.c_str(), iport.c_str(), local_addr, "P2Pool", "TCP", nullptr, nullptr);
// ConflictInMappingEntry: try to delete the old record and then add the new one again
if (result == 718) {
LOGWARN(1, "UPNP_AddPortMapping failed: ConflictInMappingEntry");
result = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, eport.c_str(), "TCP", nullptr);
if (result) {
LOGWARN(1, "UPNP_DeletePortMapping returned error " << result);
return 0;
}
else {
LOGINFO(1, "UPnP: Deleted mapping for external port " << external_port);
result = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, eport.c_str(), iport.c_str(), local_addr, "P2Pool", "TCP", nullptr, nullptr);
}
}
if (result) {
LOGWARN(1, "UPNP_AddPortMapping returned error " << result);
return 0;
}
LOGINFO(1, "UPnP: Mapped " << log::Gray() << static_cast<const char*>(ext_addr) << ':' << external_port << log::NoColor() << " to " << log::Gray() << static_cast<const char*>(local_addr) << ':' << internal_port);
return external_port;
}
void remove_portmapping(int external_port)
{
LOGINFO(1, "UPnP: trying to delete mapping for external port " << external_port);
MutexLock lock(upnp_discover.lock);
if (!upnp_discover.devlist) {
LOGWARN(1, "upnpDiscover: no UPnP IGD devices found, error " << upnp_discover.error);
return;
}
UPNPUrls urls;
IGDdatas data;
char local_addr[64] = {};
int result = UPNP_GetValidIGD(upnp_discover.devlist, &urls, &data, local_addr, sizeof(local_addr));
if (result != 1) {
LOGWARN(1, "UPNP_GetValidIGD returned " << result << ", no valid UPnP IGD devices found");
return;
}
const std::string eport = std::to_string(external_port);
result = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, eport.c_str(), "TCP", nullptr);
if (result) {
LOGWARN(1, "UPNP_DeletePortMapping returned error " << result);
}
else {
LOGINFO(1, "UPnP: Deleted mapping for external port " << external_port);
}
}
#endif
NOINLINE PerfTimer::~PerfTimer()
{
using namespace std::chrono;
const duration<double, std::milli> dt = high_resolution_clock::now() - m_start;
LOGINFO(m_level, m_name << " took " << dt.count() << " ms");
}
} // namespace p2pool
+46 -4
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -34,8 +34,8 @@
namespace p2pool {
#define P2POOL_VERSION_MAJOR 2
#define P2POOL_VERSION_MINOR 7
#define P2POOL_VERSION_MAJOR 3
#define P2POOL_VERSION_MINOR 2
extern const char* VERSION;
@@ -149,7 +149,12 @@ FORCEINLINE T read_unaligned(const T* p)
template<typename T, size_t N> FORCEINLINE constexpr size_t array_size(T(&)[N]) { return N; }
template<typename T, typename U, size_t N> FORCEINLINE constexpr size_t array_size(T(U::*)[N]) { return N; }
[[noreturn]] void panic();
[[noreturn]] void panic_stop(const char* message);
#define STR(X) #X
#define STR2(X) STR(X)
#define PANIC_STOP(...) panic_stop(__FILE__ ":" STR2(__LINE__))
void make_thread_background();
@@ -208,12 +213,26 @@ struct RandomDeviceSeed
static RandomDeviceSeed instance;
};
FORCEINLINE uint64_t xorshift64star(uint64_t x)
{
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
return x * 0x2545F4914F6CDD1DULL;
}
FORCEINLINE uint64_t seconds_since_epoch()
{
using namespace std::chrono;
return duration_cast<seconds>(steady_clock::now().time_since_epoch()).count();
}
FORCEINLINE uint64_t microseconds_since_epoch()
{
using namespace std::chrono;
return duration_cast<microseconds>(steady_clock::now().time_since_epoch()).count();
}
uint64_t bsr_reference(uint64_t x);
#ifdef HAVE_BUILTIN_CLZLL
@@ -233,6 +252,29 @@ FORCEINLINE uint64_t bsr(uint64_t x)
bool str_to_ip(bool is_v6, const char* ip, raw_ip& result);
bool is_localhost(const std::string& host);
#ifdef WITH_UPNP
void init_upnp();
void destroy_upnp();
int add_portmapping(int external_port, int internal_port);
void remove_portmapping(int external_port);
#endif
struct PerfTimer
{
FORCEINLINE PerfTimer(int level, const char* name) : m_level(level), m_name(name), m_start(std::chrono::high_resolution_clock::now()) {}
~PerfTimer();
int m_level;
const char* m_name;
std::chrono::time_point<std::chrono::high_resolution_clock> m_start;
};
#ifdef P2POOL_LOG_DISABLE
#define PERFLOG(level, name)
#else
#define PERFLOG(level, name) PerfTimer CONCAT(perf_timer_, __LINE__)(level, name)
#endif
} // namespace p2pool
void memory_tracking_start();
+63 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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.h>
#include <thread>
static_assert(sizeof(in6_addr) == 16, "struct in6_addr has invalid size");
static_assert(sizeof(in_addr) == 4, "struct in_addr has invalid size");
@@ -175,4 +176,65 @@ bool CallOnLoop(uv_loop_t* loop, T&& callback)
return false;
}
template<typename T>
void parallel_run(uv_loop_t* loop, T&& callback, bool wait = false)
{
const uint32_t THREAD_COUNT = std::thread::hardware_concurrency();
// Don't start other threads on single CPU systems
if (THREAD_COUNT <= 1) {
callback();
return;
}
// "THREAD_COUNT - 1" because current thread is already running
// No more than 8 threads because our UV worker thread pool has 8 threads
const uint32_t THREADS_TO_START = std::min<uint32_t>(THREAD_COUNT - 1, 8);
struct Callback
{
explicit FORCEINLINE Callback(T&& f) : m_func(std::move(f)) {}
Callback& operator=(Callback&&) = delete;
T m_func;
};
std::shared_ptr<Callback> cb = std::make_shared<Callback>(std::move(callback));
struct Work
{
uv_work_t req;
std::shared_ptr<Callback> cb;
};
for (size_t i = 0; i < THREADS_TO_START; ++i) {
Work* w = new Work{ {}, cb };
w->req.data = w;
const int err = uv_queue_work(loop, &w->req,
[](uv_work_t* req)
{
std::shared_ptr<Callback>& cb = reinterpret_cast<Work*>(req->data)->cb;
cb->m_func();
cb.reset();
},
[](uv_work_t* req, int)
{
delete reinterpret_cast<Work*>(req->data);
});
if (err) {
delete w;
}
}
if (wait) {
cb->m_func();
while (cb.use_count() > 1) {
std::this_thread::yield();
}
}
}
} // namespace p2pool
+1 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
+37 -36
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -58,10 +58,32 @@ ZMQReader::ZMQReader(const std::string& address, uint32_t zmq_port, const std::s
throw zmq::error_t(EFSM);
}
m_subscriber.set(zmq::sockopt::connect_timeout, 1000);
if (!m_proxy.empty()) {
m_subscriber.set(zmq::sockopt::socks_proxy, zmq::const_buffer(m_proxy.c_str(), m_proxy.length()));
}
std::string addr = "tcp://" + m_address + ':' + std::to_string(m_zmqPort);
if (!connect(addr)) {
throw zmq::error_t(EFSM);
}
m_subscriber.set(zmq::sockopt::socks_proxy, zmq::const_buffer());
addr = "tcp://127.0.0.1:" + std::to_string(m_publisherPort);
if (!connect(addr)) {
throw zmq::error_t(EFSM);
}
m_subscriber.set(zmq::sockopt::subscribe, "json-full-chain_main");
m_subscriber.set(zmq::sockopt::subscribe, "json-full-miner_data");
m_subscriber.set(zmq::sockopt::subscribe, "json-minimal-txpool_add");
const int err = uv_thread_create(&m_worker, run_wrapper, this);
if (err) {
LOGERR(1, "failed to start ZMQ thread, error " << uv_err_name(err));
throw zmq::error_t(EFSM);
throw zmq::error_t(EMTHREAD);
}
}
@@ -69,7 +91,7 @@ ZMQReader::~ZMQReader()
{
LOGINFO(1, "stopping");
m_finished.exchange(1);
m_finished.exchange(true);
try {
const char msg[] = "json-minimal-txpool_add:[]";
@@ -89,28 +111,12 @@ void ZMQReader::run_wrapper(void* arg)
void ZMQReader::run()
{
m_threadRunning = true;
ON_SCOPE_LEAVE([this]() { m_threadRunning = false; });
zmq_msg_t message = {};
try {
if (!m_proxy.empty()) {
m_subscriber.set(zmq::sockopt::socks_proxy, zmq::const_buffer(m_proxy.c_str(), m_proxy.length()));
}
std::string addr = "tcp://" + m_address + ':' + std::to_string(m_zmqPort);
if (!connect(addr)) {
return;
}
m_subscriber.set(zmq::sockopt::socks_proxy, zmq::const_buffer());
addr = "tcp://127.0.0.1:" + std::to_string(m_publisherPort);
if (!connect(addr)) {
return;
}
m_subscriber.set(zmq::sockopt::subscribe, "json-full-chain_main");
m_subscriber.set(zmq::sockopt::subscribe, "json-full-miner_data");
m_subscriber.set(zmq::sockopt::subscribe, "json-minimal-txpool_add");
zmq_msg_t message;
int rc = zmq_msg_init(&message);
if (rc != 0) {
throw zmq::error_t(errno);
@@ -130,12 +136,12 @@ void ZMQReader::run()
parse(reinterpret_cast<char*>(zmq_msg_data(&message)), zmq_msg_size(&message));
} while (true);
zmq_msg_close(&message);
}
catch (const std::exception& e) {
LOGERR(1, "exception " << e.what());
}
zmq_msg_close(&message);
}
bool ZMQReader::connect(const std::string& address)
@@ -163,21 +169,16 @@ bool ZMQReader::connect(const std::string& address)
s << "inproc://p2pool-connect-mon-" << id << '\0';
++id;
using namespace std::chrono;
const auto start_time = steady_clock::now();
monitor.init(m_subscriber, buf);
m_subscriber.connect(address);
using namespace std::chrono;
steady_clock::time_point start_time = steady_clock::now();
while (!monitor.connected && monitor.check_event(-1)) {
const steady_clock::time_point cur_time = steady_clock::now();
const int64_t elapsed_time = duration_cast<milliseconds>(cur_time - start_time).count();
if (elapsed_time >= 3000) {
if (duration_cast<milliseconds>(steady_clock::now() - start_time).count() >= 1000) {
LOGERR(1, "failed to connect to " << address);
if (m_finished.load()) {
return false;
}
start_time = cur_time;
return false;
}
}
+5 -2
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2023 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
@@ -27,6 +27,8 @@ public:
ZMQReader(const std::string& address, uint32_t zmq_port, const std::string& proxy, MinerCallbackHandler* handler);
~ZMQReader();
bool is_running() const { return m_threadRunning.load(); }
private:
static void run_wrapper(void* arg);
void run();
@@ -44,7 +46,8 @@ private:
zmq::socket_t m_publisher{ m_context, ZMQ_PUB };
zmq::socket_t m_subscriber{ m_context, ZMQ_SUB };
uint16_t m_publisherPort = 37891;
std::atomic<int> m_finished{ 0 };
std::atomic<bool> m_finished{ false };
std::atomic<bool> m_threadRunning{ false };
TxMempoolData m_tx;
MinerData m_minerData;
+6 -3
View File
@@ -5,7 +5,7 @@ option(STATIC_LIBS "Use locally built libuv and libzmq static libs" OFF)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.6.0")
if (${CMAKE_VERSION} VERSION_GREATER "3.5.2")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool_tests)
endif()
@@ -16,6 +16,9 @@ add_subdirectory(../external/src/RandomX RandomX)
set(LIBS ${LIBS} randomx)
add_definitions(-DWITH_RANDOMX)
add_definitions(-DP2POOL_UNIT_TESTS)
add_definitions(-DP2POOL_SIDECHAIN_EXTRA_1=1)
include(cmake/flags.cmake)
set(HEADERS
@@ -94,7 +97,7 @@ if (WIN32)
endif()
add_definitions(-DCURL_STATICLIB)
elseif (NOT APPLE)
set(LIBS ${LIBS} pthread gss dl)
set(LIBS ${LIBS} pthread)
endif()
if (STATIC_LIBS)
@@ -170,6 +173,6 @@ endif()
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} debug ${CURL_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} optimized ${CURL_LIBRARY} ${LIBS})
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/crypto_tests.txt" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/mainnet_test2_block.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/block.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/sidechain_dump.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/sidechain_dump_mini.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
+2 -2
View File
@@ -8,7 +8,7 @@ set(CMAKE_C_STANDARD_REQUIRED ON)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(GENERAL_FLAGS "-pthread")
set(WARNING_FLAGS "-Wall -Wextra")
set(OPTIMIZATION_FLAGS "-Ofast -s")
set(OPTIMIZATION_FLAGS "-Ofast -s -flto -fuse-linker-plugin")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
@@ -43,7 +43,7 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
set(GENERAL_FLAGS "-pthread")
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal")
set(OPTIMIZATION_FLAGS "-Ofast -funroll-loops -fmerge-all-constants")
set(OPTIMIZATION_FLAGS "-Ofast -funroll-loops -fmerge-all-constants -flto")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
Binary file not shown.
+18 -3
View File
@@ -32,6 +32,7 @@ TEST(block_template, update)
SideChain sidechain(nullptr, NetworkType::Mainnet);
BlockTemplate tpl(&sidechain, nullptr);
tpl.rng().seed(123);
auto H = [](const char* s)
{
@@ -59,7 +60,7 @@ TEST(block_template, update)
tpl.update(data, mempool, &wallet);
const PoolBlock* b = tpl.pool_block_template();
ASSERT_EQ(b->m_sidechainId, H("b708e3e456d97c43a7fcbd7b4e7aa29bdf45cd909bba07f915cb5f1d805433e6"));
ASSERT_EQ(b->m_sidechainId, H("0355a4eeb586ab4d30f3ba89f0ebf7ac1c34a55aebf1bee50af04ff893330280"));
std::vector<uint8_t> blobs;
uint64_t height;
@@ -77,8 +78,8 @@ TEST(block_template, update)
ASSERT_EQ(template_id, 1);
hash blobs_hash;
keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h, HASH_SIZE);
ASSERT_EQ(blobs_hash, H("e9154971a27c412175562d23ab458b0d3cf780a8bcecf62ff3f667fed9d3bc1d"));
keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h);
ASSERT_EQ(blobs_hash, H("70ee1661794c9a55f006432b57b0fc37922e0ccf8f1e4294b43890f8a6f44c62"));
// Test 2: mempool with high fee and low fee transactions, it must choose high fee transactions
for (uint64_t i = 0; i < 512; ++i) {
@@ -91,10 +92,24 @@ TEST(block_template, update)
tpl.update(data, mempool, &wallet);
ASSERT_EQ(b->m_transactions.size(), 203);
for (size_t i = 1; i < b->m_transactions.size(); ++i) {
ASSERT_GE(*reinterpret_cast<const uint64_t*>(b->m_transactions[i].h), 256);
}
tpl.get_hashing_blobs(0, 10000, blobs, height, diff, sidechain_diff, seed_hash, nonce_offset, template_id);
ASSERT_EQ(height, 2762973);
ASSERT_EQ(diff, 300346053753ULL);
ASSERT_EQ(sidechain_diff, sidechain.difficulty());
ASSERT_EQ(seed_hash, data.seed_hash);
ASSERT_EQ(nonce_offset, 39);
ASSERT_EQ(template_id, 2);
keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h);
ASSERT_EQ(blobs_hash, H("c74d295a9cb7e808030284e2169a6f05b685a11c6c577a774d5eb8fad175d5cd"));
destroy_crypto_cache();
}
+1 -1
View File
@@ -27,7 +27,7 @@ TEST(keccak, hashing)
hash output;
const uint8_t* data = reinterpret_cast<const uint8_t*>(input);
const int len = static_cast<int>(size);
keccak(data, len, output.h, HASH_SIZE);
keccak(data, len, output.h);
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf);
Binary file not shown.
+52 -32
View File
@@ -27,10 +27,12 @@ namespace p2pool {
TEST(pool_block, deserialize)
{
PoolBlock b;
SideChain sidechain(nullptr, NetworkType::Mainnet, "mainnet test 2");
init_crypto_cache();
constexpr uint64_t expeted_consensus_id[HASH_SIZE / sizeof(uint64_t)] = {
PoolBlock b;
SideChain sidechain(nullptr, NetworkType::Mainnet, "default");
constexpr uint64_t expected_consensus_id[HASH_SIZE / sizeof(uint64_t)] = {
0x92680bb5e77eaf22ull,
0x27446c2c6bda99e3ull,
0x008e04a9d40451b2ull,
@@ -39,9 +41,9 @@ TEST(pool_block, deserialize)
const std::vector<uint8_t>& consensus_id = sidechain.consensus_id();
ASSERT_EQ(consensus_id.size(), HASH_SIZE);
ASSERT_EQ(memcmp(consensus_id.data(), expeted_consensus_id, HASH_SIZE), 0);
ASSERT_EQ(memcmp(consensus_id.data(), expected_consensus_id, HASH_SIZE), 0);
std::ifstream f("mainnet_test2_block.dat", std::ios::binary | std::ios::ate);
std::ifstream f("block.dat", std::ios::binary | std::ios::ate);
ASSERT_EQ(f.good() && f.is_open(), true);
std::vector<uint8_t> buf(f.tellg());
@@ -55,26 +57,26 @@ TEST(pool_block, deserialize)
int outputs_offset, outputs_blob_size;
const std::vector<uint8_t> mainchain_data = b.serialize_mainchain_data(&header_size, &miner_tx_size, &outputs_offset, &outputs_blob_size);
ASSERT_EQ(mainchain_data.size(), 5607);
ASSERT_EQ(mainchain_data.size(), 1757);
ASSERT_EQ(header_size, 43);
ASSERT_EQ(miner_tx_size, 506);
ASSERT_EQ(miner_tx_size, 1457);
ASSERT_EQ(outputs_offset, 54);
ASSERT_EQ(outputs_blob_size, 420);
ASSERT_EQ(outputs_blob_size, 1371);
ASSERT_EQ(b.m_majorVersion, 14);
ASSERT_EQ(b.m_minorVersion, 14);
ASSERT_EQ(b.m_timestamp, 1630934403);
ASSERT_EQ(b.m_nonce, 2432795907);
ASSERT_EQ(b.m_txinGenHeight, 2443466);
ASSERT_EQ(b.m_outputs.size(), 11);
ASSERT_EQ(b.m_majorVersion, 16);
ASSERT_EQ(b.m_minorVersion, 16);
ASSERT_EQ(b.m_timestamp, 1679221824);
ASSERT_EQ(b.m_nonce, 1247);
ASSERT_EQ(b.m_txinGenHeight, 2845298);
ASSERT_EQ(b.m_outputs.size(), 35);
ASSERT_EQ(b.m_extraNonceSize, 4);
ASSERT_EQ(b.m_extraNonce, 28);
ASSERT_EQ(b.m_transactions.size(), 159);
ASSERT_EQ(b.m_extraNonce, 1482827308);
ASSERT_EQ(b.m_transactions.size(), 9);
ASSERT_EQ(b.m_uncles.size(), 0);
ASSERT_EQ(b.m_sidechainHeight, 53450);
ASSERT_EQ(b.m_difficulty.lo, 319296691);
ASSERT_EQ(b.m_sidechainHeight, 4674483);
ASSERT_EQ(b.m_difficulty.lo, 1854596983);
ASSERT_EQ(b.m_difficulty.hi, 0);
ASSERT_EQ(b.m_cumulativeDifficulty.lo, 12544665764606ull);
ASSERT_EQ(b.m_cumulativeDifficulty.lo, 7172845253120126ull);
ASSERT_EQ(b.m_cumulativeDifficulty.hi, 0);
ASSERT_EQ(b.m_depth, 0);
ASSERT_EQ(b.m_verified, false);
@@ -82,25 +84,40 @@ TEST(pool_block, deserialize)
ASSERT_EQ(b.m_broadcasted, false);
ASSERT_EQ(b.m_wantBroadcast, false);
RandomX_Hasher hasher(nullptr);
hash seed;
class RandomX_Hasher_Test : public RandomX_Hasher_Base
{
std::stringstream s;
s << "c293f04b0f97cf76008c6ce8b0dbd2ba5be6b734de0aec9d1b758a12d7ec6451";
s >> seed;
}
public:
bool calculate(const void* data, size_t size, uint64_t, const hash&, hash& result, bool force_light_mode) override
{
if (size == 76) {
char buf[76 * 2 + 1];
{
log::Stream s(buf);
s << log::hex_buf(reinterpret_cast<const uint8_t*>(data), size);
buf[76 * 2] = '\0';
}
const char ref[] = "1010c0c8dba006b78e04571806733a74ef1014f404484d3358bfca889a75bb0fe9aff64a41c92bdf040000ecf0a11f83c6eced7d7cdfbdcd5a193f64d334b2c5491a9c595b4527e531ae7209";
if (memcmp(buf, ref, sizeof(buf)) == 0) {
std::stringstream s;
s << "aa7a3c4a2d67cb6a728e244288219bf038024f3b511b0da197a19ec601000000";
s >> result;
return true;
}
}
return false;
}
} hasher;
hasher.set_seed(seed);
hash pow_hash;
hash seed, pow_hash;
ASSERT_EQ(b.get_pow_hash(&hasher, 0, seed, pow_hash), true);
std::stringstream s;
s << pow_hash;
ASSERT_EQ(s.str(), "f76d731c61c9c9b6c3f46be2e60c9478930b49b4455feecd41ecb9420d000000");
ASSERT_EQ(s.str(), "aa7a3c4a2d67cb6a728e244288219bf038024f3b511b0da197a19ec601000000");
ASSERT_EQ(b.m_difficulty.check_pow(pow_hash), true);
destroy_crypto_cache();
}
TEST(pool_block, verify)
@@ -114,8 +131,8 @@ TEST(pool_block, verify)
uint64_t m_txinGenHeight;
uint64_t m_sidechainHeight;
} tests[2] = {
{ "default", "sidechain_dump.dat", 2483901, 522805 },
{ "mini", "sidechain_dump_mini.dat", 2696040, 2424349 },
{ "default", "sidechain_dump.dat", 2845288, 4674368 },
{ "mini", "sidechain_dump_mini.dat", 2845293, 4129185 },
};
for (const STest& t : tests)
@@ -123,6 +140,9 @@ TEST(pool_block, verify)
PoolBlock b;
SideChain sidechain(nullptr, NetworkType::Mainnet, t.m_poolName);
// Difficulty of block 2844672
sidechain.m_testMainChainDiff = difficulty_type(321967641416ULL, 0ULL);
std::ifstream f(t.m_fileName, std::ios::binary | std::ios::ate);
ASSERT_EQ(f.good() && f.is_open(), true);
Binary file not shown.
Binary file not shown.