Compare commits

...

157 Commits

Author SHA1 Message Date
SChernykh 5b9b73abcc p2pool v1.9 2022-03-30 20:15:47 +02:00
SChernykh a2afa29052 Updated copyright 2022-03-30 14:42:26 +02:00
SChernykh a2d3dbad5e Use old bock template when update fails 2022-03-30 10:11:35 +02:00
SChernykh e50cf060cb Miner: fixed share counter initialization 2022-03-30 08:34:22 +02:00
SChernykh 915988d694 Added logging for the extra_nonce fix 2022-03-30 07:59:59 +02:00
SChernykh 345c231a9a Update config.json 2022-03-28 19:12:21 +02:00
SChernykh a7aed2f221 Fixed miner tx creation 2022-03-28 11:28:56 +02:00
SChernykh 292e2580e5 Make sure dataset init doesn't block start 2022-03-24 20:55:35 +01:00
SChernykh 028a5d0c88 Fixed memory leak after loading block cache 2022-03-24 18:30:23 +01:00
SChernykh 8b27faad6d BlockCache: fixed collisions of same height blocks 2022-03-24 16:03:12 +01:00
SChernykh cdc3206ee8 Optimized keccak 2022-03-24 12:52:57 +01:00
SChernykh 0c2b7d6010 More time handling fixes 2022-03-23 15:49:24 +01:00
SChernykh ef15c3b54f Show sidechain ID in status 2022-03-23 14:17:40 +01:00
SChernykh f4bcdc7fe9 Use steady_clock to time peer list requests 2022-03-23 11:37:05 +01:00
SChernykh aada1bb5cc Use chrono::steady_clock for internal timestamps 2022-03-23 11:30:38 +01:00
SChernykh 796850d8c5 Update CMakeLists.txt 2022-03-18 14:53:30 +01:00
SChernykh ab0bc0488e Removed libsodium as it's not used in compilation 2022-03-18 14:43:23 +01:00
SChernykh f3af02111d Update README.md 2022-03-18 10:31:06 +01:00
SChernykh c21d052d7a Refactored RNG usage across the code 2022-03-17 16:14:29 +01:00
SChernykh 62b1690780 Added an option to disable RandomX for the build 2022-03-15 17:11:45 +01:00
SChernykh 52050bbcfb Update difficulty_type_tests.cpp 2022-03-10 19:11:49 +01:00
SChernykh 0db57e5047 p2pool v1.8 2022-02-27 19:56:47 +01:00
SChernykh e7919514d4 TCPServer: fixed uninitialized variables 2022-02-25 15:31:35 +01:00
SChernykh 7ab21c6afd TCPServer: reduced memory usage 2022-02-23 17:07:50 +01:00
SChernykh 2209e0cc70 P2PServer: relaxed peer list request restriction 2022-02-22 11:06:12 +01:00
SChernykh a73b4616a4 Fixed Coverity issues 2022-02-22 00:38:08 +01:00
SChernykh 9593311332 Added bans console command 2022-02-21 19:59:57 +01:00
SChernykh 9a09d15bea P2PServer: use high_resolution_clock for ping time 2022-02-21 18:46:53 +01:00
SChernykh f082488e4b TCPServer: use steady_clock for bans 2022-02-21 18:41:36 +01:00
SChernykh c56773d4d6 Update cppcheck.yml 2022-02-21 16:59:32 +01:00
SChernykh 752e2381d6 P2PServer: added more protocol checks 2022-02-21 16:54:53 +01:00
SChernykh 03f829e18c Fixed formatting 2022-02-21 10:05:47 +01:00
Hayden Cardwell 06b66c99db Cleanup/Re-work of README 2022-02-21 09:55:14 +01:00
SChernykh 0b0ad9ff82 Fix tests compilation 2022-02-21 09:32:32 +01:00
SChernykh de9f13d393 Check for unknown command line parameters 2022-02-21 08:02:36 +01:00
SChernykh 272d206741 stats_mod api: updated min payout value 2022-02-20 17:08:27 +01:00
SChernykh c9493ec7fa Merge pull request #123 from bladedoyle/mini
Support mini pool.  Cleanup
2022-02-20 16:16:10 +01:00
Blade Doyle 5ef4134955 Support mini pool. Cleanup 2022-02-19 15:27:37 -08:00
SChernykh 8045c40026 Check for proper --local-api usage 2022-02-18 16:21:28 +01:00
SChernykh d4a1b14820 Use monerod peers to find p2pool nodes 2022-02-17 22:10:52 +01:00
SChernykh cc6ce4a366 Reset stratum share counters after initial sync 2022-02-17 11:19:11 +01:00
SChernykh 25e5f80a81 Merge pull request #121 from hayden-cardwell/master
Add a link to SethForPrivacy's Linux guide
2022-02-17 09:48:00 +01:00
Hayden Cardwell d4b339b1a3 Add a link to SethForPrivacy's Linux guide
Added a link to SethForPrivacy's guide within the Linux instructions, to potentially assist those who may need a bit more hand holding, or followed his previous guide to deploy a node.
2022-02-16 18:14:51 -06:00
SChernykh 4bdd524d84 Merge pull request #120 from SChernykh/tmp
MacOS CI: build and static link libuv and libzmq
2022-02-16 12:20:05 +01:00
SChernykh b78fe02405 MacOS CI: build and static link libuv and libzmq 2022-02-16 11:27:34 +01:00
SChernykh dfa0c7ffb1 Merge pull request #119 from SamsungGalaxyPlayer/patch-3
Add monero.com to list of compatible wallets
2022-02-15 21:33:00 +01:00
Justin Ehrenhofer ec97a14da7 Add monero.com to list of compatible wallets
Monero.com used the 4.2.8 Cake Wallet base, so all versions of that app are compatible
2022-02-15 10:06:35 -06:00
SChernykh 3a2e9e8b94 Merge pull request #117 from SamsungGalaxyPlayer/patch-1
Remove outdated note to "current" version
2022-02-15 17:03:45 +01:00
Justin Ehrenhofer 069c96aa1d Remove outdated note to "current" version 2022-02-15 09:55:05 -06:00
SChernykh 42370bf11e p2pool v1.7 2022-02-14 19:29:55 +01:00
SChernykh dc506bb1b9 Update README.md 2022-02-14 14:08:36 +01:00
SChernykh ec2f4467fb Added --mini command line option to connect to p2pool-mini 2022-02-14 13:57:59 +01:00
SChernykh 9f449320b5 StartumServer: disconnect and ban inactive clients 2022-02-08 15:00:08 +01:00
SChernykh 8767ef9e19 Display payout amount when a block is found 2022-01-29 17:09:43 +01:00
SChernykh cf184295b4 Updated version 2022-01-26 19:42:49 +01:00
SChernykh 0cd9876a46 Updated dependencies 2022-01-25 00:05:38 +01:00
SChernykh 1e4e4bd08e Added miner statistics to data api 2022-01-24 10:42:21 +01:00
SChernykh a640e41d74 Improved RNG initialization 2022-01-24 00:37:39 +01:00
SChernykh f90e00edea Moved "how to mine" up in the readme 2022-01-23 15:55:57 +01:00
SChernykh 38ffd73b54 Fixed new Coverity issue 2022-01-23 13:03:56 +01:00
SChernykh c10a5ce323 Miner code cleanup 2022-01-23 11:03:26 +01:00
SChernykh e2931f94b5 Added embedded miner 2022-01-22 23:30:19 +01:00
SChernykh 95c268be4c Update msvc-analysis.yml 2022-01-20 13:41:38 +01:00
SChernykh 4ac4f8f860 Update README.md
XMR block reward has decreased, so minimum payout is now ~0.0003 XMR
2022-01-20 13:22:34 +01:00
SChernykh 248fcee174 Update version 2022-01-09 17:19:20 +01:00
SChernykh 89bcceb986 Added p2pool mini seed nodes and default port 2021-12-30 11:10:18 +01:00
SChernykh 2b01350add Added outpeers and inpeers console commands 2021-12-26 15:28:33 +01:00
SChernykh f1b6212c82 P2PServer: add connection limits
#87 Added `--out-peers` and `--in-peers` command line options.
2021-12-26 14:59:45 +01:00
SChernykh 7d961ab3b9 Merge pull request #89 from bladedoyle/monero_p2pool_support
Monero latest release supports p2pool
2021-12-24 22:40:54 +01:00
Blade Doyle 862e7d965b Monero latest release supports p2pool 2021-12-24 11:38:53 -08:00
SChernykh da2d9f7e2e Update README.md 2021-12-07 19:11:51 +01:00
SChernykh d8bba4dc82 Updated version mismatch error message 2021-12-07 16:25:23 +01:00
SChernykh 3a8a24a5ee Updated dependencies 2021-12-07 16:09:21 +01:00
SChernykh eaf29872cb Update version 2021-12-01 18:24:36 +01:00
SChernykh 74f553274f Update c-cpp.yml
Use macos-10.15 instead of "latest".
2021-11-23 14:08:38 +01:00
SChernykh d0f1e6eb2e Merge pull request #78 from SChernykh/calcpow
Added "calc_pow" RPC support
2021-11-21 09:20:01 +01:00
SChernykh f9def6cb73 Added "calc_pow" RPC support 2021-11-21 09:12:21 +01:00
SChernykh 2f659c8ecf Update c-cpp.yml 2021-11-11 22:59:04 +01:00
SChernykh 2d559ca764 Merge pull request #71 from bladedoyle/docker-compose
docker-compose project for Monero P2Pool and all requirements
2021-11-11 22:00:29 +01:00
Blade Doyle 1328ea6aef docker-compose project for Monero P2Pool and all requirements 2021-11-11 12:52:03 -08:00
SChernykh 07cfb54bdc JSONRPCRequest: fixed undefined behavior in the error handler 2021-11-10 16:26:01 +01:00
SChernykh 5dd8d11b72 TCPServer: removed unused field 2021-11-09 15:48:59 +01:00
SChernykh dbab191bfc C/C++ CI: use GCC 11 for the ubuntu-latest build 2021-11-08 16:03:43 +01:00
SChernykh e44baad73c Update version 2021-11-05 15:39:20 +01:00
SChernykh bb7a5b4d6d Cleanup 2021-11-02 17:18:02 +01:00
SChernykh 64116643c2 Fixed block broadcasts 2021-11-01 19:36:11 +01:00
SChernykh 1e4c6ef5f4 Added block verification test 2021-11-01 18:53:34 +01:00
SChernykh 61edc6ed70 Reduce cache flush frequency to once per minute 2021-11-01 11:29:59 +01:00
SChernykh d283f91eb0 Update README.md 2021-11-01 11:07:00 +01:00
SChernykh dff2413cae TCPServer: make write buffers global
Reduced memory usage per connection.
2021-10-31 20:29:35 +01:00
SChernykh 998c2ba72f Fixed msys2 build crash 2021-10-31 20:29:35 +01:00
SChernykh b56d027dd5 Include P2P peer list into miner count 2021-10-31 20:29:35 +01:00
SChernykh eaed654012 Check for missing data from monerod on each new block
It helps in these cases:
- monerod was down/unavailable for a while and then restarted
- monerod was out of sync and then jumped several block heights ahead
2021-10-31 12:20:29 +01:00
SChernykh 981b161cc9 SideChain: faster processing of repeated broadcasts 2021-10-31 11:06:00 +01:00
SChernykh f59e9171e8 SideChain: use a separate lock for m_seenBlocks 2021-10-31 10:26:13 +01:00
SChernykh b45540ca08 TCPServer: clean up old IP bans 2021-10-29 14:54:36 +02:00
SChernykh 806e1ca0a3 SideChain: faster check for low-diff spam blocks 2021-10-29 11:39:15 +02:00
SChernykh 113e39817d Cleanup old Monero blocks data 2021-10-29 11:14:28 +02:00
SChernykh 0869326f39 SideChain: count miners in the last 48 hours 2021-10-29 10:37:12 +02:00
SChernykh 75d62c0303 SideChain: cleanup m_seenBlocks when pruning old blocks 2021-10-29 10:34:53 +02:00
SChernykh da4686b88f ZMQReader: start after initial RPC calls are finished 2021-10-28 20:05:40 +02:00
SChernykh a008eac8c6 ZMQReader: more reliable connect() 2021-10-28 19:28:33 +02:00
SChernykh 3d60ae8c32 Fix: don't start until monerod is fully synchronized 2021-10-28 18:47:28 +02:00
SChernykh 88f346414a Fixed race condition when using --no-color 2021-10-28 10:36:50 +02:00
SChernykh 2071a24a9f Update version 2021-10-27 17:27:48 +02:00
SChernykh 1c0da4513f Added --no-color command line option
`--no-color` disables colors in console output
2021-10-27 16:48:17 +02:00
SChernykh 6a002cda46 Create msvc-analysis.yml 2021-10-27 16:06:36 +02:00
SChernykh c21c9615a3 Fixed optimization flags for gcc/clang 2021-10-26 20:13:51 +02:00
SChernykh c0c210664a Show uptime in status 2021-10-26 17:55:47 +02:00
SChernykh 6859d11445 Disable console quick-edit mode on Windows
Prevent user from accidentally selecting text in console and freezing all output
2021-10-26 17:42:00 +02:00
SChernykh 111324b6e0 Added console command to show connected peers 2021-10-24 16:04:30 +02:00
SChernykh 148b9dd294 Switch to faster unordered_map/set 2021-10-22 18:34:20 +02:00
SChernykh 04d18cdf1d P2PServer: fixed m_timer leak 2021-10-21 19:19:22 +02:00
SChernykh 352ad81a0a StratumServer: faster processing of regular shares 2021-10-21 19:09:36 +02:00
SChernykh d26902468d P2PServer: peer list tweaks
- Increase outgoing connections to 10
- Stricter handling of peer list responses
2021-10-21 15:55:00 +02:00
SChernykh 0382027d70 Fixed wrong error message 2021-10-21 10:12:57 +02:00
SChernykh 966b499e5c ZMQReader: keep trying to connect on startup 2021-10-21 00:22:53 +02:00
SChernykh 9885f82e9e Limit user name length to 64 characters
Also avoid creating temporary strings (`" user " + client->m_customUser`) when logging user name
2021-10-19 21:09:08 +02:00
SChernykh 32a58d1534 Merge pull request #58 from WeebDataHoarder/master
Display stratum user on SHARE FOUND and "client found mainchain block" log entries
2021-10-19 20:10:53 +02:00
WeebDataHoarder bc341918f6 Decode custom user from stratum client, display stratum client+user on SHARE FOUND and client mainchain found message 2021-10-19 19:33:15 +02:00
SChernykh f17c23528a Update version 2021-10-17 11:00:51 +02:00
SChernykh a06f4dcdcd P2PServer: fixed ever growing memory usage 2021-10-17 10:57:33 +02:00
SChernykh 3bc03e4801 CI: added Visual Studio 2022 build 2021-10-16 15:01:29 +02:00
SChernykh 816a29c5ab Added --no-cache command line parameter 2021-10-16 13:45:28 +02:00
SChernykh 285560e120 Fixed possible sidechain split during RandomX epoch change 2021-10-16 09:57:13 +02:00
SChernykh 39ba5c4131 P2PServer: check for messages that shouldn't be sent 2021-10-15 17:09:51 +02:00
SChernykh 9b86f8e81f Peer list refactoring
- Remove peers that weren't seen for > 1 hour
- Better peer selection algorithm for PEER_LIST_RESPONSE
2021-10-15 11:32:01 +02:00
SChernykh 9e90e988fa Refactored array_size to make it always constexpr 2021-10-14 16:46:49 +02:00
SChernykh 39b2167c98 Show correct max log level in usage 2021-10-14 13:59:11 +02:00
SChernykh f3024d3556 StratumServer: submit Monero block as soon as possible
1-3 ms saved when a block is found
2021-10-13 18:57:21 +02:00
SChernykh 61ac90f1d5 Fixed typo 2021-10-13 14:56:27 +02:00
SChernykh 60a8538e0c P2PServer: show an error when there are no ZMQ messages 2021-10-09 11:01:26 +02:00
SChernykh 77a3a5857c P2PServer: show an error when there are no connections 2021-10-08 18:21:31 +02:00
SChernykh af2a8eeb08 Tweaked warning level for lagging nodes 2021-10-06 15:22:03 +02:00
SChernykh 54d0ab3658 Move most block broadcasts to log level 6 2021-10-06 09:47:41 +02:00
SChernykh c9faa92098 StratumServer: fixed "bad share" ban logic 2021-10-04 15:51:28 +02:00
SChernykh 52e5008bf4 Refactoring
- Skip calling time(nullptr) twice
- Use getter method for m_api instead of exposing it
- Moved api_udate_*() back to private since they're not used anywhere else
2021-10-04 10:28:56 +02:00
SChernykh a0a31b0f3b Merge pull request #51 from Tonux599/stratumServerJson-staging
API for stratum server statistics.
2021-10-04 11:16:37 +03:00
Thomas Clarke 7742d163f7 API for stratum server statistics.
Users can pass `--stratum-api` to command line to enable a file called `local/stats` in `--data-api` directory. `local/stats` contains statistics that `StratumServer status` provides but in JSON format. It is currently setup that `local/stats` is updated each time a new job is sent to a worker.
2021-10-03 22:32:13 +01:00
SChernykh 8106cc1c1c Update rapidjson 2021-10-02 17:16:30 +02:00
SChernykh 7d66864b01 Print P2Pool version 2021-10-02 17:06:48 +02:00
SChernykh 2a3cd13b19 P2PServer: delete old cached blocks after initial sync
Saves ~20 MB of memory
2021-10-01 15:31:47 +02:00
SChernykh 00281fb006 Updated monerod command line for optimal mining 2021-09-28 17:32:30 +02:00
SChernykh 4bcaa41c8e Clear crypto derivation/key cache when a block is found 2021-09-27 13:47:27 +02:00
SChernykh 4fce76576f ZMQ reader: abort if connect to monerod failed 2021-09-27 13:47:27 +02:00
SChernykh 19043ace90 Merge pull request #38 from QuickBASIC/fix-batch-2-electric-boogaloo
fix run as admin and add pauses
2021-09-26 19:47:22 +03:00
QuickBASIC f9d75360ba fix run as admin and add pauses 2021-09-26 12:29:04 -04:00
SChernykh f05f7c4268 Fixed wrong nonce values in submit_block error log 2021-09-26 16:50:24 +02:00
SChernykh d8d84f08c4 Update llhttp.c 2021-09-26 14:35:16 +02:00
SChernykh ae8733c187 Update RandomX 2021-09-22 17:59:58 +02:00
SChernykh 3d1db2fa22 Merge pull request #28 from QuickBASIC/readme-updates
update how to mine on p2pool section
2021-09-20 09:38:36 +03:00
Mike Justman 3adc0b8ecd fixed typo 2021-09-20 01:38:53 -04:00
QuickBASIC 08dff2a63a update how to mine on p2pool section 2021-09-19 21:59:53 -04:00
SChernykh c29f211ebf Update README.md 2021-09-19 09:21:51 +02:00
SChernykh 75b629c64c Merge pull request #27 from QuickBASIC/post-release-readme-cleanup
remove testing verbiage
2021-09-19 03:16:38 +03:00
QuickBASIC 516e7196a4 remove testing verbiage 2021-09-18 20:08:41 -04:00
SChernykh 7d1372ff05 Update README.md 2021-09-18 23:47:59 +02:00
84 changed files with 3171 additions and 745 deletions
+44 -19
View File
@@ -9,15 +9,20 @@ on:
jobs:
build-ubuntu:
runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.config.os }}
strategy:
matrix:
os: [ubuntu-latest, ubuntu-18.04]
config:
- {os: ubuntu-latest, c: gcc-11, cpp: g++-11}
- {os: ubuntu-18.04, c: gcc, cpp: g++}
steps:
- name: Install dependencies
run: sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev
run: |
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install -y git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev ${{ matrix.config.c }} ${{ matrix.config.cpp }}
- name: Checkout repository
uses: actions/checkout@v2
@@ -28,7 +33,7 @@ jobs:
run: |
mkdir build
cd build
cmake ..
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
make -j2
- name: Build tests
@@ -36,7 +41,7 @@ jobs:
cd tests
mkdir build
cd build
cmake ..
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
make -j2
- name: Run tests
@@ -45,7 +50,7 @@ jobs:
- name: Archive binary
uses: actions/upload-artifact@v2
with:
name: p2pool-${{ matrix.os }}
name: p2pool-${{ matrix.config.os }}
path: build/p2pool
build-windows-msys2:
@@ -96,7 +101,14 @@ jobs:
build-windows-msbuild:
runs-on: windows-latest
runs-on: windows-${{ matrix.config.os }}
strategy:
matrix:
config:
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\", 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"}
steps:
- name: Checkout repository
@@ -107,23 +119,20 @@ jobs:
- name: Setup cmake
uses: lukka/get-cmake@latest
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.3
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -G "Visual Studio 16 2019"
msbuild /m /p:Configuration=Release p2pool.vcxproj
cmake .. -G "${{ matrix.config.vs }}" -DWITH_RANDOMX=${{ matrix.config.rx }}
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool.vcxproj
- name: Build tests
run: |
cd tests
mkdir build
cd build
cmake .. -G "Visual Studio 16 2019"
msbuild /m /p:Configuration=Release p2pool_tests.vcxproj
cmake .. -G "${{ matrix.config.vs }}"
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool_tests.vcxproj
- name: Run tests
run: |
@@ -133,7 +142,7 @@ jobs:
- name: Archive binary
uses: actions/upload-artifact@v2
with:
name: p2pool-msbuild.exe
name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}.exe
path: build/Release/p2pool.exe
build-macos:
@@ -142,7 +151,7 @@ jobs:
strategy:
matrix:
os: [macos-latest, macos-11]
os: [macos-10.15, macos-11]
steps:
- name: Checkout repository
@@ -151,13 +160,29 @@ jobs:
submodules: recursive
- name: Install dependencies
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake libuv zmq libpgm
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake ..
make -j3
- name: Build libzmq
run: |
cd external/src/libzmq
mkdir build
cd build
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF
make -j3
- name: Build p2pool
run: |
mkdir build
cd build
cmake ..
cmake .. -DSTATIC_BINARY=ON
make -j3
- name: Build tests
@@ -165,7 +190,7 @@ jobs:
cd tests
mkdir build
cd build
cmake ..
cmake .. -DSTATIC_LIBS=ON
make -j3
- name: Run tests
+1 -1
View File
@@ -45,7 +45,7 @@ jobs:
run: |
mkdir build
cd build
cmake .. -G "Visual Studio 16 2019"
cmake .. -G "Visual Studio 17 2022"
- name: Run cppcheck
run: |
+63
View File
@@ -0,0 +1,63 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
#
# Find more information at:
# https://github.com/microsoft/msvc-code-analysis-action
name: Microsoft C++ Code Analysis
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
schedule:
- cron: '40 10 * * 0'
env:
# Path to the CMake build directory.
build: '${{ github.workspace }}/build'
config: 'Release'
jobs:
analyze:
name: Analyze
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Configure CMake
run: cmake -B ${{ env.build }} -DCMAKE_BUILD_TYPE=${{ env.config }}
# Build is not required unless generated source files are used
# - name: Build CMake
# run: cmake --build ${{ env.build }}
- name: Initialize MSVC Code Analysis
uses: microsoft/msvc-code-analysis-action@47ecec99bf7ce0399ed85cbb5012b6f24eab5959
# Provide a unique ID to access the sarif output path
id: run-analysis
with:
cmakeBuildDirectory: ${{ env.build }}
buildConfiguration: ${{ env.config }}
# Ruleset file that will determine what checks will be run
ruleset: NativeRecommendedRules.ruleset
# Upload SARIF file to GitHub Code Scanning Alerts
- name: Upload SARIF to GitHub
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: ${{ steps.run-analysis.outputs.sarif }}
# Upload SARIF file as an Artifact to download and view
- name: Upload SARIF as an Artifact
uses: actions/upload-artifact@v2
with:
name: sarif-file
path: ${{ steps.run-analysis.outputs.sarif }}
+3 -3
View File
@@ -10,12 +10,12 @@
[submodule "external/src/cppzmq"]
path = external/src/cppzmq
url = https://github.com/SChernykh/cppzmq
[submodule "external/src/libsodium"]
path = external/src/libsodium
url = https://github.com/SChernykh/libsodium
[submodule "external/src/libuv"]
path = external/src/libuv
url = https://github.com/SChernykh/libuv
[submodule "external/src/libzmq"]
path = external/src/libzmq
url = https://github.com/SChernykh/libzmq
[submodule "external/src/robin-hood-hashing"]
path = external/src/robin-hood-hashing
url = https://github.com/SChernykh/robin-hood-hashing
+26 -8
View File
@@ -1,7 +1,8 @@
cmake_minimum_required(VERSION 2.8.12)
project(p2pool)
option(STATIC_LINUX_BINARY "Build static Linux binary" OFF)
option(STATIC_BINARY "Build static binary" 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)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
@@ -9,8 +10,11 @@ if (${CMAKE_VERSION} VERSION_GREATER "3.5.2")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
endif()
add_subdirectory(external/src/RandomX)
set(LIBS randomx)
if (WITH_RANDOMX)
add_definitions(-DWITH_RANDOMX)
add_subdirectory(external/src/RandomX)
set(LIBS randomx)
endif()
include(cmake/flags.cmake)
@@ -73,6 +77,11 @@ set(SOURCES
src/zmq_reader.cpp
)
if (WITH_RANDOMX)
set(HEADERS ${HEADERS} src/miner.h)
set(SOURCES ${SOURCES} src/miner.cpp)
endif()
include_directories(src)
include_directories(external/src)
include_directories(external/src/cryptonote)
@@ -80,8 +89,11 @@ include_directories(external/src/libuv/include)
include_directories(external/src/cppzmq)
include_directories(external/src/libzmq/include)
include_directories(external/src/llhttp)
include_directories(external/src/RandomX/src)
if (WITH_RANDOMX)
include_directories(external/src/RandomX/src)
endif()
include_directories(external/src/rapidjson/include)
include_directories(external/src/robin-hood-hashing/src/include)
if (WIN32)
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
@@ -121,15 +133,21 @@ add_definitions(/DZMQ_STATIC)
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
if (STATIC_LINUX_BINARY)
if (STATIC_BINARY)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME})
if (WITH_RANDOMX)
set(STATIC_LIBS randomx)
endif()
if (NOT APPLE)
set(STATIC_LIBS ${STATIC_LIBS} pthread dl)
endif()
target_link_libraries(${CMAKE_PROJECT_NAME}
"${CMAKE_SOURCE_DIR}/external/src/libsodium/src/libsodium/.libs/libsodium.a"
"${CMAKE_SOURCE_DIR}/external/src/libzmq/build/lib/libzmq.a"
"${CMAKE_SOURCE_DIR}/external/src/libuv/build/libuv_a.a"
randomx
pthread
${STATIC_LIBS}
)
else()
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} ${LIBS})
+50
View File
@@ -0,0 +1,50 @@
FROM ubuntu:latest
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
git \
build-essential \
ca-certificates \
cmake \
libuv1-dev \
libzmq3-dev \
libsodium-dev \
libpgm-dev \
libnorm-dev \
libgss-dev
ADD . /usr/src/p2pool
WORKDIR /usr/src/p2pool
RUN git submodule update --init --recursive && \
mkdir build && \
cd build && \
cmake .. && \
make -j$(nproc)
# ---
FROM ubuntu:latest
COPY --from=0 /usr/src/p2pool/build/p2pool /
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
libzmq5 \
libuv1 \
&& \
apt-get clean
RUN groupadd -r p2pool -g 1000 && \
useradd -u 1000 -r -g p2pool -s /sbin/nologin -c "p2pool user" p2pool
RUN mkdir -p /home/p2pool/.p2pool && \
chown p2pool.p2pool /home/p2pool /home/p2pool/.p2pool
USER p2pool
EXPOSE 3333
EXPOSE 37889
VOLUME /home/p2pool/.p2pool
WORKDIR /home/p2pool/.p2pool
ENTRYPOINT ["/p2pool"]
+153 -91
View File
@@ -2,19 +2,29 @@
Decentralized pool for Monero mining.
**NOTE** This is a highly experimental and untested software. I did some extensive testing locally, but there's zero guarantee it will work for you! It requires a custom monerod version (for now). No binaries are provided yet. Testing on mainnet has started! You can check the pool status at https://p2pool.io/
Pool status and monitoring pages can be found at https://p2pool.io/, https://p2pool.io/mini/ and https://p2pool.observer/
No ETA on the official release date, but hopefully before the end of September 2021.
### Build status
### Build Status
![C/C++ CI](https://github.com/SChernykh/p2pool/actions/workflows/c-cpp.yml/badge.svg)
![CodeQL](https://github.com/SChernykh/p2pool/actions/workflows/codeql-analysis.yml/badge.svg)
![msvc-analysis](https://github.com/SChernykh/p2pool/actions/workflows/msvc-analysis.yml/badge.svg)
![cppcheck](https://github.com/SChernykh/p2pool/actions/workflows/cppcheck.yml/badge.svg)
<a href="https://scan.coverity.com/projects/schernykh-p2pool">
<img alt="Coverity Scan Build Status"
src="https://scan.coverity.com/projects/23659/badge.svg"/>
</a>
# Contents
- [Pool mining vs Solo mining vs P2Pool mining](#pool-mining-vs-solo-mining-vs-p2pool-mining)
- [Features](#features)
- [How PPLNS works in P2Pool](#how-pplns-works-in-p2pool)
- [Default P2Pool parameters](#default-p2pool-parameters)
- [How to mine on P2Pool](#how-to-mine-on-p2pool)
- [General Considerations](#general-considerations)
- [GNU/Linux](#gnulinux)
- [Windows](#windows)
- [Build instructions](#build-instructions)
- [Donations](#donations)
## Pool mining vs Solo mining vs P2Pool mining
@@ -24,7 +34,7 @@ Here's the comparison table of the different ways of mining. While pool mining i
|-|-|-|-|-|-|-|-|
|Centralized pool|Regular|0-3%|0.001-0.01 XMR|Yes|Less stable due to pool server outages|Pool admin controls your mined funds, what you mine and can execute network attacks|Only miner software is required
|Solo|Rare|0%|0.6 XMR or more|No|As stable as your Monero node|100% under your control|Monero node + optional miner
|**P2Pool**|Regular|0%|less than 0.0004 XMR|No|As stable as your Monero node|100% under your control|Monero node + P2Pool node + miner
|**P2Pool**|Regular|0%|~0.0003 XMR|No|As stable as your Monero node|100% under your control|Monero node + P2Pool node + miner
## Features
@@ -34,7 +44,7 @@ Here's the comparison table of the different ways of mining. While pool mining i
* PPLNS payout scheme
* **0% fee**
* **0 XMR payout fee**
* **Less than 0.0004 XMR minimal payout**
* **~0.0003 XMR minimal payout**
* Fast block times, down to 1 second
* Uncle blocks are supported to avoid orphans - all your shares will be accounted for!
* Configurable PPLNS window size and block time
@@ -51,13 +61,134 @@ First you need to find a pool share. This share will stay in PPLNS window for 21
* Block time: 10 seconds
* PPLNS window: 2160 blocks (6 hours)
* Minimum payout = Monero block reward/2160, currently ~0.0004 XMR
* Minimum payout = Monero block reward/2160, ~0.0003 XMR
## How to mine on P2Pool
### General Considerations
- In order to mine on P2Pool, a synced Monero node using monerod v0.17.3.0 or newer is required. If you do not currently have one configured, you can find instructions to do so [here](https://sethforprivacy.com/guides/run-a-monero-node-advanced/).
- It is highly recommended that you create a separate restricted user account for mining. P2Pool is still relatively new and may still have serious bugs/vulnerabilities.
- You have to use a primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
- Starting from P2Pool v1.7, you can add the `--mini` parameter to your P2Pool command to connect to the **p2pool-mini** sidechain. Note that it will also change the default p2p port from 37889 to 37888.
- Check that ports 18080 (Monero p2p port) and 37889/37888 (P2Pool/P2Pool mini p2p port) are open in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine.
- You can connect multiple miners to the same P2Pool node. The more the better!
- The below steps assume that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup.
- It is highly recommended to create a new mainnet wallet for P2Pool mining because **wallet addresses are public on P2Pool**.
**Wallet software compatible with P2Pool payouts**
- [Official Monero CLI and GUI v0.17.2.3 and newer](https://www.getmonero.org/downloads/)
- [Monerujo v2.1.0 "Vertant" and newer](https://www.monerujo.io/)
- [Cake Wallet v4.2.7 and newer](https://cakewallet.com/)
- [Monero.com by Cake Wallet](https://monero.com/)
- [Feather Wallet v1.0.0 and newer](https://featherwallet.org/)
- [MyMonero](https://mymonero.com/)
### GNU/Linux
1. Download the latest P2Pool binaries [here](https://github.com/SChernykh/p2pool/releases/latest).
- Alternatively, grab the latest source code for P2Pool and [build it](#build-instructions).
2. Download the latest XMRig (linux-static-x64) binary [here](https://github.com/xmrig/xmrig/releases/latest).
3. Prepare enough huge pages (required for each instance of monerod/P2Pool/XMRig):
```
sudo sysctl vm.nr_hugepages=3072
```
4. Check that ports 18080 (Monero p2p port) and 37889/37888 (P2Pool/P2Pool mini p2p port) are open in your local firewall to ensure better connectivity.
5. Start `monerod` with the following command/options:
```
./monerod --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
```
**Note:** The `--zmq-pub` option is required for P2Pool to work properly.
6. Start P2Pool with the following command/options:
```
./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS
```
7. Wait until the initial P2Pool sync is finished (shouldn't take more than 5-10 minutes).
8. Start XMRig with the following command/options:
```
./xmrig -o 127.0.0.1:3333
```
- Note that you don't need to specify your wallet address for XMRig. **Wallet addresses set in XMRig config will be ignored!**
- To set a custom fixed difficulty for your miner (for example, 10000), instead start XMRig with the following options:
```
./xmrig -u x+10000 -o 127.0.0.1:3333
```
9. XMRig should connect and start mining!
**Additional Information:**
- For a more in-depth beginner friendly walk-through with the option of using Docker, please see SethForPrivacy's guide at: https://sethforprivacy.com/guides/run-a-p2pool-node/
- You can check the p2pool.log for any warnings or errors using the following command:
```
grep -E 'WARNING|ERROR' p2pool.log
```
- P2Pool has verbose logging by default, you can reduce it by using "loglevel N" command where N is between 0 and 6. Default loglevel is 3.
- You can use `logrotate` with a config like this to control logfile growth:
```
<path-to-logfile>
{
rotate 7
daily
missingok
delaycompress
nocreate
}
```
### Windows
**Note:** *Windows SmartScreen may block incoming connections by files that are "Downloaded from the Internet". You can allow 'p2pool.exe' and 'monerod.exe' by double-clicking them, clicking "More Info", then click "Run Anyway" and then closing them immediately so you can run them from the command line. Advanced users can use the PowerShell cmdlet `Unblock-File` to remove this flag.*
1. Download the latest P2Pool binaries [here](https://github.com/SChernykh/p2pool/releases/latest).
- Alternatively, grab the latest source code for P2Pool and [build it](#build-instructions).
2. Download the latest XMRig binary [here](https://github.com/xmrig/xmrig/releases/latest).
3. Expand the P2Pool binaries into an appropriate location (`%USERPROFILE%/bin` or `C:/bin/` are good options)
4. Expand XMRig binary into an appropriate location (the same folder as P2Pool is fine).
5. Prepare huge pages to work properly (each instance of monerod/P2Pool/XMRig needs them):
- On Windows 10 or above, run XMRig at least once as Administrator (right-click Run As Administrator)
- On earlier versions of Windows, you'll need to run XMRig as Administrator at least once per login.
6. Open a command prompt and navigate to the folder where you extracted P2Pool.
**Note:** *When running the below commands, Windows Firewall may prompt to allow connections, click "Allow" if prompted.*
7. Start `monerod` with the following command/options:
```
.\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
```
**Note:** The `--zmq-pub` option is required for P2Pool to work properly.
8. Start P2Pool with the following command/options:
```
.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS
```
9. Wait until the initial P2Pool sync is finished (shouldn't take more than 5-10 minutes).
10. Start XMRig with the following command/options:
```
.\xmrig.exe -o 127.0.0.1:3333
```
- Note that you don't need to specify your wallet address for XMRig. **Wallet addresses set in XMRig config will be ignored!**
- To set a custom fixed difficulty for your miner (for example, 10000), instead start XMRig with the following options:
```
xmrig.exe -u x+10000 -o 127.0.0.1:3333
```
11. XMRig should connect and start mining!
12. *(Optional but highly recommended)* You can create a Quickstart by creating a batch (.bat) file with the following contents and placing it in your P2Pool directory along with `xmrig.exe`.
```
@ECHO OFF
start cmd /k %~dp0\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
ECHO Wait until the Monero daemon shows fully synced before continuing. This can take some time. Type 'status' in other window to check progress.
PAUSE
start cmd /k %~dp0\p2pool.exe --wallet YOUR_WALLET_ADDRESS
ECHO Wait until the daemon shows fully synced before continuing. This can take some time.
PAUSE
%~dp0\xmrig.exe -u x+30000 -o 127.0.0.1
```
## Build instructions
Please see the relevant instructions for your platform:
### Ubuntu 20.04
p2pool binary:
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on Ubuntu 20.04:
```
sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev
git clone --recursive https://github.com/SChernykh/p2pool
@@ -67,23 +198,13 @@ cmake ..
make -j$(nproc)
```
monerod binary compatible with p2pool:
```
sudo apt update && sudo apt install git build-essential cmake pkg-config libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler libudev-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-locale-dev libboost-program-options-dev libboost-regex-dev libboost-serialization-dev libboost-system-dev libboost-thread-dev ccache doxygen graphviz
git clone --recursive https://github.com/SChernykh/monero
cd monero
git checkout p2pool-api-v0.17
git submodule sync && git submodule update --init --force --recursive
make release-static -j$(nproc)
```
### Arch Linux [AUR](https://wiki.archlinux.org/title/Arch_User_Repository)
Make the package: [p2pool-git](https://aur.archlinux.org/packages/p2pool-git/)
### [Nix/NixOS](https://nixos.org)
This is a flake only project. So you have to use [nixUnstable with nix flakes](https://nixos.wiki/wiki/Flakes) to build or install p2pool.
This is a flake only project. So you have to use [nixUnstable with nix flakes](https://nixos.wiki/wiki/Flakes) to build or install P2Pool.
The commands below use the new flake specific reference-format, so be sure to also set `ca-references` in `--experimental-features`.
Because this project has submodules which are not fixed in _nixUnstable_ yet you have to use the `nix/master` branch:
@@ -101,31 +222,10 @@ Run the binary with arguments:
nix run git+https://github.com/SChernykh/p2pool?ref=master -- --help
```
### macOS
p2pool binary:
```
brew update && brew install git cmake libuv zmq libpgm
git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool
mkdir build && cd build
cmake ..
make -j$(sysctl -n hw.logicalcpu)
```
monerod binary compatible with p2pool:
```
git clone --recursive https://github.com/SChernykh/monero
cd monero
git checkout p2pool-api-v0.17
git submodule sync && git submodule update --init --force --recursive
brew update && brew bundle --file=contrib/brew/Brewfile
make -j$(sysctl -n hw.logicalcpu)
```
### Windows
p2pool binary (Visual Studio Community 2019 build):
P2Pool binary (Visual Studio Community 2019 build):
*NOTE: You need to have the "Desktop Development with C++" module installed.*
```
git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool
@@ -135,57 +235,19 @@ cmake .. -G "Visual Studio 16 2019"
```
then open generated build\p2pool.sln in Visual Studio and build it there
monerod binary compatible with p2pool:
Alternatively, you can select "Clone a repository" within the GUI, then select "Build" from the menu.
### macOS
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on your Mac:
```
git clone --recursive https://github.com/SChernykh/monero
cd monero
git checkout p2pool-api-v0.17
git submodule sync && git submodule update --init --force --recursive
brew update && brew install git cmake libuv zmq libpgm
git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool
mkdir build && cd build
cmake ..
make -j$(sysctl -n hw.logicalcpu)
```
then follow the instructions from https://github.com/monero-project/monero/#on-windows
## How to test
Mainnet test has started! **PPLNS window = 2160 blocks, block time = 10 seconds**. This guide assumes that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup. It's highly recommended to create a new mainnet wallet for testing because **wallet addresses are public on p2pool**.
**Wallet software compatible with p2pool payouts**
- Official Monero CLI and GUI v0.17.2.3 and newer
- Monerujo v2.1.0 "Vertant" and newer
- Cake Wallet v4.2.7 and newer
The purpose of this test is to bring as much hashrate as possible and check if stratum server works fine!
- Grab the latest source code for both p2pool and monerod and build them (see above, also notice that the branch name for monerod changed, you'll need to checkout p2pool-api-v0.17)
- Prepare enough huge pages (each of monerod/p2pool/xmrig needs them): `sudo sysctl vm.nr_hugepages=3072`
- Create a separate restricted user account for testing. p2pool is untested and can have serious bugs/vulnerabilities!
- Get xmrig (linux-static-x64) binary from https://github.com/xmrig/xmrig/releases/latest
- Check that ports 18080 (Monero p2p port) and 37889 (p2pool p2p port) are open in your firewall to ensure better connectivity
- Create a new mainnet wallet
- You have to use the primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining
- Run `./monerod --zmq-pub tcp://127.0.0.1:18083` **don't forget --zmq-pub parameter in the command line**
- Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-04bfd948a)** on startup. Wait until it's synchronized.
- Run `./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
- Keep both monerod and p2pool running for the whole duration of your test
- p2pool has _very_ verbose logging by default, it will spam a lot, no I mean A LOT in both console and in p2pool.log. Logs help testing immensely!
- You can use `logrotate` with a config like this to control logfile growth:
```
<path-to-logfile>
{
rotate 7
daily
missingok
delaycompress
nocreate
}
```
- Wait until initial p2pool sync is finished, it shouldn't take more than 5-10 minutes. Of course it depends on your connection speed!
- p2pool has a stratum server listening on port 3333, you can connect xmrig to it now
- Run `./xmrig -o 127.0.0.1:3333`. Note that you don't need to specify wallet address for xmrig. **Wallet address set in xmrig config will be ignored!**
- To set custom fixed difficulty for your miner (for example, 10000), run `./xmrig -u x+10000 -o 127.0.0.1:3333`
- xmrig should connect and start mining
- you can connect multiple miners to the same p2pool node. The more the better!
- From now on, watch your wallet to see if it gets anything
- Also check p2pool.log for any warnings and errors: `grep -E 'WARNING|ERROR' p2pool.log`
## Donations
+5 -5
View File
@@ -9,16 +9,16 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(WARNING_FLAGS "-Wall -Wextra -Werror")
set(OPTIMIZATION_FLAGS "-Ofast -s")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
if (WIN32)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
else()
if (STATIC_LINUX_BINARY)
if (STATIC_BINARY)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
else()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
@@ -42,9 +42,9 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal -Werror")
set(OPTIMIZATION_FLAGS "-Ofast -funroll-loops -fmerge-all-constants")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
endif()
+1 -1
View File
@@ -5,7 +5,7 @@
// Fixed difficulty for miners that connect to your node must be set in their config.json
//
{
"name": "mainnet test 2",
"name": "default",
"password": "",
"block_time": 10,
"min_diff": 100000,
+1
View File
@@ -7,3 +7,4 @@
../external/src/llhttp/
../external/src/RandomX/src/
../external/src/rapidjson/include
../external/src/robin-hood-hashing/src/include
+1 -1
View File
@@ -1,5 +1,5 @@
#!/bin/bash
cppcheck ../src -DZMQ_STATIC --platform=unix64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" -I ../src/ -I ../external/src/ -I ../external/src/cryptonote/ -I ../external/src/libuv/ -I ../external/src/cppzmq/ -I ../external/src/libzmq/ -I ../external/src/llhttp/ -I ../external/src/RandomX/src/ -I ../external/src/rapidjson/include --suppressions-list=suppressions.txt --output-file=errors_full.txt
cppcheck ../src -DZMQ_STATIC --platform=unix64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" -I ../src/ -I ../external/src/ -I ../external/src/cryptonote/ -I ../external/src/libuv/ -I ../external/src/cppzmq/ -I ../external/src/libzmq/ -I ../external/src/llhttp/ -I ../external/src/RandomX/src/ -I ../external/src/rapidjson/include -I ../external/src/robin-hood-hashing/src/include --suppressions-list=suppressions.txt --output-file=errors_full.txt
grep -v 'external' errors_full.txt | grep -v 'unmatchedSuppression' > errors_filtered.txt
if [ -s errors_filtered.txt ]; then
cat errors_filtered.txt
+42
View File
@@ -0,0 +1,42 @@
##
# Monero Wallet Address for mining rewards (replace this with your own address)
WALLET_ADDRESS="44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg"
##
# p2pool settings
#
# Use p2pool-mini (uncomment to enable)
#P2POOL_MINI="--mini"
#
# Which port to listen for miner connections
P2POOL_STRATUM_PORT=3333
#
# How much logging - (Less) 0 - 6 (More)
P2POOL_LOGLEVEL=2
##
# Monero Node Settings
#
# Version of Monero to build. Must be v0.17.3.0 or later for p2pool support. "latest" pulls the most recent release tag
MONERO_GIT_TAG=latest
#
# Limit the size of the blockchain on disk (comment to disable)
PRUNE_NODE="--prune-blockchain"
#
# Other monerod commandline options
MONERO_EXTRA_OPTIONS=""
##
# Xmrig Miner Settings
#
# Submit shares at a lower fixed difficulty to show mining progress (comment to disable, edit to customize)
FIXED_MINING_DIFFICULTY="-u x+500000"
#
# set process priority (0 idle, 2 normal to 5 highest)
MINING_CPU_PRIORITY="--cpu-priority=2"
#
# Other XMRIG commandline options
# See: https://xmrig.com/docs/miner/command-line-options
XMRIG_EXTRA_OPTIONS=""
+52
View File
@@ -0,0 +1,52 @@
# Monero P2Pool Docker Compose
Run your own <b>Monero Node + P2Pool + XMRig</b> in Docker
## Instructions
#### Install docker and docker-compose
[Install Docker](https://docs.docker.com/engine/install/)
[Install Docker Compose](https://docs.docker.com/compose/install/)
#### Clone the P2Pool project
```
git clone --recursive https://github.com/SChernykh/p2pool
```
#### Configure your Monero address for mining rewards
```
cd p2pool/docker-compose
vi .env
```
**WALLET_ADDRESS** is the only setting that needs to be updated in that file
#### Build the docker containers
```
docker-compose build
```
#### Run the node, pool, and CPU miner
```
docker-compose up
```
#### Optional
* Open ports 18080 (Monero p2p port) and 37889 (P2Pool p2p port) or 37888 (P2Pool-mini p2p port) in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine
* An XMRig CPU miner is included by default, but you can connect additional miners to this same p2pool node using port 3333
* Configure your kernel for maximum mining performance: [XMRig RandomX Optimization Guide](https://xmrig.com/docs/miner/randomx-optimization-guide)
* Small miners can mine on p2pool-mini by editing the .env file
#### Other usefull commands
* You can **run everythng in the background** by adding the "-d" argument to the "docker-compose up" command: ```docker-compose up -d```
* You can **stop everything** with CTRL-C or ```docker-compose down```
* You can see logs when running in the background for with the "docker logs" command: ```docker logs -f p2pool-xmrig``` or ```docker logs -f p2pool-p2pool``` or ```docker logs -f p2pool-monero```
* You can pause mining with: ```docker-compose pause xmrig``` and resume mining with: ```docker-compose unpause xmrig```
* You can disable mining with: ```docker-compose stop xmrig``` and re-enable mining with: ```docker-compose start xmrig```
#### Uninstall
Change to p2pool/docker-compose directory
Stop and remove all containers: ```docker-compose down```
Remove the p2pool data: ```docker volume rm p2pool```
Remove the monero data: ```docker volume rm monero```
+86
View File
@@ -0,0 +1,86 @@
---
version: '3.4'
networks:
p2pool:
driver: bridge
volumes:
p2pool:
name: p2pool
monero:
name: monero
services:
monero:
image: monero:latest
build:
context: monero
args:
- MONERO_GIT_TAG=${MONERO_GIT_TAG}
container_name: p2pool-monero
networks:
- p2pool
ports:
- 18080:18080/tcp
volumes:
- monero:/home/monero/.bitmonero:rw
- /dev/null:/home/monero/.bitmonero/bitmonero.log:rw
- /dev/hugepages:/dev/hugepages:rw
restart: unless-stopped
command: >-
--zmq-pub tcp://0.0.0.0:18083
--disable-dns-checkpoints
--enable-dns-blocklist
--non-interactive
--p2p-bind-ip=0.0.0.0
--p2p-bind-port=18080
--rpc-bind-ip=0.0.0.0
--rpc-bind-port=18081
--confirm-external-bind
${PRUNE_NODE}
${MONERO_EXTRA_OPTIONS}
p2pool:
image: p2pool:latest
build: ../
container_name: p2pool-p2pool
networks:
- p2pool
ports:
- ${P2POOL_STRATUM_PORT}:3333/tcp
- 37888:37888/tcp
- 37889:37889/tcp
volumes:
- p2pool:/home/p2pool/.p2pool:rw
- /dev/null:/home/p2pool/.p2pool/p2pool.log:rw
- /dev/hugepages:/dev/hugepages:rw
depends_on:
- monero
restart: unless-stopped
command: >-
--host monero
--wallet ${WALLET_ADDRESS}
--loglevel ${P2POOL_LOGLEVEL}
${P2POOL_MINI}
xmrig:
image: xmrig:latest
build: xmrig
container_name: p2pool-xmrig
networks:
- p2pool
privileged: true
volumes:
- /dev:/dev:ro
- /lib/modules:/lib/modules:ro
- /dev/hugepages:/dev/hugepages:rw
depends_on:
- p2pool
restart: unless-stopped
command: >-
${FIXED_MINING_DIFFICULTY}
${MINING_CPU_PRIORITY}
${XMRIG_EXTRA_OPTIONS}
--randomx-1gb-pages
-o p2pool:3333
+79
View File
@@ -0,0 +1,79 @@
FROM ubuntu:latest
ARG MONERO_GIT_TAG="latest"
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
git \
ca-certificates \
build-essential \
pkg-config \
cmake \
libuv1-dev \
libzmq3-dev \
libsodium-dev \
libpgm-dev \
libnorm-dev \
libgss-dev \
libssl-dev \
libzmq3-dev \
libunbound-dev \
libsodium-dev \
libunwind8-dev \
liblzma-dev \
libreadline6-dev \
libldns-dev \
libexpat1-dev \
libpgm-dev \
qttools5-dev-tools \
libhidapi-dev \
libusb-1.0-0-dev \
libprotobuf-dev \
protobuf-compiler \
libudev-dev \
libboost-chrono-dev \
libboost-date-time-dev \
libboost-filesystem-dev \
libboost-locale-dev \
libboost-program-options-dev \
libboost-regex-dev \
libboost-serialization-dev \
libboost-system-dev \
libboost-thread-dev \
ccache \
doxygen \
graphviz
WORKDIR /usr/src
RUN git clone --recursive https://github.com/monero-project/monero && \
cd monero && \
if [ "x$MONERO_GIT_TAG" = "xlatest" ]; then MONERO_GIT_TAG=$(git describe --tags $(git rev-list --tags --max-count=1)); fi && \
git checkout $MONERO_GIT_TAG && \
git submodule sync && git submodule update --init --force --recursive && \
make release-static -j$(nproc)
# ---
FROM ubuntu:latest
COPY --from=0 /usr/src/monero/build/Linux/*/release/bin/* /
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
libgssapi-krb5-2 \
&& \
apt-get clean
RUN groupadd -r monero -g 1000 && \
useradd -u 1000 -r -g monero -s /sbin/nologin -c "monero node user" monero
RUN mkdir -p /home/monero/.bitmonero && \
chown monero.monero /home/monero /home/monero/.bitmonero
USER monero
EXPOSE 18080 18081 18083
VOLUME /home/monero/.bitmonero
WORKDIR /home/monero
ENTRYPOINT ["/monerod"]
+37
View File
@@ -0,0 +1,37 @@
FROM ubuntu:latest
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
git \
build-essential \
ca-certificates \
cmake \
libuv1-dev \
libssl-dev \
libhwloc-dev
WORKDIR /usr/src
RUN git clone https://github.com/xmrig/xmrig.git && \
mkdir xmrig/build && \
cd xmrig/build && \
cmake .. && \
make -j$(nproc)
# ---
FROM ubuntu:latest
COPY --from=0 /usr/src/xmrig/build/xmrig /
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
libssl1.1 \
libcrypto++6 \
libhwloc15 \
&& \
apt-get clean
WORKDIR /
ENTRYPOINT ["/xmrig"]
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -2,6 +2,7 @@
#pragma warning(disable : 4061 4065 4100 4152 4242 4244 4668 4702 4710 4711 4820 5045)
#elif defined __GNUC__
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#endif
#if LLHTTP_STRICT_MODE
+3 -2
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -154,6 +154,7 @@ struct BlockCache::Impl : public nocopy_nomove
BlockCache::BlockCache()
: m_impl(new Impl())
, m_flushRunning(0)
, m_storeIndex(0)
{
}
@@ -171,7 +172,7 @@ void BlockCache::store(const PoolBlock& block)
return;
}
uint8_t* data = m_impl->m_data + (static_cast<size_t>(block.m_sidechainHeight % NUM_BLOCKS) * BLOCK_SIZE);
uint8_t* data = m_impl->m_data + (static_cast<size_t>((m_storeIndex++) % NUM_BLOCKS) * BLOCK_SIZE);
*reinterpret_cast<uint32_t*>(data) = static_cast<uint32_t>(n1 + n2);
memcpy(data + sizeof(uint32_t), block.m_mainChainData.data(), n1);
+2 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,6 +37,7 @@ private:
struct Impl;
Impl* m_impl;
std::atomic<uint32_t> m_flushRunning;
std::atomic<uint32_t> m_storeIndex;
};
} // namespace p2pool
+108 -26
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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>
@@ -66,7 +66,7 @@ BlockTemplate::BlockTemplate(p2pool* pool)
m_mempoolTxsOrder.reserve(1024);
m_shares.reserve(m_pool->side_chain().chain_window_size() * 2);
for (size_t i = 0; i < array_size(m_oldTemplates); ++i) {
for (size_t i = 0; i < array_size(&BlockTemplate::m_oldTemplates); ++i) {
m_oldTemplates[i] = new BlockTemplate(*this);
}
@@ -79,7 +79,7 @@ BlockTemplate::BlockTemplate(p2pool* pool)
BlockTemplate::~BlockTemplate()
{
for (size_t i = 0; i < array_size(m_oldTemplates); ++i) {
for (size_t i = 0; i < array_size(&BlockTemplate::m_oldTemplates); ++i) {
delete m_oldTemplates[i];
}
@@ -185,17 +185,22 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
WriteLock lock(m_lock);
if (m_templateId > 0) {
*m_oldTemplates[m_templateId % array_size(m_oldTemplates)] = *this;
*m_oldTemplates[m_templateId % array_size(&BlockTemplate::m_oldTemplates)] = *this;
}
++m_templateId;
// When block template generation fails for any reason
auto use_old_template = [this]() {
const uint32_t id = m_templateId - 1;
LOGWARN(4, "using old block template with ID = " << id);
*this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)];
};
m_height = data.height;
m_difficulty = data.difficulty;
m_seedHash = data.seed_hash;
const time_t cur_time = time(nullptr);
// Only choose transactions that were received 10 or more seconds ago
size_t total_mempool_transactions;
{
@@ -205,6 +210,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
total_mempool_transactions = mempool.m_transactions.size();
const uint64_t cur_time = seconds_since_epoch();
for (auto& it : mempool.m_transactions) {
if (cur_time >= it.second.time_received + 10) {
m_mempoolTxs.emplace_back(it.second);
@@ -252,7 +259,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_poolBlockTemplate->m_minorVersion = HARDFORK_SUPPORTED_VERSION;
// Timestamp
m_timestamp = cur_time;
m_timestamp = time(nullptr);
if (m_timestamp <= data.median_timestamp) {
LOGWARN(2, "timestamp adjusted from " << m_timestamp << " to " << data.median_timestamp + 1 << ". Fix your system time!");
m_timestamp = data.median_timestamp + 1;
@@ -275,21 +282,26 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_pool->side_chain().fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares);
if (!SideChain::split_reward(max_reward, m_shares, m_rewards)) {
use_old_template();
return;
}
const uint64_t max_reward_amounts_weight = std::accumulate(m_rewards.begin(), m_rewards.end(), 0ULL,
[](uint64_t a, uint64_t b)
{
writeVarint(b, [&a](uint8_t) { ++a; });
return a;
});
auto get_reward_amounts_weight = [this]() {
return std::accumulate(m_rewards.begin(), m_rewards.end(), 0ULL,
[](uint64_t a, uint64_t b)
{
writeVarint(b, [&a](uint8_t) { ++a; });
return a;
});
};
uint64_t max_reward_amounts_weight = get_reward_amounts_weight();
if (!create_miner_tx(data, m_shares, max_reward_amounts_weight, true)) {
if (create_miner_tx(data, m_shares, max_reward_amounts_weight, true) < 0) {
use_old_template();
return;
}
const uint64_t miner_tx_weight = m_minerTx.size();
uint64_t miner_tx_weight = m_minerTx.size();
// Select transactions from the mempool
uint64_t final_reward, final_fees, final_weight;
@@ -427,17 +439,64 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
}
if (!SideChain::split_reward(final_reward, m_shares, m_rewards)) {
use_old_template();
return;
}
m_finalReward = final_reward;
if (!create_miner_tx(data, m_shares, max_reward_amounts_weight, false)) {
return;
const int create_miner_tx_result = create_miner_tx(data, m_shares, max_reward_amounts_weight, false);
if (create_miner_tx_result < 0) {
if (create_miner_tx_result == -3) {
// Too many extra bytes were added, refine max_reward_amounts_weight and miner_tx_weight
LOGINFO(4, "Readjusting miner_tx to reduce extra nonce size");
// The difference between max possible reward and the actual reward can't reduce the size of output amount varints by more than 1 byte each
// So block weight will be >= current weight - number of outputs
const uint64_t w = (final_weight > m_rewards.size()) ? (final_weight - m_rewards.size()) : 0;
// Block reward will be <= r due to how block size penalty works
const uint64_t r = get_block_reward(base_reward, data.median_weight, final_fees, w);
if (!SideChain::split_reward(r, m_shares, m_rewards)) {
use_old_template();
return;
}
max_reward_amounts_weight = get_reward_amounts_weight();
if (create_miner_tx(data, m_shares, max_reward_amounts_weight, true) < 0) {
use_old_template();
return;
}
final_weight -= miner_tx_weight;
final_weight += m_minerTx.size();
miner_tx_weight = m_minerTx.size();
final_reward = get_block_reward(base_reward, data.median_weight, final_fees, final_weight);
if (!SideChain::split_reward(final_reward, m_shares, m_rewards)) {
use_old_template();
return;
}
if (create_miner_tx(data, m_shares, max_reward_amounts_weight, false) < 0) {
use_old_template();
return;
}
LOGINFO(4, "New extra nonce size = " << m_poolBlockTemplate->m_extraNonceSize);
}
else {
use_old_template();
return;
}
}
if (m_minerTx.size() != miner_tx_weight) {
LOGERR(1, "miner tx size changed after adjusting reward");
use_old_template();
return;
}
@@ -600,7 +659,7 @@ void BlockTemplate::fill_optimal_knapsack(const MinerData& data, uint64_t base_r
}
#endif
bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run)
int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run)
{
// Miner transaction (coinbase)
m_minerTx.clear();
@@ -655,12 +714,12 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
if (dry_run) {
if (reward_amounts_weight != max_reward_amounts_weight) {
LOGERR(1, "create_miner_tx: incorrect miner rewards during the dry run (" << reward_amounts_weight << " != " << max_reward_amounts_weight << ")");
return false;
return -1;
}
}
else if (reward_amounts_weight > max_reward_amounts_weight) {
LOGERR(1, "create_miner_tx: incorrect miner rewards during the real run (" << reward_amounts_weight << " > " << max_reward_amounts_weight << ")");
return false;
return -2;
}
m_poolBlockTemplate->m_txkeyPub = m_txkeyPub;
@@ -676,6 +735,10 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
const uint64_t corrected_extra_nonce_size = EXTRA_NONCE_SIZE + max_reward_amounts_weight - reward_amounts_weight;
if (corrected_extra_nonce_size > EXTRA_NONCE_SIZE) {
if (corrected_extra_nonce_size > EXTRA_NONCE_MAX_SIZE) {
LOGWARN(4, "create_miner_tx: corrected_extra_nonce_size (" << corrected_extra_nonce_size << ") is too large");
return -3;
}
LOGINFO(4, "increased EXTRA_NONCE from " << EXTRA_NONCE_SIZE << " to " << corrected_extra_nonce_size << " bytes to maintain miner tx weight");
}
writeVarint(corrected_extra_nonce_size, m_minerTxExtra);
@@ -701,7 +764,7 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
// Not a part of transaction hash data
m_minerTx.push_back(0);
return true;
return 1;
}
hash BlockTemplate::calc_sidechain_hash() const
@@ -838,6 +901,25 @@ void BlockTemplate::calc_merkle_tree_main_branch()
}
}
bool BlockTemplate::get_difficulties(const uint32_t template_id, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const
{
ReadLock lock(m_lock);
if (template_id == m_templateId) {
mainchain_difficulty = m_difficulty;
sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
return true;
}
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) {
return old->get_difficulties(template_id, mainchain_difficulty, sidechain_difficulty);
}
return false;
}
uint32_t BlockTemplate::get_hashing_blob(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const
{
ReadLock lock(m_lock);
@@ -852,7 +934,7 @@ uint32_t BlockTemplate::get_hashing_blob(const uint32_t template_id, uint32_t ex
return get_hashing_blob_nolock(extra_nonce, blob);
}
const BlockTemplate* old = m_oldTemplates[template_id % array_size(m_oldTemplates)];
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) {
return old->get_hashing_blob(template_id, extra_nonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset);
@@ -953,7 +1035,7 @@ std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id
ReadLock lock(m_lock);
if (template_id != m_templateId) {
const BlockTemplate* old = m_oldTemplates[template_id % array_size(m_oldTemplates)];
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) {
return old->get_block_template_blob(template_id, nonce_offset, extra_nonce_offset);
}
@@ -983,7 +1065,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
m_poolBlockTemplate->m_nonce = nonce;
m_poolBlockTemplate->m_extraNonce = extra_nonce;
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_nonceOffset, &nonce, NONCE_SIZE);
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_extraNonceOffsetInTemplate, &extra_nonce, NONCE_SIZE);
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_extraNonceOffsetInTemplate, &extra_nonce, EXTRA_NONCE_SIZE);
SideChain& side_chain = m_pool->side_chain();
@@ -999,7 +1081,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
}
hash pow_hash;
if (!check.get_pow_hash(m_pool->hasher(), m_seedHash, pow_hash)) {
if (!check.get_pow_hash(m_pool->hasher(), check.m_txinGenHeight, m_seedHash, pow_hash)) {
LOGERR(1, "PoW check failed for the sidechain block. Fix it! ");
}
else if (!check.m_difficulty.check_pow(pow_hash)) {
@@ -1016,7 +1098,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
return;
}
BlockTemplate* old = m_oldTemplates[template_id % array_size(m_oldTemplates)];
BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) {
old->submit_sidechain_block(template_id, nonce, extra_nonce);
+4 -3
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,6 +40,7 @@ public:
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet);
bool get_difficulties(const uint32_t template_id, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
uint32_t get_hashing_blob(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const;
uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
@@ -49,7 +50,7 @@ public:
void update_tx_keys();
FORCEINLINE uint64_t height() const { return m_height; }
FORCEINLINE time_t timestamp() const { return m_timestamp; }
FORCEINLINE uint64_t timestamp() const { return m_timestamp; }
FORCEINLINE difficulty_type difficulty() const { return m_difficulty; }
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
@@ -60,7 +61,7 @@ private:
p2pool* m_pool;
private:
bool create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run);
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_miner_tx_hash(uint32_t extra_nonce) const;
void calc_merkle_tree_main_branch();
+40 -3
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,6 +54,7 @@
#include <atomic>
#include <chrono>
#include <iostream>
#include <random>
#include <signal.h>
@@ -84,6 +85,7 @@ constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 14;
constexpr uint8_t MINER_REWARD_UNLOCK_TIME = 60;
constexpr uint8_t NONCE_SIZE = 4;
constexpr uint8_t EXTRA_NONCE_SIZE = 4;
constexpr uint8_t EXTRA_NONCE_MAX_SIZE = EXTRA_NONCE_SIZE + 10;
constexpr uint8_t TX_VERSION = 2;
constexpr uint8_t TXIN_GEN = 0xFF;
constexpr uint8_t TXOUT_TO_KEY = 2;
@@ -113,6 +115,8 @@ FORCEINLINE uint64_t udiv128(uint64_t hi, uint64_t lo, uint64_t divisor, uint64_
}
#endif
template<typename T> FORCEINLINE T round_up(T a, size_t granularity) { return static_cast<T>(((a + (granularity - static_cast<size_t>(1))) / granularity) * granularity); }
struct hash
{
alignas(8) uint8_t h[HASH_SIZE];
@@ -232,7 +236,7 @@ struct TxMempoolData
uint64_t blob_size;
uint64_t weight;
uint64_t fee;
time_t time_received;
uint64_t time_received;
};
struct MinerData
@@ -258,7 +262,7 @@ struct MinerData
uint64_t median_timestamp;
std::vector<TxMempoolData> tx_backlog;
std::chrono::system_clock::time_point time_received;
std::chrono::high_resolution_clock::time_point time_received;
};
struct ChainMain
@@ -279,6 +283,39 @@ enum class NetworkType {
Stagenet,
};
struct raw_ip
{
alignas(8) uint8_t data[16];
FORCEINLINE bool operator<(const raw_ip& other) const
{
const uint64_t* a = reinterpret_cast<const uint64_t*>(data);
const uint64_t* b = reinterpret_cast<const uint64_t*>(other.data);
if (a[1] < b[1]) return true;
if (a[1] > b[1]) return false;
return a[0] < b[0];
}
FORCEINLINE bool operator==(const raw_ip& other) const
{
const uint64_t* a = reinterpret_cast<const uint64_t*>(data);
const uint64_t* b = reinterpret_cast<const uint64_t*>(other.data);
return (a[0] == b[0]) && (a[1] == b[1]);
}
FORCEINLINE bool operator!=(const raw_ip& other) const { return !operator==(other); }
};
static_assert(sizeof(raw_ip) == 16, "struct raw_ip has invalid size");
void* malloc_hook(size_t n) noexcept;
void* realloc_hook(void* ptr, size_t size) noexcept;
void* calloc_hook(size_t count, size_t size) noexcept;
void free_hook(void* p) noexcept;
} // namespace p2pool
#include "util.h"
+78 -6
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -21,6 +21,9 @@
#include "p2pool.h"
#include "stratum_server.h"
#include "p2p_server.h"
#ifdef WITH_RANDOMX
#include "miner.h"
#endif
#include "side_chain.h"
#include <iostream>
@@ -69,7 +72,11 @@ typedef struct cmd {
cmdfunc *func;
} cmd;
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_exit;
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_showpeers, do_showbans, do_outpeers, do_inpeers, do_exit;
#ifdef WITH_RANDOMX
static cmdfunc do_start_mining, do_stop_mining;
#endif
static cmd cmds[] = {
{ STRCONST("help"), "", "display list of commands", do_help },
@@ -77,17 +84,24 @@ static cmd cmds[] = {
{ STRCONST("loglevel"), "<level>", "set log level", do_loglevel },
{ STRCONST("addpeers"), "<peeraddr>", "add peer", do_addpeers },
{ STRCONST("droppeers"), "", "disconnect all peers", do_droppeers },
{ STRCONST("peers"), "", "show all peers", do_showpeers },
{ STRCONST("bans"), "", "show all banned IPs", do_showbans },
{ STRCONST("outpeers"), "", "set maximum number of outgoing connections", do_outpeers },
{ STRCONST("inpeers"), "", "set maximum number of incoming connections", do_inpeers },
#ifdef WITH_RANDOMX
{ STRCONST("start_mining"), "<threads>", "start mining", do_start_mining },
{ STRCONST("stop_mining"), "", "stop mining", do_stop_mining },
#endif
{ STRCONST("exit"), "", "terminate p2pool", do_exit },
{ STRCNULL, NULL, NULL, NULL }
};
static int do_help(p2pool * /* m_pool */, const char * /* args */)
{
int i;
LOGINFO(0, "List of commands");
for (i=0; cmds[i].name.len; i++)
for (int i = 0; cmds[i].name.len; ++i) {
LOGINFO(0, cmds[i].name.str << " " << cmds[i].arg << "\t" << cmds[i].descr);
}
return 0;
}
@@ -100,13 +114,18 @@ static int do_status(p2pool *m_pool, const char * /* args */)
if (m_pool->p2p_server()) {
m_pool->p2p_server()->print_status();
}
#ifdef WITH_RANDOMX
if (m_pool->miner()) {
m_pool->miner()->print_status();
}
#endif
bkg_jobs_tracker.print_status();
return 0;
}
static int do_loglevel(p2pool * /* m_pool */, const char *args)
{
int level = atoi(args);
int level = strtol(args, nullptr, 10);
level = std::min(std::max(level, 0), log::MAX_GLOBAL_LOG_LEVEL);
log::GLOBAL_LOG_LEVEL = level;
LOGINFO(0, "log level set to " << level);
@@ -129,6 +148,59 @@ static int do_droppeers(p2pool *m_pool, const char * /* args */)
return 0;
}
static int do_showpeers(p2pool* m_pool, const char* /* args */)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->show_peers();
}
return 0;
}
static int do_showbans(p2pool* m_pool, const char* /* args */)
{
if (m_pool->stratum_server()) {
m_pool->stratum_server()->print_bans();
}
if (m_pool->p2p_server()) {
m_pool->p2p_server()->print_bans();
}
return 0;
}
static int do_outpeers(p2pool* m_pool, const char* args)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->set_max_outgoing_peers(strtoul(args, nullptr, 10));
LOGINFO(0, "max outgoing peers set to " << m_pool->p2p_server()->max_outgoing_peers());
}
return 0;
}
static int do_inpeers(p2pool* m_pool, const char* args)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->set_max_incoming_peers(strtoul(args, nullptr, 10));
LOGINFO(0, "max incoming peers set to " << m_pool->p2p_server()->max_incoming_peers());
}
return 0;
}
#ifdef WITH_RANDOMX
static int do_start_mining(p2pool* m_pool, const char* args)
{
uint32_t threads = strtoul(args, nullptr, 10);
threads = std::min(std::max(threads, 1u), 64u);
m_pool->start_mining(threads);
return 0;
}
static int do_stop_mining(p2pool* m_pool, const char* /*args*/)
{
m_pool->stop_mining();
return 0;
}
#endif
static int do_exit(p2pool *m_pool, const char * /* args */)
{
bkg_jobs_tracker.wait();
+1 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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 -10
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -19,8 +19,6 @@
#include "crypto.h"
#include "keccak.h"
#include "uv_util.h"
#include <random>
#include <unordered_map>
extern "C" {
#include "crypto-ops.h"
@@ -33,9 +31,12 @@ namespace {
class RandomBytes
{
public:
RandomBytes() : rd(), rng(rd()), dist(0, 255)
RandomBytes() : rng(RandomDeviceSeed::instance), dist(0, 255)
{
uv_mutex_init_checked(&m);
// Diffuse the initial state in case it has low quality
rng.discard(10000);
}
~RandomBytes()
@@ -55,7 +56,6 @@ public:
private:
uv_mutex_t m;
std::random_device rd;
std::mt19937_64 rng;
std::uniform_int_distribution<> dist;
};
@@ -227,22 +227,50 @@ public:
return true;
}
void clear()
{
MutexLock lock(m);
derivations.clear();
public_keys.clear();
}
private:
uv_mutex_t m;
std::unordered_map<std::array<uint8_t, HASH_SIZE * 2>, hash> derivations;
std::unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, hash> derivations;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
};
static Cache cache;
static Cache* cache = nullptr;
bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation)
{
return cache.get_derivation(key1, key2, derivation);
return cache->get_derivation(key1, key2, derivation);
}
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key)
{
return cache.get_public_key(derivation, output_index, base, derived_key);
return cache->get_public_key(derivation, output_index, base, derived_key);
}
void init_crypto_cache()
{
if (!cache) {
cache = new Cache();
}
}
void destroy_crypto_cache()
{
if (cache) {
delete cache;
cache = nullptr;
}
}
void clear_crypto_cache()
{
cache->clear();
}
} // namespace p2pool
+5 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,4 +24,8 @@ bool check_keys(const hash& pub, const hash& sec);
bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation);
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key);
void init_crypto_cache();
void destroy_crypto_cache();
void clear_crypto_cache();
} // 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 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
+17 -5
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,7 +25,7 @@ static constexpr char log_category_prefix[] = "JSONRPCRequest ";
namespace p2pool {
JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb)
JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
: m_socket{}
, m_connect{}
, m_write{}
@@ -38,7 +38,7 @@ JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, C
{
m_readBuf[0] = '\0';
uv_tcp_init(uv_default_loop_checked(), &m_socket);
uv_tcp_init(loop ? loop : uv_default_loop_checked(), &m_socket);
uv_tcp_nodelay(&m_socket, 1);
sockaddr_storage addr;
@@ -55,13 +55,25 @@ JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, C
m_connect.data = this;
m_write.data = this;
const size_t len = strlen(req);
const char* uri = "/json_rpc";
size_t len = req ? strlen(req) : 0;
if (!len) {
LOGERR(1, "Empty JSONRPCRequest, fix the code!");
m_valid = false;
return;
}
if (req[0] == '/') {
uri = req;
len = 0;
}
m_request.reserve(std::max<size_t>(len + 128, log::Stream::BUF_SIZE + 1));
m_request.resize(log::Stream::BUF_SIZE + 1);
log::Stream s(m_request.data());
s << "POST /json_rpc HTTP/1.1\nContent-Type: application/json\nContent-Length: " << len << "\n\n";
s << "POST " << uri << " HTTP/1.1\nContent-Type: application/json\nContent-Length: " << len << "\n\n";
m_request.resize(s.m_pos);
m_request.insert(m_request.end(), req, req + len);
+7 -6
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,20 +26,21 @@ public:
static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb)
{
// It will be deleted in one of the tcp callbacks eventually
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), nullptr);
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), nullptr, nullptr);
if (!r->m_valid) {
delete r;
}
}
template<typename T, typename U>
static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb, U&& close_cb)
static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr)
{
// It will be deleted in one of the tcp callbacks eventually
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), new Callback<U>(std::move(close_cb)));
CallbackBase* close_callback = new Callback<U>(std::move(close_cb));
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), close_callback, loop);
if (!r->m_valid) {
constexpr char err[] = "internal error";
close_cb(err, sizeof(err) - 1);
(*close_callback)(err, sizeof(err) - 1);
delete r;
}
}
@@ -62,7 +63,7 @@ private:
T m_cb;
};
JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb);
JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
~JSONRPCRequest();
static void on_connect(uv_connect_t* req, int status);
+13 -9
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -48,17 +48,21 @@ NOINLINE void keccakf(uint64_t* st)
bc[3] = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
bc[4] = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
for (int i = 0; i < 5; ++i) {
uint64_t t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1);
st[i + 0 ] ^= t;
st[i + 5] ^= t;
st[i + 10] ^= t;
st[i + 15] ^= t;
st[i + 20] ^= t;
#define X(i) { \
const uint64_t t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); \
st[i + 0 ] ^= t; \
st[i + 5] ^= t; \
st[i + 10] ^= t; \
st[i + 15] ^= t; \
st[i + 20] ^= t; \
}
X(0); X(1); X(2); X(3); X(4);
#undef X
// Rho Pi
uint64_t t = st[1];
const uint64_t t = st[1];
st[ 1] = ROTL64(st[ 6], 44);
st[ 6] = ROTL64(st[ 9], 20);
st[ 9] = ROTL64(st[22], 61);
+1 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
+52 -6
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,7 @@ namespace p2pool {
namespace log {
int GLOBAL_LOG_LEVEL = 3;
bool CONSOLE_COLORS = true;
#ifndef P2POOL_LOG_DISABLE
@@ -35,6 +36,7 @@ static volatile bool stopped = false;
static volatile bool worker_started = false;
#ifdef _WIN32
static const HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
static const HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
static const HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE);
#endif
@@ -52,7 +54,7 @@ public:
: m_writePos(0)
, m_readPos(0)
{
is_main_thread = true;
set_main_thread();
m_logFile.open(log_file_name, std::ios::app | std::ios::binary);
@@ -63,12 +65,20 @@ public:
uv_cond_init(&m_cond);
uv_mutex_init(&m_mutex);
uv_thread_create(&m_worker, run_wrapper, this);
const int err = uv_thread_create(&m_worker, run_wrapper, this);
if (err) {
fprintf(stderr, "failed to start logger thread (%s), aborting\n", uv_err_name(err));
abort();
}
do {} while (!worker_started);
#ifdef _WIN32
DWORD dwConsoleMode;
if (GetConsoleMode(hStdIn, &dwConsoleMode)) {
SetConsoleMode(hStdIn, dwConsoleMode & ~ENABLE_QUICK_EDIT_MODE);
}
if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
SetConsoleMode(hStdOut, dwConsoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
@@ -81,9 +91,13 @@ public:
}
}
FORCEINLINE ~Worker()
~Worker()
{
stop();
try {
stop();
}
catch (...) {
}
}
FORCEINLINE void stop()
@@ -155,6 +169,13 @@ private:
p += 3;
size -= 3;
// Read CONSOLE_COLORS only once because its value can be changed in another thread
const bool c = CONSOLE_COLORS;
if (!c) {
strip_colors(p, size);
}
#ifdef _WIN32
DWORD k;
WriteConsole((severity == 1) ? hStdOut : hStdErr, p, size, &k, nullptr);
@@ -172,7 +193,9 @@ private:
}
if (m_logFile.is_open()) {
strip_colors(p, size);
if (c) {
strip_colors(p, size);
}
if (severity == 1) {
m_logFile.write("NOTICE ", 8);
@@ -301,6 +324,29 @@ NOINLINE void Stream::writeCurrentTime()
m_numberWidth = 1;
}
NOINLINE void put_rawip(const raw_ip& value, Stream* wrapper)
{
const char* addr_str;
char addr_str_buf[64];
static constexpr uint8_t ipv4_prefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255 };
const bool is_v6 = (memcmp(value.data, ipv4_prefix, 12) != 0);
if (is_v6) {
addr_str = inet_ntop(AF_INET6, value.data, addr_str_buf, sizeof(addr_str_buf));
}
else {
addr_str = inet_ntop(AF_INET, value.data + 12, addr_str_buf, sizeof(addr_str_buf));
}
if (addr_str) {
*wrapper << addr_str;
}
else {
*wrapper << "N/A";
}
}
} // namespace log
} // namespace p2pool
+10 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -22,6 +22,7 @@ namespace p2pool {
namespace log {
extern int GLOBAL_LOG_LEVEL;
extern bool CONSOLE_COLORS;
constexpr int MAX_GLOBAL_LOG_LEVEL = 6;
enum class Severity {
@@ -371,6 +372,7 @@ template<> struct log::Stream::Entry<XMRAmount>
template<> struct log::Stream::Entry<NetworkType>
{
// cppcheck-suppress constParameter
static NOINLINE void put(const NetworkType& value, Stream* wrapper)
{
switch (value) {
@@ -382,6 +384,13 @@ template<> struct log::Stream::Entry<NetworkType>
}
};
void put_rawip(const raw_ip& value, Stream* wrapper);
template<> struct log::Stream::Entry<raw_ip>
{
static FORCEINLINE void put(const raw_ip& value, Stream* wrapper) { put_rawip(value, wrapper); }
};
namespace {
template<log::Severity severity> void apply_severity(log::Stream&);
+24 -6
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -16,13 +16,14 @@
*/
#include "common.h"
#include "crypto.h"
#include "p2pool.h"
#include "stratum_server.h"
#include "p2p_server.h"
static void usage()
void p2pool_usage()
{
printf(
printf("P2Pool %s\n"
"\nUsage:\n\n" \
"--wallet Wallet address to mine to. Subaddresses and integrated addresses are not supported!\n"
"--host IP address of your Monero node, default is 127.0.0.1\n"
@@ -32,12 +33,25 @@ static void usage()
"--p2p Comma-separated list of IP:port for p2p server to listen on\n"
"--addpeers Comma-separated list of IP:port of other p2pool nodes to connect to\n"
"--light-mode Don't allocate RandomX dataset, saves 2GB of RAM\n"
"--loglevel Verbosity of the log, integer number between 0 and 5\n"
"--loglevel Verbosity of the log, integer number between 0 and %d\n"
"--config Name of the p2pool config file\n"
"--data-api Path to the p2pool JSON data (use it in tandem with an external web-server)\n"
"--local-api Enable /local/ path in api path for Stratum Server and built-in miner statistics\n"
"--stratum-api An alias for --local-api\n"
"--no-cache Disable p2pool.cache\n"
"--no-color Disable colors in console output\n"
"--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes\n"
"--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 1000)\n"
"--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 1000)\n"
"--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"
"--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",
p2pool::VERSION,
p2pool::log::MAX_GLOBAL_LOG_LEVEL,
p2pool::DEFAULT_P2P_PORT,
p2pool::DEFAULT_P2P_PORT_MINI,
#ifdef _WIN32
"p2pool.exe"
#else
@@ -54,13 +68,13 @@ void memory_tracking_stop();
int main(int argc, char* argv[])
{
if (argc == 1) {
usage();
p2pool_usage();
return 0;
}
for (int i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "/help") || !strcmp(argv[i], "-h") || !strcmp(argv[i], "/h")) {
usage();
p2pool_usage();
return 0;
}
}
@@ -68,10 +82,14 @@ int main(int argc, char* argv[])
int result;
memory_tracking_start();
p2pool::init_crypto_cache();
{
p2pool::p2pool pool(argc, argv);
result = pool.run();
}
p2pool::destroy_crypto_cache();
memory_tracking_stop();
return result;
+41 -11
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -120,9 +120,9 @@ FORCEINLINE static void remove_allocation(void* p)
__debugbreak();
}
FORCEINLINE static void* allocate_noexcept(size_t n) noexcept
void* malloc_hook(size_t n) noexcept
{
void* p = malloc(n + sizeof(TrackedAllocation));
void* p = malloc(n);
if (p) {
add_alocation(p, n);
}
@@ -131,25 +131,46 @@ FORCEINLINE static void* allocate_noexcept(size_t n) noexcept
FORCEINLINE static void* allocate(size_t n)
{
void* p = allocate_noexcept(n);
void* p = malloc_hook(n);
if (!p) {
throw std::bad_alloc();
}
return p;
}
FORCEINLINE static void deallocate(void* p)
void free_hook(void* p) noexcept
{
remove_allocation(p);
free(p);
}
void* realloc_hook(void* ptr, size_t size) noexcept
{
remove_allocation(ptr);
void* p = realloc(ptr, size);
if (p) {
add_alocation(p, size);
}
return p;
}
void* calloc_hook(size_t count, size_t size) noexcept
{
void* p = calloc(count, size);
if (p) {
add_alocation(p, size);
}
return p;
}
} // p2pool
void memory_tracking_start()
{
using namespace p2pool;
uv_replace_allocator(malloc_hook, realloc_hook, calloc_hook, free_hook);
uv_mutex_init_checked(&allocation_lock);
track_memory = true;
}
@@ -207,14 +228,23 @@ void memory_tracking_stop()
NOINLINE void* operator new(size_t n) { return p2pool::allocate(n); }
NOINLINE void* operator new[](size_t n) { return p2pool::allocate(n); }
NOINLINE void* operator new(size_t n, const std::nothrow_t&) noexcept { return p2pool::allocate_noexcept(n); }
NOINLINE void* operator new[](size_t n, const std::nothrow_t&) noexcept { return p2pool::allocate_noexcept(n); }
NOINLINE void operator delete(void* p) noexcept { p2pool::deallocate(p); }
NOINLINE void operator delete[](void* p) noexcept { p2pool::deallocate(p); }
NOINLINE void operator delete(void* p, size_t) noexcept { p2pool::deallocate(p); }
NOINLINE void operator delete[](void* p, size_t) noexcept { p2pool::deallocate(p); }
NOINLINE void* operator new(size_t n, const std::nothrow_t&) noexcept { return p2pool::malloc_hook(n); }
NOINLINE void* operator new[](size_t n, const std::nothrow_t&) noexcept { return p2pool::malloc_hook(n); }
NOINLINE void operator delete(void* p) noexcept { p2pool::free_hook(p); }
NOINLINE void operator delete[](void* p) noexcept { p2pool::free_hook(p); }
NOINLINE void operator delete(void* p, size_t) noexcept { p2pool::free_hook(p); }
NOINLINE void operator delete[](void* p, size_t) noexcept { p2pool::free_hook(p); }
#else
void memory_tracking_start() {}
void memory_tracking_stop() {}
namespace p2pool {
void* malloc_hook(size_t n) noexcept { return malloc(n); }
void* realloc_hook(void* ptr, size_t size) noexcept { return realloc(ptr, size); }
void* calloc_hook(size_t count, size_t size) noexcept { return calloc(count, size); }
void free_hook(void* p) noexcept { free(p); }
}
#endif
+2 -2
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -44,7 +44,7 @@ void Mempool::add(const TxMempoolData& tx)
void Mempool::swap(std::vector<TxMempoolData>& transactions)
{
const time_t cur_time = time(nullptr);
const uint64_t cur_time = seconds_since_epoch();
WriteLock lock(m_lock);
+2 -3
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,7 +18,6 @@
#pragma once
#include "uv_util.h"
#include <unordered_map>
namespace p2pool {
@@ -35,7 +34,7 @@ public:
public:
mutable uv_rwlock_t m_lock;
std::unordered_map<hash, TxMempoolData> m_transactions;
unordered_map<hash, TxMempoolData> m_transactions;
};
} // namespace p2pool
+224
View File
@@ -0,0 +1,224 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include "miner.h"
#include "p2pool.h"
#include "stratum_server.h"
#include "block_template.h"
#include "pow_hash.h"
#include "randomx.h"
#include "params.h"
#include "p2pool_api.h"
#include <thread>
static constexpr char log_category_prefix[] = "Miner ";
using namespace std::chrono;
namespace p2pool {
Miner::Miner(p2pool* pool, uint32_t threads)
: m_pool(pool)
, m_threads(threads)
, m_stopped(false)
, m_startTimestamp(high_resolution_clock::now())
, m_nonce(0)
, m_nonceTimestamp(m_startTimestamp)
, m_extraNonce(0xF19E3779U)
, m_totalHashes(0)
, m_sharesFound(0)
, m_job{}
, m_jobIndex(0)
{
on_block(m_pool->block_template());
m_minerThreads.reserve(threads);
for (uint32_t i = 0; i < threads; ++i) {
WorkerData* data = new WorkerData{ this, i + 1, threads, {} };
const int err = uv_thread_create(&data->m_worker, run, data);
if (err) {
LOGERR(1, "failed to start worker thread " << data->m_index << '/' << threads << ", error " << uv_err_name(err));
delete data;
continue;
}
m_minerThreads.push_back(data);
}
}
Miner::~Miner()
{
m_stopped = true;
for (WorkerData* data : m_minerThreads) {
uv_thread_join(&data->m_worker);
delete data;
}
}
void Miner::print_status()
{
const uint32_t hash_count = 0 - m_nonce.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;
LOGINFO(0, "status" <<
"\nThreads = " << m_threads <<
"\nHashrate = " << log::Hashrate(hr)
);
}
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_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
const uint32_t hash_count = 0 - m_nonce.exchange(0);
m_jobIndex = next_index;
const auto cur_ts = high_resolution_clock::now();
const double dt = static_cast<double>(duration_cast<nanoseconds>(cur_ts - m_nonceTimestamp).count()) / 1e9;
m_nonceTimestamp = cur_ts;
m_totalHashes += hash_count;
if (m_pool->api() && m_pool->params().m_localStats) {
m_pool->api()->set(p2pool_api::Category::LOCAL, "miner",
[cur_ts, hash_count, dt, this](log::Stream& s)
{
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
const double time_running = static_cast<double>(duration_cast<milliseconds>(cur_ts - m_startTimestamp).count()) / 1e3;
s << "{\"current_hashrate\":" << hr
<< ",\"total_hashes\":" << m_totalHashes
<< ",\"time_running\":" << time_running
<< ",\"shares_found\":" << m_sharesFound.load()
<< ",\"threads\":" << m_threads
<< "}";
});
}
}
void Miner::run(void* data)
{
WorkerData* d = static_cast<WorkerData*>(data);
LOGINFO(1, "worker thread " << d->m_index << '/' << d->m_count << " started");
make_thread_background();
d->m_miner->run(d);
LOGINFO(1, "worker thread " << d->m_index << '/' << d->m_count << " stopped");
}
void Miner::run(WorkerData* data)
{
RandomX_Hasher_Base* hasher = m_pool->hasher();
randomx_cache* cache = hasher->cache();
randomx_dataset* dataset = hasher->dataset();
if (!cache && !dataset) {
LOGERR(1, "worker thread " << data->m_index << '/' << data->m_count << ": RandomX cache and dataset are not ready");
return;
}
randomx_flags flags = randomx_get_flags();
if (dataset) {
flags |= RANDOMX_FLAG_FULL_MEM;
}
randomx_vm* vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, dataset ? nullptr : cache, dataset);
if (!vm) {
LOGWARN(1, "couldn't allocate RandomX VM using large pages");
vm = randomx_create_vm(flags, dataset ? nullptr : cache, dataset);
if (!vm) {
LOGERR(1, "couldn't allocate RandomX VM");
return;
}
}
uint32_t index = 0;
Job job[2];
uint32_t seed_counter = 0;
bool first = true;
Miner* miner = data->m_miner;
while (!m_stopped) {
if (hasher->seed_counter() != seed_counter) {
LOGINFO(5, "worker thread " << data->m_index << '/' << data->m_count << " paused (waiting for RandomX cache/dataset update)");
hasher->sync_wait();
seed_counter = hasher->seed_counter();
if (flags & RANDOMX_FLAG_FULL_MEM) {
dataset = hasher->dataset();
randomx_vm_set_dataset(vm, dataset);
}
else {
cache = hasher->cache();
randomx_vm_set_cache(vm, cache);
}
LOGINFO(5, "worker thread " << data->m_index << '/' << data->m_count << " resumed");
}
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);
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);
hash h;
randomx_calculate_hash_next(vm, job[index].m_blob, job[index].m_blobSize, &h);
if (j.m_diff.check_pow(h)) {
LOGINFO(0, log::Green() << "worker thread " << data->m_index << '/' << data->m_count << " found a mainchain block, submitting it");
m_pool->submit_block_async(j.m_templateId, j.m_nonce, j.m_extraNonce);
m_pool->block_template().update_tx_keys();
}
if (j.m_sidechainDiff.check_pow(h)) {
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << j.m_height << ", diff " << j.m_sidechainDiff << ", worker thread " << data->m_index << '/' << data->m_count);
m_pool->submit_sidechain_block(j.m_templateId, j.m_nonce, j.m_extraNonce);
++m_sharesFound;
}
std::this_thread::yield();
}
randomx_destroy_vm(vm);
}
void Miner::Job::set_nonce(uint32_t nonce, uint32_t extra_nonce)
{
m_nonce = nonce;
m_extraNonce = extra_nonce;
uint8_t* p = m_blob + m_nonceOffset;
p[0] = static_cast<uint8_t>(nonce);
p[1] = static_cast<uint8_t>(nonce >> 8);
p[2] = static_cast<uint8_t>(nonce >> 16);
p[3] = static_cast<uint8_t>(nonce >> 24);
}
} // namespace p2pool
+83
View File
@@ -0,0 +1,83 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "uv_util.h"
#include <chrono>
namespace p2pool {
class p2pool;
class BlockTemplate;
class Miner
{
public:
Miner(p2pool* pool, uint32_t threads);
~Miner();
void print_status();
void on_block(const BlockTemplate& block);
private:
static void run(void* data);
p2pool* m_pool;
uint32_t m_threads;
struct WorkerData
{
Miner* m_miner;
uint32_t m_index;
uint32_t m_count;
uv_thread_t m_worker;
};
std::vector<WorkerData*> m_minerThreads;
volatile bool m_stopped;
std::chrono::high_resolution_clock::time_point m_startTimestamp;
std::atomic<uint32_t> m_nonce;
std::chrono::high_resolution_clock::time_point m_nonceTimestamp;
const uint32_t m_extraNonce;
uint64_t m_totalHashes;
std::atomic<uint32_t> m_sharesFound;
struct Job
{
uint8_t m_blob[128] = {};
uint32_t m_blobSize = 0;
uint32_t m_templateId = 0;
difficulty_type m_diff = {};
difficulty_type m_sidechainDiff = {};
uint64_t m_height = 0;
size_t m_nonceOffset = 0;
uint32_t m_nonce = 0;
uint32_t m_extraNonce = 0;
void set_nonce(uint32_t nonce, uint32_t extra_nonce);
};
Job m_job[2];
volatile uint32_t m_jobIndex;
void run(WorkerData* data);
};
} // namespace p2pool
+450 -101
View File
File diff suppressed because it is too large Load Diff
+37 -13
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,8 +18,6 @@
#pragma once
#include "tcp_server.h"
#include <random>
#include <unordered_map>
namespace p2pool {
@@ -30,6 +28,7 @@ class BlockCache;
static constexpr size_t P2P_BUF_SIZE = 128 * 1024;
static constexpr size_t PEER_LIST_RESPONSE_MAX_PEERS = 16;
static constexpr int DEFAULT_P2P_PORT = 37889;
static constexpr int DEFAULT_P2P_PORT_MINI = 37888;
class P2PServer : public TCPServer<P2P_BUF_SIZE, P2P_BUF_SIZE>
{
@@ -49,6 +48,7 @@ public:
~P2PServer();
void add_cached_block(const PoolBlock& block);
void clear_cached_blocks();
void store_in_cache(const PoolBlock& block);
void connect_to_peers(const std::string& peer_list);
@@ -108,13 +108,22 @@ public:
bool m_handshakeComplete;
bool m_handshakeInvalid;
int m_listenPort;
time_t m_nextPeerListRequest;
time_t m_lastAlive;
time_t m_lastBroadcastTimestamp;
time_t m_lastBlockrequestTimestamp;
uv_rwlock_t m_broadcastedHashesLock;
std::set<hash> m_broadcastedHashes;
uint32_t m_fastPeerListRequestCount;
uint64_t m_prevIncomingPeerListRequest;
uint64_t m_nextOutgoingPeerListRequest;
std::chrono::high_resolution_clock::time_point m_lastPeerListRequestTime;
int m_peerListPendingRequests;
int64_t m_pingTime;
int m_blockPendingRequests;
uint64_t m_lastAlive;
uint64_t m_lastBroadcastTimestamp;
uint64_t m_lastBlockrequestTimestamp;
hash m_broadcastedHashes[8];
std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 };
};
void broadcast(const PoolBlock& block);
@@ -122,15 +131,25 @@ public:
uint64_t get_peerId() const { return m_peerId; }
void print_status() override;
void show_peers();
size_t peer_list_size() const { return m_peerList.size(); }
uint32_t max_outgoing_peers() const { return m_maxOutgoingPeers; }
uint32_t max_incoming_peers() const { return m_maxIncomingPeers; }
void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 1000U); }
void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 1000U); }
private:
p2pool* m_pool;
BlockCache* m_cache;
bool m_cacheLoaded;
std::string m_initialPeerList;
uint32_t m_maxOutgoingPeers;
uint32_t m_maxIncomingPeers;
uv_rwlock_t m_cachedBlocksLock;
std::unordered_map<hash, PoolBlock*> m_cachedBlocks;
unordered_map<hash, PoolBlock*>* m_cachedBlocks;
private:
static void on_timer(uv_timer_t* timer) { reinterpret_cast<P2PServer*>(timer->data)->on_timer(); }
@@ -138,23 +157,26 @@ private:
void flush_cache();
void download_missing_blocks();
void check_zmq();
void update_peer_connections();
void update_peer_list();
void save_peer_list_async();
void save_peer_list();
void load_peer_list();
void load_monerod_peer_list();
void update_peer_in_list(bool is_v6, const raw_ip& ip, int port);
void remove_peer_from_list(P2PClient* client);
void remove_peer_from_list(const raw_ip& ip);
uv_mutex_t m_rngLock;
std::random_device m_rd;
std::mt19937_64 m_rng;
uv_mutex_t m_blockLock;
PoolBlock* m_block;
uv_timer_t m_timer;
uint64_t m_timerCounter;
uint64_t m_timerInterval;
uint64_t m_peerId;
@@ -166,10 +188,12 @@ private:
raw_ip m_addr;
int m_port;
uint32_t m_numFailedConnections;
uint64_t m_lastSeen;
};
std::vector<Peer> m_peerList;
time_t m_peerListLastSaved;
std::vector<Peer> m_peerListMonero;
uint64_t m_peerListLastSaved;
struct Broadcast
{
@@ -183,7 +207,7 @@ private:
std::vector<Broadcast*> m_broadcastQueue;
uv_mutex_t m_missingBlockRequestsLock;
std::set<std::pair<uint64_t, uint64_t>> m_missingBlockRequests;
unordered_set<std::pair<uint64_t, uint64_t>> m_missingBlockRequests;
static void on_broadcast(uv_async_t* handle) { reinterpret_cast<P2PServer*>(handle->data)->on_broadcast(); }
void on_broadcast();
+171 -36
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,10 +27,14 @@
#include "side_chain.h"
#include "stratum_server.h"
#include "p2p_server.h"
#ifdef WITH_RANDOMX
#include "miner.h"
#endif
#include "params.h"
#include "console_commands.h"
#include "crypto.h"
#include "p2pool_api.h"
#include "pool_block.h"
#include <thread>
#include <fstream>
@@ -49,7 +53,11 @@ p2pool::p2pool(int argc, char* argv[])
, m_params(new Params(argc, argv))
, m_updateSeed(true)
, m_submitBlockData{}
, m_zmqLastActive(0)
, m_startTime(seconds_since_epoch())
{
LOGINFO(1, log::LightCyan() << VERSION);
if (!m_params->m_wallet.valid()) {
LOGERR(1, "Invalid wallet address. Try \"p2pool --help\".");
panic();
@@ -103,10 +111,36 @@ p2pool::p2pool(int argc, char* argv[])
uv_mutex_init_checked(&m_foundBlocksLock);
uv_mutex_init_checked(&m_submitBlockDataLock);
m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath);
m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath, m_params->m_localStats);
if (m_params->m_localStats && !m_api) {
LOGERR(1, "--local-api and --stratum-api command line parameters can't be used without --data-api");
panic();
}
m_sideChain = new SideChain(this, type, m_params->m_mini ? "mini" : nullptr);
if (m_params->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];
log::Stream s(buf);
s << "[::]:" << p2p_port << ",0.0.0.0:" << p2p_port << '\0';
m_params->m_p2pAddresses = buf;
}
#ifdef WITH_RANDOMX
if (m_params->m_disableRandomX) {
m_hasher = new RandomX_Hasher_RPC(this);
}
else {
m_hasher = new RandomX_Hasher(this);
}
#else
m_hasher = new RandomX_Hasher_RPC(this);
#endif
m_sideChain = new SideChain(this, type);
m_hasher = new RandomX_Hasher(this);
m_blockTemplate = new BlockTemplate(this);
m_mempool = new Mempool();
m_consoleCommands = new ConsoleCommands(this);
@@ -127,9 +161,9 @@ p2pool::~p2pool()
delete m_consoleCommands;
}
bool p2pool::calculate_hash(const void* data, size_t size, const hash& seed, hash& result)
bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result)
{
return m_hasher->calculate(data, size, seed, result);
return m_hasher->calculate(data, size, height, seed, result);
}
uint64_t p2pool::get_seed_height(uint64_t height)
@@ -171,6 +205,8 @@ void p2pool::handle_tx(TxMempoolData& tx)
#if TEST_MEMPOOL_PICKING_ALGORITHM
m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet);
#endif
m_zmqLastActive = seconds_since_epoch();
}
void p2pool::handle_miner_data(MinerData& data)
@@ -197,10 +233,12 @@ void p2pool::handle_miner_data(MinerData& data)
c.reward = 0;
m_mainchainByHash[c.id] = c;
cleanup_mainchain_data(data.height);
}
data.tx_backlog.clear();
data.time_received = std::chrono::system_clock::now();
data.time_received = std::chrono::high_resolution_clock::now();
m_minerData = data;
m_updateSeed = true;
update_median_timestamp();
@@ -218,12 +256,49 @@ void p2pool::handle_miner_data(MinerData& data)
"\n---------------------------------------------------------------------------------------------------------------"
);
if (!is_main_thread) {
if (!is_main_thread()) {
update_block_template_async();
}
else {
update_block_template();
}
m_zmqLastActive = seconds_since_epoch();
if (m_serversStarted.load()) {
std::vector<uint64_t> missing_heights;
{
WriteLock lock(m_mainchainLock);
for (uint64_t h = data.height; h && (h + BLOCK_HEADERS_REQUIRED > data.height); --h) {
if (m_mainchainByHeight.find(h) == m_mainchainByHeight.end()) {
LOGWARN(3, "Mainchain data for height " << h << " is missing, requesting it from monerod again");
missing_heights.push_back(h);
}
}
}
for (uint64_t h : missing_heights) {
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf);
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << h << "}}\0";
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
[this, h](const char* data, size_t size)
{
ChainMain block;
if (!parse_block_header(data, size, block)) {
LOGERR(1, "couldn't download block header for height " << h);
}
},
[h](const char* data, size_t size)
{
if (size > 0) {
LOGERR(1, "couldn't download block header for height " << h << ", error " << log::const_buf(data, size));
}
});
}
}
}
const char* BLOCK_FOUND = "\n\
@@ -276,8 +351,13 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
", reward = " << log::Gray() << log::XMRAmount(data.reward));
if (!sidechain_id.empty()) {
if (side_chain().has_block(sidechain_id)) {
PoolBlock* block = side_chain().find_block(sidechain_id);
if (block) {
LOGINFO(0, log::LightGreen() << "BLOCK FOUND: main chain block at height " << data.height << " was mined by this p2pool" << BLOCK_FOUND);
const uint64_t payout = block->get_payout(params().m_wallet);
if (payout) {
LOGINFO(0, log::LightCyan() << "You received a payout of " << log::LightGreen() << log::XMRAmount(payout) << log::LightCyan() << " in block " << log::LightGreen() << data.height);
}
api_update_block_found(&data);
}
else {
@@ -286,6 +366,8 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
}
api_update_network_stats();
m_zmqLastActive = seconds_since_epoch();
}
void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
@@ -370,6 +452,10 @@ void p2pool::submit_block() const
request = "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"submit_block\",\"params\":[\"";
const uint32_t template_id = submit_data.template_id;
const uint32_t nonce = submit_data.nonce;
const uint32_t extra_nonce = submit_data.extra_nonce;
for (size_t i = 0; i < submit_data.blob.size(); ++i) {
char buf[16];
@@ -389,10 +475,6 @@ void p2pool::submit_block() const
}
request.append("\"]}");
const uint32_t template_id = submit_data.template_id;
const uint32_t nonce = submit_data.nonce;
const uint32_t extra_nonce = submit_data.extra_nonce;
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, request.c_str(),
[height, diff, template_id, nonce, extra_nonce, is_external](const char* data, size_t size)
{
@@ -523,8 +605,14 @@ void p2pool::download_block_headers(uint64_t current_height)
if (parse_block_headers_range(data, size) == BLOCK_HEADERS_REQUIRED) {
update_median_timestamp();
if (m_serversStarted.exchange(1) == 0) {
m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this);
m_stratumServer = new StratumServer(this);
m_p2pServer = new P2PServer(this);
#ifdef WITH_RANDOMX
if (m_params->m_minerThreads) {
start_mining(m_params->m_minerThreads);
}
#endif
api_update_network_stats();
}
}
@@ -591,22 +679,14 @@ void p2pool::update_median_timestamp()
void p2pool::stratum_on_block()
{
#if 0
uint8_t hashing_blob[128];
uint64_t height;
difficulty_type difficulty;
difficulty_type sidechain_difficulty;
hash seed_hash;
size_t nonce_offset;
uint32_t template_id;
m_blockTemplate->get_hashing_blob(0, hashing_blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset, template_id);
submit_block(template_id, 0, 0);
#else
#ifdef WITH_RANDOMX
if (m_miner) {
m_miner->on_block(*m_blockTemplate);
}
#endif
if (m_stratumServer) {
m_stratumServer->on_block(*m_blockTemplate);
}
#endif
}
void p2pool::get_info()
@@ -678,18 +758,22 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
const auto& result = doc["result"];
struct {
bool busy_syncing, mainnet, testnet, stagenet;
bool busy_syncing, synchronized, mainnet, testnet, stagenet;
} info;
if (!PARSE(result, info, busy_syncing) || !PARSE(result, info, mainnet) || !PARSE(result, info, testnet) || !PARSE(result, info, stagenet)) {
if (!PARSE(result, info, busy_syncing) ||
!PARSE(result, info, synchronized) ||
!PARSE(result, info, mainnet) ||
!PARSE(result, info, testnet) ||
!PARSE(result, info, stagenet)) {
LOGWARN(1, "get_info RPC response is invalid, trying again in 1 second");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_info();
return;
}
if (info.busy_syncing) {
LOGINFO(1, "monerod is busy syncing, trying again in 1 second");
if (info.busy_syncing || !info.synchronized) {
LOGINFO(1, "monerod is " << (info.busy_syncing ? "busy syncing" : "not synchronized") << ", trying again in 1 second");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_info();
return;
@@ -759,10 +843,14 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
return;
}
if (version < 0x30008) {
const uint64_t required = m_params->m_disableRandomX ? 0x30009 : 0x30008;
if (version < required) {
const uint64_t version_hi = version >> 16;
const uint64_t version_lo = version & 65535;
LOGERR(1, "monerod RPC v" << version_hi << '.' << version_lo << " is incompatible, update to RPC >= v3.8");
const uint64_t required_version_hi = required >> 16;
const uint64_t required_version_lo = required & 65535;
LOGERR(1, "monerod RPC v" << version_hi << '.' << version_lo << " is incompatible, update to RPC >= v" << required_version_hi << '.' << required_version_lo << " (Monero v0.17.3.0 or newer)");
panic();
}
@@ -961,7 +1049,7 @@ void p2pool::api_update_pool_stats()
uint64_t t;
const difficulty_type& diff = m_sideChain->difficulty();
const uint64_t hashrate = udiv128(diff.hi, diff.lo, m_sideChain->block_time(), &t);
const uint64_t miners = m_sideChain->miner_count();
const uint64_t miners = std::max<uint64_t>(m_sideChain->miner_count(), m_p2pServer ? m_p2pServer->peer_list_size() : 0U);
const difficulty_type total_hashes = m_sideChain->total_hashes();
time_t last_block_found_time = 0;
@@ -1024,7 +1112,7 @@ void p2pool::api_update_stats_mod()
s << last_block_found_hash << '\0';
memcpy(last_block_found_buf + 4, "...", 4);
const uint64_t miners = m_sideChain->miner_count();
const uint64_t miners = std::max<uint64_t>(m_sideChain->miner_count(), m_p2pServer ? m_p2pServer->peer_list_size() : 0U);
uint64_t t;
const difficulty_type& diff = m_sideChain->difficulty();
@@ -1041,7 +1129,7 @@ void p2pool::api_update_stats_mod()
m_api->set(p2pool_api::Category::GLOBAL, "stats_mod",
[&mainnet_tip, last_block_found_time, &last_block_found_buf, last_block_found_height, miners, hashrate, round_hashes, stratum_port](log::Stream& s)
{
s << "{\"config\":{\"ports\":[{\"port\":" << stratum_port << ",\"tls\":false}],\"fee\":0,\"minPaymentThreshold\":400000000},\"network\":{\"height\":"
s << "{\"config\":{\"ports\":[{\"port\":" << stratum_port << ",\"tls\":false}],\"fee\":0,\"minPaymentThreshold\":300000000},\"network\":{\"height\":"
<< mainnet_tip.height << "},\"pool\":{\"stats\":{\"lastBlockFound\":\""
<< last_block_found_time << "000\"},\"blocks\":[\""
<< static_cast<char*>(last_block_found_buf) << static_cast<char*>(last_block_found_buf) + HASH_SIZE * 2 - 4 << ':'
@@ -1053,8 +1141,35 @@ void p2pool::api_update_stats_mod()
});
}
void p2pool::cleanup_mainchain_data(uint64_t height)
{
// Expects m_mainchainLock to be already locked here
// Deletes everything older than 720 blocks, except for the 3 latest RandomX seed heights
constexpr uint64_t PRUNE_DISTANCE = BLOCK_HEADERS_REQUIRED;
const uint64_t seed_height = get_seed_height(height);
const std::array<uint64_t, 3> seed_heights{ seed_height, seed_height - SEEDHASH_EPOCH_BLOCKS, seed_height - SEEDHASH_EPOCH_BLOCKS * 2 };
for (auto it = m_mainchainByHeight.begin(); it != m_mainchainByHeight.end();) {
const uint64_t h = it->first;
if (h + PRUNE_DISTANCE >= height) {
break;
}
if (std::find(seed_heights.begin(), seed_heights.end(), h) == seed_heights.end()) {
m_mainchainByHash.erase(it->second.id);
it = m_mainchainByHeight.erase(it);
}
else {
++it;
}
}
}
void p2pool::api_update_block_found(const ChainMain* data)
{
clear_crypto_cache();
if (!m_api) {
return;
}
@@ -1114,6 +1229,23 @@ bool p2pool::get_difficulty_at_height(uint64_t height, difficulty_type& diff)
return true;
}
#ifdef WITH_RANDOMX
void p2pool::start_mining(uint32_t threads)
{
stop_mining();
m_miner = new Miner(this, threads);
}
void p2pool::stop_mining()
{
Miner* miner = m_miner;
if (miner) {
m_miner = nullptr;
delete miner;
}
}
#endif
static void on_signal(uv_signal_t* handle, int signum)
{
p2pool* pool = reinterpret_cast<p2pool*>(handle->data);
@@ -1206,11 +1338,11 @@ int p2pool::run()
}
try {
ZMQReader z(m_params->m_host.c_str(), m_params->m_zmqPort, this);
get_info();
load_found_blocks();
const int rc = uv_run(uv_default_loop_checked(), UV_RUN_DEFAULT);
LOGINFO(1, "uv_run exited, result = " << rc);
delete m_ZMQReader;
}
catch (const std::exception& e) {
const char* s = e.what();
@@ -1222,6 +1354,9 @@ int p2pool::run()
bkg_jobs_tracker.wait();
#ifdef WITH_RANDOMX
delete m_miner;
#endif
delete m_stratumServer;
delete m_p2pServer;
+34 -10
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -19,19 +19,20 @@
#include "uv_util.h"
#include <map>
#include <unordered_map>
namespace p2pool {
struct Params;
class RandomX_Hasher;
class RandomX_Hasher_Base;
class BlockTemplate;
class Mempool;
class SideChain;
class StratumServer;
class P2PServer;
class Miner;
class ConsoleCommands;
class p2pool_api;
class ZMQReader;
class p2pool : public MinerCallbackHandler
{
@@ -49,13 +50,18 @@ public:
SideChain& side_chain() { return *m_sideChain; }
const MinerData& miner_data() const { return m_minerData; }
RandomX_Hasher* hasher() const { return m_hasher; }
bool calculate_hash(const void* data, size_t size, const hash& seed, hash& result);
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);
static uint64_t get_seed_height(uint64_t height);
bool get_seed(uint64_t height, hash& seed) const;
StratumServer* stratum_server() const { return m_stratumServer; }
P2PServer* p2p_server() const { return m_p2pServer; }
#ifdef WITH_RANDOMX
Miner* miner() const { return m_miner; }
#endif
virtual void handle_tx(TxMempoolData& tx) override;
virtual void handle_miner_data(MinerData& data) override;
@@ -76,6 +82,14 @@ public:
bool get_difficulty_at_height(uint64_t height, difficulty_type& diff);
#ifdef WITH_RANDOMX
void start_mining(uint32_t threads);
void stop_mining();
#endif
uint64_t zmq_last_active() const { return m_zmqLastActive; }
uint64_t start_time() const { return m_startTime; }
private:
p2pool(const p2pool&) = delete;
p2pool(p2pool&&) = delete;
@@ -92,7 +106,7 @@ private:
p2pool_api* m_api;
SideChain* m_sideChain;
RandomX_Hasher* m_hasher;
RandomX_Hasher_Base* m_hasher;
BlockTemplate* m_blockTemplate;
MinerData m_minerData;
bool m_updateSeed;
@@ -100,7 +114,7 @@ private:
mutable uv_rwlock_t m_mainchainLock;
std::map<uint64_t, ChainMain> m_mainchainByHeight;
std::unordered_map<hash, ChainMain> m_mainchainByHash;
unordered_map<hash, ChainMain> m_mainchainByHash;
enum { TIMESTAMP_WINDOW = 60 };
bool get_timestamps(uint64_t (&timestamps)[TIMESTAMP_WINDOW]) const;
@@ -125,6 +139,8 @@ private:
void api_update_pool_stats();
void api_update_stats_mod();
void cleanup_mainchain_data(uint64_t height);
struct FoundBlock
{
FORCEINLINE FoundBlock(time_t _t, uint64_t _h, const hash& _id, const difficulty_type& _block_diff, const difficulty_type& _total_hashes)
@@ -148,14 +164,17 @@ private:
std::atomic<uint32_t> m_serversStarted{ 0 };
StratumServer* m_stratumServer = nullptr;
P2PServer* m_p2pServer = nullptr;
#ifdef WITH_RANDOMX
Miner* m_miner = nullptr;
#endif
ConsoleCommands* m_consoleCommands;
struct SubmitBlockData
{
uint32_t template_id;
uint32_t nonce;
uint32_t extra_nonce;
uint32_t template_id = 0;
uint32_t nonce = 0;
uint32_t extra_nonce = 0;
std::vector<uint8_t> blob;
};
@@ -165,6 +184,11 @@ private:
uv_async_t m_submitBlockAsync;
uv_async_t m_blockTemplateAsync;
uv_async_t m_stopAsync;
uint64_t m_zmqLastActive;
uint64_t m_startTime;
ZMQReader* m_ZMQReader = nullptr;
};
} // namespace p2pool
+9 -3
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,7 +28,7 @@ static constexpr char log_category_prefix[] = "P2Pool API ";
namespace p2pool {
p2pool_api::p2pool_api(const std::string& api_path) : m_apiPath(api_path)
p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats): m_apiPath(api_path)
{
if (m_apiPath.empty()) {
LOGERR(1, "api path is empty");
@@ -60,9 +60,14 @@ p2pool_api::p2pool_api(const std::string& api_path) : m_apiPath(api_path)
m_networkPath = m_apiPath + "network/";
m_poolPath = m_apiPath + "pool/";
m_localPath = m_apiPath + "local/";
create_dir(m_networkPath);
create_dir(m_poolPath);
if (local_stats) {
create_dir(m_localPath);
}
}
p2pool_api::~p2pool_api()
@@ -109,6 +114,7 @@ void p2pool_api::dump_to_file_async_internal(const Category& category, const cha
case Category::GLOBAL: path = m_apiPath + filename; break;
case Category::NETWORK: path = m_networkPath + filename; break;
case Category::POOL: path = m_poolPath + filename; break;
case Category::LOCAL: path = m_localPath + filename; break;
}
{
@@ -121,7 +127,7 @@ void p2pool_api::dump_to_file_async_internal(const Category& category, const cha
void p2pool_api::dump_to_file()
{
std::unordered_map<std::string, std::vector<char>> data;
unordered_map<std::string, std::vector<char>> data;
{
MutexLock lock(m_dumpDataLock);
data = std::move(m_dumpData);
+5 -4
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,20 +18,20 @@
#pragma once
#include "uv_util.h"
#include <unordered_map>
namespace p2pool {
class p2pool_api
{
public:
explicit p2pool_api(const std::string& api_path);
p2pool_api(const std::string& api_path, const bool local_stats);
~p2pool_api();
enum class Category {
GLOBAL,
NETWORK,
POOL,
LOCAL,
};
void on_stop();
@@ -80,9 +80,10 @@ private:
std::string m_apiPath;
std::string m_networkPath;
std::string m_poolPath;
std::string m_localPath;
uv_mutex_t m_dumpDataLock;
std::unordered_map<std::string, std::vector<char>> m_dumpData;
unordered_map<std::string, std::vector<char>> m_dumpData;
uv_async_t m_dumpToFileAsync;
};
+65 -14
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -20,54 +20,115 @@
#include "stratum_server.h"
#include "p2p_server.h"
void p2pool_usage();
namespace p2pool {
Params::Params(int argc, char* argv[])
{
for (int i = 1; i < argc; ++i) {
bool ok = false;
if ((strcmp(argv[i], "--host") == 0) && (i + 1 < argc)) {
m_host = argv[++i];
ok = true;
}
if ((strcmp(argv[i], "--rpc-port") == 0) && (i + 1 < argc)) {
m_rpcPort = static_cast<uint32_t>(atoi(argv[++i]));
m_rpcPort = strtoul(argv[++i], nullptr, 10);
ok = true;
}
if ((strcmp(argv[i], "--zmq-port") == 0) && (i + 1 < argc)) {
m_zmqPort = static_cast<uint32_t>(atoi(argv[++i]));
m_zmqPort = strtoul(argv[++i], nullptr, 10);
ok = true;
}
if (strcmp(argv[i], "--light-mode") == 0) {
m_lightMode = true;
ok = true;
}
if ((strcmp(argv[i], "--wallet") == 0) && (i + 1 < argc)) {
m_wallet.decode(argv[++i]);
ok = true;
}
if ((strcmp(argv[i], "--stratum") == 0) && (i + 1 < argc)) {
m_stratumAddresses = argv[++i];
ok = true;
}
if ((strcmp(argv[i], "--p2p") == 0) && (i + 1 < argc)) {
m_p2pAddresses = argv[++i];
ok = true;
}
if ((strcmp(argv[i], "--addpeers") == 0) && (i + 1 < argc)) {
m_p2pPeerList = argv[++i];
ok = true;
}
if ((strcmp(argv[i], "--loglevel") == 0) && (i + 1 < argc)) {
const int level = std::min(std::max(atoi(argv[++i]), 0), log::MAX_GLOBAL_LOG_LEVEL);
const int level = std::min(std::max<int>(strtol(argv[++i], nullptr, 10), 0), log::MAX_GLOBAL_LOG_LEVEL);
log::GLOBAL_LOG_LEVEL = level;
ok = true;
}
if ((strcmp(argv[i], "--config") == 0) && (i + 1 < argc)) {
m_config = argv[++i];
ok = true;
}
if ((strcmp(argv[i], "--data-api") == 0) && (i + 1 < argc)) {
m_apiPath = argv[++i];
ok = true;
}
if ((strcmp(argv[i], "--local-api") == 0) || (strcmp(argv[i], "--stratum-api") == 0)) {
m_localStats = true;
ok = true;
}
if (strcmp(argv[i], "--no-cache") == 0) {
m_blockCache = false;
ok = true;
}
if (strcmp(argv[i], "--no-color") == 0) {
log::CONSOLE_COLORS = false;
ok = true;
}
if (strcmp(argv[i], "--no-randomx") == 0) {
m_disableRandomX = true;
ok = true;
}
if ((strcmp(argv[i], "--out-peers") == 0) && (i + 1 < argc)) {
m_maxOutgoingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 1000UL);
ok = true;
}
if ((strcmp(argv[i], "--in-peers") == 0) && (i + 1 < argc)) {
m_maxIncomingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 1000UL);
ok = true;
}
if ((strcmp(argv[i], "--start-mining") == 0) && (i + 1 < argc)) {
m_minerThreads = std::min(std::max(strtoul(argv[++i], nullptr, 10), 1UL), 64UL);
ok = true;
}
if (strcmp(argv[i], "--mini") == 0) {
m_mini = true;
ok = true;
}
if (!ok) {
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
p2pool_usage();
panic();
}
}
@@ -80,16 +141,6 @@ Params::Params(int argc, char* argv[])
m_stratumAddresses = buf;
}
if (m_p2pAddresses.empty()) {
const int p2p_port = DEFAULT_P2P_PORT;
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf);
s << "[::]:" << p2p_port << ",0.0.0.0:" << p2p_port << '\0';
m_p2pAddresses = buf;
}
}
bool Params::ok() const
+12 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,6 +37,17 @@ struct Params
std::string m_p2pPeerList;
std::string m_config;
std::string m_apiPath;
bool m_localStats = false;
bool m_blockCache = true;
#ifdef WITH_RANDOMX
bool m_disableRandomX = false;
#else
bool m_disableRandomX = true;
#endif
uint32_t m_maxOutgoingPeers = 10;
uint32_t m_maxIncomingPeers = 1000;
uint32_t m_minerThreads = 0;
bool m_mini = false;
};
} // namespace p2pool
+22 -12
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,14 +53,13 @@ PoolBlock::PoolBlock()
, m_invalid(false)
, m_broadcasted(false)
, m_wantBroadcast(false)
, m_localTimestamp(time(nullptr))
, m_localTimestamp(seconds_since_epoch())
{
uv_mutex_init_checked(&m_lock);
m_mainChainData.reserve(48 * 1024);
m_outputs.reserve(2048);
m_transactions.reserve(256);
m_tmpInts.reserve(m_transactions.capacity() * HASH_SIZE);
m_sideChainData.reserve(512);
m_uncles.reserve(8);
m_tmpTxExtra.reserve(80);
@@ -110,14 +109,13 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_cumulativeDifficulty = b.m_cumulativeDifficulty;
m_sidechainId = b.m_sidechainId;
m_tmpTxExtra = b.m_tmpTxExtra;
m_tmpInts = b.m_tmpInts;
m_depth = b.m_depth;
m_verified = b.m_verified;
m_invalid = b.m_invalid;
m_broadcasted = b.m_broadcasted;
m_wantBroadcast = b.m_wantBroadcast;
m_localTimestamp = time(nullptr);
m_localTimestamp = seconds_since_epoch();
if (lock_result == 0) {
uv_mutex_unlock(&b.m_lock);
@@ -227,7 +225,7 @@ void PoolBlock::serialize_sidechain_data()
writeVarint(m_cumulativeDifficulty.hi, m_sideChainData);
}
bool PoolBlock::get_pow_hash(RandomX_Hasher* hasher, const hash& seed_hash, hash& pow_hash)
bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash)
{
alignas(8) uint8_t hashes[HASH_SIZE * 3];
@@ -276,28 +274,40 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher* hasher, const hash& seed_hash, hash
cnt >>= 1;
m_tmpInts.resize(cnt * HASH_SIZE);
memcpy(m_tmpInts.data(), h, (cnt * 2 - count) * HASH_SIZE);
std::vector<uint8_t> tmp_ints(cnt * HASH_SIZE);
memcpy(tmp_ints.data(), h, (cnt * 2 - count) * HASH_SIZE);
for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, m_tmpInts.data() + j * HASH_SIZE, HASH_SIZE);
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, tmp_ints.data() + j * HASH_SIZE, HASH_SIZE);
}
while (cnt > 2) {
cnt >>= 1;
for (i = 0, j = 0; j < cnt; i += 2, ++j) {
keccak(m_tmpInts.data() + i * HASH_SIZE, HASH_SIZE * 2, m_tmpInts.data() + j * HASH_SIZE, HASH_SIZE);
keccak(tmp_ints.data() + i * HASH_SIZE, HASH_SIZE * 2, tmp_ints.data() + j * HASH_SIZE, HASH_SIZE);
}
}
keccak(m_tmpInts.data(), HASH_SIZE * 2, blob + blob_size, HASH_SIZE);
keccak(tmp_ints.data(), HASH_SIZE * 2, blob + blob_size, HASH_SIZE);
}
}
blob_size += HASH_SIZE;
writeVarint(count, [&blob, &blob_size](uint8_t b) { blob[blob_size++] = b; });
return hasher->calculate(blob, blob_size, seed_hash, pow_hash);
return hasher->calculate(blob, blob_size, height, seed_hash, pow_hash);
}
uint64_t PoolBlock::get_payout(const Wallet& w) const
{
for (size_t i = 0, n = m_outputs.size(); i < n; ++i) {
hash eph_public_key;
if ((w.get_eph_public_key(m_txkeySec, i, eph_public_key)) && (eph_public_key == m_outputs[i].m_ephPublicKey)) {
return m_outputs[i].m_reward;
}
}
return 0;
}
} // namespace p2pool
+6 -5
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,7 +28,7 @@
namespace p2pool {
class RandomX_Hasher;
class RandomX_Hasher_Base;
class SideChain;
/*
@@ -121,7 +121,6 @@ struct PoolBlock
// Just temporary stuff, not a part of the block
std::vector<uint8_t> m_tmpTxExtra;
std::vector<uint8_t> m_tmpInts;
uint64_t m_depth;
@@ -131,13 +130,15 @@ struct PoolBlock
bool m_broadcasted;
bool m_wantBroadcast;
time_t m_localTimestamp;
uint64_t m_localTimestamp;
void serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash);
void serialize_sidechain_data();
int deserialize(const uint8_t* data, size_t size, SideChain& sidechain);
bool get_pow_hash(RandomX_Hasher* hasher, const hash& seed_hash, hash& pow_hash);
bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash);
uint64_t get_payout(const Wallet& w) const;
};
} // namespace p2pool
+3 -4
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -187,7 +187,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
READ_VARINT(m_extraNonceSize);
// Sanity check
if ((m_extraNonceSize < EXTRA_NONCE_SIZE) || (m_extraNonceSize > EXTRA_NONCE_SIZE + 10)) return __LINE__;
if ((m_extraNonceSize < EXTRA_NONCE_SIZE) || (m_extraNonceSize > EXTRA_NONCE_MAX_SIZE)) return __LINE__;
const int extra_nonce_offset = static_cast<int>((data - data_begin) + outputs_blob_size_diff);
READ_BUF(&m_extraNonce, EXTRA_NONCE_SIZE);
@@ -341,7 +341,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
// Defaults for off-chain variables
m_tmpTxExtra.clear();
m_tmpInts.clear();
m_depth = 0;
@@ -351,7 +350,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
m_broadcasted = false;
m_wantBroadcast = false;
m_localTimestamp = time(nullptr);
m_localTimestamp = seconds_since_epoch();
return 0;
}
+139 -14
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -19,22 +19,29 @@
#include "pow_hash.h"
#include "p2pool.h"
#include "params.h"
#ifdef WITH_RANDOMX
#include "randomx.h"
#include "configuration.h"
#include "virtual_machine.hpp"
#endif
#include "json_rpc_request.h"
#include "json_parsers.h"
#include <rapidjson/document.h>
#include <thread>
static constexpr char log_category_prefix[] = "RandomX_Hasher ";
namespace p2pool {
#ifdef WITH_RANDOMX
RandomX_Hasher::RandomX_Hasher(p2pool* pool)
: m_pool(pool)
, m_cache{}
, m_dataset(nullptr)
, m_seed{}
, m_index(0)
, m_setSeedCounter(0)
, m_seedCounter(0)
, m_oldSeedCounter(0)
{
uint64_t memory_allocated = 0;
@@ -54,7 +61,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
const randomx_flags flags = randomx_get_flags();
for (size_t i = 0; i < array_size(m_cache); ++i) {
for (size_t i = 0; i < array_size(&RandomX_Hasher::m_cache); ++i) {
m_cache[i] = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
if (!m_cache[i]) {
LOGWARN(1, "couldn't allocate RandomX cache using large pages");
@@ -70,7 +77,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
uv_rwlock_init_checked(&m_datasetLock);
uv_rwlock_init_checked(&m_cacheLock);
for (size_t i = 0; i < array_size(m_vm); ++i) {
for (size_t i = 0; i < array_size(&RandomX_Hasher::m_vm); ++i) {
uv_mutex_init_checked(&m_vm[i].mutex);
m_vm[i].vm = nullptr;
}
@@ -91,7 +98,7 @@ RandomX_Hasher::~RandomX_Hasher()
uv_rwlock_destroy(&m_datasetLock);
uv_rwlock_destroy(&m_cacheLock);
for (size_t i = 0; i < array_size(m_vm); ++i) {
for (size_t i = 0; i < array_size(&RandomX_Hasher::m_vm); ++i) {
{
MutexLock lock(m_vm[i].mutex);
if (m_vm[i].vm) {
@@ -105,7 +112,7 @@ RandomX_Hasher::~RandomX_Hasher()
randomx_release_dataset(m_dataset);
}
for (size_t i = 0; i < array_size(m_cache); ++i) {
for (size_t i = 0; i < array_size(&RandomX_Hasher::m_cache); ++i) {
if (m_cache[i]) {
randomx_release_cache(m_cache[i]);
}
@@ -116,6 +123,10 @@ RandomX_Hasher::~RandomX_Hasher()
void RandomX_Hasher::set_seed_async(const hash& seed)
{
if (m_seed[m_index] == seed) {
return;
}
struct Work
{
p2pool* pool;
@@ -124,10 +135,7 @@ void RandomX_Hasher::set_seed_async(const hash& seed)
uv_work_t req;
};
Work* work = new Work{};
work->pool = m_pool;
work->hasher = this;
work->seed = seed;
Work* work = new Work{ m_pool, this, seed, {} };
work->req.data = work;
const int err = uv_queue_work(uv_default_loop_checked(), &work->req,
@@ -164,7 +172,7 @@ void RandomX_Hasher::set_seed(const hash& seed)
WriteLock lock(m_datasetLock);
uv_rwlock_wrlock(&m_cacheLock);
m_setSeedCounter.fetch_add(1);
m_seedCounter.fetch_add(1);
if (m_seed[m_index] == seed) {
uv_rwlock_wrunlock(&m_cacheLock);
@@ -216,6 +224,11 @@ void RandomX_Hasher::set_seed(const hash& seed)
numThreads /= 2;
}
// wait for set_old_seed() before initializing dataset
while (m_oldSeedCounter.load() == 0) {
std::this_thread::yield();
}
LOGINFO(1, log::LightCyan() << "running " << numThreads << " threads to update dataset");
ReadLock lock2(m_cacheLock);
@@ -266,8 +279,8 @@ void RandomX_Hasher::set_seed(const hash& seed)
void RandomX_Hasher::set_old_seed(const hash& seed)
{
// set_seed() must go first, wait for it
while (m_setSeedCounter.load() == 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
while (m_seedCounter.load() == 0) {
std::this_thread::yield();
}
LOGINFO(1, "old seed " << log::LightBlue() << seed);
@@ -275,6 +288,8 @@ void RandomX_Hasher::set_old_seed(const hash& seed)
{
WriteLock lock(m_cacheLock);
m_oldSeedCounter.fetch_add(1);
const uint32_t old_index = m_index ^ 1;
m_seed[old_index] = seed;
@@ -302,7 +317,13 @@ void RandomX_Hasher::set_old_seed(const hash& seed)
LOGINFO(1, log::LightCyan() << "old cache updated");
}
bool RandomX_Hasher::calculate(const void* data, size_t size, const hash& seed, hash& result)
void RandomX_Hasher::sync_wait()
{
ReadLock lock(m_datasetLock);
ReadLock lock2(m_cacheLock);
}
bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*/, const hash& seed, hash& result)
{
// First try to use the dataset if it's ready
if (uv_rwlock_tryrdlock(&m_datasetLock) == 0) {
@@ -347,5 +368,109 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, const hash& seed,
return false;
}
#endif
RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
: m_pool(pool)
, m_loopThread{}
{
int err = uv_loop_init(&m_loop);
if (err) {
LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
panic();
}
uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
uv_async_init(&m_loop, &m_kickTheLoopAsync, nullptr);
m_shutdownAsync.data = this;
uv_mutex_init_checked(&m_requestMutex);
uv_mutex_init_checked(&m_condMutex);
err = uv_cond_init(&m_cond);
if (err) {
LOGERR(1, "failed to create cond, error " << uv_err_name(err));
panic();
}
err = uv_thread_create(&m_loopThread, loop, this);
if (err) {
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
panic();
}
}
RandomX_Hasher_RPC::~RandomX_Hasher_RPC()
{
uv_async_send(&m_shutdownAsync);
uv_thread_join(&m_loopThread);
uv_mutex_destroy(&m_requestMutex);
uv_mutex_destroy(&m_condMutex);
uv_cond_destroy(&m_cond);
LOGINFO(1, "stopped");
}
void RandomX_Hasher_RPC::loop(void* data)
{
LOGINFO(1, "event loop started");
RandomX_Hasher_RPC* hasher = static_cast<RandomX_Hasher_RPC*>(data);
uv_run(&hasher->m_loop, UV_RUN_DEFAULT);
uv_loop_close(&hasher->m_loop);
LOGINFO(1, "event loop stopped");
}
bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t height, const hash& /*seed*/, hash& h)
{
MutexLock lock(m_requestMutex);
const uint8_t* data = reinterpret_cast<const uint8_t*>(data_ptr);
const uint8_t major_version = data[0];
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf);
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"calc_pow\",\"params\":{\"major_version\":" << major_version <<
",\"height\":" << height <<
",\"block_blob\":\"" << log::hex_buf(data, size) << '"' <<
",\"seed_hash\":\"\"}}";
volatile int result = 0;
volatile bool done = false;
JSONRPCRequest::call(m_pool->params().m_host.c_str(), m_pool->params().m_rpcPort, buf,
[&result, &h](const char* data, size_t size)
{
rapidjson::Document doc;
if (doc.Parse(data, size).HasParseError() || !parseValue(doc, "result", h)) {
LOGWARN(3, "RPC calc_pow: invalid JSON response (parse error)");
result = -1;
return;
}
result = 1;
},
[this, &result, &done](const char* data, size_t size)
{
if (size > 0) {
LOGWARN(3, "RPC calc_pow: server returned error " << log::const_buf(data, size));
result = -1;
}
MutexLock lock2(m_condMutex);
done = true;
uv_cond_signal(&m_cond);
}, &m_loop);
uv_async_send(&m_kickTheLoopAsync);
{
MutexLock lock2(m_condMutex);
while (!done) {
uv_cond_wait(&m_cond, &m_condMutex);
}
}
return result > 0;
}
} // namespace p2pool
+64 -9
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,25 +27,47 @@ namespace p2pool {
class p2pool;
class RandomX_Hasher
class RandomX_Hasher_Base
{
public:
virtual ~RandomX_Hasher_Base() {}
virtual void set_seed_async(const hash&) {}
virtual void set_old_seed(const hash&) {}
virtual randomx_cache* cache() const { return nullptr; }
virtual randomx_dataset* dataset() const { return nullptr; }
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;
};
#ifdef WITH_RANDOMX
class RandomX_Hasher : public RandomX_Hasher_Base
{
public:
explicit RandomX_Hasher(p2pool* pool);
~RandomX_Hasher();
void set_seed_async(const hash& seed);
void set_seed_async(const hash& seed) override;
void set_seed(const hash& seed);
void set_old_seed(const hash& seed);
void set_old_seed(const hash& seed) override;
bool calculate(const void* data, size_t size, const hash& seed, hash& result);
randomx_cache* cache() const override { return m_cache[m_index]; }
randomx_dataset* dataset() const override { return m_dataset; }
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;
private:
struct ThreadSafeVM
{
uv_mutex_t mutex;
randomx_vm* vm;
uv_mutex_t mutex{};
randomx_vm* vm = nullptr;
};
p2pool* m_pool;
@@ -62,12 +84,45 @@ private:
// 1: light VM for the previous seed
// 2: full dataset VM for the current seed
enum { FULL_DATASET_VM = 2 };
ThreadSafeVM m_vm[3];
ThreadSafeVM m_vm[3]{};
hash m_seed[2];
uint32_t m_index;
std::atomic<uint32_t> m_setSeedCounter;
std::atomic<uint32_t> m_seedCounter;
std::atomic<uint32_t> m_oldSeedCounter;
};
#endif
class RandomX_Hasher_RPC : public RandomX_Hasher_Base
{
public:
explicit RandomX_Hasher_RPC(p2pool* pool);
~RandomX_Hasher_RPC();
bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) override;
private:
static void loop(void* data);
p2pool* m_pool;
uv_mutex_t m_requestMutex;
uv_loop_t m_loop;
uv_thread_t m_loopThread;
uv_mutex_t m_condMutex;
uv_cond_t m_cond;
uv_async_t m_shutdownAsync;
uv_async_t m_kickTheLoopAsync;
static void on_shutdown(uv_async_t* async)
{
RandomX_Hasher_RPC* server = reinterpret_cast<RandomX_Hasher_RPC*>(async->data);
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_shutdownAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_kickTheLoopAsync), nullptr);
}
};
} // namespace p2pool
+164 -71
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,12 +21,15 @@
#include "pool_block.h"
#include "wallet.h"
#include "block_template.h"
#ifdef WITH_RANDOMX
#include "randomx.h"
#include "dataset.hpp"
#include "configuration.h"
#include "intrin_portable.h"
#endif
#include "keccak.h"
#include "p2p_server.h"
#include "stratum_server.h"
#include "params.h"
#include "json_parsers.h"
#include <rapidjson/document.h>
@@ -51,9 +54,8 @@ static_assert(1 <= UNCLE_BLOCK_DEPTH && UNCLE_BLOCK_DEPTH <= 10, "Invalid UNCLE_
namespace p2pool {
static constexpr uint8_t default_consensus_id[HASH_SIZE] = {
34,175,126,231,181,11,104,146,227,153,218,107,44,108,68,39,178,81,4,212,169,4,142,0,177,110,157,240,68,7,249,24
};
static constexpr uint8_t default_consensus_id[HASH_SIZE] = { 34,175,126,231,181,11,104,146,227,153,218,107,44,108,68,39,178,81,4,212,169,4,142,0,177,110,157,240,68,7,249,24 };
static constexpr uint8_t mini_consensus_id[HASH_SIZE] = { 57,130,201,26,149,174,199,250,66,80,189,18,108,216,194,220,136,23,63,24,64,113,221,44,219,86,39,163,53,24,126,196 };
SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
: m_pool(pool)
@@ -77,8 +79,11 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
}
uv_mutex_init_checked(&m_sidechainLock);
uv_mutex_init_checked(&m_seenBlocksLock);
m_difficultyData.reserve(m_chainWindowSize);
m_tmpShares.reserve(m_chainWindowSize * 2);
m_tmpRewards.reserve(m_chainWindowSize * 2);
LOGINFO(1, "generating consensus ID");
@@ -94,12 +99,18 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
<< m_unclePenalty << '\0';
constexpr char default_config[] = "mainnet\0" "default\0" "\0" "10\0" "100000\0" "2160\0" "20\0";
constexpr char mini_config[] = "mainnet\0" "mini\0" "\0" "10\0" "100000\0" "2160\0" "20\0";
// Hardcoded default consensus ID
if (memcmp(buf, default_config, sizeof(default_config) - 1) == 0) {
m_consensusId.assign(default_consensus_id, default_consensus_id + HASH_SIZE);
}
// Hardcoded mini consensus ID
else if (memcmp(buf, mini_config, sizeof(mini_config) - 1) == 0) {
m_consensusId.assign(mini_consensus_id, mini_consensus_id + HASH_SIZE);
}
else {
#ifdef WITH_RANDOMX
const randomx_flags flags = randomx_get_flags();
randomx_cache* cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
if (!cache) {
@@ -134,6 +145,10 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
keccak(reinterpret_cast<uint8_t*>(scratchpad), static_cast<int>(scratchpad_size * sizeof(rx_vec_i128)), id.h, HASH_SIZE);
randomx_release_cache(cache);
m_consensusId.assign(id.h, id.h + HASH_SIZE);
#else
LOGERR(1, "Can't calculate consensus ID without RandomX library");
panic();
#endif
}
s.m_pos = 0;
@@ -141,12 +156,14 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
// Hide most consensus ID bytes, we only want it on screen to show that we're on the right sidechain
memset(buf + 8, '*', HASH_SIZE * 2 - 16);
LOGINFO(1, "consensus ID = " << log::LightCyan() << static_cast<char*>(buf));
m_consensusIdDisplayStr.assign(buf);
LOGINFO(1, "consensus ID = " << log::LightCyan() << m_consensusIdDisplayStr.c_str());
}
SideChain::~SideChain()
{
uv_mutex_destroy(&m_sidechainLock);
uv_mutex_destroy(&m_seenBlocksLock);
for (auto& it : m_blocksById) {
delete it.second;
}
@@ -259,6 +276,11 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
get_shares(&block, shares);
}
P2PServer* SideChain::p2pServer() const
{
return m_pool ? m_pool->p2p_server() : nullptr;
}
bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) const
{
shares.clear();
@@ -343,18 +365,24 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
bool SideChain::block_seen(const PoolBlock& block)
{
MutexLock lock(m_sidechainLock);
// Check if it's some old block
if (m_chainTip && m_chainTip->m_sidechainHeight > block.m_sidechainHeight + m_chainWindowSize * 2 &&
block.m_cumulativeDifficulty < m_chainTip->m_cumulativeDifficulty) {
const PoolBlock* tip = m_chainTip;
if (tip && tip->m_sidechainHeight > block.m_sidechainHeight + m_chainWindowSize * 2 &&
block.m_cumulativeDifficulty < tip->m_cumulativeDifficulty) {
return true;
}
// Check if it was received before
MutexLock lock(m_seenBlocksLock);
return !m_seenBlocks.insert(block.m_sidechainId).second;
}
void SideChain::unsee_block(const PoolBlock& block)
{
MutexLock lock(m_seenBlocksLock);
m_seenBlocks.erase(block.m_sidechainId);
}
extern const char* BLOCK_FOUND;
bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks)
@@ -364,7 +392,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
return false;
}
difficulty_type min_accepted_diff;
bool too_low_diff = (block.m_difficulty < m_curDifficulty);
{
MutexLock lock(m_sidechainLock);
if (m_blocksById.find(block.m_sidechainId) != m_blocksById.end()) {
@@ -372,23 +400,25 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
return true;
}
// Find the minimum difficulty in the current PPLNS window
min_accepted_diff = m_curDifficulty;
for (PoolBlock* tmp = m_chainTip; tmp && (tmp->m_sidechainHeight + m_chainWindowSize > m_chainTip->m_sidechainHeight); tmp = get_parent(tmp)) {
if (tmp->m_difficulty < min_accepted_diff) {
min_accepted_diff = tmp->m_difficulty;
// This is mainly an anti-spam measure, not an actual verification step
if (too_low_diff) {
// Reduce required diff by 50% (by doubling this block's diff) to account for alternative chains
difficulty_type diff2 = block.m_difficulty;
diff2 += block.m_difficulty;
for (PoolBlock* tmp = m_chainTip; tmp && (tmp->m_sidechainHeight + m_chainWindowSize > m_chainTip->m_sidechainHeight); tmp = get_parent(tmp)) {
if (diff2 >= tmp->m_difficulty) {
too_low_diff = false;
break;
}
}
}
}
LOGINFO(4, "add_external_block: height = " << block.m_sidechainHeight << ", id = " << block.m_sidechainId << ", mainchain height = " << block.m_txinGenHeight);
// Reduce it by 50% to account for alternative chains. This is mainly an anti-spam measure, not an actual verification step
min_accepted_diff.lo = (min_accepted_diff.lo >> 1) | (min_accepted_diff.hi << 63);
min_accepted_diff.hi >>= 1;
if (block.m_difficulty < min_accepted_diff) {
LOGWARN(4, "add_external_block: block has too low difficulty " << block.m_difficulty << ", expected >= " << min_accepted_diff << ". Ignoring it.");
if (too_low_diff) {
LOGWARN(4, "add_external_block: block has too low difficulty " << block.m_difficulty << ", expected >= ~" << m_curDifficulty << ". Ignoring it.");
return true;
}
@@ -407,13 +437,15 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
hash seed;
if (!m_pool->get_seed(block.m_txinGenHeight, seed)) {
LOGWARN(3, "add_external_block: couldn't get seed hash for mainchain height " << block.m_txinGenHeight);
unsee_block(block);
return false;
}
hash pow_hash;
if (!block.get_pow_hash(m_pool->hasher(), seed, pow_hash)) {
LOGWARN(3, "add_external_block: couldn't get PoW hash for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight);
return false;
if (!block.get_pow_hash(m_pool->hasher(), block.m_txinGenHeight, seed, pow_hash)) {
LOGWARN(3, "add_external_block: couldn't get PoW hash for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight << ". Ignoring it.");
unsee_block(block);
return true;
}
// Check if it has the correct parent and difficulty to go right to monerod for checking
@@ -458,6 +490,11 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
m_watchBlockSidechainId = {};
data = m_watchBlock;
block_found = true;
const uint64_t payout = block.get_payout(m_pool->params().m_wallet);
if (payout) {
LOGINFO(0, log::LightCyan() << "You received a payout of " << log::LightGreen() << log::XMRAmount(payout) << log::LightCyan() << " in block " << log::LightGreen() << data.height);
}
}
}
@@ -477,11 +514,6 @@ void SideChain::add_block(const PoolBlock& block)
", verified = " << (block.m_verified ? 1 : 0)
);
// Save it for faster syncing on the next p2pool start
if (m_pool->p2p_server()) {
m_pool->p2p_server()->store_in_cache(block);
}
PoolBlock* new_block = new PoolBlock(block);
MutexLock lock(m_sidechainLock);
@@ -504,6 +536,11 @@ void SideChain::add_block(const PoolBlock& block)
if (new_block->m_verified) {
if (!new_block->m_invalid) {
update_chain_tip(new_block);
// Save it for faster syncing on the next p2pool start
if (P2PServer* server = p2pServer()) {
server->store_in_cache(*new_block);
}
}
}
else {
@@ -513,10 +550,16 @@ void SideChain::add_block(const PoolBlock& block)
m_seenWallets[new_block->m_minerWallet.spend_public_key()] = new_block->m_localTimestamp;
}
bool SideChain::has_block(const hash& id)
PoolBlock* SideChain::find_block(const hash& id)
{
MutexLock lock(m_sidechainLock);
return m_blocksById.find(id) != m_blocksById.end();
auto it = m_blocksById.find(id);
if (it != m_blocksById.end()) {
return it->second;
}
return nullptr;
}
void SideChain::watch_mainchain_block(const ChainMain& data, const hash& possible_id)
@@ -556,21 +599,34 @@ bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob)
bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob)
{
std::vector<MinerShare> shares;
std::vector<uint64_t> rewards;
shares.reserve(m_chainWindowSize * 2);
rewards.reserve(m_chainWindowSize * 2);
blob.clear();
MutexLock lock(m_sidechainLock);
if (!get_shares(block, shares) || !split_reward(total_reward, shares, rewards) || (rewards.size() != shares.size())) {
auto it = m_blocksById.find(block->m_sidechainId);
if (it != m_blocksById.end()) {
PoolBlock* b = it->second;
const size_t n = b->m_outputs.size();
blob.reserve(n * 38 + 64);
writeVarint(n, blob);
for (const PoolBlock::TxOutput& output : b->m_outputs) {
writeVarint(output.m_reward, blob);
blob.emplace_back(TXOUT_TO_KEY);
blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
}
block->m_outputs = b->m_outputs;
return true;
}
if (!get_shares(block, m_tmpShares) || !split_reward(total_reward, m_tmpShares, m_tmpRewards) || (m_tmpRewards.size() != m_tmpShares.size())) {
return false;
}
const size_t n = shares.size();
const size_t n = m_tmpShares.size();
blob.clear();
blob.reserve(n * 38 + 64);
writeVarint(n, blob);
@@ -580,16 +636,16 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
hash eph_public_key;
for (size_t i = 0; i < n; ++i) {
writeVarint(rewards[i], blob);
writeVarint(m_tmpRewards[i], blob);
blob.emplace_back(TXOUT_TO_KEY);
if (!shares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key)) {
if (!m_tmpShares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key)) {
LOGWARN(6, "get_eph_public_key failed at index " << i);
}
blob.insert(blob.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
block->m_outputs.emplace_back(rewards[i], eph_public_key);
block->m_outputs.emplace_back(m_tmpRewards[i], eph_public_key);
}
return true;
@@ -690,31 +746,39 @@ void SideChain::print_status()
const uint64_t hashrate_est = total_reward ? udiv128(product[1], product[0], total_reward, &rem) : 0;
const double block_share = total_reward ? ((static_cast<double>(your_reward) * 100.0) / static_cast<double>(total_reward)) : 0.0;
uint32_t our_blocks_in_window_total = std::accumulate(our_blocks_in_window.begin(), our_blocks_in_window.end(), decltype(our_blocks_in_window)::value_type(0));
uint32_t our_uncles_in_window_total = std::accumulate(our_uncles_in_window.begin(), our_uncles_in_window.end(), decltype(our_uncles_in_window)::value_type(0));
const uint32_t our_blocks_in_window_total = std::accumulate(our_blocks_in_window.begin(), our_blocks_in_window.end(), 0U);
const uint32_t our_uncles_in_window_total = std::accumulate(our_uncles_in_window.begin(), our_uncles_in_window.end(), 0U);
std::string our_blocks_in_window_chart;
our_blocks_in_window_chart.reserve(our_blocks_in_window.size());
for(const auto& p : our_blocks_in_window){
our_blocks_in_window_chart += (p > 0 ? (p > 9 ? "+" : std::to_string(p)) : ".");
if (our_blocks_in_window_total) {
our_blocks_in_window_chart.reserve(our_blocks_in_window.size() + 32);
our_blocks_in_window_chart = "\nYour shares position = [";
for (uint32_t p : our_blocks_in_window) {
our_blocks_in_window_chart += (p ? ((p > 9) ? '+' : static_cast<char>('0' + p)) : '.');
}
our_blocks_in_window_chart += ']';
}
std::string our_uncles_in_window_chart;
our_uncles_in_window_chart.reserve(our_uncles_in_window.size());
for(const auto& p : our_uncles_in_window){
our_uncles_in_window_chart += (p > 0 ? (p > 9 ? "+" : std::to_string(p)) : ".");
if (our_uncles_in_window_total) {
our_uncles_in_window_chart.reserve(our_uncles_in_window.size() + 32);
our_uncles_in_window_chart = "\nYour uncles position = [";
for (uint32_t p : our_uncles_in_window) {
our_uncles_in_window_chart += (p ? ((p > 9) ? '+' : static_cast<char>('0' + p)) : '.');
}
our_uncles_in_window_chart += ']';
}
LOGINFO(0, "status" <<
"\nMain chain height = " << m_pool->block_template().height() <<
"\nMain chain hashrate = " << log::Hashrate(network_hashrate) <<
"\nSide chain ID = " << (is_default() ? "default" : (is_mini() ? "mini" : m_consensusIdDisplayStr.c_str())) <<
"\nSide chain height = " << tip_height + 1 <<
"\nSide chain hashrate = " << log::Hashrate(pool_hashrate) <<
(hashrate_est ? "\nYour hashrate (pool-side) = " : "") << (hashrate_est ? log::Hashrate(hashrate_est) : log::Hashrate()) <<
"\nPPLNS window = " << total_blocks_in_window << " blocks (+" << total_uncles_in_window << " uncles, " << total_orphans << " orphans)" <<
"\nYour shares = " << our_blocks_in_window_total << " blocks (+" << our_uncles_in_window_total << " uncles, " << our_orphans << " orphans)" <<
(our_blocks_in_window_total > 0 ? "\nYour shares position = " : "") << (our_blocks_in_window_total > 0 ? "[" + our_blocks_in_window_chart + "]" : "") <<
(our_uncles_in_window_total > 0 ? "\nYour uncles position = " : "") << (our_uncles_in_window_total > 0 ? "[" + our_uncles_in_window_chart + "]" : "") <<
"\nYour shares = " << our_blocks_in_window_total << " blocks (+" << our_uncles_in_window_total << " uncles, " << our_orphans << " orphans)"
<< our_blocks_in_window_chart << our_uncles_in_window_chart <<
"\nBlock reward share = " << block_share << "% (" << log::XMRAmount(your_reward) << ')'
);
}
@@ -726,13 +790,13 @@ difficulty_type SideChain::total_hashes() const
uint64_t SideChain::miner_count()
{
const time_t cur_time = time(nullptr);
const uint64_t cur_time = seconds_since_epoch();
MutexLock lock(m_sidechainLock);
// Delete wallets that weren't seen for more than 24 hours and return how many remain
// Delete wallets that weren't seen for more than 72 hours and return how many remain
for (auto it = m_seenWallets.begin(); it != m_seenWallets.end();) {
if (it->second + 24 * 60 * 60 <= cur_time) {
if (it->second + 72 * 60 * 60 <= cur_time) {
it = m_seenWallets.erase(it);
}
else {
@@ -743,7 +807,7 @@ uint64_t SideChain::miner_count()
return m_seenWallets.size();
}
time_t SideChain::last_updated() const
uint64_t SideChain::last_updated() const
{
return m_chainTip ? m_chainTip->m_localTimestamp : 0;
}
@@ -753,6 +817,11 @@ bool SideChain::is_default() const
return (memcmp(m_consensusId.data(), default_consensus_id, HASH_SIZE) == 0);
}
bool SideChain::is_mini() const
{
return (memcmp(m_consensusId.data(), mini_consensus_id, HASH_SIZE) == 0);
}
bool SideChain::split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards)
{
const size_t num_shares = shares.size();
@@ -933,7 +1002,8 @@ void SideChain::verify_loop(PoolBlock* block)
// This block is now verified
if (is_longer_chain(highest_block, block)) {
bool is_alternative;
if (is_longer_chain(highest_block, block, is_alternative)) {
highest_block = block;
}
else if (highest_block && (highest_block->m_sidechainHeight > block->m_sidechainHeight)) {
@@ -943,17 +1013,19 @@ void SideChain::verify_loop(PoolBlock* block)
", height " << block->m_sidechainHeight);
}
P2PServer* server = p2pServer();
// If it came through a broadcast, send it to our peers
if (block->m_wantBroadcast && !block->m_broadcasted) {
block->m_broadcasted = true;
if (m_pool->p2p_server() && (block->m_depth < UNCLE_BLOCK_DEPTH)) {
m_pool->p2p_server()->broadcast(*block);
if (server && (block->m_depth < UNCLE_BLOCK_DEPTH)) {
server->broadcast(*block);
}
}
// Save it for faster syncing on the next p2pool start
if (m_pool->p2p_server()) {
m_pool->p2p_server()->store_in_cache(*block);
if (server) {
server->store_in_cache(*block);
}
// Try to verify blocks on top of this one
@@ -1278,7 +1350,8 @@ void SideChain::update_chain_tip(PoolBlock* block)
return;
}
if (is_longer_chain(m_chainTip, block)) {
bool is_alternative;
if (is_longer_chain(m_chainTip, block, is_alternative)) {
difficulty_type diff;
if (get_difficulty(block, m_difficultyData, diff)) {
m_chainTip = block;
@@ -1289,7 +1362,15 @@ void SideChain::update_chain_tip(PoolBlock* block)
", main chain height = " << log::Gray() << m_chainTip->m_txinGenHeight);
block->m_wantBroadcast = true;
m_pool->update_block_template_async();
if (m_pool) {
m_pool->update_block_template_async();
// Reset stratum share counters when switching to an alternative chain to avoid confusion
StratumServer* s = m_pool->stratum_server();
if (s && is_alternative) {
s->reset_share_counters();
}
}
prune_old_blocks();
}
}
@@ -1305,7 +1386,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
m_pool->update_block_template_async();
}
if (m_pool->p2p_server() && block->m_wantBroadcast && !block->m_broadcasted) {
if (p2pServer() && block->m_wantBroadcast && !block->m_broadcasted) {
block->m_broadcasted = true;
#ifdef DEBUG_BROADCAST_DELAY_MS
struct Work
@@ -1316,7 +1397,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
};
Work* work = new Work{};
work->req.data = work;
work->server = m_pool->p2p_server();
work->server = p2pServer();
work->block = block;
const int err = uv_queue_work(uv_default_loop(), &work->req,
[](uv_work_t*)
@@ -1335,7 +1416,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
LOGERR(1, "update_chain_tip: uv_queue_work failed, error " << uv_err_name(err));
}
#else
m_pool->p2p_server()->broadcast(*block);
p2pServer()->broadcast(*block);
#endif
}
}
@@ -1351,8 +1432,10 @@ PoolBlock* SideChain::get_parent(const PoolBlock* block)
return nullptr;
}
bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candidate)
bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative)
{
is_alternative = false;
if (!candidate || !candidate->m_verified || candidate->m_invalid) {
return false;
}
@@ -1395,6 +1478,8 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
}
// They're on totally different chains. Compare total difficulties over the last m_chainWindowSize blocks
is_alternative = true;
difficulty_type block_total_diff;
difficulty_type candidate_total_diff;
@@ -1510,7 +1595,8 @@ void SideChain::prune_old_blocks()
const uint64_t prune_distance = m_chainWindowSize * 2 + 120 / m_targetBlockTime;
// Remove old blocks from alternative unconnected chains after long enough time
const time_t prune_time = time(nullptr) - m_chainWindowSize * 4 * m_targetBlockTime;
const uint64_t cur_time = seconds_since_epoch();
const uint64_t prune_delay = m_chainWindowSize * 4 * m_targetBlockTime;
if (m_chainTip->m_sidechainHeight < prune_distance) {
return;
@@ -1525,12 +1611,13 @@ void SideChain::prune_old_blocks()
std::vector<PoolBlock*>& v = it->second;
v.erase(std::remove_if(v.begin(), v.end(),
[this, prune_distance, prune_time, &num_blocks_pruned, height](PoolBlock* block)
[this, prune_distance, cur_time, prune_delay, &num_blocks_pruned, height](PoolBlock* block)
{
if ((block->m_depth >= prune_distance) || (block->m_localTimestamp <= prune_time)) {
if ((block->m_depth >= prune_distance) || (cur_time >= block->m_localTimestamp + prune_delay)) {
auto it2 = m_blocksById.find(block->m_sidechainId);
if (it2 != m_blocksById.end()) {
m_blocksById.erase(it2);
unsee_block(*block);
delete block;
++num_blocks_pruned;
}
@@ -1552,6 +1639,12 @@ void SideChain::prune_old_blocks()
if (num_blocks_pruned) {
LOGINFO(4, "pruned " << num_blocks_pruned << " old blocks at heights <= " << h);
// If side-chain started pruning blocks it means the initial sync is complete
// It's now safe to delete cached blocks
if (p2pServer()) {
p2pServer()->clear_cached_blocks();
}
}
}
+18 -9
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -19,12 +19,11 @@
#include "uv_util.h"
#include <map>
#include <unordered_map>
#include <unordered_set>
namespace p2pool {
class p2pool;
class P2PServer;
struct DifficultyData;
struct PoolBlock;
class Wallet;
@@ -47,11 +46,12 @@ public:
void fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares);
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);
void get_missing_blocks(std::vector<hash>& missing_blocks);
bool has_block(const hash& id);
PoolBlock* find_block(const hash& id);
void watch_mainchain_block(const ChainMain& data, const hash& possible_id);
bool get_block_blob(const hash& id, std::vector<uint8_t>& blob);
@@ -69,13 +69,17 @@ public:
difficulty_type total_hashes() const;
uint64_t block_time() const { return m_targetBlockTime; }
uint64_t miner_count();
time_t last_updated() const;
uint64_t last_updated() const;
bool is_default() const;
bool is_mini() const;
const PoolBlock* chainTip() const { return m_chainTip; }
static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards);
private:
p2pool* m_pool;
P2PServer* p2pServer() const;
NetworkType m_networkType;
private:
@@ -87,7 +91,7 @@ private:
PoolBlock* get_parent(const PoolBlock* block);
// Checks if "candidate" has longer (higher difficulty) chain than "block"
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate);
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative);
void update_depths(PoolBlock* block);
void prune_old_blocks();
@@ -97,9 +101,13 @@ private:
mutable uv_mutex_t m_sidechainLock;
PoolBlock* m_chainTip;
std::map<uint64_t, std::vector<PoolBlock*>> m_blocksByHeight;
std::unordered_map<hash, PoolBlock*> m_blocksById;
std::unordered_set<hash> m_seenBlocks;
std::unordered_map<hash, time_t> m_seenWallets;
unordered_map<hash, PoolBlock*> m_blocksById;
unordered_map<hash, uint64_t> m_seenWallets;
std::vector<MinerShare> m_tmpShares;
std::vector<uint64_t> m_tmpRewards;
uv_mutex_t m_seenBlocksLock;
unordered_set<hash> m_seenBlocks;
std::vector<DifficultyData> m_difficultyData;
@@ -111,6 +119,7 @@ private:
uint64_t m_unclePenalty;
std::vector<uint8_t> m_consensusId;
std::string m_consensusIdDisplayStr;
difficulty_type m_curDifficulty;
+208 -56
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#include "p2pool.h"
#include "side_chain.h"
#include "params.h"
#include "p2pool_api.h"
static constexpr char log_category_prefix[] = "StratumServer ";
@@ -38,8 +39,7 @@ StratumServer::StratumServer(p2pool* pool)
: TCPServer(StratumClient::allocate)
, m_pool(pool)
, m_extraNonce(0)
, m_rd{}
, m_rng(m_rd())
, m_rng(RandomDeviceSeed::instance)
, m_cumulativeHashes(0)
, m_cumulativeHashesAtLastShare(0)
, m_hashrateDataHead(0)
@@ -48,8 +48,12 @@ StratumServer::StratumServer(p2pool* pool)
, m_hashrateDataTail_24h(0)
, m_cumulativeFoundSharesDiff(0.0)
, m_totalFoundShares(0)
, m_apiLastUpdateTime(0)
{
m_hashrateData[0] = { time(nullptr), 0 };
// Diffuse the initial state in case it has low quality
m_rng.discard(10000);
m_hashrateData[0] = { seconds_since_epoch(), 0 };
uv_mutex_init_checked(&m_blobsQueueLock);
uv_mutex_init_checked(&m_rngLock);
@@ -174,6 +178,28 @@ void StratumServer::on_block(const BlockTemplate& block)
}
}
bool StratumServer::get_custom_user(const char* s, std::string& user)
{
user.clear();
// Find first of '+' or '.', drop non-printable characters
while (s && (user.length() < 64)) {
const char c = *s;
if (!c) {
break;
}
if ((c == '+') || (c == '.')) {
break;
}
// Limit to printable ASCII characters
if (c >= ' ' && c <= '~') {
user += c;
}
++s;
}
return !user.empty();
}
bool StratumServer::get_custom_diff(const char* s, difficulty_type& diff)
{
const char* diff_str = nullptr;
@@ -223,13 +249,17 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
target = std::max(target, client->m_customDiff.target());
}
if (get_custom_user(login, client->m_customUser)) {
LOGINFO(5, "client " << log::Gray() << static_cast<char*>(client->m_addrString) << " set custom user " << client->m_customUser);
}
uint32_t job_id;
{
MutexLock lock(client->m_jobsLock);
job_id = client->m_perConnectionJobId++;
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(client->m_jobs)];
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
saved_job.job_id = job_id;
saved_job.extra_nonce = extra_nonce;
saved_job.template_id = template_id;
@@ -307,7 +337,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
{
MutexLock lock(client->m_jobsLock);
const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(client->m_jobs)];
const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
if (saved_job.job_id == job_id) {
template_id = saved_job.template_id;
extra_nonce = saved_job.extra_nonce;
@@ -317,6 +347,27 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
}
if (found) {
BlockTemplate& block = m_pool->block_template();
difficulty_type mainchain_diff, sidechain_diff;
if (!block.get_difficulties(template_id, mainchain_diff, sidechain_diff)) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
return send(client,
[id](void* buf)
{
log::Stream s(reinterpret_cast<char*>(buf));
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Stale share\"}}\n";
return s.m_pos;
});
}
if (mainchain_diff.check_pow(resultHash)) {
const std::string& s = client->m_customUser;
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (!s.empty() ? " user " : "") << s << " found a mainchain block, submitting it");
m_pool->submit_block_async(template_id, nonce, extra_nonce);
block.update_tx_keys();
}
SubmittedShare* share;
{
@@ -334,6 +385,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
share->m_req.data = share;
share->m_server = this;
share->m_client = client;
share->m_clientAddr = client->m_addr;
share->m_clientResetCounter = client->m_resetCounter.load();
share->m_rpcId = client->m_rpcId;
share->m_id = id;
@@ -342,13 +394,23 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
share->m_extraNonce = extra_nonce;
share->m_target = target;
share->m_resultHash = resultHash;
share->m_sidechainDifficulty = sidechain_diff;
// If this share is below sidechain difficulty, process it in this thread because it'll be quick
if (!share->m_sidechainDifficulty.check_pow(share->m_resultHash)) {
on_share_found(&share->m_req);
on_after_share_found(&share->m_req, 0);
return true;
}
// Else switch to a worker thread to check PoW which can take a long time
const int err = uv_queue_work(&m_loop, &share->m_req, on_share_found, on_after_share_found);
if (err) {
LOGERR(1, "uv_queue_work failed, error " << uv_err_name(err));
MutexLock lock(m_submittedSharesPoolLock);
m_submittedSharesPool.push_back(share);
return false;
// If uv_queue_work failed, process this share here anyway
on_share_found(&share->m_req);
on_after_share_found(&share->m_req, 0);
}
return true;
@@ -375,10 +437,17 @@ uint64_t StratumServer::get_random64()
void StratumServer::print_status()
{
update_hashrate_data(0);
update_hashrate_data(0, seconds_since_epoch());
print_stratum_status();
}
void StratumServer::reset_share_counters()
{
m_cumulativeHashesAtLastShare = 0;
m_cumulativeFoundSharesDiff = 0.0;
m_totalFoundShares = 0;
}
void StratumServer::print_stratum_status() const
{
uint64_t hashes_15m, hashes_1h, hashes_24h, total_hashes;
@@ -413,8 +482,9 @@ void StratumServer::print_stratum_status() const
const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0;
double average_effort = 0.0;
if (m_cumulativeFoundSharesDiff > 0.0) {
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / m_cumulativeFoundSharesDiff;
const double diff = m_cumulativeFoundSharesDiff;
if (diff > 0.0) {
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / diff;
}
LOGINFO(0, "status" <<
@@ -456,6 +526,8 @@ void StratumServer::on_blobs_ready()
size_t numClientsProcessed = 0;
uint32_t extra_nonce = 0;
const uint64_t cur_time = seconds_since_epoch();
{
MutexLock lock2(m_clientsListLock);
@@ -463,7 +535,12 @@ void StratumServer::on_blobs_ready()
++numClientsProcessed;
if (!client->m_rpcId) {
// Not logged in yet, on_login() will send the job to this client
// 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;
}
@@ -485,7 +562,7 @@ void StratumServer::on_blobs_ready()
job_id = client->m_perConnectionJobId++;
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(client->m_jobs)];
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
saved_job.job_id = job_id;
saved_job.extra_nonce = extra_nonce;
saved_job.template_id = data->m_templateId;
@@ -528,12 +605,9 @@ void StratumServer::on_blobs_ready()
LOGINFO(3, "sent new job to " << extra_nonce << '/' << numClientsProcessed << " clients");
}
void StratumServer::update_hashrate_data(uint64_t target)
void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp)
{
const time_t timestamp = time(nullptr);
uint64_t rem;
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
constexpr size_t N = array_size(&StratumServer::m_hashrateData);
WriteLock lock(m_hashrateDataLock);
@@ -545,20 +619,20 @@ void StratumServer::update_hashrate_data(uint64_t target)
head.m_cumulativeHashes = m_cumulativeHashes;
}
else {
m_hashrateDataHead = (m_hashrateDataHead + 1) % array_size(m_hashrateData);
m_hashrateDataHead = (m_hashrateDataHead + 1) % N;
data[m_hashrateDataHead] = { timestamp, m_cumulativeHashes };
}
while (data[m_hashrateDataTail_15m].m_timestamp + 15 * 60 < timestamp) {
m_hashrateDataTail_15m = (m_hashrateDataTail_15m + 1) % array_size(m_hashrateData);
m_hashrateDataTail_15m = (m_hashrateDataTail_15m + 1) % N;
}
while (data[m_hashrateDataTail_1h].m_timestamp + 60 * 60 < timestamp) {
m_hashrateDataTail_1h = (m_hashrateDataTail_1h + 1) % array_size(m_hashrateData);
m_hashrateDataTail_1h = (m_hashrateDataTail_1h + 1) % N;
}
while (data[m_hashrateDataTail_24h].m_timestamp + 60 * 60 * 24 < timestamp) {
m_hashrateDataTail_24h = (m_hashrateDataTail_24h + 1) % array_size(m_hashrateData);
m_hashrateDataTail_24h = (m_hashrateDataTail_24h + 1) % N;
}
}
@@ -570,42 +644,41 @@ void StratumServer::on_share_found(uv_work_t* req)
StratumClient* client = share->m_client;
StratumServer* server = share->m_server;
p2pool* pool = server->m_pool;
BlockTemplate& block = pool->block_template();
uint64_t target = share->m_target;
if (target >= TARGET_4_BYTES_LIMIT) {
target = (target >> 32) << 32;
}
uint64_t rem;
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
if (pool->stopped()) {
LOGWARN(0, "p2pool is shutting down, but a share was found. Trying to process it anyway!");
}
uint8_t blob[128];
uint64_t height;
difficulty_type difficulty;
difficulty_type sidechain_difficulty;
hash seed_hash;
size_t nonce_offset;
if (share->m_sidechainDifficulty.check_pow(share->m_resultHash)) {
uint8_t blob[128];
uint64_t height;
difficulty_type difficulty;
difficulty_type sidechain_difficulty;
hash seed_hash;
size_t nonce_offset;
const uint32_t blob_size = block.get_hashing_blob(share->m_templateId, share->m_extraNonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset);
if (!blob_size) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
share->m_result = SubmittedShare::Result::STALE;
return;
}
const uint32_t blob_size = pool->block_template().get_hashing_blob(share->m_templateId, share->m_extraNonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset);
if (!blob_size) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
share->m_result = SubmittedShare::Result::STALE;
return;
}
const bool mainchain_solution = difficulty.check_pow(share->m_resultHash);
const bool sidechain_solution = sidechain_difficulty.check_pow(share->m_resultHash);
if (mainchain_solution || sidechain_solution) {
for (uint32_t i = 0, nonce = share->m_nonce; i < sizeof(share->m_nonce); ++i) {
blob[nonce_offset + i] = nonce & 255;
nonce >>= 8;
}
hash pow_hash;
if (!pool->calculate_hash(blob, blob_size, seed_hash, pow_hash)) {
if (!pool->calculate_hash(blob, blob_size, height, seed_hash, pow_hash)) {
LOGWARN(3, "client " << static_cast<char*>(client->m_addrString) << " couldn't check share PoW");
share->m_result = SubmittedShare::Result::COULDNT_CHECK_POW;
return;
@@ -617,9 +690,6 @@ void StratumServer::on_share_found(uv_work_t* req)
return;
}
uint64_t rem;
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
const uint64_t n = server->m_cumulativeHashes + hashes;
const double diff = sidechain_difficulty.to_double();
const double effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
@@ -628,23 +698,18 @@ void StratumServer::on_share_found(uv_work_t* req)
server->m_cumulativeFoundSharesDiff += diff;
++server->m_totalFoundShares;
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", effort " << effort << '%');
if (mainchain_solution) {
pool->submit_block_async(share->m_templateId, share->m_nonce, share->m_extraNonce);
block.update_tx_keys();
}
if (sidechain_solution) {
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
}
const std::string& s = client->m_customUser;
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", client " << static_cast<char*>(client->m_addrString) << (!s.empty() ? " user " : "") << s << ", effort " << effort << '%');
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
}
// Send the response to miner
const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t));
if (LIKELY(value < target)) {
server->update_hashrate_data(target);
const uint64_t timestamp = seconds_since_epoch();
server->update_hashrate_data(hashes, timestamp);
server->api_update_local_stats(timestamp);
share->m_result = SubmittedShare::Result::OK;
}
else {
@@ -670,6 +735,8 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
StratumClient* client = share->m_client;
StratumServer* server = share->m_server;
const bool bad_share = (share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW);
if ((client->m_resetCounter.load() == share->m_clientResetCounter) && (client->m_rpcId == share->m_rpcId)) {
const bool result = server->send(client,
[share](void* buf)
@@ -695,7 +762,7 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
return s.m_pos;
});
if ((share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW)) {
if (bad_share) {
client->ban(DEFAULT_BAN_TIME);
client->close();
}
@@ -703,10 +770,14 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
client->close();
}
}
else if (bad_share) {
server->ban(share->m_clientAddr, DEFAULT_BAN_TIME);
}
}
StratumServer::StratumClient::StratumClient()
: m_rpcId(0)
, m_connectedTime(0)
, m_jobs{}
, m_perConnectionJobId(0)
, m_customDiff{}
@@ -723,9 +794,17 @@ void StratumServer::StratumClient::reset()
{
Client::reset();
m_rpcId = 0;
m_connectedTime = 0;
memset(m_jobs, 0, sizeof(m_jobs));
m_perConnectionJobId = 0;
m_customDiff = {};
m_customUser.clear();
}
bool StratumServer::StratumClient::on_connect()
{
m_connectedTime = seconds_since_epoch();
return true;
}
bool StratumServer::StratumClient::on_read(char* data, uint32_t size)
@@ -914,4 +993,77 @@ bool StratumServer::StratumClient::process_submit(rapidjson::Document& doc, uint
return static_cast<StratumServer*>(m_owner)->on_submit(this, id, job_id.GetString(), nonce.GetString(), result.GetString());
}
void StratumServer::api_update_local_stats(uint64_t timestamp)
{
if (!m_pool->api() || !m_pool->params().m_localStats) {
return;
}
// Rate limit to no more than once in 60 seconds.
if (timestamp < m_apiLastUpdateTime + 60) {
return;
}
m_apiLastUpdateTime = timestamp;
uint64_t hashes_15m, hashes_1h, hashes_24h, total_hashes;
int64_t dt_15m, dt_1h, dt_24h;
uint64_t hashes_since_last_share;
{
ReadLock lock(m_hashrateDataLock);
total_hashes = m_cumulativeHashes;
hashes_since_last_share = m_cumulativeHashes - m_cumulativeHashesAtLastShare;
const HashrateData* data = m_hashrateData;
const HashrateData& head = data[m_hashrateDataHead];
const HashrateData& tail_15m = data[m_hashrateDataTail_15m];
const HashrateData& tail_1h = data[m_hashrateDataTail_1h];
const HashrateData& tail_24h = data[m_hashrateDataTail_24h];
hashes_15m = head.m_cumulativeHashes - tail_15m.m_cumulativeHashes;
dt_15m = static_cast<int64_t>(head.m_timestamp - tail_15m.m_timestamp);
hashes_1h = head.m_cumulativeHashes - tail_1h.m_cumulativeHashes;
dt_1h = static_cast<int64_t>(head.m_timestamp - tail_1h.m_timestamp);
hashes_24h = head.m_cumulativeHashes - tail_24h.m_cumulativeHashes;
dt_24h = static_cast<int64_t>(head.m_timestamp - tail_24h.m_timestamp);
}
const uint64_t hashrate_15m = (dt_15m > 0) ? (hashes_15m / dt_15m) : 0;
const uint64_t hashrate_1h = (dt_1h > 0) ? (hashes_1h / dt_1h ) : 0;
const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0;
double average_effort = 0.0;
const double diff = m_cumulativeFoundSharesDiff;
if (diff > 0.0) {
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / diff;
}
int shares_found = m_totalFoundShares;
double current_effort = static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double();
int connections = m_numConnections;
int incoming_connections = m_numIncomingConnections;
m_pool->api()->set(p2pool_api::Category::LOCAL, "stats",
[hashrate_15m, hashrate_1h, hashrate_24h, total_hashes, shares_found, average_effort, current_effort, connections, incoming_connections](log::Stream& s)
{
s << "{\"hashrate_15m\":" << hashrate_15m
<< ",\"hashrate_1h\":" << hashrate_1h
<< ",\"hashrate_24h\":" << hashrate_24h
<< ",\"total_hashes\":" << total_hashes
<< ",\"shares_found\":" << shares_found
<< ",\"average_effort\":" << average_effort
<< ",\"current_effort\":" << current_effort
<< ",\"connections\":" << connections
<< ",\"incoming_connections\":" << incoming_connections
<< "}";
});
}
} // namespace p2pool
+14 -6
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -19,7 +19,6 @@
#include "tcp_server.h"
#include <rapidjson/document.h>
#include <random>
namespace p2pool {
@@ -45,7 +44,7 @@ public:
static Client* allocate() { return new StratumClient(); }
void reset() override;
bool on_connect() override { return true; }
bool on_connect() override;
bool on_read(char* data, uint32_t size) override;
bool process_request(char* data, uint32_t size);
@@ -53,6 +52,7 @@ public:
bool process_submit(rapidjson::Document& doc, uint32_t id);
uint32_t m_rpcId;
uint64_t m_connectedTime;
uv_mutex_t m_jobsLock;
@@ -65,6 +65,7 @@ public:
uint32_t m_perConnectionJobId;
difficulty_type m_customDiff;
std::string m_customUser;
};
bool on_login(StratumClient* client, uint32_t id, const char* login);
@@ -73,10 +74,13 @@ public:
void print_status() override;
void reset_share_counters();
private:
void print_stratum_status() const;
static bool get_custom_diff(const char* s, difficulty_type& diff);
static bool get_custom_user(const char* s, std::string& user);
static void on_share_found(uv_work_t* req);
static void on_after_share_found(uv_work_t* req, int status);
@@ -104,7 +108,6 @@ private:
std::atomic<uint32_t> m_extraNonce;
uv_mutex_t m_rngLock;
std::random_device m_rd;
std::mt19937_64 m_rng;
struct SubmittedShare
@@ -112,6 +115,7 @@ private:
uv_work_t m_req;
StratumServer* m_server;
StratumClient* m_client;
raw_ip m_clientAddr;
uint32_t m_clientResetCounter;
uint32_t m_rpcId;
uint32_t m_id;
@@ -120,6 +124,7 @@ private:
uint32_t m_extraNonce;
uint64_t m_target;
hash m_resultHash;
difficulty_type m_sidechainDifficulty;
enum class Result {
STALE,
@@ -135,7 +140,7 @@ private:
struct HashrateData
{
time_t m_timestamp;
uint64_t m_timestamp;
uint64_t m_cumulativeHashes;
};
@@ -152,7 +157,10 @@ private:
double m_cumulativeFoundSharesDiff;
uint32_t m_totalFoundShares;
void update_hashrate_data(uint64_t target);
uint64_t m_apiLastUpdateTime;
void update_hashrate_data(uint64_t hashes, uint64_t timestamp);
void api_update_local_stats(uint64_t timestamp);
};
} // namespace p2pool
+14 -46
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,8 +18,6 @@
#pragma once
#include "uv_util.h"
#include <map>
#include <set>
namespace p2pool {
@@ -46,40 +44,11 @@ public:
int listen_port() const { return m_listenPort; }
struct raw_ip
{
alignas(8) uint8_t data[16];
FORCEINLINE bool operator<(const raw_ip& other) const
{
const uint64_t* a = reinterpret_cast<const uint64_t*>(data);
const uint64_t* b = reinterpret_cast<const uint64_t*>(other.data);
if (a[1] < b[1]) return true;
if (a[1] > b[1]) return false;
return a[0] < b[0];
}
FORCEINLINE bool operator==(const raw_ip& other) const
{
const uint64_t* a = reinterpret_cast<const uint64_t*>(data);
const uint64_t* b = reinterpret_cast<const uint64_t*>(other.data);
return (a[0] == b[0]) && (a[1] == b[1]);
}
FORCEINLINE bool operator!=(const raw_ip& other) const { return !operator==(other); }
};
static_assert(sizeof(raw_ip) == 16, "struct raw_ip has invalid size");
static_assert(sizeof(in6_addr) == 16, "struct in6_addr has invalid size");
static_assert(sizeof(in_addr) == 4, "struct in_addr has invalid size");
bool connect_to_peer(bool is_v6, const raw_ip& ip, int port);
virtual void on_connect_failed(bool is_v6, const raw_ip& ip, int port);
void ban(const raw_ip& ip, uint64_t seconds);
virtual void print_bans();
struct Client
{
@@ -108,7 +77,6 @@ public:
Client* m_next;
uv_tcp_t m_socket;
uv_write_t m_write;
uv_connect_t m_connectRequest;
bool m_isV6;
@@ -121,21 +89,21 @@ public:
char m_readBuf[READ_BUF_SIZE];
uint32_t m_numRead;
struct WriteBuf
{
Client* m_client;
uv_write_t m_write;
char m_data[WRITE_BUF_SIZE];
};
uv_mutex_t m_writeBuffersLock;
std::vector<WriteBuf*> m_writeBuffers;
std::atomic<uint32_t> m_resetCounter{ 0 };
uv_mutex_t m_sendLock;
};
struct WriteBuf
{
Client* m_client = nullptr;
uv_write_t m_write = {};
std::vector<uint8_t> m_data;
};
uv_mutex_t m_writeBuffersLock;
std::vector<WriteBuf*> m_writeBuffers;
struct SendCallbackBase
{
virtual ~SendCallbackBase() {}
@@ -193,12 +161,12 @@ protected:
uint32_t m_numIncomingConnections;
uv_mutex_t m_bansLock;
std::map<raw_ip, time_t> m_bans;
unordered_map<raw_ip, std::chrono::steady_clock::time_point> m_bans;
bool is_banned(const raw_ip& ip);
uv_mutex_t m_pendingConnectionsLock;
std::set<raw_ip> m_pendingConnections;
unordered_set<raw_ip> m_pendingConnections;
uv_async_t m_dropConnectionsAsync;
static void on_drop_connections(uv_async_t* async) { reinterpret_cast<TCPServer*>(async->data)->close_sockets(false); }
+77 -40
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,12 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
uv_mutex_init_checked(&m_clientsListLock);
uv_mutex_init_checked(&m_bansLock);
uv_mutex_init_checked(&m_pendingConnectionsLock);
uv_mutex_init_checked(&m_writeBuffersLock);
m_writeBuffers.resize(DEFAULT_BACKLOG);
for (size_t i = 0; i < m_writeBuffers.size(); ++i) {
m_writeBuffers[i] = new WriteBuf();
}
m_preallocatedClients.reserve(DEFAULT_BACKLOG);
for (int i = 0; i < DEFAULT_BACKLOG; ++i) {
@@ -101,7 +107,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::parse_address_list(const std::str
}
const int port = atoi(address.substr(k2 + 1).c_str());
if ((port > 0) && (port < 655356)) {
if ((port > 0) && (port < 65536)) {
callback(is_v6, address, ip, port);
}
else {
@@ -308,7 +314,6 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(bool is_v6, const
return connect_to_peer_nolock(client, is_v6, reinterpret_cast<sockaddr*>(&addr));
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connect_failed(bool, const raw_ip&, int)
{
@@ -317,11 +322,17 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connect_failed(bool, const raw
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::is_banned(const raw_ip& ip)
{
const auto cur_time = std::chrono::steady_clock::now();
MutexLock lock(m_bansLock);
auto it = m_bans.find(ip);
if ((it != m_bans.end()) && (time(nullptr) < it->second)) {
return true;
if (it != m_bans.end()) {
const bool banned = (cur_time < it->second);
if (!banned) {
m_bans.erase(it);
}
return banned;
}
return false;
@@ -426,14 +437,14 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::shutdown_tcp()
using namespace std::chrono;
const system_clock::time_point start_time = system_clock::now();
const auto start_time = steady_clock::now();
int64_t counter = 0;
uv_async_t asy;
constexpr uint32_t timeout_seconds = 30;
while (!m_loopStopped) {
const int64_t elapsed_time = duration_cast<milliseconds>(system_clock::now() - start_time).count();
const int64_t elapsed_time = duration_cast<milliseconds>(steady_clock::now() - start_time).count();
if (elapsed_time >= (counter + 1) * 1000) {
++counter;
@@ -462,6 +473,14 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::shutdown_tcp()
uv_mutex_destroy(&m_bansLock);
uv_mutex_destroy(&m_pendingConnectionsLock);
{
MutexLock lock(m_writeBuffersLock);
for (WriteBuf* buf : m_writeBuffers) {
delete buf;
}
}
uv_mutex_destroy(&m_writeBuffersLock);
LOGINFO(1, "stopped");
}
@@ -476,8 +495,34 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::print_status()
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::ban(const raw_ip& ip, uint64_t seconds)
{
const auto ban_time = std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
MutexLock lock(m_bansLock);
m_bans[ip] = time(nullptr) + seconds;
m_bans[ip] = ban_time;
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::print_bans()
{
using namespace std::chrono;
const auto cur_time = steady_clock::now();
std::vector<std::pair<raw_ip, std::chrono::steady_clock::time_point>> bans;
{
MutexLock lock(m_bansLock);
bans.reserve(m_bans.size());
for (const auto& b : m_bans) {
bans.emplace_back(std::make_pair(b.first, b.second));
}
}
for (const auto& b : bans) {
if (cur_time < b.second) {
const uint64_t t = duration_cast<seconds>(b.second - cur_time).count();
LOGINFO(0, b.first << " is banned (" << t << " seconds left)");
}
}
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
@@ -489,48 +534,52 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
MutexLock lock0(client->m_sendLock);
typename Client::WriteBuf* buf = nullptr;
WriteBuf* buf = nullptr;
{
MutexLock lock(client->m_writeBuffersLock);
if (!client->m_writeBuffers.empty()) {
buf = client->m_writeBuffers.back();
client->m_writeBuffers.pop_back();
MutexLock lock(m_writeBuffersLock);
if (!m_writeBuffers.empty()) {
buf = m_writeBuffers.back();
m_writeBuffers.pop_back();
}
}
if (!buf) {
buf = new typename Client::WriteBuf();
buf = new WriteBuf();
}
const size_t bytes_written = callback(buf->m_data);
// callback_buf is used in only 1 thread, so it's safe
static uint8_t callback_buf[WRITE_BUF_SIZE];
const size_t bytes_written = callback(callback_buf);
if (bytes_written > sizeof(buf->m_data)) {
LOGERR(0, "send callback wrote " << bytes_written << " bytes, expected no more than " << sizeof(buf->m_data) << " bytes");
if (bytes_written > WRITE_BUF_SIZE) {
LOGERR(0, "send callback wrote " << bytes_written << " bytes, expected no more than " << WRITE_BUF_SIZE << " bytes");
panic();
}
if (bytes_written == 0) {
LOGWARN(1, "send callback wrote 0 bytes, nothing to do");
{
MutexLock lock(client->m_writeBuffersLock);
client->m_writeBuffers.push_back(buf);
MutexLock lock(m_writeBuffersLock);
m_writeBuffers.push_back(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);
uv_buf_t bufs[1];
bufs[0].base = buf->m_data;
bufs[0].base = reinterpret_cast<char*>(buf->m_data.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) {
{
MutexLock lock(client->m_writeBuffersLock);
client->m_writeBuffers.push_back(buf);
MutexLock lock(m_writeBuffersLock);
m_writeBuffers.push_back(buf);
}
LOGWARN(1, "failed to start writing data to client connection " << static_cast<const char*>(client->m_addrString) << ", error " << uv_err_name(err));
return false;
@@ -547,6 +596,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::loop(void* data)
TCPServer* server = static_cast<TCPServer*>(data);
uv_run(&server->m_loop, UV_RUN_DEFAULT);
uv_loop_close(&server->m_loop);
LOGINFO(1, "event loop stopped");
server->m_loopStopped = true;
}
@@ -761,27 +811,14 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::Client()
{
Client::reset();
uv_mutex_init_checked(&m_writeBuffersLock);
uv_mutex_init_checked(&m_sendLock);
m_readBuf[0] = '\0';
m_writeBuffers.resize(2);
for (size_t i = 0; i < m_writeBuffers.size(); ++i) {
m_writeBuffers[i] = new WriteBuf();
}
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::~Client()
{
{
MutexLock lock(m_writeBuffersLock);
for (WriteBuf* buf : m_writeBuffers) {
delete buf;
}
}
uv_mutex_destroy(&m_writeBuffersLock);
uv_mutex_destroy(&m_sendLock);
}
@@ -794,7 +831,6 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::reset()
m_prev = nullptr;
m_next = nullptr;
memset(&m_socket, 0, sizeof(m_socket));
memset(&m_write, 0, sizeof(m_write));
memset(&m_connectRequest, 0, sizeof(m_connectRequest));
m_isV6 = false;
m_isIncoming = false;
@@ -858,12 +894,13 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::on_read(uv_stream_t* stre
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::on_write(uv_write_t* req, int status)
{
Client::WriteBuf* buf = static_cast<Client::WriteBuf*>(req->data);
WriteBuf* buf = static_cast<WriteBuf*>(req->data);
Client* client = buf->m_client;
TCPServer* server = client->m_owner;
{
MutexLock lock(client->m_writeBuffersLock);
client->m_writeBuffers.push_back(buf);
if (server) {
MutexLock lock(server->m_writeBuffersLock);
server->m_writeBuffers.push_back(buf);
}
if (status != 0) {
+25 -4
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -29,6 +29,22 @@ static constexpr char log_category_prefix[] = "Util ";
namespace p2pool {
#define STR2(X) STR(X)
#define STR(X) #X
const char* VERSION = "v1.9 (built"
#if defined(__clang__)
" with clang/" __clang_version__
#elif defined(__GNUC__)
" with GCC/" STR2(__GNUC__) "." STR2(__GNUC_MINOR__) "." STR2(__GNUC_PATCHLEVEL__)
#elif defined(_MSC_VER)
" with MSVC/" STR2(_MSC_VER)
#endif
" on " __DATE__ ")";
#undef STR2
#undef STR
MinerCallbackHandler::~MinerCallbackHandler() {}
void panic()
@@ -40,7 +56,7 @@ void panic()
__debugbreak();
}
#endif
exit(1);
abort();
} while (true);
}
@@ -209,7 +225,7 @@ void uv_rwlock_init_checked(uv_rwlock_t* lock)
uv_loop_t* uv_default_loop_checked()
{
if (!is_main_thread) {
if (!is_main_thread()) {
LOGERR(1, "uv_default_loop() can only be used by the main thread. Fix the code!");
#ifdef _WIN32
if (IsDebuggerPresent()) {
@@ -323,7 +339,10 @@ void BackgroundJobTracker::print_status()
}
BackgroundJobTracker bkg_jobs_tracker;
thread_local bool is_main_thread = false;
static thread_local bool main_thread = false;
void set_main_thread() { main_thread = true; }
bool is_main_thread() { return main_thread; }
bool resolve_host(std::string& host, bool& is_v6)
{
@@ -363,4 +382,6 @@ bool resolve_host(std::string& host, bool& is_v6)
return true;
}
RandomDeviceSeed RandomDeviceSeed::instance;
} // namespace p2pool
+77 -16
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -17,8 +17,25 @@
#pragma once
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4623 5026 5027)
#endif
#define ROBIN_HOOD_MALLOC(size) p2pool::malloc_hook(size)
#define ROBIN_HOOD_CALLOC(count, size) p2pool::calloc_hook((count), (size))
#define ROBIN_HOOD_FREE(ptr) p2pool::free_hook(ptr)
#include "robin_hood.h"
#ifdef _MSC_VER
#pragma warning(pop)
#endif
namespace p2pool {
extern const char* VERSION;
template<typename T> struct not_implemented { enum { value = 0 }; };
struct nocopy_nomove
@@ -93,6 +110,7 @@ FORCEINLINE void writeVarint(T value, std::vector<uint8_t>& out)
}
template<typename T, size_t N> FORCEINLINE constexpr size_t array_size(T(&)[N]) { return N; }
template<typename T, typename U, size_t N> FORCEINLINE constexpr size_t array_size(T(U::*)[N]) { return N; }
[[noreturn]] void panic();
@@ -115,38 +133,81 @@ private:
};
extern BackgroundJobTracker bkg_jobs_tracker;
extern thread_local bool is_main_thread;
void set_main_thread();
bool is_main_thread();
bool resolve_host(std::string& host, bool& is_v6);
template <typename Key, typename T>
using unordered_map = robin_hood::detail::Table<false, 80, Key, T, robin_hood::hash<Key>, std::equal_to<Key>>;
template <typename Key>
using unordered_set = robin_hood::detail::Table<false, 80, Key, void, robin_hood::hash<Key>, std::equal_to<Key>>;
// Fills the whole initial MT19937-64 state with non-deterministic random numbers
struct RandomDeviceSeed
{
using result_type = std::random_device::result_type;
static_assert(sizeof(result_type) >= 4, "result_type must have at least 32 bits");
template<typename T>
static void generate(T begin, T end)
{
std::random_device rd;
for (T i = begin; i != end; ++i) {
*i = rd();
}
}
static RandomDeviceSeed instance;
};
FORCEINLINE uint64_t seconds_since_epoch()
{
using namespace std::chrono;
return duration_cast<seconds>(steady_clock::now().time_since_epoch()).count();
}
} // namespace p2pool
namespace std {
namespace robin_hood {
template<>
struct hash<p2pool::hash>
{
FORCEINLINE size_t operator()(const p2pool::hash& value) const
FORCEINLINE size_t operator()(const p2pool::hash& value) const noexcept
{
uint64_t result = 0xcbf29ce484222325ull;
for (size_t i = 0; i < p2pool::HASH_SIZE; ++i) {
result = (result ^ value.h[i]) * 0x100000001b3ull;
}
return static_cast<size_t>(result);
return hash_bytes(value.h, p2pool::HASH_SIZE);
}
};
template<size_t N>
struct hash<std::array<uint8_t, N>>
{
FORCEINLINE size_t operator()(const std::array<uint8_t, N>& value) const
FORCEINLINE size_t operator()(const std::array<uint8_t, N>& value) const noexcept
{
uint64_t result = 0xcbf29ce484222325ull;
for (size_t i = 0; i < N; ++i) {
result = (result ^ value[i]) * 0x100000001b3ull;
}
return static_cast<size_t>(result);
return hash_bytes(value.data(), N);
}
};
} // namespace std
template<>
struct hash<p2pool::raw_ip>
{
FORCEINLINE size_t operator()(const p2pool::raw_ip& value) const noexcept
{
return hash_bytes(value.data, sizeof(value.data));
}
};
template<>
struct hash<std::pair<uint64_t, uint64_t>>
{
FORCEINLINE size_t operator()(const std::pair<uint64_t, uint64_t>& value) const noexcept
{
static_assert(sizeof(value) == sizeof(uint64_t) * 2, "Invalid std::pair<uint64_t, uint64_t> size");
return hash_bytes(&value, sizeof(value));
}
};
} // namespace robin_hood
+4 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -19,6 +19,9 @@
#include <uv.h>
static_assert(sizeof(in6_addr) == 16, "struct in6_addr has invalid size");
static_assert(sizeof(in_addr) == 4, "struct in_addr has invalid size");
namespace p2pool {
struct MutexLock : public nocopy_nomove
+2 -2
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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>
@@ -79,7 +79,7 @@ static_assert(rev_alphabet.num_symbols == 58, "Check alphabet");
namespace p2pool {
Wallet::Wallet(const char* address)
Wallet::Wallet(const char* address) : m_prefix(0), m_checksum(0), m_type(NetworkType::Invalid)
{
decode(address);
}
+1 -1
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
+60 -5
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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
@@ -76,16 +76,26 @@ ZMQReader::~ZMQReader()
}
}
void ZMQReader::run_wrapper(void* arg)
{
reinterpret_cast<ZMQReader*>(arg)->run();
LOGINFO(1, "worker thread stopped");
}
void ZMQReader::run()
{
try {
char addr[32];
snprintf(addr, sizeof(addr), "tcp://%s:%u", m_address, m_zmqPort);
m_subscriber.connect(addr);
if (!connect(addr)) {
return;
}
snprintf(addr, sizeof(addr), "tcp://127.0.0.1:%u", m_publisherPort);
m_subscriber.connect(addr);
if (!connect(addr)) {
return;
}
m_subscriber.set(zmq::sockopt::subscribe, "json-full-chain_main");
m_subscriber.set(zmq::sockopt::subscribe, "json-full-miner_data");
@@ -118,7 +128,52 @@ void ZMQReader::run()
LOGERR(1, "exception " << e.what() << ", aborting");
panic();
}
LOGINFO(1, "worker thread stopped");
}
bool ZMQReader::connect(const char* address)
{
struct ConnectMonitor : public zmq::monitor_t
{
void on_event_connected(const zmq_event_t&, const char* address) ZMQ_OVERRIDE
{
LOGINFO(1, "connected to " << address);
connected = true;
}
bool connected = false;
} monitor;
static uint64_t id = 0;
if (!id) {
std::random_device rd;
id = (static_cast<uint64_t>(rd()) << 32) | static_cast<uint32_t>(rd());
}
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf);
s << "inproc://p2pool-connect-mon-" << id << '\0';
++id;
monitor.init(m_subscriber, buf);
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) {
LOGERR(1, "failed to connect to " << address);
if (m_finished.load()) {
return false;
}
start_time = cur_time;
}
}
return true;
}
void ZMQReader::parse(char* data, size_t size)
@@ -152,7 +207,7 @@ void ZMQReader::parse(char* data, size_t size)
return;
}
m_tx.time_received = time(nullptr);
m_tx.time_received = seconds_since_epoch();
for (SizeType i = 0, n = doc.Size(); i < n; ++i) {
const auto& v = doc[i];
+3 -2
View File
@@ -1,6 +1,6 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021-2022 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,8 +28,9 @@ public:
~ZMQReader();
private:
static void run_wrapper(void* arg) { reinterpret_cast<ZMQReader*>(arg)->run(); }
static void run_wrapper(void* arg);
void run();
bool connect(const char* address);
void parse(char* data, size_t size);
+29 -15
View File
@@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 2.8.12)
project(p2pool_tests)
option(STATIC_LIBS "Use locally built libuv and libzmq static libs" OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 14)
@@ -17,6 +19,7 @@ set(LIBS gtest)
add_subdirectory(../external/src/RandomX RandomX)
set(LIBS ${LIBS} randomx)
add_definitions(-DWITH_RANDOMX)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(WARNING_FLAGS "")
@@ -84,7 +87,9 @@ set(SOURCES
../src/json_rpc_request.cpp
../src/keccak.cpp
../src/log.cpp
../src/memory_leak_debug.cpp
../src/mempool.cpp
../src/miner.cpp
../src/p2p_server.cpp
../src/p2pool.cpp
../src/p2pool_api.cpp
@@ -107,6 +112,7 @@ include_directories(../external/src/libzmq/include)
include_directories(../external/src/llhttp)
include_directories(../external/src/RandomX/src)
include_directories(../external/src/rapidjson/include)
include_directories(../external/src/robin-hood-hashing/src/include)
include_directories(src)
include_directories(googletest/googletest/include)
@@ -116,17 +122,28 @@ elseif (NOT APPLE)
set(LIBS ${LIBS} pthread gss dl)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
find_library(ZMQ_LIBRARY_DEBUG NAMES libzmq-v142-mt-sgd-4_3_5 PATHS "../external/lib/libzmq/Debug")
find_library(ZMQ_LIBRARY NAMES libzmq-v142-mt-s-4_3_5 PATHS "../external/lib/libzmq/Release")
find_library(UV_LIBRARY_DEBUG NAMES uv_a PATHS "../external/lib/libuv/Debug")
find_library(UV_LIBRARY NAMES uv_a PATHS "../external/lib/libuv/Release")
elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
find_library(ZMQ_LIBRARY_DEBUG NAMES zmq libzmq.a)
find_library(ZMQ_LIBRARY NAMES zmq libzmq.a)
find_library(UV_LIBRARY_DEBUG NAMES uv libuv.a)
find_library(UV_LIBRARY NAMES uv libuv.a)
find_library(SODIUM_LIBRARY sodium)
if (STATIC_LIBS)
set(ZMQ_LIBRARY "${CMAKE_SOURCE_DIR}/../external/src/libzmq/build/lib/libzmq.a")
set(ZMQ_LIBRARY_DEBUG "${CMAKE_SOURCE_DIR}/../external/src/libzmq/build/lib/libzmq.a")
set(UV_LIBRARY_DEBUG "${CMAKE_SOURCE_DIR}/../external/src/libuv/build/libuv_a.a")
set(UV_LIBRARY "${CMAKE_SOURCE_DIR}/../external/src/libuv/build/libuv_a.a")
else()
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
find_library(ZMQ_LIBRARY_DEBUG NAMES libzmq-v142-mt-sgd-4_3_5 PATHS "../external/lib/libzmq/Debug")
find_library(ZMQ_LIBRARY NAMES libzmq-v142-mt-s-4_3_5 PATHS "../external/lib/libzmq/Release")
find_library(UV_LIBRARY_DEBUG NAMES uv_a PATHS "../external/lib/libuv/Debug")
find_library(UV_LIBRARY NAMES uv_a PATHS "../external/lib/libuv/Release")
elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
find_library(ZMQ_LIBRARY_DEBUG NAMES zmq libzmq.a)
find_library(ZMQ_LIBRARY NAMES zmq libzmq.a)
find_library(UV_LIBRARY_DEBUG NAMES uv libuv.a)
find_library(UV_LIBRARY NAMES uv libuv.a)
find_library(SODIUM_LIBRARY sodium)
endif()
if (SODIUM_LIBRARY)
set(LIBS ${LIBS} ${SODIUM_LIBRARY})
endif()
endif()
find_library(PGM_LIBRARY pgm)
@@ -140,13 +157,10 @@ if (NORM_LIBRARY)
set(LIBS ${LIBS} ${NORM_LIBRARY})
endif()
if (SODIUM_LIBRARY)
set(LIBS ${LIBS} ${SODIUM_LIBRARY})
endif()
add_definitions(/DZMQ_STATIC /DP2POOL_LOG_DISABLE)
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} ${LIBS})
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/crypto_tests.txt" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/mainnet_test2_block.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/sidechain_dump.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
+4
View File
@@ -25,6 +25,8 @@ namespace p2pool {
TEST(crypto, derivation)
{
init_crypto_cache();
// Run the tests twice to check how crypto cache works
for (int i = 0; i < 2; ++i) {
std::ifstream f("crypto_tests.txt");
@@ -61,6 +63,8 @@ TEST(crypto, derivation)
}
} while (!f.eof());
}
destroy_crypto_cache();
}
}
+4 -4
View File
@@ -254,7 +254,7 @@ TEST(difficulty_type, check_pow)
// Max hash value that passes this difficulty
memcpy(h.h, data, HASH_SIZE);
EXPECT_EQ(diff.check_pow(h), true);
ASSERT_EQ(diff.check_pow(h), true);
// Add 1 to data (256-bit number)
for (int j = 0; j <= 3; ++j) {
@@ -267,7 +267,7 @@ TEST(difficulty_type, check_pow)
// Min hash value that fails this difficulty
memcpy(h.h, data, HASH_SIZE);
EXPECT_EQ(diff.check_pow(h), false);
ASSERT_EQ(diff.check_pow(h), false);
}
const uint64_t target = diff.target();
@@ -276,14 +276,14 @@ TEST(difficulty_type, check_pow)
for (int j = 0; j < 10000; ++j) {
const uint64_t data[4] = { r(), r(), r(), r() % target };
memcpy(h.h, data, HASH_SIZE);
EXPECT_EQ(diff.check_pow(h), true);
ASSERT_EQ(diff.check_pow(h), true);
}
// Random values that fail
for (int j = 0; j < 10000; ++j) {
const uint64_t data[4] = { r(), r(), r(), target + (r() % (std::numeric_limits<uint64_t>::max() - target + 1)) };
memcpy(h.h, data, HASH_SIZE);
EXPECT_EQ(diff.check_pow(h), false);
ASSERT_EQ(diff.check_pow(h), false);
}
}
}
+2
View File
@@ -17,6 +17,8 @@
#include "gtest/gtest.h"
void p2pool_usage() {}
int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
+41 -2
View File
@@ -16,6 +16,7 @@
*/
#include "common.h"
#include "crypto.h"
#include "pool_block.h"
#include "pow_hash.h"
#include "side_chain.h"
@@ -72,7 +73,6 @@ TEST(pool_block, deserialize)
ASSERT_EQ(b.m_cumulativeDifficulty.lo, 12544665764606ull);
ASSERT_EQ(b.m_cumulativeDifficulty.hi, 0);
ASSERT_EQ(b.m_tmpTxExtra.size(), 0);
ASSERT_EQ(b.m_tmpInts.size(), 0);
ASSERT_EQ(b.m_depth, 0);
ASSERT_EQ(b.m_verified, false);
ASSERT_EQ(b.m_invalid, false);
@@ -91,7 +91,7 @@ TEST(pool_block, deserialize)
hasher.set_seed(seed);
hash pow_hash;
ASSERT_EQ(b.get_pow_hash(&hasher, seed, pow_hash), true);
ASSERT_EQ(b.get_pow_hash(&hasher, 0, seed, pow_hash), true);
std::stringstream s;
s << pow_hash;
@@ -100,4 +100,43 @@ TEST(pool_block, deserialize)
ASSERT_EQ(b.m_difficulty.check_pow(pow_hash), true);
}
TEST(pool_block, verify)
{
init_crypto_cache();
PoolBlock b;
SideChain sidechain(nullptr, NetworkType::Mainnet);
std::ifstream f("sidechain_dump.dat", std::ios::binary | std::ios::ate);
ASSERT_EQ(f.good() && f.is_open(), true);
std::vector<uint8_t> buf(f.tellg());
f.seekg(0);
f.read(reinterpret_cast<char*>(buf.data()), buf.size());
ASSERT_EQ(f.good(), true);
for (const uint8_t *p = buf.data(), *e = buf.data() + buf.size(); p < e;) {
ASSERT_TRUE(p + sizeof(uint32_t) <= e);
const uint32_t n = *reinterpret_cast<const uint32_t*>(p);
p += sizeof(uint32_t);
ASSERT_TRUE(p + n <= e);
ASSERT_EQ(b.deserialize(p, n, sidechain), 0);
p += n;
sidechain.add_block(b);
ASSERT_TRUE(sidechain.find_block(b.m_sidechainId) != nullptr);
}
const PoolBlock* tip = sidechain.chainTip();
ASSERT_TRUE(tip != nullptr);
ASSERT_TRUE(tip->m_verified);
ASSERT_FALSE(tip->m_invalid);
ASSERT_EQ(tip->m_txinGenHeight, 2483901);
ASSERT_EQ(tip->m_sidechainHeight, 522805);
destroy_crypto_cache();
}
}
Binary file not shown.