Compare commits

...

299 Commits

Author SHA1 Message Date
SChernykh d40bb61da8 p2pool v2.3
C/C++ CI / build-ubuntu (map[c:gcc-12 cpp:g++-12 os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-static-libs (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (push) Has been cancelled
C/C++ CI / build-windows-msys2 (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-11 cpp:g++-11 os:ubuntu-20.04]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:ON vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\amd64\ os:2022 rx:OFF vs:Visual Studio 17 2022]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\amd64\ os:2022 rx:ON vs:Visual Studio 17 2022]) (push) Has been cancelled
C/C++ CI / build-macos (macos-11) (push) Has been cancelled
C/C++ CI / build-macos (macos-12) (push) Has been cancelled
cppcheck / cppcheck-ubuntu (push) Has been cancelled
cppcheck / cppcheck-windows (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
Microsoft C++ Code Analysis / Analyze (push) Has been cancelled
Sync test / sync-test-macos (push) Has been cancelled
Sync test / sync-test-windows (push) Has been cancelled
Sync test / sync-test-ubuntu (push) Has been cancelled
2022-08-30 18:01:45 +02:00
SChernykh 4733d42932 TCPServer: more informative error messages 2022-08-27 10:52:02 +02:00
SChernykh e6030dcc3f Update googletest 2022-08-26 15:50:31 +02:00
SChernykh 6615fa67f5 API: fixed error messages 2022-08-26 09:39:44 +02:00
SChernykh dc4b88d08e API: use unique temp file names 2022-08-25 09:52:58 +02:00
SChernykh cc2abe3c30 Update rapidjson 2022-08-24 18:18:31 +02:00
SChernykh 70640b65ea Updated dependencies 2022-08-24 18:14:47 +02:00
SChernykh 4599d81a59 API: save to temp file first and then rename it 2022-08-24 17:28:53 +02:00
SChernykh 3ea5b575b7 JSON RPC: added more error checks 2022-08-24 15:17:23 +02:00
SChernykh b9eb66e2b3 Added a test for mini chain 2022-08-23 21:37:55 +02:00
SChernykh bde5b19c77 CI: added sync test 2022-08-23 19:49:41 +02:00
SChernykh cc60ab3d63 StratumServer: added scoring system for clients
Ban clients only after a few bad shares without good shares compensating them.
2022-08-22 11:07:11 +02:00
SChernykh 42f7e6f486 CI: fixed MSVC analysis 2022-08-19 22:16:48 +02:00
SChernykh 5f68b5923d CI: added Ubuntu 22.04 2022-08-18 16:43:19 +02:00
SChernykh 85abbdd699 Fixed NixOS files 2022-08-18 16:07:59 +02:00
SChernykh 3802886722 Fixed "unknown command" message 2022-08-17 19:37:18 +02:00
SChernykh d4e27a27cb Tweaked SideChain::get_missing_blocks() 2022-08-17 16:44:40 +02:00
SChernykh e882b91b06 Reduced lock contention in Cache::get_derivation() 2022-08-16 18:15:41 +02:00
SChernykh 4c8dbee79d More fine-grained locks in crypto cache 2022-08-15 18:39:28 +02:00
SChernykh 1438175477 CI: macos-10.15 is deprecated 2022-08-15 16:09:41 +02:00
SChernykh 30861bbf91 Speedup SideChain::get_outputs_blob() 2022-08-15 13:48:21 +02:00
SChernykh ea6a19a260 Fixed data race 2022-08-06 11:15:09 +02:00
SChernykh fc81fa8745 JSON RPC: added more error checking 2022-08-06 10:03:10 +02:00
SChernykh 948436e57e Fixed coverity issue 2022-08-05 15:27:39 +02:00
SChernykh 3dde71f1ff Fixed processing of long console commands 2022-08-05 09:09:14 +02:00
SChernykh be0e220fe4 Merge pull request #190 from bladedoyle/monero_18
support monero v0.18.0.0 and minor fixes
2022-07-18 11:38:48 +02:00
Blade Doyle 2e9a209a78 support monero v0.18.0.0 and minor fixes 2022-07-17 08:03:29 -07:00
SChernykh 3ce4f06b6c Update CMakeLists.txt 2022-07-14 20:57:22 +02:00
SChernykh 555414cd51 SideChain: improved precalculation coverage 2022-07-14 19:29:41 +02:00
SChernykh c33033b2f4 Code cleanup 2022-07-14 14:28:06 +02:00
SChernykh 3c697c2d7e Fixed potential deadlock
Can't lock `m_minerLock` inside SideChain update
2022-07-14 13:15:35 +02:00
SChernykh 0e7c1aa481 SideChain: stricter checks for number of threads 2022-07-14 11:39:19 +02:00
SChernykh 7d5ff13f75 Fixed cppcheck warnings 2022-07-14 11:23:56 +02:00
SChernykh 02a8a512dc SideChain: precalculate tx pubkeys for faster sync 2022-07-14 10:28:25 +02:00
SChernykh 4733f46a28 Added uv_cond_init_checked 2022-07-14 09:02:08 +02:00
SChernykh 945de348f3 Rewrote get_view_tag without std::find_if 2022-07-12 09:50:49 +02:00
SChernykh 20dd9d7adc More efficient view tags caching 2022-07-11 16:42:26 +02:00
SChernykh c5bd184bbc P2PServer: don't deserialize the same block twice 2022-07-10 10:24:03 +02:00
SChernykh ae161fac49 Merge pull request #181 from bladedoyle/more_docker_compose_configure
add more configurations; add statistics webserver
2022-07-10 09:15:11 +02:00
Blade Doyle 8eb780a8b5 add more configurations; add statistics webserver 2022-07-09 16:54:43 -07:00
SChernykh 1c33cc9f3d p2pool v2.2.1
C/C++ CI / build-ubuntu (map[c:gcc cpp:g++ os:ubuntu-18.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-static-libs (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (push) Has been cancelled
C/C++ CI / build-windows-msys2 (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:ON vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\amd64\ os:2022 rx:OFF vs:Visual Studio 17 2022]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\amd64\ os:2022 rx:ON vs:Visual Studio 17 2022]) (push) Has been cancelled
C/C++ CI / build-macos (macos-10.15) (push) Has been cancelled
C/C++ CI / build-macos (macos-11) (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-11 cpp:g++-11 os:ubuntu-latest]) (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
cppcheck / cppcheck-windows (push) Has been cancelled
cppcheck / cppcheck-ubuntu (push) Has been cancelled
Microsoft C++ Code Analysis / Analyze (push) Failing after 16m37s
2022-07-08 17:47:28 +02:00
SChernykh 9d692d5194 ConsoleCommands: check if console is available 2022-07-07 08:32:37 +02:00
SChernykh a7da7f932c Update libuv to Version 1.44.1 (Stable) 2022-07-06 20:22:02 +02:00
SChernykh 2e4f7adee4 TCPServer: added more checks during disconnects 2022-07-06 14:17:09 +02:00
SChernykh dcb822f812 ConsoleCommands: read from stdin using libuv 2022-07-05 22:08:18 +02:00
SChernykh 5da286fed4 JSON RPC: added HTTP headers 2022-07-05 14:54:04 +02:00
SChernykh 4ec0fe8d96 Fixed JSON RPC handling during shutdown 2022-07-05 14:34:22 +02:00
SChernykh 340a3e85c8 Fixed autodiff when running for a long time
16-bit wrap around wasn't processed correctly
2022-07-05 10:02:18 +02:00
SChernykh 64baf660bb JSON RPC: fixed shutdown on connect error 2022-07-05 08:36:16 +02:00
SChernykh 4f24f18b27 Updated Monero network upgrade info 2022-07-01 09:23:28 +02:00
SChernykh c5ea4dca67 p2pool v2.2
C/C++ CI / build-ubuntu (map[c:gcc cpp:g++ os:ubuntu-18.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-static-libs (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-11 cpp:g++-11 os:ubuntu-latest]) (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (push) Has been cancelled
C/C++ CI / build-windows-msys2 (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:ON vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\amd64\ os:2022 rx:OFF vs:Visual Studio 17 2022]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\amd64\ os:2022 rx:ON vs:Visual Studio 17 2022]) (push) Has been cancelled
C/C++ CI / build-macos (macos-10.15) (push) Has been cancelled
C/C++ CI / build-macos (macos-11) (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
cppcheck / cppcheck-windows (push) Has been cancelled
cppcheck / cppcheck-ubuntu (push) Has been cancelled
Microsoft C++ Code Analysis / Analyze (push) Has been cancelled
2022-06-30 17:58:40 +02:00
SChernykh deaf47155f Miner: reset share counter when synchronized 2022-06-30 12:13:17 +02:00
SChernykh a1d1420ec3 Miner: display shares found in status 2022-06-30 10:15:51 +02:00
SChernykh a133a25cc3 Update c-cpp.yml 2022-06-29 19:17:43 +02:00
SChernykh 65e15b5205 Turned off more unused curl stuff 2022-06-29 18:40:39 +02:00
SChernykh ac123939e5 Fixed MSVC builds 2022-06-29 12:38:28 +02:00
SChernykh 2cafc570a8 Updated dependencies 2022-06-29 08:36:51 +02:00
SChernykh 9e5d5cecd4 Added get_tx_keys tests 2022-06-28 18:00:01 +02:00
SChernykh deb8d59130 Fixed cppcheck error 2022-06-28 10:58:41 +02:00
SChernykh 14bbf96feb Enforce deterministic tx keys starting from v15 2022-06-28 10:44:16 +02:00
SChernykh ee1c5a0a56 Update RandomX 2022-06-27 16:44:03 +02:00
SChernykh a281db1cbb Fixed unaligned read 2022-06-27 16:20:45 +02:00
SChernykh b25e5449c8 Fixed rare "low diff share" errors 2022-06-26 17:07:19 +02:00
SChernykh b267c19a45 Limit autodiff to 4M for max compatibility 2022-06-26 17:06:21 +02:00
SChernykh 0bda37057a Merge pull request #176 from kpcyrd/install-on-archlinux
README: Update install instructions for Arch Linux
2022-06-20 23:00:27 +02:00
SChernykh 2aa8d2bdcc Code cleanup 2022-06-20 22:37:33 +02:00
kpcyrd eebcbb5cc2 README: Update install instructions for Arch Linux 2022-06-20 22:27:23 +02:00
SChernykh acf37ff10f Added json parser tests 2022-06-20 10:50:30 +02:00
SChernykh 56c239a368 Merge pull request #175 from bladedoyle/docker_compose_configure
Add docker-compose configuration menu
2022-06-20 07:30:14 +02:00
Blade Doyle ae7386e7be Add docker-compose configuration menu 2022-06-20 01:55:48 +00:00
SChernykh 9ddf510716 Merge pull request #174 from bladedoyle/add_libcurl
Add libcurl to p2pool Dockerfile
2022-06-16 00:52:19 +02:00
Blade Doyle d79fefe546 Add libcurl to p2pool Dockerfile; adjust a few monerod options 2022-06-15 21:58:37 +00:00
SChernykh 921cbb0b69 ConsoleCommands: don't exit on EOF 2022-06-15 07:54:53 +02:00
SChernykh 7f1b849394 Fixed new cppcheck issues 2022-06-13 07:30:59 +02:00
SChernykh 9f4388843a Fixed error messages 2022-06-10 07:48:47 +02:00
SChernykh 37fba30c79 JSON RPC: fixed error checking 2022-06-08 18:58:32 +02:00
SChernykh 8f1fd55e2f Fixed more memory leaks in TCPServer 2022-06-07 22:25:27 +02:00
SChernykh 8af9e9b27d Fixed memory leaks 2022-06-07 19:40:13 +02:00
SChernykh 8b681d6efb Added memory hooks for CURL 2022-06-07 16:02:08 +02:00
SChernykh 75b9a0228b Updated Termux build instructions 2022-06-07 15:34:28 +02:00
SChernykh cbc00e6416 Use gcc-11 for build-ubuntu-static-libs 2022-06-07 14:37:26 +02:00
SChernykh a0778c8d7b CURL error checking 2022-06-06 17:08:36 +02:00
SChernykh 0c4dc82c65 Merge pull request #169 from SChernykh/curl
Change llhttp to libcurl
2022-06-06 15:56:25 +02:00
SChernykh a35d4b2701 Change llhttp to libcurl 2022-06-06 15:08:37 +02:00
SChernykh 05b0973a23 More reliable p2pool shutdown logic 2022-06-03 18:28:10 +02:00
SChernykh 09f46beeb4 SideChain: added more checks to is_longer_chain() 2022-06-03 17:28:46 +02:00
SChernykh b4c7eea4d9 Updated CodeQL action to v2 2022-06-02 11:22:43 +02:00
SChernykh 8159ecfce4 Merge pull request #167 from bladedoyle/issue150
Fix for Issue #150 - xmrig container not building
2022-06-01 21:10:14 +02:00
Blade Doyle 662e2b5ba0 Fix for Issue #150 - xmrig container not building 2022-06-01 11:03:29 -07:00
SChernykh 5ca9f968aa Update RandomX 2022-06-01 16:18:52 +02:00
SChernykh 6ef34bb3f5 Enabled more compiler warnings 2022-06-01 16:13:18 +02:00
SChernykh 9a5be0a321 p2pool v2.1 2022-05-31 18:20:21 +02:00
SChernykh c203f6eb0a Fixed a new cppcheck issue 2022-05-31 16:51:09 +02:00
SChernykh b713f4e702 CI: added Linux aarch64 build 2022-05-31 16:38:28 +02:00
SChernykh 111087d96a Fixed block reward share calculation 2022-05-28 22:27:46 +02:00
SChernykh 9e7666d12f Fixed data races 2022-05-28 21:46:50 +02:00
SChernykh cb147773b5 Updated cppcheck workflow on Linux 2022-05-26 21:19:01 +02:00
SChernykh d84bef8f14 Added block reward share percent to miner api 2022-05-26 11:08:16 +02:00
SChernykh 32bb318af4 Fixed cppcheck workflow on Windows 2022-05-25 20:05:17 +02:00
SChernykh 055b5e2256 Updated dependencies 2022-05-25 12:54:40 +02:00
SChernykh c1754d122f Update RandomX 2022-05-24 15:42:37 +02:00
SChernykh 6d121122f8 Fixed compiler warnings in tests 2022-05-24 15:18:09 +02:00
SChernykh bfa8334aeb Fixed executable stack 2022-05-23 21:41:17 +02:00
SChernykh 0e222afd6a Exit if server startup fails 2022-05-23 15:50:25 +02:00
SChernykh b191962d32 Handle startup errors without calling abort() 2022-05-23 09:37:11 +02:00
SChernykh 1cf13e142a SideChain: fixed alternative chain detection 2022-05-16 15:59:48 +02:00
SChernykh 8f1f81749d StratumServer: reset cumulative hashes too 2022-05-16 14:36:11 +02:00
SChernykh 72d70eba16 SideChain: added "synchronized" log message 2022-05-16 01:48:37 +02:00
SChernykh ae3612f595 Fixed worker diff display when autodiff is off 2022-05-15 18:13:36 +02:00
SChernykh 37b6457054 Show worker hashrate only when autodiff is on 2022-05-15 01:15:21 +02:00
SChernykh 9579be5c7d Optimized SideChain::miner_count() 2022-05-12 22:18:08 +02:00
SChernykh d05d47fa75 Fixed compilation 2022-05-12 16:10:55 +02:00
SChernykh bd88c04e9e SideChain: mutex locks refactoring 2022-05-12 15:24:02 +02:00
SChernykh eb3a3872eb Fixed missing blocks download logic 2022-05-12 08:49:34 +02:00
SChernykh 5cea7be405 Allow only one pending get_miner_data 2022-05-12 01:09:59 +02:00
SChernykh 516c382fe4 Update keccak.cpp 2022-05-11 16:13:00 +02:00
SChernykh 710e55422a Ignore duplicate get_miner_data 2022-05-11 16:10:10 +02:00
SChernykh 3406cf78b4 Don't return stale chain tip to other peers 2022-05-11 15:07:54 +02:00
SChernykh 8fa354796c Reload peer list when there are no connections 2022-05-11 14:09:28 +02:00
SChernykh 6c6ef1c1b8 Restart ZMQ connection if it looks dead 2022-05-11 13:50:47 +02:00
SChernykh c3c326c680 Suppress MSVC warnings 2022-05-11 10:55:36 +02:00
SChernykh 92e20485b2 Log: don't allow side effects in LOG() calls 2022-05-10 21:41:53 +02:00
SChernykh edd782ac23 Fixed custom worker names 2022-05-10 14:01:10 +02:00
SChernykh a4d84e318d TCPServer: refactored Client constructor 2022-05-09 19:13:34 +02:00
SChernykh 08ee599f79 Use the same compilation flags for tests 2022-05-09 17:57:31 +02:00
SChernykh 134f2d68a3 Fixed data races 2022-05-09 16:10:55 +02:00
SChernykh d23c46ff84 Added bsr() function to util 2022-05-09 13:02:06 +02:00
SChernykh 820c5db5e8 Log: check for logrotate only once per loop 2022-05-09 09:58:43 +02:00
SChernykh 990916ab62 StratumServer: added autodiff
Autodiff is enabled by default, target time is 30 seconds. It can be disabled with `--no-autodiff` command line option.
2022-05-08 19:03:00 +02:00
SChernykh d4e362cd76 TCPServer: optimized Client struct size 2022-05-07 12:35:31 +02:00
SChernykh 1dd06cc509 Removed std::string to avoid memory allocation 2022-05-07 11:00:29 +02:00
SChernykh 2e2bd1d137 Added log padding 2022-05-06 20:04:01 +02:00
SChernykh 0818d91f4f StratumServer: added console command workers 2022-05-06 13:19:56 +02:00
SChernykh 6e1f34b168 Termux build instructions 2022-05-05 15:53:04 +02:00
SChernykh 1a35177633 Fixed unsafe usage of log::Stream buffers 2022-05-05 14:38:56 +02:00
SChernykh c45689b02b Fixed pthread_cancel detection 2022-05-03 21:03:06 +02:00
SChernykh 8f27d940e3 Don't ban localhost 2022-05-03 13:11:55 +02:00
SChernykh 6b4640c413 Fixes for Termux 2022-05-03 07:32:47 +02:00
SChernykh 8aebcdc580 p2pool v2.0 2022-04-30 17:50:27 +02:00
SChernykh e23a2e77a8 Updated dependencies 2022-04-30 12:10:09 +02:00
SChernykh 0f9362e14e More tests for derive_view_tag 2022-04-29 22:25:22 +02:00
SChernykh ab974b9684 Monero network upgrade information 2022-04-29 14:35:06 +02:00
SChernykh 54acfee036 Merge pull request #144 from SChernykh/view_tags
v15 hardfork changes
2022-04-27 19:56:42 +02:00
SChernykh a78ddb50d5 Fixed logger thread timing logic 2022-04-24 16:22:22 +02:00
SChernykh b2f86a5f6b Ban peers with stuck or lagging monerod 2022-04-23 11:29:17 +02:00
SChernykh 2a77695832 Fixed data races in miner 2022-04-09 18:25:12 +02:00
SChernykh 21d8cb917b Fixed data race in logger 2022-04-09 10:41:20 +02:00
SChernykh 2c70bf8616 View tags support
See http://github.com/monero-project/monero/pull/8061
2022-04-09 00:56:42 +02:00
SChernykh 7522111bb7 Fixed data races 2022-04-09 00:38:15 +02:00
SChernykh 7b5b910f3f Undefined behaviour fixes 2022-04-08 22:49:08 +02:00
SChernykh afb52e4235 Check return value (Coverity fix) 2022-04-07 19:33:39 +02:00
SChernykh 93cd9a659a Code cleanup 2022-04-07 19:11:20 +02:00
SChernykh 9e287ab4f0 Logger fixes
- Fixed a race condition when stopping the logger
- Flush log file only once per batch of log lines
2022-04-07 16:55:35 +02:00
SChernykh 094209f311 P2PServer: optimized update_peer_connections() 2022-04-07 16:48:01 +02:00
SChernykh dee66c3f32 Added varint unit tests 2022-04-06 16:34:16 +02:00
SChernykh 32533e3c96 FreeBSD build 2022-04-05 19:36:37 +02:00
SChernykh a073923a82 Updated dependencies 2022-04-05 11:32:39 +02:00
SChernykh 3484b24caa Updated dependencies 2022-04-04 19:56:42 +02:00
SChernykh 1b3c1eb850 P2PServer: increased peer timeout to 15 minutes 2022-03-30 22:47:10 +02:00
SChernykh 7fe032915f Ignore SIGPIPE 2022-03-30 21:53:52 +02:00
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
123 changed files with 8239 additions and 17906 deletions
+2
View File
@@ -0,0 +1,2 @@
**/build
**/docker-compose
+197 -31
View File
@@ -1,23 +1,24 @@
name: C/C++ CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
on: [push, pull_request]
jobs:
build-ubuntu:
runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.config.os }}
strategy:
matrix:
os: [ubuntu-latest, ubuntu-18.04]
config:
- {os: ubuntu-20.04, c: gcc-11, cpp: g++-11}
- {os: ubuntu-22.04, c: gcc-12, cpp: g++-12}
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 libcurl4-openssl-dev libidn2-0-dev ${{ matrix.config.c }} ${{ matrix.config.cpp }}
- name: Checkout repository
uses: actions/checkout@v2
@@ -28,16 +29,16 @@ jobs:
run: |
mkdir build
cd build
cmake ..
make -j2
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
make -j$(nproc)
- name: Build tests
run: |
cd tests
mkdir build
cd build
cmake ..
make -j2
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
make -j$(nproc)
- name: Run tests
run: cd tests/build && ./p2pool_tests
@@ -45,7 +46,122 @@ jobs:
- name: Archive binary
uses: actions/upload-artifact@v2
with:
name: p2pool-${{ matrix.os }}
name: p2pool-${{ matrix.config.os }}
path: build/p2pool
build-ubuntu-static-libs:
runs-on: ubuntu-22.04
steps:
- name: Install dependencies
run: |
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install -y git build-essential cmake autoconf libgss-dev gcc-12 g++-12
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100 --slave /usr/bin/g++ g++ /usr/bin/g++-12 --slave /usr/bin/gcov gcov /usr/bin/gcov-12
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Build libcurl
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j$(nproc)
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake .. -DBUILD_TESTING=OFF
make -j$(nproc)
- name: Build libzmq
run: |
cd external/src/libzmq
mkdir build
cd build
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j$(nproc)
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -DSTATIC_LIBS=ON
make -j$(nproc)
- name: Build tests
run: |
cd tests
mkdir build
cd build
cmake .. -DSTATIC_LIBS=ON
make -j$(nproc)
- name: Run tests
run: cd tests/build && ./p2pool_tests
- name: Archive binary
uses: actions/upload-artifact@v2
with:
name: p2pool-ubuntu-static-libs
path: build/p2pool
build-ubuntu-aarch64:
runs-on: ubuntu-22.04
steps:
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y git build-essential cmake autoconf gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Build libcurl
run: |
cd external/src/curl
autoreconf -fi
./configure --host=aarch64-linux-gnu --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j$(nproc)
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DBUILD_TESTING=OFF
make -j$(nproc)
- name: Build libzmq
run: |
cd external/src/libzmq
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j$(nproc)
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DCMAKE_STRIP=/usr/bin/aarch64-linux-gnu-strip -DSTATIC_LIBS=ON -DARCH_ID=aarch64
make -j$(nproc)
- name: Archive binary
uses: actions/upload-artifact@v2
with:
name: p2pool-ubuntu-latest-aarch64
path: build/p2pool
build-windows-msys2:
@@ -66,22 +182,45 @@ jobs:
uses: eine/setup-msys2@v2
with:
update: true
install: mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-libuv
install: mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake make autoconf libtool automake
- name: Build libcurl
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j$(nproc)
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake .. -G "Unix Makefiles" -DBUILD_TESTING=OFF
make -j$(nproc)
- name: Build libzmq
run: |
cd external/src/libzmq
mkdir build
cd build
cmake .. -G "Unix Makefiles" -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF -DZMQ_HAVE_IPC=OFF
make -j$(nproc)
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -G "Unix Makefiles"
make -j2
cmake .. -G "Unix Makefiles" -DSTATIC_LIBS=ON
make -j$(nproc)
- name: Build tests
run: |
cd tests
mkdir build
cd build
cmake .. -G "Unix Makefiles"
make -j2
cmake .. -G "Unix Makefiles" -DSTATIC_LIBS=ON
make -j$(nproc)
- name: Run tests
run: |
@@ -96,7 +235,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 +253,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 +276,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 +285,7 @@ jobs:
strategy:
matrix:
os: [macos-latest, macos-11]
os: [macos-11, macos-12]
steps:
- name: Checkout repository
@@ -151,13 +294,36 @@ 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 autoconf libtool automake
- name: Build libcurl
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j3
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake .. -DBUILD_TESTING=OFF
make -j3
- name: Build libzmq
run: |
cd external/src/libzmq
mkdir build
cd build
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j3
- name: Build p2pool
run: |
mkdir build
cd build
cmake ..
cmake .. -DSTATIC_BINARY=ON
make -j3
- name: Build tests
@@ -165,7 +331,7 @@ jobs:
cd tests
mkdir build
cd build
cmake ..
cmake .. -DSTATIC_LIBS=ON
make -j3
- name: Run tests
+4 -7
View File
@@ -13,10 +13,7 @@ name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '44 11 * * 0'
@@ -40,7 +37,7 @@ jobs:
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
sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev libcurl4-openssl-dev libidn2-0-dev
- name: Checkout repository
uses: actions/checkout@v2
@@ -49,7 +46,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -60,7 +57,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v2
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -74,4 +71,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2
+24 -12
View File
@@ -1,10 +1,6 @@
name: cppcheck
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
on: [push, pull_request]
jobs:
cppcheck-ubuntu:
@@ -12,14 +8,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install cppcheck
run: sudo apt update && sudo apt install cppcheck
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Checkout cppcheck
uses: actions/checkout@v3
with:
repository: danmar/cppcheck
path: cppcheck-main
- name: Build cppcheck
run: |
cd cppcheck-main
make -j$(nproc) cppcheck
- name: Run cppcheck
run: |
cd cppcheck
@@ -30,14 +34,22 @@ jobs:
runs-on: windows-latest
steps:
- name: Install cppcheck
run: choco install cppcheck
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Checkout cppcheck
uses: actions/checkout@v3
with:
repository: danmar/cppcheck
path: cppcheck-main
- name: Build cppcheck
run: |
cd cppcheck-main
& "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\msbuild.exe" /m /p:Configuration=Release /p:Platform=x64
- name: Setup cmake
uses: lukka/get-cmake@latest
@@ -45,7 +57,7 @@ jobs:
run: |
mkdir build
cd build
cmake .. -G "Visual Studio 16 2019"
cmake .. -G "Visual Studio 17 2022"
- name: Run cppcheck
run: |
+61
View File
@@ -0,0 +1,61 @@
# 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:
pull_request:
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@1e7491d1428ffa2e8aa70916d0d0309c7ca32baf
# 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@v2
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 }}
+103
View File
@@ -0,0 +1,103 @@
name: Sync test
on: [push, pull_request]
jobs:
sync-test-ubuntu:
runs-on: ubuntu-22.04
steps:
- name: Install dependencies
run: |
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install -y git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev libcurl4-openssl-dev libidn2-0-dev gcc-12 g++-12
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -DDEV_TEST_SYNC=ON -DCMAKE_C_COMPILER=gcc-12 -DCMAKE_CXX_COMPILER=g++-12
make -j$(nproc)
- name: Run p2pool
timeout-minutes: 15
run: |
cd build
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 4
- name: Archive p2pool.log
uses: actions/upload-artifact@v2
with:
name: p2pool_ubuntu.log
path: build/p2pool.log
sync-test-macos:
runs-on: macos-12
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: recursive
- name: Install dependencies
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake autoconf libtool automake libuv zmq libpgm curl
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -DDEV_TEST_SYNC=ON
make -j3
- name: Run p2pool
timeout-minutes: 15
run: |
cd build
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 4
- name: Archive p2pool.log
uses: actions/upload-artifact@v2
with:
name: p2pool_macos.log
path: build/p2pool.log
sync-test-windows:
runs-on: windows-2022
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: recursive
- name: Setup cmake
uses: lukka/get-cmake@latest
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -G "Visual Studio 17 2022" -DDEV_TEST_SYNC=ON
& "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\msbuild" /m /p:Configuration=Release p2pool.vcxproj
- name: Run p2pool
timeout-minutes: 15
run: |
cd build/Release
./p2pool.exe --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 4
- name: Archive p2pool.log
uses: actions/upload-artifact@v2
with:
name: p2pool_windows.log
path: build/Release/p2pool.log
+6 -3
View File
@@ -10,12 +10,15 @@
[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
[submodule "external/src/curl"]
path = external/src/curl
url = https://github.com/SChernykh/curl
+100 -19
View File
@@ -1,22 +1,30 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 3.6)
project(p2pool)
option(STATIC_LINUX_BINARY "Build static Linux binary" OFF)
option(STATIC_BINARY "Build static binary" OFF)
option(STATIC_LIBS "Link libuv and libzmq statically" OFF)
option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned off, p2pool will rely on monerod for verifying RandomX hashes" ON)
option(DEV_TEST_SYNC "[Developer only] Sync test, stop p2pool after sync is complete" OFF)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
if (${CMAKE_VERSION} VERSION_GREATER "3.5.2")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
if (WITH_RANDOMX)
add_definitions(-DWITH_RANDOMX)
add_subdirectory(external/src/RandomX)
set(LIBS randomx)
endif()
add_subdirectory(external/src/RandomX)
set(LIBS randomx)
if (DEV_TEST_SYNC)
add_definitions(-DDEV_TEST_SYNC)
endif()
include(cmake/flags.cmake)
set(HEADERS
external/src/cryptonote/crypto-ops.h
external/src/llhttp/llhttp.h
src/block_cache.h
src/block_template.h
src/common.h
@@ -47,9 +55,6 @@ set(HEADERS
set(SOURCES
external/src/cryptonote/crypto-ops-data.c
external/src/cryptonote/crypto-ops.c
external/src/llhttp/api.c
external/src/llhttp/http.c
external/src/llhttp/llhttp.c
src/block_cache.cpp
src/block_template.cpp
src/console_commands.cpp
@@ -73,20 +78,40 @@ set(SOURCES
src/zmq_reader.cpp
)
if (WITH_RANDOMX)
set(HEADERS ${HEADERS} src/miner.h)
set(SOURCES ${SOURCES} src/miner.cpp)
endif()
if (NOT STATIC_BINARY AND NOT STATIC_LIBS)
include(FindCURL)
endif()
if (CURL_INCLUDE_DIRS)
include_directories(CURL_INCLUDE_DIRS)
else()
include_directories(external/src/curl/include)
endif()
include_directories(src)
include_directories(external/src)
include_directories(external/src/cryptonote)
include_directories(external/src/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)
add_definitions(-DCURL_STATICLIB)
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(LIBS ${LIBS} pthread)
elseif (NOT APPLE)
set(LIBS ${LIBS} pthread gss dl)
set(LIBS ${LIBS} pthread dl)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
@@ -94,11 +119,25 @@ if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
find_library(ZMQ_LIBRARY NAMES libzmq-v142-mt-s-4_3_5 PATHS "external/lib/libzmq/Release")
find_library(UV_LIBRARY_DEBUG NAMES uv_a PATHS "external/lib/libuv/Debug")
find_library(UV_LIBRARY NAMES uv_a PATHS "external/lib/libuv/Release")
find_library(CURL_LIBRARY_DEBUG NAMES libcurld PATHS "external/lib/libcurl/Debug")
find_library(CURL_LIBRARY NAMES libcurl PATHS "external/lib/libcurl/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)
if (WIN32)
find_library(CURL_LIBRARY_DEBUG NAMES libcurl.a PATHS "external/src/curl/lib/.libs" NO_DEFAULT_PATH)
find_library(CURL_LIBRARY NAMES libcurl.a PATHS "external/src/curl/lib/.libs" NO_DEFAULT_PATH)
else()
if (CURL_LIBRARIES)
set(CURL_LIBRARY_DEBUG ${CURL_LIBRARIES})
set(CURL_LIBRARY ${CURL_LIBRARIES})
else()
find_library(CURL_LIBRARY_DEBUG NAMES curl)
find_library(CURL_LIBRARY NAMES curl)
endif()
endif()
find_library(SODIUM_LIBRARY sodium)
endif()
@@ -117,20 +156,62 @@ if (SODIUM_LIBRARY)
set(LIBS ${LIBS} ${SODIUM_LIBRARY})
endif()
if(APPLE)
find_library(FOUNDATION_LIB Foundation)
find_library(CORE_FOUNDATION_LIB CoreFoundation)
find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration)
set(LIBS ${LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB})
endif()
add_definitions(/DZMQ_STATIC)
set(CMAKE_REQUIRED_FLAGS "${GENERAL_FLAGS}")
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("int main(){ return __builtin_clzll(1);}" HAVE_BUILTIN_CLZLL)
check_cxx_source_compiles("#include <intrin.h>\n#pragma intrinsic(_BitScanReverse64)\nint main(){unsigned long r;_BitScanReverse64(&r,1);return r;}" HAVE_BITSCANREVERSE64)
if (HAVE_BUILTIN_CLZLL)
add_definitions(/DHAVE_BUILTIN_CLZLL)
endif()
if (HAVE_BITSCANREVERSE64)
add_definitions(/DHAVE_BITSCANREVERSE64)
endif()
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
if (STATIC_LINUX_BINARY)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME})
if (STATIC_BINARY OR STATIC_LIBS)
if (WIN32)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} "${CMAKE_PROJECT_NAME}.exe")
else()
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME})
endif()
if (WITH_RANDOMX)
set(STATIC_LIBS randomx)
endif()
if (WIN32)
set(STATIC_LIBS ${STATIC_LIBS} ws2_32 iphlpapi userenv psapi wldap32)
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(STATIC_LIBS ${STATIC_LIBS} pthread)
elseif (APPLE)
find_library(FOUNDATION_LIB Foundation)
find_library(CORE_FOUNDATION_LIB CoreFoundation)
find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration)
set(STATIC_LIBS ${STATIC_LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB})
else()
set(STATIC_LIBS ${STATIC_LIBS} pthread dl)
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
"${CMAKE_SOURCE_DIR}/external/src/curl/lib/.libs/libcurl.a"
${STATIC_LIBS}
)
else()
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} ${LIBS})
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} debug ${CURL_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} optimized ${CURL_LIBRARY} ${LIBS})
endif()
+53
View File
@@ -0,0 +1,53 @@
FROM ubuntu:20.04
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 \
libcurl4-openssl-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:20.04
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 \
libcurl4 \
&& \
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
EXPOSE 37888
VOLUME /home/p2pool/.p2pool
WORKDIR /home/p2pool/.p2pool
ENTRYPOINT ["/p2pool"]
+190 -128
View File
@@ -2,17 +2,30 @@
Decentralized pool for Monero mining.
Mining on the 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/
### 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)
![cppcheck](https://github.com/SChernykh/p2pool/actions/workflows/cppcheck.yml/badge.svg)
![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)
- [Monero version support](#monero-version-support)
- [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
@@ -22,7 +35,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
@@ -32,7 +45,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
@@ -49,15 +62,145 @@ 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
## Monero version support
Monero will undergo a network upgrade on August 13th, 2022 (block 2,688,888). In order to continue mining after that date, you must update both Monero and P2Pool software to the latest available versions as soon as they are released.
|Monero protocol version|Required Monero software version|Required P2Pool version
|-|-|-|
|v14 (active until August 13th, 2022)|v0.17.3.0 or newer|v1.0 or newer
|v15, v16 (active after August 13th, 2022)|v0.18.0.0 or newer|v2.2 or newer
## How to mine on P2Pool
### General Considerations
- In order to mine on P2Pool, a synced Monero node using monerod v0.17.3.0 or newer is required. If you do not currently have one configured, you can find instructions to do so [here](https://sethforprivacy.com/guides/run-a-monero-node-advanced/).
- It is highly recommended that you create a separate restricted user account for mining. While P2Pool has been battle-tested for a long time now, any software may have unknown bugs/vulnerabilities.
- 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
sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev libcurl4-openssl-dev libidn2-0-dev
git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool
mkdir build && cd build
@@ -65,23 +208,15 @@ 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](https://archlinux.org/packages/community/x86_64/p2pool/)
### Arch Linux [AUR](https://wiki.archlinux.org/title/Arch_User_Repository)
Make the package: [p2pool-git](https://aur.archlinux.org/packages/p2pool-git/)
```
pacman -S p2pool
```
### [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:
@@ -91,39 +226,17 @@ nix shell github:nixos/nix/master
Run the binary:
```
nix run git+https://github.com/SChernykh/p2pool?ref=master
nix run git+https://github.com/SChernykh/p2pool?ref=master&submodules=1
```
Run the binary with arguments:
```
nix run git+https://github.com/SChernykh/p2pool?ref=master -- --help
```
### 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)
nix run git+https://github.com/SChernykh/p2pool?ref=master&submodules=1 -- --help
```
### 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
@@ -136,91 +249,40 @@ then open generated build\p2pool.sln in Visual Studio and build it there
Alternatively, you can select "Clone a repository" within the GUI, then select "Build" from the menu.
monerod binary compatible with p2pool:
### 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 curl
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 mine on P2Pool
### FreeBSD
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 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
- Monerujo v2.1.0 "Vertant" and newer
- Cake Wallet v4.2.7 and newer
- MyMonero
**General Considerations**
- Create a separate restricted user account for mining. p2pool is relatively new and may still have serious bugs/vulnerabilities!
- You have to use the primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
- Check that ports 18080 (Monero p2p port) and 37889 (p2pool 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!
Step-by-step guide:
### GNU/Linux
- Download binaries from https://github.com/SChernykh/p2pool/releases/latest
- Alternatively, 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`
- 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
- Run `./monerod --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist` **don't forget --zmq-pub parameter in the command line**
- Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-7dbb0d1fc)** on startup. Wait until it's synchronized.
- Run `./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
- 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, once completed xmrig should be able to connect to the stratum server on port 3333.
- 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
- Also check p2pool.log for any warnings and errors: `grep -E 'WARNING|ERROR' p2pool.log`
### 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.*
- Download p2pool binaries from https://github.com/SChernykh/p2pool/releases/latest
- Download xmrig binary from https://github.com/xmrig/xmrig/releases/latest *(xmrig-6.15.0-gcc-win64.zip is the current version)*
- Expand the p2pool binaries into an appropriate location (`%USERPROFILE%/bin` or `C:/bin/` are good options)
- Expand xmrig binary into appropriate location (same folder as p2pool is fine)
- Prepare huge pages (each 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 it as admin at least once per login.
- Open a command prompt and navigate to the folder where you extracted p2pool.
- *When running these commands, Windows Firewall may prompt to allow connections, click "Allow"*
- Run `.\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist` *NOTE: don't forget --zmq-pub parameter in the command line*
- Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-7dbb0d1fc)** on startup. Wait until it's synchronized.
- Run `.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
- Wait until initial p2pool sync is finished, it shouldn't take more than 5-10 minutes, once completed xmrig should be able to connect to the stratum server on port 3333.
- Run `.\xmrig.exe -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.exe -u x+10000 -o 127.0.0.1:3333`
- Windows Quickstart: Create a batch (.bat) file with the following contents and place it in your p2pool directory along with xmrig.exe.
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on FreeBSD:
```
@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
pkg install git cmake libuv libzmq4 curl
git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool
mkdir build && cd build
cmake ..
make
```
### Android (Termux)
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally in Termux:
```
pkg install git build-essential cmake libuv libzmq libcurl
git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool
mkdir build && cd build
cmake ..
make -j$(nproc)
```
## Donations
+20 -18
View File
@@ -6,45 +6,47 @@ set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(WARNING_FLAGS "-Wall -Wextra -Werror")
set(GENERAL_FLAGS "-pthread")
set(WARNING_FLAGS "-Wall -Wextra -Wcast-align -Wcast-qual -Wlogical-op -Wstrict-overflow=2 -Wundef -Wformat=2 -Wpointer-arith -Werror")
set(OPTIMIZATION_FLAGS "-Ofast -s")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GENERAL_FLAGS} ${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++")
endif()
endif()
elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
set(GENERAL_FLAGS "")
set(WARNING_FLAGS "/Wall /WX /sdl")
set(SECURITY_FLAGS "/GS /guard:cf")
set(OPTIMIZATION_FLAGS "/O2 /Oi /Ob2 /Ot /DNDEBUG /GL")
set(CMAKE_C_FLAGS_DEBUG "${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
set(CMAKE_CXX_FLAGS_DEBUG "${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
set(CMAKE_C_FLAGS_DEBUG "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
set(CMAKE_CXX_FLAGS_DEBUG "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Od /Ob0 /Zi /MTd /fsanitize=address")
set(CMAKE_C_FLAGS_RELEASE "${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
set(CMAKE_CXX_FLAGS_RELEASE "${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
set(CMAKE_C_FLAGS_RELEASE "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
set(CMAKE_CXX_FLAGS_RELEASE "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} ${OPTIMIZATION_FLAGS} /MT")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal -Werror")
set(GENERAL_FLAGS "-pthread")
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal -Wunreachable-code-aggressive -Wmissing-prototypes -Wmissing-variable-declarations -Werror")
set(OPTIMIZATION_FLAGS "-Ofast -funroll-loops -fmerge-all-constants")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GENERAL_FLAGS} ${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,
+4 -3
View File
@@ -1,9 +1,10 @@
../src/
../external/src/
../external/src/cryptonote/
../external/src/libuv/
../external/src/libuv/include/
../external/src/cppzmq/
../external/src/libzmq/
../external/src/llhttp/
../external/src/libzmq/include/
../external/src/curl/include
../external/src/RandomX/src/
../external/src/rapidjson/include
../external/src/robin-hood-hashing/src/include
+1 -1
View File
@@ -1,5 +1,5 @@
@echo off
"C:\Program Files\cppcheck\cppcheck.exe" --project=..\build\p2pool.vcxproj --project-configuration="Release|x64" -DZMQ_STATIC --platform=win64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" --includes-file=includes.txt --suppressions-list=suppressions.txt --output-file=errors_full.txt
"..\cppcheck-main\bin\cppcheck.exe" --project=..\build\p2pool.vcxproj --project-configuration="Release|x64" -DSIZE_MAX=UINT64_MAX -DRAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN --platform=win64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" --includes-file=includes.txt --suppressions-list=suppressions.txt --output-file=errors_full.txt
findstr /V /C:"external\src" errors_full.txt > errors_filtered.txt
for /f %%i in ("errors_filtered.txt") do set size=%%~zi
if %size% gtr 0 (
+2 -2
View File
@@ -1,6 +1,6 @@
#!/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
grep -v 'external' errors_full.txt | grep -v 'unmatchedSuppression' > errors_filtered.txt
../cppcheck-main/cppcheck ../src -DSIZE_MAX=UINT64_MAX -DRAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN --platform=unix64 --std=c++14 --enable=all --inconclusive --inline-suppr --template="{file}:{line}:{id}{inconclusive: INCONCLUSIVE} {message}" --includes-file=includes.txt --suppressions-list=suppressions.txt --output-file=errors_full.txt
grep -v 'external' errors_full.txt > errors_filtered.txt
if [ -s errors_filtered.txt ]; then
cat errors_filtered.txt
exit 1
-3
View File
@@ -1,6 +1,3 @@
missingIncludeSystem
unusedFunction
useStlAlgorithm
functionStatic
functionConst
unmatchedSuppression
+57
View File
@@ -0,0 +1,57 @@
# 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/)
Note: The docker compose plugin uses the command "docker compose" while the pip installed command is "docker-compose".
#### Clone the P2Pool project
```
git clone --recursive https://github.com/SChernykh/p2pool
```
#### Configure your Monero address for mining rewards
```
cd p2pool/docker-compose
./configure
```
Make sure to set your own monero **Wallet Address**. The default is to donate mining to P2Pool development.
#### Build the docker containers
```
docker compose build --no-cache
```
#### Run the node, pool, and CPU miner (or updated configuration)
```
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 (or alternate if configured) when you set it as "exposed" in the configuration
* Configure your kernel for maximum mining performance: [XMRig RandomX Optimization Guide](https://xmrig.com/docs/miner/randomx-optimization-guide)
* Many optional configurations and customizations are available by running './configure'
#### Other usefull commands
* You can **run everything in the background** by adding the "-d" argument to the "docker compose up" command: ```docker compose up -d```
* You can **stop everything** with CTRL-C or ```docker compose down```
* You can **update** by building new images with the ```--no-cache``` option. Example: ```docker compose build --no-cache``` or just update Monero with: ```docker compose build --no-cache monero``` followed by ```docker compose up```
* You can see logs when running in the background for with the "docker compose logs" command: ```docker compose logs -f```
* You can pause mining with: ```docker compose pause xmrig``` and resume mining with: ```docker compose unpause xmrig```
* You can disable mining with: ```docker compose stop xmrig``` and re-enable mining with: ```docker compose start xmrig```
* You can view your Server Statistics using a web browser if you enabled that feature in the configuration at: http://localhost:3380 (or alternate port as configured)
#### Uninstall
Change to p2pool/docker-compose directory <br />
Stop and remove all containers: ```docker compose down``` <br />
Remove the p2pool data: ```docker volume rm p2pool``` <br />
Remove the p2pool-mini data: ```docker volume rm p2pool-mini``` <br />
Remove the monero data: ```docker volume rm monero```
+14
View File
@@ -0,0 +1,14 @@
FROM python:3.10.5-bullseye
WORKDIR /app
ADD requirements.txt /app/
RUN /usr/local/bin/pip3 install -r requirements.txt
ADD configure.py /app/
ADD defaults /app/
ADD current_config.jinja2 /app/
ADD docker-compose.jinja2 /app/
ENTRYPOINT ["/usr/local/bin/python3"]
CMD ["/app/configure.py"]
+951
View File
@@ -0,0 +1,951 @@
#!/bin/env python3
import sys
import time
import json
from jinja2 import Template
import npyscreen
# Help text in a box at the bottom of the screen
class HelpBoxBase(npyscreen.BoxTitle):
def splitlines(self):
all_help = []
for fld, msg in self.help_msgs.items():
if fld[-1] != ":":
fld += ":"
message = [m.lstrip() for m in msg if m != ""]
helpline = f"{fld:<28}{'. '.join(message)}"
all_help.append(helpline)
all_help.append("")
return all_help
def display_help_message(self, field):
self.set_values(
self.help_msgs.get(field, "No help availabe for {}".format(field))
)
self.clear()
self.display()
class P2PoolHelpBox(HelpBoxBase):
help_msgs = {
"Wallet Address:": [
"Your monero wallet address for receiving mining reqards",
"",
"Note: You have to use a primary wallet address for mining",
" Subaddresses and integrated addresses are not supported!",
],
"P2Pool Sidechain:": [
"Which P2Pool sidechain to mine on",
" use main for faster miners",
" use mini for slower miners",
],
"Enable Server Statistics": [
"Provide access to your P2Pool server statictics via a web interface",
],
"Statistics Port:": [
"Port number (or IP:Port) to expose web access to your P2Pool server",
"statictics",
],
"Expose Stratum Port": [
"Expose the P2Pool stratum port to your network so external miners",
"can connect",
"Note: You may choose to open this port in your hosts firewall and/or",
" router to allow miners outside your network to connect",
],
"Stratum Port:": [
"Port number (or IP:Port) to expose P2Pool stratum to your network to",
"allow external miners to connect",
"Note: You may choose to open this port in your hosts firewall and/or",
" router to allow miners outside your network to connect",
],
"P2Pool Log Level:": [
"Verbosity of the log; (Less) 0 - 6 (More)",
],
"Enable Autodiff": [
"Use automatic difficulty adjustment for miners connected to stratum",
],
"Enable Light Mode": [
"Don't allocate RandomX dataset, saves 2GB of RAM",
],
"Disable Cache": [
"Disable p2pool.cache (not recommended)",
],
"Additional P2Pool Options:": [
"Additional options to pass to p2pool commandline",
"",
"Note: Advanced - Only add options if you know what you are doing",
" See ouput of 'p2pool --help' for available options",
],
"Next": [
"Next configuration menu",
],
"Save": [
"Save current configuration and exit",
],
"Cancel": [
"Exit without saving",
],
}
class MoneroHelpBox(HelpBoxBase):
help_msgs = {
"Configure Monero Node": [
"Configure and run a Monero Node",
"",
"Note: You must either configure a local node or specify a public node",
],
"Monero Version:": [
"Version of Monero to build; 'latest' for the most recent release",
"",
"Note: Must be v0.17.3.0 or later for p2pool support",
" See: https://github.com/monero-project/monero/tags",
],
"Prune Blockchain": [
"Prune the Monero node to limit the size of the blockchain on disk",
],
"Monero Log Level:": [
"Verbosity of the log; (Less) 0 - 4 (More)",
"",
"Note: settings above 0 are very noisy",
],
"Expose RPC Port": [
"Expose restricted RPC API port to your network so external services",
"(wallets for example) can connect",
"Note: You may choose to open this port in your hosts firewall and/or",
" router to allow services outside your network to connect",
],
"RPC Port:": [
"TCP port to listen on for RPC connections",
],
"RPC Login:": [
"Specify username[:password] required to connect to the RPC API",
],
"Limit Data Rates": [
"Set a limit value for incoming and outgoing data transfer",
],
"Rate Limit Up:": [
"Set outgoing data transfer limit [kB/s]",
],
"Rate Limit Down:": [
"Set incoming data transfer limit [kB/s]",
],
"Sync Pruned Blocks": [
"Accept pruned blocks instead of pruning yourself to save",
"network transfer",
],
"Fast Block Sync": [
'Sync up most of the way by using embedded, "known" (old) block',
"hashes without calculating the block hash to verify the proof of work",
"",
"Note: Faster initial sync by trusting the monerod binary",
],
"Public Node:": [
"Public Monero Node to Use",
"",
"Note: The public node must have both Monero RPC and zmq-pub ports",
" available",
],
"Node Login:": [
"Specify username[:password] required to connect to public monero",
"node RPC API (if required)",
],
"Additional monerod Options:": [
"Additional options to pass to monerod commandline",
"",
"Note: Advanced - Only add options if you know what you are doing",
" See 'https://monerodocs.org/interacting/monerod-reference/'",
],
"Prev": [
"Previous configuration menu",
],
"Next": [
"Next configuration menu",
],
"Save": [
"Save current configuration and exit",
],
"Cancel": [
"Exit without saving",
],
}
class XMRigHelpBox(HelpBoxBase):
help_msgs = {
"Configure XMRig CPU Miner": [
"Configure and run an XMRig CPU Miner",
"",
"Note: You must either configure am XMRig CPU Miner or expose the",
" P2Pool stratum port and connect an external miner, or both",
],
"Username:": [
"Set a username for the miner",
],
"Use Fixed Difficulty": [
"Used a fixed minig difficulty",
"",
"Note: Allows you to see XMRig submitting shares below P2Pool threshold",
],
"Fixed Difficulty:": [
"Set a fixed mining difficulty",
"",
"Note: Allows you to see XMRig submitting shares below P2Pool threshold",
],
"CPU Use %:": [
"Maximum CPU threads count (in percentage) hint for autoconfig",
"Note: Applies to cores only. If you have HyperThreading enabled you",
" should divide this value by 2 (use 0-50%). Reference:",
" https://github.com/xmrig/xmrig/issues/1670#issuecomment-644433778",
],
"CPU Priority:": [
"Set process priority (0 idle, 2 normal to 5 highest)",
],
"Additional XMRig Options:": [
"Additional options to pass to xmrig",
"",
"Note: Advanced - Only add options if you know what you are doing",
" See 'https://xmrig.com/docs/miner/command-line-options'",
],
"Prev": [
"Previous configuration menu",
],
"Save": [
"Save current configuration and exit",
],
"Cancel": [
"Exit without saving",
],
}
##
# Custom (integer) values for title slider
class IntegerSlider(npyscreen.Slider):
def translate_value(self):
from_val = int(str(self.value).split(".")[0])
out_of_val = int(str(self.out_of).split(".")[0])
if from_val >= 1000:
from_val = str(from_val / 1000).split(".")[0] + "K"
out_of_val = str(out_of_val / 1000).split(".")[0] + "K"
return "{}/{}".format(from_val, out_of_val)
class TitleIntegerSlider(npyscreen.TitleSlider):
_entry_type = IntegerSlider
##
# Patched updateDependents for FormControlCheckbox
class PatchedFormControlCheckbox(npyscreen.FormControlCheckbox):
def updateDependents(self):
if self.value:
for w in self._visibleWhenSelected:
try:
w.fc_visible
except AttributeError:
w.fc_visible = {}
w.fc_visible[self.name] = True
for w in self._notVisibleWhenSelected:
try:
w.fc_visible
except AttributeError:
w.fc_visible = {}
w.fc_visible[self.name] = False
else:
for w in self._visibleWhenSelected:
try:
w.fc_visible
except AttributeError:
w.fc_visible = {}
w.fc_visible[self.name] = False
for w in self._notVisibleWhenSelected:
try:
w.fc_visible
except AttributeError:
w.fc_visible = {}
w.fc_visible[self.name] = True
for w in self._visibleWhenSelected + self._notVisibleWhenSelected:
w.hidden = False in w.fc_visible.values()
w.editable = not False in w.fc_visible.values()
self.parent.display()
def set_value(self, value):
self.value = value
self.display()
def display(self):
self.updateDependents()
super()
class PrevButton(npyscreen.Button):
def whenToggled(self):
self.value = False
self.parent.prev_form()
class NextButton(npyscreen.Button):
def whenToggled(self):
self.value = False
self.parent.next_form()
class SaveButton(npyscreen.Button):
def whenToggled(self):
self.find_parent_app().save_and_exit()
class CancelButton(npyscreen.Button):
def whenToggled(self):
self.find_parent_app().cancel_and_exit()
##
# Config Forms Base Class
class ConfigFormBase(npyscreen.FormBaseNew):
name_size = 20
indent = 5
current_config = None
defaults = None
ALLOW_RESIZE = False
def while_editing(self, arg):
self.help.display_help_message(arg.name)
self.display()
def get_default_config(self):
# Return defaults
if self.defaults is None:
with open("defaults") as defaults_file:
self.defaults = json.load(defaults_file)
return self.defaults
def get_current_config(self):
# Return current config
if self.current_config is None:
# Read current config
with open("/docker-compose/current_config") as current_config:
self.current_config = json.load(current_config)
return self.current_config
def reset_defaults(self, arg):
# Set config to default values
ok = npyscreen.notify_ok_cancel(
"Set current form values to defaults", title="Reset to Defaults"
)
if not ok:
return
defaults = self.get_default_config()
self.set_config(defaults)
##
# P2Pool Configuration Form
class P2PoolConfigForm(ConfigFormBase):
def create(self):
# Add Hot-Key Controls
self.add_handlers({"^D": self.reset_defaults})
# Add P2Pool Configuration
self.add(
npyscreen.TitleText,
name="## P2Pool Configuration",
editable=False,
begin_entry_at=50,
)
self.wallet_address = self.add(
npyscreen.TitleText,
name="Wallet Address:",
value="",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.nextrely += 1
self.sidechain = self.add(
npyscreen.TitleSelectOne,
name="P2Pool Sidechain:",
values=["main", "mini"],
scroll_exit=True,
max_height=2,
value=[
0,
],
begin_entry_at=self.name_size,
relx=self.indent,
)
self.nextrely += 1
self.enable_statistics = self.add(
PatchedFormControlCheckbox,
name="Enable Server Statistics",
value=True,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.statistics_port = self.add(
npyscreen.TitleText,
name="Statistics Port:",
value="3334",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.enable_statistics.addVisibleWhenSelected(self.statistics_port)
self.nextrely += 1
self.expose_stratum_port = self.add(
PatchedFormControlCheckbox,
name="Expose Stratum Port",
value=True,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.stratum_port = self.add(
npyscreen.TitleText,
name="Stratum Port:",
value="3333",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.expose_stratum_port.addVisibleWhenSelected(self.stratum_port)
self.nextrely += 1
self.p2pool_log_level = self.add(
TitleIntegerSlider,
name="P2Pool Log Level:",
out_of=6,
value=3,
lowest=0,
step=1,
width=43,
begin_entry_at=20,
label=True,
block_color=None,
relx=self.indent,
)
self.nextrely += 1
self.autodiff = self.add(
PatchedFormControlCheckbox,
name="Enable Autodiff",
value=True,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.nextrely += 1
self.light_mode = self.add(
PatchedFormControlCheckbox,
name="Enable Light Mode",
value=False,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.nextrely += 1
self.no_cache = self.add(
npyscreen.Checkbox,
name="Disable Cache",
value=False,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.nextrely += 1
self.p2pool_extra = self.add(
npyscreen.TitleText,
name="Additional P2Pool Options:",
value="",
begin_entry_at=self.name_size + 10,
relx=self.indent,
)
self.nextrely += 1
self.nextrely += 1
# Add "Next", "Save", and "Cancel" buttons
self.next_button = self.add(NextButton, name="Next", relx=1)
self.nextrely -= 1
self.save_button = self.add(SaveButton, name="Save", relx=8)
self.nextrely -= 1
self.cancel_button = self.add(CancelButton, name="Cancel", relx=15)
self.nextrely += 1
# Add Help Box
self.help = self.add(
P2PoolHelpBox,
name="Commands: ^D: Load Defaults - ^C: Exit Without Saving",
values=[""],
editable=False,
)
# Start with current config
self.set_config(self.get_current_config())
def next_form(self):
self.find_parent_app().switchForm("MONERO")
def set_config(self, config):
self.wallet_address.set_value(config["wallet_address"])
self.sidechain.set_value(config["sidechain"])
self.enable_statistics.set_value(config["enable_statistics"])
self.statistics_port.set_value(config["statistics_port"])
self.expose_stratum_port.set_value(config["expose_stratum_port"])
self.stratum_port.set_value(config["stratum_port"])
self.p2pool_log_level.set_value(config["p2pool_log_level"])
self.autodiff.set_value(config["enable_autodiff"])
self.light_mode.set_value(config["light_mode"])
self.no_cache.value = config["no_cache"]
self.p2pool_extra.set_value(config["p2pool_options"])
self.DISPLAY()
def get_config(self):
config = {
"wallet_address": self.wallet_address.value,
"sidechain": self.sidechain.value,
"enable_statistics": self.enable_statistics.value,
"statistics_port": self.statistics_port.value,
"expose_stratum_port": self.expose_stratum_port.value,
"stratum_port": self.stratum_port.value,
"p2pool_log_level": self.p2pool_log_level.value,
"enable_autodiff": self.autodiff.value,
"light_mode": self.light_mode.value,
"no_cache": self.no_cache.value,
"p2pool_options": self.p2pool_extra.value,
}
return config
##
# Monero Configuration Form
class MoneroConfigForm(ConfigFormBase):
def create(self):
# Add Hot-Key Controls
self.add_handlers({"^D": self.reset_defaults})
# Add Monero Configuration
self.add(
npyscreen.TitleText,
name="## Monero Node Configuration",
editable=False,
begin_entry_at=50,
)
self.configure_monero_node = self.add(
PatchedFormControlCheckbox,
name="Configure Monero Node",
value=True,
relx=self.indent,
)
self.nextrely += 1
self.monero_git_tag = self.add(
npyscreen.TitleText,
name="Monero Version:",
value="latest",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.monero_git_tag)
self.prune_node = self.add(
PatchedFormControlCheckbox,
name="Prune Blockchain",
value=True,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.prune_node)
self.monero_log_level = self.add(
TitleIntegerSlider,
name="Monero Log Level:",
out_of=4,
value=0,
lowest=0,
step=1,
width=43,
begin_entry_at=20,
label=True,
block_color=None,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.monero_log_level)
self.nextrely += 1
self.expose_rpc_port = self.add(
PatchedFormControlCheckbox,
name="Expose RPC Port",
value=False,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.expose_rpc_port)
self.rpc_port = self.add(
npyscreen.TitleText,
name="RPC Port:",
value="18081",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.rpc_port)
self.expose_rpc_port.addVisibleWhenSelected(self.rpc_port)
self.rpc_login = self.add(
npyscreen.TitleText,
name="RPC Login:",
value="",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.expose_rpc_port.addVisibleWhenSelected(self.rpc_login)
self.configure_monero_node.addVisibleWhenSelected(self.rpc_login)
self.nextrely += 1
self.limit_data_rates = self.add(
PatchedFormControlCheckbox,
name="Limit Data Rates",
value=False,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.limit_data_rates)
self.rate_limit_up = self.add(
npyscreen.TitleText,
name="Rate Limit Up:",
value="2048",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.limit_data_rates.addVisibleWhenSelected(self.rate_limit_up)
self.configure_monero_node.addVisibleWhenSelected(self.rate_limit_up)
self.rate_limit_down = self.add(
npyscreen.TitleText,
name="Rate Limit Down:",
value="8192",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.limit_data_rates.addVisibleWhenSelected(self.rate_limit_down)
self.configure_monero_node.addVisibleWhenSelected(self.rate_limit_down)
self.nextrely += 1
self.sync_pruned_blocks = self.add(
npyscreen.Checkbox,
name="Sync Pruned Blocks",
value=False,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.sync_pruned_blocks)
self.prune_node.addVisibleWhenSelected(self.sync_pruned_blocks)
self.fast_sync = self.add(
npyscreen.Checkbox,
name="Fast Block Sync",
value=False,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.fast_sync)
self.nextrely += 1
self.public_node = self.add(
npyscreen.TitleText,
name="Public Node:",
value="",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addInvisibleWhenSelected(self.public_node)
self.node_login = self.add(
npyscreen.TitleText,
name="Node Login:",
value="",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addInvisibleWhenSelected(self.node_login)
self.nextrely += 1
self.monero_extra = self.add(
npyscreen.TitleText,
name="Additional monerod Options:",
value="",
begin_entry_at=self.name_size + 10,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.monero_extra)
self.nextrely += 1
self.nextrely += 1
# Add "Prev", "Next", "Save", and "Cancel" buttons
self.prev_button = self.add(PrevButton, name="Prev", relx=1)
self.nextrely -= 1
self.next_button = self.add(NextButton, name="Next", relx=8)
self.nextrely -= 1
self.save_button = self.add(SaveButton, name="Save", relx=15)
self.nextrely -= 1
self.cancel_button = self.add(CancelButton, name="Cancel", relx=22)
self.nextrely += 1
# Add Help Box
self.help = self.add(
MoneroHelpBox,
name="Commands: ^D: Load Defaults - ^C: Exit Without Saving",
values=[""],
editable=False,
)
# Start with current config
self.set_config(self.get_current_config())
def prev_form(self):
self.find_parent_app().switchForm("MAIN")
def next_form(self):
self.find_parent_app().switchForm("XMRIG")
def set_config(self, config):
self.configure_monero_node.set_value(config["configure_monero"])
self.monero_git_tag.set_value(config["monero_version"])
self.prune_node.value = config["prune_blockchain"]
self.monero_log_level.set_value(config["monero_log_level"])
self.expose_rpc_port.set_value(config["expose_rpc_port"])
self.rpc_port.set_value(config["rpc_port"])
self.rpc_login.set_value(config["rpc_login"])
self.limit_data_rates.set_value(config["limit_data_rates"])
self.rate_limit_up.set_value(config["rate_limit_up"])
self.rate_limit_down.set_value(config["rate_limit_down"])
self.sync_pruned_blocks.value = config["sync_pruned_blocks"]
self.fast_sync.value = config["fast_sync"]
self.monero_extra.set_value(config["monero_options"])
self.public_node.set_value(config["public_monero_node"])
self.node_login.set_value(config["monero_node_login"])
self.DISPLAY()
def get_config(self):
config = {
"configure_monero": self.configure_monero_node.value,
"monero_version": self.monero_git_tag.value,
"prune_blockchain": self.prune_node.value,
"monero_log_level": self.monero_log_level.value,
"expose_rpc_port": self.expose_rpc_port.value,
"rpc_port": self.rpc_port.value,
"rpc_login": self.rpc_login.value,
"limit_data_rates": self.limit_data_rates.value,
"rate_limit_up": self.rate_limit_up.value,
"rate_limit_down": self.rate_limit_down.value,
"sync_pruned_blocks": self.sync_pruned_blocks.value,
"fast_sync": self.fast_sync.value,
"monero_options": self.monero_extra.value,
"public_monero_node": self.public_node.value,
"monero_node_login": self.node_login.value,
}
return config
##
# XMRig Configuration Form
class XMRigConfigForm(ConfigFormBase):
def create(self):
# Add Hot-Key Controls
self.add_handlers({"^D": self.reset_defaults})
# Hidden caryover from P2Pool form
self.autodiff = self.add(
PatchedFormControlCheckbox,
name="Enable Autodiff",
value=True,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.autodiff.hide = True
self.autodiff.editable = False
self.nextrely -= 1
# Add XMRig Configuration
self.add(
npyscreen.TitleText,
name="## XMRig Miner Configuration",
editable=False,
begin_entry_at=50,
)
self.configure_xmrig_miner = self.add(
PatchedFormControlCheckbox,
name="Configure XMRig CPU Miner",
value=True,
relx=self.indent,
)
self.nextrely += 1
self.username = self.add(
npyscreen.TitleText,
name="Username:",
value="",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_xmrig_miner.addVisibleWhenSelected(self.username)
self.nextrely += 1
self.use_fixed_difficulty = self.add(
PatchedFormControlCheckbox,
name="Use Fixed Difficulty",
value=True,
relx=self.indent,
)
self.configure_xmrig_miner.addVisibleWhenSelected(self.use_fixed_difficulty)
self.autodiff.addInvisibleWhenSelected(self.use_fixed_difficulty)
self.fixed_difficulty = self.add(
TitleIntegerSlider,
name="Fixed Difficulty:",
out_of=2000000,
value=500000,
lowest=50000,
step=50000,
width=51,
begin_entry_at=20,
label=True,
relx=self.indent,
)
self.configure_xmrig_miner.addVisibleWhenSelected(self.fixed_difficulty)
self.use_fixed_difficulty.addVisibleWhenSelected(self.fixed_difficulty)
self.autodiff.addInvisibleWhenSelected(self.fixed_difficulty)
self.nextrely += 1
self.cpu_threads = self.add(
TitleIntegerSlider,
name="CPU Use %:",
out_of=100,
value=100,
lowest=1,
step=10,
width=48,
begin_entry_at=20,
label=True,
relx=self.indent,
)
self.configure_xmrig_miner.addVisibleWhenSelected(self.cpu_threads)
self.nextrely += 1
self.cpu_priority = self.add(
TitleIntegerSlider,
name="CPU Priority:",
out_of=5,
value=2,
lowest=0,
step=1,
width=48,
begin_entry_at=20,
label=True,
relx=self.indent,
)
self.configure_xmrig_miner.addVisibleWhenSelected(self.cpu_priority)
self.nextrely += 1
self.xmrig_extra = self.add(
npyscreen.TitleText,
name="Additional XMRig Options:",
value="",
begin_entry_at=self.name_size + 10,
relx=self.indent,
)
self.configure_xmrig_miner.addVisibleWhenSelected(self.xmrig_extra)
self.nextrely += 1
self.nextrely += 1
# Add "Prev", "Save", and "Cancel" buttons
self.prev_button = self.add(PrevButton, name="Prev", relx=1)
self.nextrely -= 1
self.save_button = self.add(SaveButton, name="Save", relx=8)
self.nextrely -= 1
self.cancel_button = self.add(CancelButton, name="Cancel", relx=15)
self.nextrely += 1
# Add Help Box
self.help = self.add(
XMRigHelpBox,
name="Commands: ^D: Load Defaults - ^C: Exit Without Saving",
values=[""],
editable=False,
)
# Start with current config
self.set_config(self.get_current_config())
def prev_form(self):
self.find_parent_app().switchForm("MONERO")
def next_form(self):
self.find_parent_app().switchForm(None)
def beforeEditing(self):
# Cary autodiff value from P2Pool config form
enable_autodiff = self.find_parent_app().get_p2pool_config()["enable_autodiff"]
self.autodiff.set_value(enable_autodiff)
def set_config(self, config):
self.configure_xmrig_miner.set_value(config["configure_xmrig"])
self.username.set_value(config["xmrig_username"])
self.use_fixed_difficulty.set_value(config["use_fixed_difficulty"])
self.fixed_difficulty.set_value(config["fixed_difficulty"])
self.cpu_threads.set_value(config["cpu_percent"])
self.cpu_priority.set_value(config["cpu_priority"])
self.xmrig_extra.set_value(config["xmrig_options"])
self.DISPLAY()
def get_config(self):
config = {
"configure_xmrig": self.configure_xmrig_miner.value,
"xmrig_username": self.username.value,
"use_fixed_difficulty": self.use_fixed_difficulty.value,
"fixed_difficulty": self.fixed_difficulty.value,
"cpu_percent": self.cpu_threads.value,
"cpu_priority": self.cpu_priority.value,
"xmrig_options": self.xmrig_extra.value,
}
return config
##
# Our P2Pool configuration App
class ConfigApp(npyscreen.NPSAppManaged):
current_config = None
defaults = None
def onStart(self):
self.p2pool_form = self.addForm(
"MAIN",
P2PoolConfigForm,
name="P2Pool for docker-compose: P2Pool Configuration",
minimum_lines=35,
minimum_columns=80,
)
self.monero_form = self.addForm(
"MONERO",
MoneroConfigForm,
name="P2Pool for docker-compose: Monero Configuration",
minimum_lines=35,
minimum_columns=80,
)
self.xmrig_form = self.addForm(
"XMRIG",
XMRigConfigForm,
name="P2Pool for docker-compose: XMRig Configuration",
minimum_lines=35,
minimum_columns=80,
)
def get_p2pool_config(self):
return self.p2pool_form.get_config()
def get_config(self):
p2pool_config = self.p2pool_form.get_config()
monero_config = self.monero_form.get_config()
xmrig_config = self.xmrig_form.get_config()
return p2pool_config | monero_config | xmrig_config
def save_and_exit(self):
# Get config from all forms
config = self.get_config()
# Save "current config" values file
with open("current_config.jinja2", "r") as current_config:
template = current_config.read()
rendered = Template(template).render(config)
with open("/docker-compose/current_config", "w") as current_config:
current_config.write(rendered)
# Render and save docker-compose file
with open("docker-compose.jinja2", "r") as compose_file:
template = compose_file.read()
rendered = Template(template).render(config, trim_blocks=True)
with open("/docker-compose/docker-compose.yml", "w") as compose_file:
compose_file.write(rendered)
npyscreen.notify("Saved current settings", title="Saved")
self.switchForm(None)
self.saved = True
def cancel_and_exit(self):
self.switchForm(None)
self.saved = False
##
if __name__ == "__main__":
try:
time.sleep(1) # Give docker a second to initialize the terminal
App = ConfigApp()
App.run()
print("\n\n")
if App.saved:
print("Configuration Saved")
else:
print("Configuration Aborted")
sys.exit(1)
except KeyboardInterrupt:
print("Configuration Aborted")
sys.exit(1)
+35
View File
@@ -0,0 +1,35 @@
{
"wallet_address": {{ wallet_address | tojson(indent=2) }},
"configure_monero": {{ configure_monero | tojson(indent=2) }},
"configure_xmrig": {{ configure_xmrig | tojson(indent=2) }},
"sidechain": {{ sidechain | tojson(indent=2) }},
"enable_statistics": {{ enable_statistics | tojson(indent=2) }},
"statistics_port": {{ statistics_port | tojson(indent=2) }},
"expose_stratum_port": {{ expose_stratum_port | tojson(indent=2) }},
"stratum_port": {{ stratum_port | tojson(indent=2) }},
"p2pool_log_level": {{ p2pool_log_level | int | tojson(indent=2) }},
"enable_autodiff": {{ enable_autodiff | tojson(indent=2) }},
"light_mode": {{ light_mode | tojson(indent=2) }},
"no_cache": {{ no_cache | tojson(indent=2) }},
"p2pool_options": {{ p2pool_options | tojson(indent=2) }},
"monero_version": {{ monero_version | tojson(indent=2) }},
"prune_blockchain": {{ prune_blockchain | tojson(indent=2) }},
"monero_log_level": {{ monero_log_level | int | tojson(indent=2) }},
"expose_rpc_port": {{ expose_rpc_port | tojson(indent=2) }},
"rpc_port": {{ rpc_port | tojson(indent=2) }},
"rpc_login": {{ rpc_login | tojson(indent=2) }},
"limit_data_rates": {{ limit_data_rates | tojson(indent=2) }},
"rate_limit_up": {{ rate_limit_up | tojson(indent=2) }},
"rate_limit_down": {{ rate_limit_down | tojson(indent=2) }},
"sync_pruned_blocks": {{ sync_pruned_blocks | tojson(indent=2) }},
"fast_sync": {{ fast_sync | tojson(indent=2) }},
"monero_options": {{ monero_options | tojson(indent=2) }},
"public_monero_node": {{ public_monero_node | tojson(indent=2) }},
"monero_node_login": {{ monero_node_login | tojson(indent=2) }},
"xmrig_username": {{ xmrig_username | tojson(indent=2) }},
"use_fixed_difficulty": {{ use_fixed_difficulty | tojson(indent=2) }},
"fixed_difficulty": {{ fixed_difficulty | int | tojson(indent=2) }},
"cpu_percent": {{ cpu_percent | int | tojson(indent=2) }},
"cpu_priority": {{ cpu_priority | int | tojson(indent=2) }},
"xmrig_options": {{ xmrig_options | tojson(indent=2) }}
}
+35
View File
@@ -0,0 +1,35 @@
{
"wallet_address": "44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg",
"configure_monero": true,
"configure_xmrig": true,
"sidechain": [0],
"enable_statistics": true,
"statistics_port": "3380",
"expose_stratum_port": false,
"stratum_port": "3333",
"p2pool_log_level": 3,
"enable_autodiff": true,
"light_mode": false,
"no_cache": false,
"p2pool_options": "",
"monero_version": "latest",
"prune_blockchain": true,
"monero_log_level": 0,
"expose_rpc_port": false,
"rpc_port": "18081",
"rpc_login": "",
"limit_data_rates": false,
"rate_limit_up": "2048",
"rate_limit_down": "8192",
"sync_pruned_blocks": false,
"fast_sync": false,
"monero_options": "",
"public_monero_node": "",
"monero_node_login": "",
"xmrig_username": "p2pool",
"use_fixed_difficulty": true,
"fixed_difficulty": 500000,
"cpu_percent": 100,
"cpu_priority": 2,
"xmrig_options": ""
}
+182
View File
@@ -0,0 +1,182 @@
---
version: '3.4'
networks:
p2pool:
driver: bridge
volumes:
p2pool:
{% if sidechain[0] == 0 %}
name: p2pool
{% else %}
name: p2pool-mini
{% endif %}
{% if configure_monero == True %}
monero:
name: monero
{% endif %}
services:
p2pool:
image: p2pool:latest
build: ../
container_name: p2pool-p2pool
networks:
- p2pool
privileged: true
ports:
{% if sidechain[0] == 0 %}
- 37888:37888/tcp
{% else %}
- 37889:37889/tcp
{% endif %}
{% if expose_stratum_port == True %}
- {{ stratum_port }}:3333/tcp
{% endif %}
volumes:
- p2pool:/home/p2pool/.p2pool:rw
- /dev/null:/home/p2pool/.p2pool/p2pool.log:rw
- /dev/hugepages:/dev/hugepages:rw
{% if configure_monero == True %}
depends_on:
- monero
{% endif %}
restart: unless-stopped
command: >-
{% if configure_monero == True %}
--host monero
{% else %}
--host {{ public_monero_node }}
--rpc-login {{ monero_node_login }}
{% endif %}
--wallet {{ wallet_address }}
--loglevel {{ p2pool_log_level | int }}
{% if sidechain[0] == 1 %}
--mini
{% endif %}
{% if enable_autodiff == False %}
--no-autodiff
{% endif %}
{% if enable_statistics == True %}
--local-api
--data-api /home/p2pool/.p2pool
{% endif %}
{% if light_mode == True %}
--light-mode
{% endif %}
{% if no_cache == True %}
--no-cache
{% endif %}
{% if rpc_login != "" %}
--rpc-login {{ rpc_login }}
{% endif %}
{% if p2pool_options != "" %}
{{ p2pool_options }}
{% endif %}
{% if enable_statistics == True %}
statistics:
image: statistics:latest
build:
context: statistics
container_name: p2pool-statistics
networks:
- p2pool
ports:
- {{ statistics_port }}:80/tcp
volumes:
- p2pool:/data:ro
depends_on:
- p2pool
restart: unless-stopped
{% endif %}
{% if configure_monero == True %}
monero:
image: monero:latest
build:
context: monero
args:
- MONERO_GIT_TAG={{ monero_version }}
container_name: p2pool-monero
networks:
- p2pool
ports:
- 18080:18080/tcp
{% if expose_rpc_port == True %}
- {{ rpc_port }}:18081/tcp
{% endif %}
volumes:
- monero:/home/monero/.bitmonero:rw
- /dev/null:/home/monero/.bitmonero/bitmonero.log:rw
- /dev/hugepages:/dev/hugepages:rw
restart: unless-stopped
command: >-
--zmq-pub tcp://0.0.0.0:18083
--disable-dns-checkpoints
--enable-dns-blocklist
--non-interactive
--p2p-bind-ip=0.0.0.0
--p2p-bind-port=18080
--rpc-bind-ip=0.0.0.0
--rpc-bind-port=18081
--restricted-rpc
--confirm-external-bind
--log-level={{ monero_log_level | int }}
{% if prune_blockchain == True %}
--prune-blockchain
{% if sync_pruned_blocks == True %}
--sync-pruned-blocks
{% endif %}
{% endif %}
{% if rpc_login != "" %}
--rpc-login {{ rpc_login }}
{% endif %}
{% if limit_data_rates == True %}
--limit-rate-up {{ rate_limit_up }}
--limit-rate-down {{ rate_limit_down }}
{% endif %}
{% if fast_sync == True %}
--fast-block-sync=1
{% else %}
--fast-block-sync=0
{% endif %}
{% if monero_options != "" %}
{{ monero_options }}
{% endif %}
{% endif %}
{% if configure_xmrig == True %}
xmrig:
image: xmrig:latest
build: xmrig
container_name: p2pool-xmrig
networks:
- p2pool
privileged: true
volumes:
- /dev:/dev:ro
- /lib/modules:/lib/modules:ro
- /dev/hugepages:/dev/hugepages:rw
depends_on:
- p2pool
restart: unless-stopped
command: >-
--randomx-1gb-pages
-o p2pool:3333
{% if enable_autodiff == False and use_fixed_difficulty == True %}
-u {{ xmrig_username }}+{{ fixed_difficulty | int }}
{% else %}
-u {{ xmrig_username }}
{% endif %}
--cpu-max-threads-hint={{ cpu_percent | int }}
--cpu-priority={{ cpu_priority | int }}
{% if xmrig_options != "" %}
{{ xmrig_options }}
{% endif %}
{% endif %}
+3
View File
@@ -0,0 +1,3 @@
npyscreen==4.10.5
MarkupSafe==2.0.1
jinja2==2.11.3
Vendored Executable
+45
View File
@@ -0,0 +1,45 @@
#!/bin/bash
echo ""
echo ""
echo "Verifying Requirements:"
DOCKER_VER=$(docker version -f "{{.Server.Version}}" 2> /dev/null)
if [ -z "$DOCKER_VER" ]; then
echo "Docker not found; install it: https://docs.docker.com/engine/install/"
exit 1
fi
if [ "$(echo "$DOCKER_VER"| cut -d'.' -f 1)" -ge 19 ] && \
[ "$(echo "$DOCKER_VER"| cut -d'.' -f 2)" -ge 0 ] && \
[ "$(echo "$DOCKER_VER"| cut -d'.' -f 3)" -ge 3 ]; then
echo "Docker Found; OK"
else
echo "Docker version less than 19.0.3; upgrade it: https://docs.docker.com/engine/install/"
exit 1
fi
docker compose version 2>&1 > /dev/null
COMPOSE_PLUGIN_RC=$?
docker-compose --version 2>&1 > /dev/null
COMPOSE_CLI_RC=$?
if [ "$COMPOSE_PLUGIN_RC" -eq 0 ] || [ "$COMPOSE_CLI_RC" -eq 0 ]; then
echo "Docker Compose found; OK"
if [ "$COMPOSE_PLUGIN_RC" -eq 0 ]; then
COMPOSE_COMMAND="docker compose"
else
COMPOSE_COMMAND="docker-compose"
fi
else
echo "Docker Compose not found; install it: https://docs.docker.com/compose/install/compose-plugin/"
exit 1
fi
echo ""
echo ""
echo "Building and Running P2Pool docker-compose Configuration"
docker build -t p2pool_config:latest cfg
docker run -it --rm -v $PWD:/docker-compose --user $(id -u):$(id -g) p2pool_config:latest
CONFIGURE_RC=$?
echo ""
echo ""
if [ "$CONFIGURE_RC" -eq 0 ]; then
echo "P2Pool is configured. Start the project with: $COMPOSE_COMMAND up --build -d"
else
exit 1
fi
+37
View File
@@ -0,0 +1,37 @@
{
"wallet_address": "44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg",
"configure_monero": true,
"configure_xmrig": true,
"sidechain": [
0
],
"enable_statistics": true,
"statistics_port": "3380",
"expose_stratum_port": false,
"stratum_port": "3333",
"p2pool_log_level": 3,
"enable_autodiff": true,
"light_mode": false,
"no_cache": false,
"p2pool_options": "",
"monero_version": "latest",
"prune_blockchain": true,
"monero_log_level": 0,
"expose_rpc_port": false,
"rpc_port": "18081",
"rpc_login": "",
"limit_data_rates": false,
"rate_limit_up": "2048",
"rate_limit_down": "8192",
"sync_pruned_blocks": false,
"fast_sync": false,
"monero_options": "",
"public_monero_node": "",
"monero_node_login": "",
"xmrig_username": "p2pool",
"use_fixed_difficulty": true,
"fixed_difficulty": 500000,
"cpu_percent": 100,
"cpu_priority": 2,
"xmrig_options": ""
}
+143
View File
@@ -0,0 +1,143 @@
---
version: '3.4'
networks:
p2pool:
driver: bridge
volumes:
p2pool:
name: p2pool
monero:
name: monero
services:
p2pool:
image: p2pool:latest
build: ../
container_name: p2pool-p2pool
networks:
- p2pool
privileged: true
ports:
- 37888:37888/tcp
volumes:
- p2pool:/home/p2pool/.p2pool:rw
- /dev/null:/home/p2pool/.p2pool/p2pool.log:rw
- /dev/hugepages:/dev/hugepages:rw
depends_on:
- monero
restart: unless-stopped
command: >-
--host monero
--wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg
--loglevel 3
--local-api
--data-api /home/p2pool/.p2pool
statistics:
image: statistics:latest
build:
context: statistics
container_name: p2pool-statistics
networks:
- p2pool
ports:
- 3380:80/tcp
volumes:
- p2pool:/data:ro
depends_on:
- p2pool
restart: unless-stopped
monero:
image: monero:latest
build:
context: monero
args:
- MONERO_GIT_TAG=latest
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
--restricted-rpc
--confirm-external-bind
--log-level=0
--prune-blockchain
--fast-block-sync=0
xmrig:
image: xmrig:latest
build: xmrig
container_name: p2pool-xmrig
networks:
- p2pool
privileged: true
volumes:
- /dev:/dev:ro
- /lib/modules:/lib/modules:ro
- /dev/hugepages:/dev/hugepages:rw
depends_on:
- p2pool
restart: unless-stopped
command: >-
--randomx-1gb-pages
-o p2pool:3333
-u p2pool
--cpu-max-threads-hint=100
--cpu-priority=2
+59
View File
@@ -0,0 +1,59 @@
# Multistage docker build, requires docker 17.05
# builder stage
FROM ubuntu:20.04 as builder
ARG MONERO_GIT_TAG="latest"
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
automake \
autotools-dev \
bsdmainutils \
build-essential \
ca-certificates \
ccache \
cmake \
curl \
git \
libtool \
pkg-config \
gperf
WORKDIR /src
RUN git clone --recursive https://github.com/monero-project/monero && \
cd monero && \
if [ "x$MONERO_GIT_TAG" = "xlatest" ]; then MONERO_GIT_TAG=$(git describe --tags $(git rev-list --tags --max-count=1)); fi && \
git checkout $MONERO_GIT_TAG && \
git submodule sync && git submodule update --init --force --recursive && \
make -j$(nproc) depends target=$(contrib/depends/config.guess)
# ---
# runtime stage
FROM ubuntu:20.04
COPY --from=0 /src/monero/build/*/release/bin /usr/local/bin/
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
ca-certificates \
netcat \
&& \
apt-get clean && \
rm -rf /var/lib/apt
RUN groupadd -r monero -g 1000 && \
adduser --uid 1000 --gid 1000 --system --disabled-password monero && \
mkdir -p /home/monero/.bitmonero && \
chown -R monero:monero /home/monero/.bitmonero
USER monero
WORKDIR /home/monero
EXPOSE 18080 18081 18083
VOLUME /home/monero/.bitmonero
ENTRYPOINT ["/usr/local/bin/monerod"]
+8
View File
@@ -0,0 +1,8 @@
FROM python:slim
COPY app /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["/app/p2pool_statistics.py"]
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
import json
from datetime import datetime
from prefixed import Float
import humanfriendly
from flask import Flask, render_template
app = Flask(__name__)
##
# Add some custom jinja filters
def timeago(value):
"""Format a date time to human friendly time ago"""
if value is None:
return ""
if type(value) is int:
dt = datetime.fromtimestamp(value).replace(microsecond=0)
now = datetime.now().replace(microsecond=0)
return humanfriendly.format_timespan(now - dt)
app.jinja_env.filters["timeago"] = timeago
def human_numbers(value):
"""Format a number in human readable format"""
if value is None:
return ""
return "{:!.3h}".format(Float(value))
app.jinja_env.filters["humanize"] = human_numbers
##
# Get Pool Instance Birth Date
def birthdate():
try:
with open("/data/p2pool.blocks") as reader:
first_block = reader.readline().rstrip()
bday_ts = int(first_block.split(" ")[0])
bday = timeago(bday_ts)
return bday
except Exception as e:
return "unknown time"
##
# The App Routes
@app.route("/")
def render():
try:
my_bday = birthdate()
with open("/data/stats_mod", "r") as reader:
stats_mod = json.loads(reader.read())
with open("/data/pool/stats", "r") as reader:
pool_stats = json.loads(reader.read())
with open("/data/network/stats", "r") as reader:
network_stats = json.loads(reader.read())
with open("/data/local/stats", "r") as reader:
local_stats = json.loads(reader.read())
return render_template(
"index.html",
my_bday=my_bday,
stats_mod=stats_mod,
pool_stats=pool_stats,
network_stats=network_stats,
local_stats=local_stats,
)
except Exception as e:
return render_template("oops.html", error=str(e))
##
# main()
if __name__ == "__main__":
app.run(debug=False, host="0.0.0.0", port=80)
@@ -0,0 +1,3 @@
flask
prefixed
humanfriendly
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because one or more lines are too long
@@ -0,0 +1,158 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="{{url_for('static', filename='bootstrap.min.css')}}">
<title>Monero P2Pool Server Statistics</title>
</head>
<body style="font-size:85%;">
<script src="{{url_for('static', filename='jquery-3.2.1.slim.min.js')}}"></script>
<script src="{{url_for('static', filename='popper.min.js')}}"></script>
<script src="{{url_for('static', filename='bootstrap.min.js')}}"></script>
<div style="text-align:center;">
<img src="{{url_for('static', filename='monero-symbol-480.png')}}" width="75px" height="75px" alt="Monero"/>
<h1 style="color: #FF6600;">P2Pool Server Statistics</h1>
</div>
<div class="card-group">
<div class="col-sm-4 grid-margin stretch-card">
<div class="card">
<div class="card-header text-black mb-1 pb-1" style="text-align: center; background-color: #FF6600;"><h2>Local Pool</h2></div>
<div class="card-body mb-0 pb-0">
<h6 class="card-subtitle text-muted" style="font-size:80%;">(note: stats reset on restart)</h6>
<div class="table-responsive table-hover table-condensed table-striped">
<table class="table">
<tbody>
<tr>
<td>Hashrate 15 Minutes</td>
<td>{{ local_stats["hashrate_15m"]|humanize }}H/s</td>
</tr>
<tr>
<td>Hashrate 1 Hour</td>
<td>{{ local_stats["hashrate_1h"]|humanize }}H/s</td>
</tr>
<tr>
<td>Hashrate 24 Hours</td>
<td>{{ local_stats["hashrate_24h"]|humanize }}H/s</td>
</tr>
<tr>
<td>Shares Found</td>
<td>{{ local_stats["shares_found"] }}</td>
</tr>
<tr>
<td>Current Effort</td>
<td>{{ local_stats["current_effort"] }}%</td>
</tr>
<tr>
<td>Average Effort</td>
<td>{{ local_stats["average_effort"] }}%</td>
</tr>
<tr>
<td>Total Hashes</td>
<td>{{ local_stats["total_hashes"]|humanize }}</td>
</tr>
<tr>
<td>Miner Connections</td>
<td>{{ local_stats["incoming_connections"] }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-sm-4 grid-margin stretch-card">
<div class="card">
<div class="card-header text-black mb-1 pb-1" style="text-align: center; background-color: #FF6600;"><h2>Global Pool</h2></div>
<div class="card-body mb-0 pb-0">
<h6 class="card-subtitle text-muted" style="font-size:80%;">&nbsp;</h6>
<div class="table-responsive table-hover table-condensed table-striped">
<table class="table">
<tbody>
<tr>
<td>Hash Rate</td>
<td>{{ pool_stats["pool_statistics"]["hashRate"]|humanize }}H/s</td>
</tr>
<tr>
<td>Round Hashes</td>
<td>{{ stats_mod["pool"]["roundHashes"]|humanize }}</td>
</tr>
<tr>
<td>Last Block Found</td>
<td>{{ pool_stats["pool_statistics"]["lastBlockFound"] }} <br>
{{ pool_stats["pool_statistics"]["lastBlockFoundTime"]|timeago }} ago</td>
</tr>
<tr>
<td>Payout Method</td>
<td>{{ pool_stats["pool_list"]|join(',') }}<br>2160 block window (~6 hours)</td>
</tr>
</tbody>
</table>
</div>
<h6 class="card-subtitle text-muted" style="font-size:80%;">(since instance birth: {{ my_bday }} ago)</h6>
<div class="table-responsive table-hover table-condensed table-striped">
<table class="table">
<tbody>
<tr>
<td>Total Hashes</td>
<td>{{ pool_stats["pool_statistics"]["totalHashes"]|humanize }}</td>
</tr>
<tr>
<td>Blocks Found</td>
<td>{{ pool_stats["pool_statistics"]["totalBlocksFound"] }}</td>
</tr>
<tr>
<td>Known Miners</td>
<td>{{ pool_stats["pool_statistics"]["miners"] }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-sm-4 grid-margin stretch-card">
<div class="card">
<div class="card-header text-black mb-1 pb-1" style="text-align: center; background-color: #FF6600;"><h2>Monero Network</h2></div>
<div class="card-body mb-0 pb-0">
<h6 class="card-subtitle text-muted" style="font-size:80%;">&nbsp;</h6>
<div class="table-responsive table-hover table-condensed table-striped">
<table class="table">
<tbody>
<tr>
<td>Height</td>
<td>{{ network_stats["height"] }}<br>
{{ network_stats["timestamp"]|timeago }} ago</td>
</tr>
<tr>
<td>Difficulty</td>
<td>{{ network_stats["difficulty"]|humanize }}</td>
</tr>
<tr>
<td>Reward</td>
<td>0.{{ network_stats["reward"] }} Monero</td>
</tr>
<tr>
<td>Head&nbsp;Hash</td>
<td>{{ network_stats["hash"] }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
window.setInterval('refresh()', 30000); // Call refresh function every 30000 milliseconds (30 seconds).
function refresh() {
window .location.reload();
}
</script>
</body>
</html>
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Oops</title>
</head>
<body>
<h2>Ooops, something went wrong:</h2>
{{ error }}
(Maybe you need to wait a few minutes for p2pool to start and sync for the first time?)
</body>
</html>
+37
View File
@@ -0,0 +1,37 @@
FROM ubuntu:20.04
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:20.04
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+44 -8
View File
@@ -3831,15 +3831,51 @@ int sc_isnonzero(const unsigned char *s) {
s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1;
}
int ge_p3_is_point_at_infinity(const ge_p3 *p) {
// X = 0 and Y == Z
int n;
for (n = 0; n < 10; ++n)
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) {
// https://eprint.iacr.org/2008/522
// X == T == 0 and Y/Z == 1
// note: convert all pieces to canonical bytes in case rounding is required (i.e. an element is > q)
// note2: even though T = XY/Z is true for valid point representations (implying it isn't necessary to
// test T == 0), the input to this function might NOT be valid, so we must test T == 0
char result_X_bytes[32];
fe_tobytes((unsigned char*)&result_X_bytes, p->X);
// X != 0
for (int i = 0; i < 32; ++i)
{
if (p->X[n] | p->T[n])
return 0;
if (p->Y[n] != p->Z[n])
if (result_X_bytes[i])
return 0;
}
return 1;
char result_T_bytes[32];
fe_tobytes((unsigned char*)&result_T_bytes, p->T);
// T != 0
for (int i = 0; i < 32; ++i)
{
if (result_T_bytes[i])
return 0;
}
char result_Y_bytes[32];
char result_Z_bytes[32];
fe_tobytes((unsigned char*)&result_Y_bytes, p->Y);
fe_tobytes((unsigned char*)&result_Z_bytes, p->Z);
// Y != Z
for (int i = 0; i < 32; ++i)
{
if (result_Y_bytes[i] != result_Z_bytes[i])
return 0;
}
// is Y nonzero? then Y/Z == 1
for (int i = 0; i < 32; ++i)
{
if (result_Y_bytes[i] != 0)
return 1;
}
// Y/Z = 0/0
return 0;
}
+1 -1
View File
@@ -162,4 +162,4 @@ void fe_add(fe h, const fe f, const fe g);
void fe_tobytes(unsigned char *, const fe);
void fe_invert(fe out, const fe z);
int ge_p3_is_point_at_infinity(const ge_p3 *p);
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p);
Vendored Submodule
+1
Submodule external/src/curl added at e5926fe5f9
-22
View File
@@ -1,22 +0,0 @@
This software is licensed under the MIT License.
Copyright Fedor Indutny, 2018.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
-375
View File
@@ -1,375 +0,0 @@
#ifdef _MSC_VER
#pragma warning(disable : 4100 4668 4710 4711 4820)
#elif defined __GNUC__
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "llhttp.h"
#define CALLBACK_MAYBE(PARSER, NAME) \
do { \
const llhttp_settings_t* settings; \
settings = (const llhttp_settings_t*) (PARSER)->settings; \
if (settings == NULL || settings->NAME == NULL) { \
err = 0; \
break; \
} \
err = settings->NAME((PARSER)); \
} while (0)
#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \
do { \
const llhttp_settings_t* settings; \
settings = (const llhttp_settings_t*) (PARSER)->settings; \
if (settings == NULL || settings->NAME == NULL) { \
err = 0; \
break; \
} \
err = settings->NAME((PARSER), (START), (LEN)); \
if (err == -1) { \
err = HPE_USER; \
llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \
} \
} while (0)
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
const llhttp_settings_t* settings) {
llhttp__internal_init(parser);
parser->type = type;
parser->settings = (void*) settings;
}
#if defined(__wasm__)
extern int wasm_on_message_begin(llhttp_t * p);
extern int wasm_on_url(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_status(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_headers_complete(llhttp_t * p, int status_code,
uint8_t upgrade, int should_keep_alive);
extern int wasm_on_body(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_message_complete(llhttp_t * p);
static int wasm_on_headers_complete_wrap(llhttp_t* p) {
return wasm_on_headers_complete(p, p->status_code, p->upgrade,
llhttp_should_keep_alive(p));
}
const llhttp_settings_t wasm_settings = {
wasm_on_message_begin,
wasm_on_url,
wasm_on_status,
wasm_on_header_field,
wasm_on_header_value,
wasm_on_headers_complete_wrap,
wasm_on_body,
wasm_on_message_complete,
NULL,
NULL,
};
llhttp_t* llhttp_alloc(llhttp_type_t type) {
llhttp_t* parser = malloc(sizeof(llhttp_t));
llhttp_init(parser, type, &wasm_settings);
return parser;
}
void llhttp_free(llhttp_t* parser) {
free(parser);
}
/* Some getters required to get stuff from the parser */
uint8_t llhttp_get_type(llhttp_t* parser) {
return parser->type;
}
uint8_t llhttp_get_http_major(llhttp_t* parser) {
return parser->http_major;
}
uint8_t llhttp_get_http_minor(llhttp_t* parser) {
return parser->http_minor;
}
uint8_t llhttp_get_method(llhttp_t* parser) {
return parser->method;
}
int llhttp_get_status_code(llhttp_t* parser) {
return parser->status_code;
}
uint8_t llhttp_get_upgrade(llhttp_t* parser) {
return parser->upgrade;
}
#endif // defined(__wasm__)
void llhttp_reset(llhttp_t* parser) {
llhttp_type_t type = parser->type;
const llhttp_settings_t* settings = parser->settings;
void* data = parser->data;
uint8_t lenient_flags = parser->lenient_flags;
llhttp__internal_init(parser);
parser->type = type;
parser->settings = (void*) settings;
parser->data = data;
parser->lenient_flags = lenient_flags;
}
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
return llhttp__internal_execute(parser, data, data + len);
}
void llhttp_settings_init(llhttp_settings_t* settings) {
memset(settings, 0, sizeof(*settings));
}
llhttp_errno_t llhttp_finish(llhttp_t* parser) {
int err;
/* We're in an error state. Don't bother doing anything. */
if (parser->error != 0) {
return 0;
}
switch (parser->finish) {
case HTTP_FINISH_SAFE_WITH_CB:
CALLBACK_MAYBE(parser, on_message_complete);
if (err != HPE_OK) return err;
/* FALLTHROUGH */
case HTTP_FINISH_SAFE:
return HPE_OK;
case HTTP_FINISH_UNSAFE:
parser->reason = "Invalid EOF state";
return HPE_INVALID_EOF_STATE;
default:
abort();
}
}
void llhttp_pause(llhttp_t* parser) {
if (parser->error != HPE_OK) {
return;
}
parser->error = HPE_PAUSED;
parser->reason = "Paused";
}
void llhttp_resume(llhttp_t* parser) {
if (parser->error != HPE_PAUSED) {
return;
}
parser->error = 0;
}
void llhttp_resume_after_upgrade(llhttp_t* parser) {
if (parser->error != HPE_PAUSED_UPGRADE) {
return;
}
parser->error = 0;
}
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
return parser->error;
}
const char* llhttp_get_error_reason(const llhttp_t* parser) {
return parser->reason;
}
void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
parser->reason = reason;
}
const char* llhttp_get_error_pos(const llhttp_t* parser) {
return parser->error_pos;
}
const char* llhttp_errno_name(llhttp_errno_t err) {
#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
switch (err) {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
default: abort();
}
#undef HTTP_ERRNO_GEN
}
const char* llhttp_method_name(llhttp_method_t method) {
#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING;
switch (method) {
HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN)
default: abort();
}
#undef HTTP_METHOD_GEN
}
void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_HEADERS;
} else {
parser->lenient_flags &= ~LENIENT_HEADERS;
}
}
void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_CHUNKED_LENGTH;
} else {
parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH;
}
}
void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_KEEP_ALIVE;
} else {
parser->lenient_flags &= ~LENIENT_KEEP_ALIVE;
}
}
/* Callbacks */
int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_message_begin);
return err;
}
int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p);
return err;
}
int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_url_complete);
return err;
}
int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p);
return err;
}
int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_status_complete);
return err;
}
int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p);
return err;
}
int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_header_field_complete);
return err;
}
int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p);
return err;
}
int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_header_value_complete);
return err;
}
int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_headers_complete);
return err;
}
int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_message_complete);
return err;
}
int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p);
return err;
}
int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_chunk_header);
return err;
}
int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_chunk_complete);
return err;
}
/* Private */
void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
const char* msg) {
if (p == endp) {
fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
s->flags, msg);
} else {
fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s,
s->type, s->flags, *p, msg);
}
}
-155
View File
@@ -1,155 +0,0 @@
#ifdef _MSC_VER
#pragma warning(disable : 4100 4668 4710 4711 4820)
#elif defined __GNUC__
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#include <stdio.h>
#ifndef LLHTTP__TEST
# include "llhttp.h"
#else
# define llhttp_t llparse_t
#endif /* */
int llhttp_message_needs_eof(const llhttp_t* parser);
int llhttp_should_keep_alive(const llhttp_t* parser);
int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
const char* endp) {
/* Set this here so that on_headers_complete() callbacks can see it */
if ((parser->flags & F_UPGRADE) &&
(parser->flags & F_CONNECTION_UPGRADE)) {
/* For responses, "Upgrade: foo" and "Connection: upgrade" are
* mandatory only when it is a 101 Switching Protocols response,
* otherwise it is purely informational, to announce support.
*/
parser->upgrade =
(parser->type == HTTP_REQUEST || parser->status_code == 101);
} else {
parser->upgrade = (parser->method == HTTP_CONNECT);
}
return 0;
}
/* Return values:
* 0 - No body, `restart`, message_complete
* 1 - CONNECT request, `restart`, message_complete, and pause
* 2 - chunk_size_start
* 3 - body_identity
* 4 - body_identity_eof
* 5 - invalid transfer-encoding for request
*/
int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
const char* endp) {
int hasBody;
hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;
if (parser->upgrade && (parser->method == HTTP_CONNECT ||
(parser->flags & F_SKIPBODY) || !hasBody)) {
/* Exit, the rest of the message is in a different protocol. */
return 1;
}
if (parser->flags & F_SKIPBODY) {
return 0;
} else if (parser->flags & F_CHUNKED) {
/* chunked encoding - ignore Content-Length header, prepare for a chunk */
return 2;
} else if (parser->flags & F_TRANSFER_ENCODING) {
if (parser->type == HTTP_REQUEST &&
(parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0) {
/* RFC 7230 3.3.3 */
/* If a Transfer-Encoding header field
* is present in a request and the chunked transfer coding is not
* the final encoding, the message body length cannot be determined
* reliably; the server MUST respond with the 400 (Bad Request)
* status code and then close the connection.
*/
return 5;
} else {
/* RFC 7230 3.3.3 */
/* If a Transfer-Encoding header field is present in a response and
* the chunked transfer coding is not the final encoding, the
* message body length is determined by reading the connection until
* it is closed by the server.
*/
return 4;
}
} else {
if (!(parser->flags & F_CONTENT_LENGTH)) {
if (!llhttp_message_needs_eof(parser)) {
/* Assume content-length 0 - read the next */
return 0;
} else {
/* Read body until EOF */
return 4;
}
} else if (parser->content_length == 0) {
/* Content-Length header given but zero: Content-Length: 0\r\n */
return 0;
} else {
/* Content-Length header given and non-zero */
return 3;
}
}
}
int llhttp__after_message_complete(llhttp_t* parser, const char* p,
const char* endp) {
int should_keep_alive;
should_keep_alive = llhttp_should_keep_alive(parser);
parser->finish = HTTP_FINISH_SAFE;
parser->flags = 0;
/* NOTE: this is ignored in loose parsing mode */
return should_keep_alive;
}
int llhttp_message_needs_eof(const llhttp_t* parser) {
if (parser->type == HTTP_REQUEST) {
return 0;
}
/* See RFC 2616 section 4.4 */
if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
parser->status_code == 204 || /* No Content */
parser->status_code == 304 || /* Not Modified */
(parser->flags & F_SKIPBODY)) { /* response to a HEAD request */
return 0;
}
/* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */
if ((parser->flags & F_TRANSFER_ENCODING) &&
(parser->flags & F_CHUNKED) == 0) {
return 1;
}
if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {
return 0;
}
return 1;
}
int llhttp_should_keep_alive(const llhttp_t* parser) {
if (parser->http_major > 0 && parser->http_minor > 0) {
/* HTTP/1.1 */
if (parser->flags & F_CONNECTION_CLOSE) {
return 0;
}
} else {
/* HTTP/1.0 or earlier */
if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
return 0;
}
}
return !llhttp_message_needs_eof(parser);
}
-14933
View File
File diff suppressed because it is too large Load Diff
-564
View File
@@ -1,564 +0,0 @@
#ifndef INCLUDE_LLHTTP_H_
#define INCLUDE_LLHTTP_H_
#define LLHTTP_VERSION_MAJOR 6
#define LLHTTP_VERSION_MINOR 0
#define LLHTTP_VERSION_PATCH 4
#ifndef LLHTTP_STRICT_MODE
# define LLHTTP_STRICT_MODE 0
#endif
#ifndef INCLUDE_LLHTTP_ITSELF_H_
#define INCLUDE_LLHTTP_ITSELF_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef struct llhttp__internal_s llhttp__internal_t;
struct llhttp__internal_s {
int32_t _index;
void* _span_pos0;
void* _span_cb0;
int32_t error;
const char* reason;
const char* error_pos;
void* data;
void* _current;
uint64_t content_length;
uint8_t type;
uint8_t method;
uint8_t http_major;
uint8_t http_minor;
uint8_t header_state;
uint8_t lenient_flags;
uint8_t upgrade;
uint8_t finish;
uint16_t flags;
uint16_t status_code;
void* settings;
};
int llhttp__internal_init(llhttp__internal_t* s);
int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLHTTP_ITSELF_H_ */
#ifndef LLLLHTTP_C_HEADERS_
#define LLLLHTTP_C_HEADERS_
#ifdef __cplusplus
extern "C" {
#endif
enum llhttp_errno {
HPE_OK = 0,
HPE_INTERNAL = 1,
HPE_STRICT = 2,
HPE_LF_EXPECTED = 3,
HPE_UNEXPECTED_CONTENT_LENGTH = 4,
HPE_CLOSED_CONNECTION = 5,
HPE_INVALID_METHOD = 6,
HPE_INVALID_URL = 7,
HPE_INVALID_CONSTANT = 8,
HPE_INVALID_VERSION = 9,
HPE_INVALID_HEADER_TOKEN = 10,
HPE_INVALID_CONTENT_LENGTH = 11,
HPE_INVALID_CHUNK_SIZE = 12,
HPE_INVALID_STATUS = 13,
HPE_INVALID_EOF_STATE = 14,
HPE_INVALID_TRANSFER_ENCODING = 15,
HPE_CB_MESSAGE_BEGIN = 16,
HPE_CB_HEADERS_COMPLETE = 17,
HPE_CB_MESSAGE_COMPLETE = 18,
HPE_CB_CHUNK_HEADER = 19,
HPE_CB_CHUNK_COMPLETE = 20,
HPE_PAUSED = 21,
HPE_PAUSED_UPGRADE = 22,
HPE_PAUSED_H2_UPGRADE = 23,
HPE_USER = 24
};
typedef enum llhttp_errno llhttp_errno_t;
enum llhttp_flags {
F_CONNECTION_KEEP_ALIVE = 0x1,
F_CONNECTION_CLOSE = 0x2,
F_CONNECTION_UPGRADE = 0x4,
F_CHUNKED = 0x8,
F_UPGRADE = 0x10,
F_CONTENT_LENGTH = 0x20,
F_SKIPBODY = 0x40,
F_TRAILING = 0x80,
F_TRANSFER_ENCODING = 0x200
};
typedef enum llhttp_flags llhttp_flags_t;
enum llhttp_lenient_flags {
LENIENT_HEADERS = 0x1,
LENIENT_CHUNKED_LENGTH = 0x2,
LENIENT_KEEP_ALIVE = 0x4
};
typedef enum llhttp_lenient_flags llhttp_lenient_flags_t;
enum llhttp_type {
HTTP_BOTH = 0,
HTTP_REQUEST = 1,
HTTP_RESPONSE = 2
};
typedef enum llhttp_type llhttp_type_t;
enum llhttp_finish {
HTTP_FINISH_SAFE = 0,
HTTP_FINISH_SAFE_WITH_CB = 1,
HTTP_FINISH_UNSAFE = 2
};
typedef enum llhttp_finish llhttp_finish_t;
enum llhttp_method {
HTTP_DELETE = 0,
HTTP_GET = 1,
HTTP_HEAD = 2,
HTTP_POST = 3,
HTTP_PUT = 4,
HTTP_CONNECT = 5,
HTTP_OPTIONS = 6,
HTTP_TRACE = 7,
HTTP_COPY = 8,
HTTP_LOCK = 9,
HTTP_MKCOL = 10,
HTTP_MOVE = 11,
HTTP_PROPFIND = 12,
HTTP_PROPPATCH = 13,
HTTP_SEARCH = 14,
HTTP_UNLOCK = 15,
HTTP_BIND = 16,
HTTP_REBIND = 17,
HTTP_UNBIND = 18,
HTTP_ACL = 19,
HTTP_REPORT = 20,
HTTP_MKACTIVITY = 21,
HTTP_CHECKOUT = 22,
HTTP_MERGE = 23,
HTTP_MSEARCH = 24,
HTTP_NOTIFY = 25,
HTTP_SUBSCRIBE = 26,
HTTP_UNSUBSCRIBE = 27,
HTTP_PATCH = 28,
HTTP_PURGE = 29,
HTTP_MKCALENDAR = 30,
HTTP_LINK = 31,
HTTP_UNLINK = 32,
HTTP_SOURCE = 33,
HTTP_PRI = 34,
HTTP_DESCRIBE = 35,
HTTP_ANNOUNCE = 36,
HTTP_SETUP = 37,
HTTP_PLAY = 38,
HTTP_PAUSE = 39,
HTTP_TEARDOWN = 40,
HTTP_GET_PARAMETER = 41,
HTTP_SET_PARAMETER = 42,
HTTP_REDIRECT = 43,
HTTP_RECORD = 44,
HTTP_FLUSH = 45
};
typedef enum llhttp_method llhttp_method_t;
#define HTTP_ERRNO_MAP(XX) \
XX(0, OK, OK) \
XX(1, INTERNAL, INTERNAL) \
XX(2, STRICT, STRICT) \
XX(3, LF_EXPECTED, LF_EXPECTED) \
XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
XX(6, INVALID_METHOD, INVALID_METHOD) \
XX(7, INVALID_URL, INVALID_URL) \
XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
XX(9, INVALID_VERSION, INVALID_VERSION) \
XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
XX(13, INVALID_STATUS, INVALID_STATUS) \
XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \
XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
XX(21, PAUSED, PAUSED) \
XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \
XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \
XX(24, USER, USER) \
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
XX(30, MKCALENDAR, MKCALENDAR) \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
XX(33, SOURCE, SOURCE) \
#define RTSP_METHOD_MAP(XX) \
XX(1, GET, GET) \
XX(3, POST, POST) \
XX(6, OPTIONS, OPTIONS) \
XX(35, DESCRIBE, DESCRIBE) \
XX(36, ANNOUNCE, ANNOUNCE) \
XX(37, SETUP, SETUP) \
XX(38, PLAY, PLAY) \
XX(39, PAUSE, PAUSE) \
XX(40, TEARDOWN, TEARDOWN) \
XX(41, GET_PARAMETER, GET_PARAMETER) \
XX(42, SET_PARAMETER, SET_PARAMETER) \
XX(43, REDIRECT, REDIRECT) \
XX(44, RECORD, RECORD) \
XX(45, FLUSH, FLUSH) \
#define HTTP_ALL_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
XX(30, MKCALENDAR, MKCALENDAR) \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
XX(33, SOURCE, SOURCE) \
XX(34, PRI, PRI) \
XX(35, DESCRIBE, DESCRIBE) \
XX(36, ANNOUNCE, ANNOUNCE) \
XX(37, SETUP, SETUP) \
XX(38, PLAY, PLAY) \
XX(39, PAUSE, PAUSE) \
XX(40, TEARDOWN, TEARDOWN) \
XX(41, GET_PARAMETER, GET_PARAMETER) \
XX(42, SET_PARAMETER, SET_PARAMETER) \
XX(43, REDIRECT, REDIRECT) \
XX(44, RECORD, RECORD) \
XX(45, FLUSH, FLUSH) \
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LLLLHTTP_C_HEADERS_ */
#ifndef INCLUDE_LLHTTP_API_H_
#define INCLUDE_LLHTTP_API_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#if defined(__wasm__)
#define LLHTTP_EXPORT __attribute__((visibility("default")))
#else
#define LLHTTP_EXPORT
#endif
typedef llhttp__internal_t llhttp_t;
typedef struct llhttp_settings_s llhttp_settings_t;
typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
typedef int (*llhttp_cb)(llhttp_t*);
struct llhttp_settings_s {
/* Possible return values 0, -1, `HPE_PAUSED` */
llhttp_cb on_message_begin;
/* Possible return values 0, -1, HPE_USER */
llhttp_data_cb on_url;
llhttp_data_cb on_status;
llhttp_data_cb on_header_field;
llhttp_data_cb on_header_value;
/* Possible return values:
* 0 - Proceed normally
* 1 - Assume that request/response has no body, and proceed to parsing the
* next message
* 2 - Assume absence of body (as above) and make `llhttp_execute()` return
* `HPE_PAUSED_UPGRADE`
* -1 - Error
* `HPE_PAUSED`
*/
llhttp_cb on_headers_complete;
/* Possible return values 0, -1, HPE_USER */
llhttp_data_cb on_body;
/* Possible return values 0, -1, `HPE_PAUSED` */
llhttp_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
* Possible return values 0, -1, `HPE_PAUSED`
*/
llhttp_cb on_chunk_header;
llhttp_cb on_chunk_complete;
/* Information-only callbacks, return value is ignored */
llhttp_cb on_url_complete;
llhttp_cb on_status_complete;
llhttp_cb on_header_field_complete;
llhttp_cb on_header_value_complete;
};
/* Initialize the parser with specific type and user settings.
*
* NOTE: lifetime of `settings` has to be at least the same as the lifetime of
* the `parser` here. In practice, `settings` has to be either a static
* variable or be allocated with `malloc`, `new`, etc.
*/
LLHTTP_EXPORT
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
const llhttp_settings_t* settings);
#if defined(__wasm__)
LLHTTP_EXPORT
llhttp_t* llhttp_alloc(llhttp_type_t type);
LLHTTP_EXPORT
void llhttp_free(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_type(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_http_major(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_http_minor(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_method(llhttp_t* parser);
LLHTTP_EXPORT
int llhttp_get_status_code(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_upgrade(llhttp_t* parser);
#endif // defined(__wasm__)
/* Reset an already initialized parser back to the start state, preserving the
* existing parser type, callback settings, user data, and lenient flags.
*/
LLHTTP_EXPORT
void llhttp_reset(llhttp_t* parser);
/* Initialize the settings object */
LLHTTP_EXPORT
void llhttp_settings_init(llhttp_settings_t* settings);
/* Parse full or partial request/response, invoking user callbacks along the
* way.
*
* If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
* interrupts, and such errno is returned from `llhttp_execute()`. If
* `HPE_PAUSED` was used as a errno, the execution can be resumed with
* `llhttp_resume()` call.
*
* In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
* is returned after fully parsing the request/response. If the user wishes to
* continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
*
* NOTE: if this function ever returns a non-pause type error, it will continue
* to return the same error upon each successive call up until `llhttp_init()`
* is called.
*/
LLHTTP_EXPORT
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
/* This method should be called when the other side has no further bytes to
* send (e.g. shutdown of readable side of the TCP connection.)
*
* Requests without `Content-Length` and other messages might require treating
* all incoming bytes as the part of the body, up to the last byte of the
* connection. This method will invoke `on_message_complete()` callback if the
* request was terminated safely. Otherwise a error code would be returned.
*/
LLHTTP_EXPORT
llhttp_errno_t llhttp_finish(llhttp_t* parser);
/* Returns `1` if the incoming message is parsed until the last byte, and has
* to be completed by calling `llhttp_finish()` on EOF
*/
LLHTTP_EXPORT
int llhttp_message_needs_eof(const llhttp_t* parser);
/* Returns `1` if there might be any other messages following the last that was
* successfully parsed.
*/
LLHTTP_EXPORT
int llhttp_should_keep_alive(const llhttp_t* parser);
/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
* appropriate error reason.
*
* Important: do not call this from user callbacks! User callbacks must return
* `HPE_PAUSED` if pausing is required.
*/
LLHTTP_EXPORT
void llhttp_pause(llhttp_t* parser);
/* Might be called to resume the execution after the pause in user's callback.
* See `llhttp_execute()` above for details.
*
* Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
*/
LLHTTP_EXPORT
void llhttp_resume(llhttp_t* parser);
/* Might be called to resume the execution after the pause in user's callback.
* See `llhttp_execute()` above for details.
*
* Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
*/
LLHTTP_EXPORT
void llhttp_resume_after_upgrade(llhttp_t* parser);
/* Returns the latest return error */
LLHTTP_EXPORT
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
/* Returns the verbal explanation of the latest returned error.
*
* Note: User callback should set error reason when returning the error. See
* `llhttp_set_error_reason()` for details.
*/
LLHTTP_EXPORT
const char* llhttp_get_error_reason(const llhttp_t* parser);
/* Assign verbal description to the returned error. Must be called in user
* callbacks right before returning the errno.
*
* Note: `HPE_USER` error code might be useful in user callbacks.
*/
LLHTTP_EXPORT
void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
/* Returns the pointer to the last parsed byte before the returned error. The
* pointer is relative to the `data` argument of `llhttp_execute()`.
*
* Note: this method might be useful for counting the number of parsed bytes.
*/
LLHTTP_EXPORT
const char* llhttp_get_error_pos(const llhttp_t* parser);
/* Returns textual name of error code */
LLHTTP_EXPORT
const char* llhttp_errno_name(llhttp_errno_t err);
/* Returns textual name of HTTP method */
LLHTTP_EXPORT
const char* llhttp_method_name(llhttp_method_t method);
/* Enables/disables lenient header value parsing (disabled by default).
*
* Lenient parsing disables header value token checks, extending llhttp's
* protocol support to highly non-compliant clients/server. No
* `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
* lenient parsing is "on".
*
* **(USE AT YOUR OWN RISK)**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_headers(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and
* `Content-Length` headers (disabled by default).
*
* Normally `llhttp` would error when `Transfer-Encoding` is present in
* conjunction with `Content-Length`. This error is important to prevent HTTP
* request smuggling, but may be less desirable for small number of cases
* involving legacy servers.
*
* **(USE AT YOUR OWN RISK)**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0
* requests responses.
*
* Normally `llhttp` would error on (in strict mode) or discard (in loose mode)
* the HTTP request/response after the request/response with `Connection: close`
* and `Content-Length`. This is important to prevent cache poisoning attacks,
* but might interact badly with outdated and insecure clients. With this flag
* the extra request/response will be parsed normally.
*
* **(USE AT YOUR OWN RISK)**
*/
void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLHTTP_API_H_ */
#endif /* INCLUDE_LLHTTP_H_ */
+1 -1
View File
@@ -23,7 +23,7 @@
};
buildInputs = builtins.attrValues {
inherit (pkgs) libuv zeromq libsodium gss;
inherit (pkgs) libuv zeromq libsodium gss curl;
};
installPhase = ''
+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
@@ -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);
@@ -197,7 +198,7 @@ void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
continue;
}
if (block.deserialize(data + sizeof(uint32_t), n, side_chain) == 0) {
if (block.deserialize(data + sizeof(uint32_t), n, side_chain, uv_default_loop_checked()) == 0) {
server.add_cached_block(block);
++blocks_loaded;
}
+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
+126 -42
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>
@@ -50,6 +50,8 @@ BlockTemplate::BlockTemplate(p2pool* pool)
, m_difficulty{}
, m_seedHash{}
, m_timestamp(0)
, m_txkeyPub{}
, m_txkeySec{}
, m_poolBlockTemplate(new PoolBlock())
, m_finalReward(0)
{
@@ -66,20 +68,18 @@ 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);
}
#if TEST_MEMPOOL_PICKING_ALGORITHM
m_knapsack.reserve(512 * 309375);
#endif
update_tx_keys();
}
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];
}
@@ -115,7 +115,7 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
m_extraNonceOffsetInTemplate = b.m_extraNonceOffsetInTemplate;
m_numTransactionHashes = b.m_numTransactionHashes;
m_prevId = b.m_prevId;
m_height = b.m_height;
m_height = b.m_height.load();
m_difficulty = b.m_difficulty;
m_seedHash = b.m_seedHash;
m_timestamp = b.m_timestamp;
@@ -185,17 +185,24 @@ 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)];
};
get_tx_keys(m_txkeyPub, m_txkeySec, miner_wallet->spend_public_key(), data.prev_id);
m_height = data.height;
m_difficulty = data.difficulty;
m_seedHash = data.seed_hash;
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 +212,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 +261,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 +284,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 +441,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;
}
@@ -487,7 +548,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end());
PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain());
const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain(), nullptr);
if (result != 0) {
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
}
@@ -516,7 +577,6 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_rewards.clear();
m_mempoolTxs.clear();
m_mempoolTxsOrder.clear();
m_shares.clear();
}
#if TEST_MEMPOOL_PICKING_ALGORITHM
@@ -600,7 +660,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();
@@ -630,6 +690,8 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
m_poolBlockTemplate->m_outputs.clear();
m_poolBlockTemplate->m_outputs.reserve(num_outputs);
const uint8_t tx_type = m_poolBlockTemplate->get_tx_type();
uint64_t reward_amounts_weight = 0;
for (size_t i = 0; i < num_outputs; ++i) {
writeVarint(m_rewards[i], [this, &reward_amounts_weight](uint8_t b)
@@ -637,30 +699,36 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
m_minerTx.push_back(b);
++reward_amounts_weight;
});
m_minerTx.push_back(TXOUT_TO_KEY);
m_minerTx.push_back(tx_type);
uint8_t view_tag = 0;
if (dry_run) {
m_minerTx.insert(m_minerTx.end(), HASH_SIZE, 0);
}
else {
hash eph_public_key;
if (!shares[i].m_wallet->get_eph_public_key(m_txkeySec, i, eph_public_key)) {
if (!shares[i].m_wallet->get_eph_public_key(m_txkeySec, i, eph_public_key, view_tag)) {
LOGERR(1, "get_eph_public_key failed at index " << i);
}
m_minerTx.insert(m_minerTx.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key);
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key, tx_type, view_tag);
}
if (tx_type == TXOUT_TO_TAGGED_KEY) {
m_minerTx.emplace_back(view_tag);
}
}
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 +744,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 +773,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 +910,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 +943,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 +1044,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);
}
@@ -968,13 +1059,6 @@ std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id
return m_blockTemplateBlob;
}
void BlockTemplate::update_tx_keys()
{
WriteLock lock(m_lock);
generate_keys(m_txkeyPub, m_txkeySec);
}
void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
{
WriteLock lock(m_lock);
@@ -983,7 +1067,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();
@@ -993,13 +1077,13 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end());
PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), side_chain);
const int result = check.deserialize(buf.data(), buf.size(), side_chain, nullptr);
if (result != 0) {
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
}
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 +1100,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);
+5 -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
@@ -40,27 +40,26 @@ 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;
uint32_t get_hashing_blobs(uint32_t extra_nonce_start, uint32_t count, std::vector<uint8_t>& blobs, uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
std::vector<uint8_t> get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const;
void update_tx_keys();
FORCEINLINE uint64_t height() const { return m_height; }
FORCEINLINE time_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);
FORCEINLINE uint64_t final_reward() const { return m_finalReward; }
FORCEINLINE const std::vector<MinerShare>& shares() const { return m_shares; }
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();
@@ -82,7 +81,7 @@ private:
size_t m_numTransactionHashes;
hash m_prevId;
uint64_t m_height;
std::atomic<uint64_t> m_height;
difficulty_type m_difficulty;
hash m_seedHash;
+63 -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
@@ -19,22 +19,25 @@
#ifdef _MSC_VER
#pragma warning(disable : 4005 4061 4365 4464 4625 4626 4668 4710 4711 4804 4820 5039 5045 5220)
#pragma warning(disable : 4005 4061 4365 4464 4625 4626 4668 4710 4711 4804 4820 5039 5045 5220 5246)
#define FORCEINLINE __forceinline
#define NOINLINE __declspec(noinline)
#define LIKELY(expression) expression
#define MSVC_PRAGMA(...) __pragma(__VA_ARGS__)
#elif __GNUC__
#define FORCEINLINE __attribute__((always_inline)) inline
#define NOINLINE __attribute__((noinline))
#define LIKELY(expression) __builtin_expect(expression, 1)
#define MSVC_PRAGMA(...)
#else
#define FORCEINLINE inline
#define NOINLINE
#define LIKELY(expression) expression
#define MSVC_PRAGMA(...)
#endif
@@ -54,6 +57,7 @@
#include <atomic>
#include <chrono>
#include <iostream>
#include <random>
#include <signal.h>
@@ -80,13 +84,16 @@
namespace p2pool {
constexpr size_t HASH_SIZE = 32;
constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 14;
constexpr uint8_t HARDFORK_VIEW_TAGS_VERSION = 15;
constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 16;
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;
constexpr uint8_t TXOUT_TO_TAGGED_KEY = 3;
constexpr uint8_t TX_EXTRA_TAG_PUBKEY = 1;
constexpr uint8_t TX_EXTRA_NONCE = 2;
constexpr uint8_t TX_EXTRA_MERGE_MINING_TAG = 3;
@@ -113,6 +120,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];
@@ -192,11 +201,16 @@ struct difficulty_type
FORCEINLINE bool operator==(const difficulty_type& other) const { return (lo == other.lo) && (hi == other.hi); }
FORCEINLINE bool operator!=(const difficulty_type& other) const { return (lo != other.lo) || (hi != other.hi); }
FORCEINLINE bool operator==(uint64_t other) const { return (lo == other) && (hi == 0); }
FORCEINLINE bool operator!=(uint64_t other) const { return (lo != other) || (hi != 0); }
friend std::ostream& operator<<(std::ostream& s, const difficulty_type& d);
friend std::istream& operator>>(std::istream& s, difficulty_type& d);
FORCEINLINE double to_double() const { return static_cast<double>(hi) * 18446744073709551616.0 + static_cast<double>(lo); }
FORCEINLINE bool empty() const { return (lo == 0) && (hi == 0); }
// Finds a 64-bit target for mining (target = 2^64 / difficulty) and rounds up the result of division
// Because of that, there's a very small chance that miners will find a hash that meets the target but is still wrong (hash * difficulty >= 2^256)
// A proper difficulty check is in check_pow()
@@ -232,7 +246,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 +272,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 +293,50 @@ 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); }
FORCEINLINE bool is_localhost() const
{
static constexpr raw_ip localhost_ipv4 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01 };
static constexpr raw_ip localhost_ipv6 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
return (*this == localhost_ipv4) || (*this == localhost_ipv6);
}
};
static_assert(sizeof(raw_ip) == 16, "struct raw_ip has invalid size");
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;
char* strdup_hook(const char* s) noexcept;
extern const char* BLOCK_FOUND;
} // namespace p2pool
#include "util.h"
+181 -61
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>
@@ -28,27 +31,57 @@ static constexpr char log_category_prefix[] = "ConsoleCommands ";
namespace p2pool {
bool ConsoleCommands::stopped = false;
ConsoleCommands::ConsoleCommands(p2pool* pool)
: m_pool(pool)
, m_loop{}
, m_shutdownAsync{}
, m_tty{}
, m_loopThread{}
, m_readBuf{}
, m_readBufInUse(false)
{
m_worker = new std::thread(&ConsoleCommands::run, this);
if (uv_guess_handle(0) != UV_TTY) {
LOGERR(1, "tty is not available");
throw std::exception();
}
int err = uv_loop_init(&m_loop);
if (err) {
LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
throw std::exception();
}
err = uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
throw std::exception();
}
m_shutdownAsync.data = this;
err = uv_tty_init(&m_loop, &m_tty, 0, 1);
if (err) {
LOGERR(1, "uv_tty_init failed, error " << uv_err_name(err));
throw std::exception();
}
m_tty.data = this;
err = uv_read_start(reinterpret_cast<uv_stream_t*>(&m_tty), allocCallback, stdinReadCallback);
if (err) {
LOGERR(1, "uv_read_start failed, error " << uv_err_name(err));
throw std::exception();
}
err = uv_thread_create(&m_loopThread, loop, this);
if (err) {
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
throw std::exception();
}
}
ConsoleCommands::~ConsoleCommands()
{
stopped = true;
#ifdef _WIN32
TerminateThread(reinterpret_cast<HANDLE>(m_worker->native_handle()), 0);
#else
pthread_cancel(m_worker->native_handle());
#endif
m_worker->join();
delete m_worker;
uv_async_send(&m_shutdownAsync);
uv_thread_join(&m_loopThread);
LOGINFO(1, "stopped");
}
@@ -60,7 +93,7 @@ typedef struct strconst {
#define STRCONST(x) {x, sizeof(x)-1}
#define STRCNULL {NULL, 0}
typedef int (cmdfunc)(p2pool *pool, const char *args);
typedef void (cmdfunc)(p2pool *pool, const char *args);
typedef struct cmd {
strconst name;
@@ -69,7 +102,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_showworkers, 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,21 +114,28 @@ 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("workers"), "", "show all connected workers", do_showworkers },
{ 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 */)
static void 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;
}
}
static int do_status(p2pool *m_pool, const char * /* args */)
static void do_status(p2pool *m_pool, const char * /* args */)
{
m_pool->side_chain().print_status();
if (m_pool->stratum_server()) {
@@ -100,79 +144,155 @@ static int do_status(p2pool *m_pool, const char * /* args */)
if (m_pool->p2p_server()) {
m_pool->p2p_server()->print_status();
}
#ifdef WITH_RANDOMX
m_pool->print_miner_status();
#endif
bkg_jobs_tracker.print_status();
return 0;
}
static int do_loglevel(p2pool * /* m_pool */, const char *args)
static void do_loglevel(p2pool * /* m_pool */, const char *args)
{
int level = 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);
return 0;
}
static int do_addpeers(p2pool *m_pool, const char *args)
static void do_addpeers(p2pool *m_pool, const char *args)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->connect_to_peers(args);
}
return 0;
}
static int do_droppeers(p2pool *m_pool, const char * /* args */)
static void do_droppeers(p2pool *m_pool, const char * /* args */)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->drop_connections();
}
return 0;
}
static int do_exit(p2pool *m_pool, const char * /* args */)
static void do_showpeers(p2pool* m_pool, const char* /* args */)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->show_peers();
}
}
static void do_showworkers(p2pool* m_pool, const char* /* args */)
{
if (m_pool->stratum_server()) {
m_pool->stratum_server()->show_workers();
}
}
static void do_showbans(p2pool* m_pool, const char* /* args */)
{
if (m_pool->stratum_server()) {
m_pool->stratum_server()->print_bans();
}
if (m_pool->p2p_server()) {
m_pool->p2p_server()->print_bans();
}
}
static void do_outpeers(p2pool* m_pool, const char* args)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->set_max_outgoing_peers(strtoul(args, nullptr, 10));
LOGINFO(0, "max outgoing peers set to " << m_pool->p2p_server()->max_outgoing_peers());
}
}
static void do_inpeers(p2pool* m_pool, const char* args)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->set_max_incoming_peers(strtoul(args, nullptr, 10));
LOGINFO(0, "max incoming peers set to " << m_pool->p2p_server()->max_incoming_peers());
}
}
#ifdef WITH_RANDOMX
static void do_start_mining(p2pool* m_pool, const char* args)
{
uint32_t threads = strtoul(args, nullptr, 10);
threads = std::min(std::max(threads, 1u), 64u);
m_pool->start_mining(threads);
}
static void do_stop_mining(p2pool* m_pool, const char* /*args*/)
{
m_pool->stop_mining();
}
#endif
static void do_exit(p2pool *m_pool, const char * /* args */)
{
bkg_jobs_tracker.wait();
m_pool->stop();
return 1;
}
void ConsoleCommands::run()
void ConsoleCommands::allocCallback(uv_handle_t* handle, size_t /*suggested_size*/, uv_buf_t* buf)
{
LOGINFO(1, "started");
ConsoleCommands* pThis = static_cast<ConsoleCommands*>(handle->data);
std::string command;
command.reserve(1024);
if (pThis->m_readBufInUse) {
buf->len = 0;
buf->base = nullptr;
return;
}
do {
std::getline(std::cin, command);
buf->len = sizeof(pThis->m_readBuf);
buf->base = pThis->m_readBuf;
pThis->m_readBufInUse = true;
}
if (std::cin.eof()) {
LOGINFO(1, "EOF, stopping");
return;
}
void ConsoleCommands::stdinReadCallback(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
{
ConsoleCommands* pThis = static_cast<ConsoleCommands*>(stream->data);
if (stopped) {
LOGINFO(1, "stopping");
return;
}
if (nread > 0) {
std::string& command = pThis->m_command;
command.append(buf->base, nread);
cmd* c = cmds;
for (; c->name.len; ++c) {
if (!strncmp(command.c_str(), c->name.str, c->name.len)) {
const char *args = command.c_str() + c->name.len + 1;
if (c->func(m_pool, args)) {
LOGINFO(1, "exit requested, stopping");
return;
}
do {
size_t k = command.find_first_of("\r\n");
if (k == std::string::npos) {
break;
}
}
command[k] = '\0';
if (!c->name.len) {
LOGWARN(0, "Unknown command " << command);
}
} while (true);
cmd* c = cmds;
for (; c->name.len; ++c) {
if (!strncmp(command.c_str(), c->name.str, c->name.len)) {
const char* args = (c->name.len + 1 <= k) ? (command.c_str() + c->name.len + 1) : "";
c->func(pThis->m_pool, args);
break;
}
}
if (!c->name.len) {
LOGWARN(0, "Unknown command " << command.c_str());
}
k = command.find_first_not_of("\r\n", k + 1);
command.erase(0, k);
} while (true);
}
else if (nread < 0) {
LOGWARN(4, "read error " << uv_err_name(static_cast<int>(nread)));
}
pThis->m_readBufInUse = false;
}
void ConsoleCommands::loop(void* data)
{
LOGINFO(1, "event loop started");
ConsoleCommands* pThis = static_cast<ConsoleCommands*>(data);
uv_run(&pThis->m_loop, UV_RUN_DEFAULT);
uv_loop_close(&pThis->m_loop);
LOGINFO(1, "event loop stopped");
}
} // namespace p2pool
+23 -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
@@ -17,7 +17,7 @@
#pragma once
#include <thread>
#include "uv_util.h"
namespace p2pool {
@@ -31,10 +31,28 @@ public:
private:
p2pool* m_pool;
std::thread* m_worker;
static bool stopped;
void run();
uv_loop_t m_loop;
uv_async_t m_shutdownAsync;
uv_tty_t m_tty;
uv_thread_t m_loopThread;
char m_readBuf[64];
bool m_readBufInUse;
std::string m_command;
static void loop(void* data);
static void on_shutdown(uv_async_t* async)
{
ConsoleCommands* pThis = reinterpret_cast<ConsoleCommands*>(async->data);
uv_close(reinterpret_cast<uv_handle_t*>(&pThis->m_shutdownAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pThis->m_tty), nullptr);
}
static void allocCallback(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
static void stdinReadCallback(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf);
};
} // namespace p2pool
+157 -48
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,13 +19,15 @@
#include "crypto.h"
#include "keccak.h"
#include "uv_util.h"
#include <random>
#include <unordered_map>
extern "C" {
#include "crypto-ops.h"
}
// l = 2^252 + 27742317777372353535851937790883648493.
// l fits 15 times in 32 bytes (iow, 15 l is the highest multiple of l that fits in 32 bytes)
static constexpr uint8_t limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 };
namespace p2pool {
namespace {
@@ -33,9 +35,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 +60,6 @@ public:
private:
uv_mutex_t m;
std::random_device rd;
std::mt19937_64 rng;
std::uniform_int_distribution<> dist;
};
@@ -79,10 +83,6 @@ static FORCEINLINE bool less32(const uint8_t* k0, const uint8_t* k1)
// cppcheck-suppress constParameter
void generate_keys(hash& pub, hash& sec)
{
// l = 2^252 + 27742317777372353535851937790883648493.
// l fits 15 times in 32 bytes (iow, 15 l is the highest multiple of l that fits in 32 bytes)
static constexpr uint8_t limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 };
do {
do { randomBytes(sec.h); } while (!less32(sec.h, limit));
sc_reduce32(sec.h);
@@ -93,6 +93,29 @@ void generate_keys(hash& pub, hash& sec)
ge_p3_tobytes(pub.h, &point);
}
void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len)
{
uint32_t counter = 0;
do {
do {
++counter;
keccak_custom([entropy, len, counter](int offset)
{
if (offset < static_cast<int>(len)) {
return entropy[offset];
}
return static_cast<uint8_t>(counter >> ((offset - len) * 8));
}, static_cast<int>(len + sizeof(counter)), sec.h, HASH_SIZE);
} while (!less32(sec.h, limit));
sc_reduce32(sec.h);
} while (!sc_isnonzero(sec.h));
ge_p3 point;
ge_scalarmult_base(&point, sec.h);
ge_p3_tobytes(pub.h, &point);
}
bool check_keys(const hash& pub, const hash& sec)
{
// From ge_scalarmult_base's comment: "preconditions a[31] <= 127"
@@ -109,9 +132,9 @@ bool check_keys(const hash& pub, const hash& sec)
return pub == pub_check;
}
static FORCEINLINE void hash_to_scalar(const uint8_t* data, size_t length, uint8_t(&res)[HASH_SIZE])
static FORCEINLINE void hash_to_scalar(const uint8_t* data, int length, uint8_t (&res)[HASH_SIZE])
{
keccak(data, static_cast<int>(length), res, HASH_SIZE);
keccak(data, length, res, HASH_SIZE);
sc_reduce32(res);
}
@@ -122,18 +145,13 @@ static FORCEINLINE void derivation_to_scalar(const hash& derivation, size_t outp
uint8_t output_index[(sizeof(size_t) * 8 + 6) / 7];
} buf;
uint8_t* begin = buf.derivation;
uint8_t* end = buf.output_index;
memcpy(buf.derivation, derivation.h, sizeof(buf.derivation));
size_t k = output_index;
while (k >= 0x80) {
*(end++) = (static_cast<uint8_t>(k) & 0x7F) | 0x80;
k >>= 7;
}
*(end++) = static_cast<uint8_t>(k);
uint8_t* p = buf.output_index;
writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; });
hash_to_scalar(begin, end - begin, res);
const uint8_t* data = buf.derivation;
hash_to_scalar(data, static_cast<int>(p - data), res);
}
class Cache
@@ -141,45 +159,63 @@ class Cache
public:
Cache()
{
uv_mutex_init_checked(&m);
uv_rwlock_init_checked(&derivations_lock);
uv_rwlock_init_checked(&public_keys_lock);
uv_rwlock_init_checked(&tx_keys_lock);
}
~Cache()
{
uv_mutex_destroy(&m);
uv_rwlock_destroy(&derivations_lock);
uv_rwlock_destroy(&public_keys_lock);
uv_rwlock_destroy(&tx_keys_lock);
}
bool get_derivation(const hash& key1, const hash& key2, hash& derivation)
bool get_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag)
{
std::array<uint8_t, HASH_SIZE * 2> index;
memcpy(index.data(), key1.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE, key2.h, HASH_SIZE);
derivation = {};
{
MutexLock lock(m);
ReadLock lock(derivations_lock);
auto it = derivations.find(index);
if (it != derivations.end()) {
derivation = it->second;
return true;
const DerivationEntry& entry = it->second;
derivation = entry.m_derivation;
if (entry.find_view_tag(output_index, view_tag)) {
return true;
}
}
}
ge_p3 point;
ge_p2 point2;
ge_p1p1 point3;
if (derivation.empty()) {
ge_p3 point;
ge_p2 point2;
ge_p1p1 point3;
if (ge_frombytes_vartime(&point, key1.h) != 0) {
return false;
if (ge_frombytes_vartime(&point, key1.h) != 0) {
return false;
}
ge_scalarmult(&point2, key2.h, &point);
ge_mul8(&point3, &point2);
ge_p1p1_to_p2(&point2, &point3);
ge_tobytes(reinterpret_cast<uint8_t*>(&derivation), &point2);
}
ge_scalarmult(&point2, key2.h, &point);
ge_mul8(&point3, &point2);
ge_p1p1_to_p2(&point2, &point3);
ge_tobytes(reinterpret_cast<uint8_t*>(&derivation), &point2);
derive_view_tag(derivation, output_index, view_tag);
{
MutexLock lock(m);
derivations.emplace(index, derivation);
WriteLock lock(derivations_lock);
DerivationEntry& entry = derivations.emplace(index, DerivationEntry{ derivation, {} }).first->second;
const uint32_t k = static_cast<uint32_t>(output_index << 8) | view_tag;
if (std::find(entry.m_viewTags.begin(), entry.m_viewTags.end(), k) == entry.m_viewTags.end()) {
entry.m_viewTags.emplace_back(k);
}
}
return true;
@@ -193,7 +229,7 @@ public:
memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t));
{
MutexLock lock(m);
ReadLock lock(public_keys_lock);
auto it = public_keys.find(index);
if (it != public_keys.end()) {
derived_key = it->second;
@@ -220,32 +256,84 @@ public:
ge_tobytes(derived_key.h, &point5);
{
MutexLock lock(m);
WriteLock lock(public_keys_lock);
public_keys.emplace(index, derived_key);
}
return true;
}
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id)
{
std::array<uint8_t, HASH_SIZE * 2> index;
memcpy(index.data(), wallet_spend_key.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE);
{
ReadLock lock(tx_keys_lock);
auto it = tx_keys.find(index);
if (it != tx_keys.end()) {
pub = it->second.first;
sec = it->second.second;
return;
}
}
static constexpr char domain[] = "tx_secret_key";
static constexpr size_t N = sizeof(domain) - 1;
uint8_t entropy[N + HASH_SIZE * 2];
memcpy(entropy, domain, N);
memcpy(entropy + N, wallet_spend_key.h, HASH_SIZE);
memcpy(entropy + N + HASH_SIZE, monero_block_id.h, HASH_SIZE);
generate_keys_deterministic(pub, sec, entropy, sizeof(entropy));
{
WriteLock lock(tx_keys_lock);
tx_keys.emplace(index, std::pair<hash, hash>(pub, sec));
}
}
void clear()
{
MutexLock lock(m);
derivations.clear();
public_keys.clear();
{ WriteLock lock(derivations_lock); derivations.clear(); }
{ WriteLock lock(public_keys_lock); public_keys.clear(); }
{ WriteLock lock(tx_keys_lock); tx_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;
struct DerivationEntry
{
hash m_derivation;
std::vector<uint32_t> m_viewTags;
bool find_view_tag(size_t output_index, uint8_t& view_tag) const {
for (uint32_t k : m_viewTags) {
if ((k >> 8) == output_index) {
view_tag = static_cast<uint8_t>(k);
return true;
}
}
return false;
}
};
uv_rwlock_t derivations_lock;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, DerivationEntry> derivations;
uv_rwlock_t public_keys_lock;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
uv_rwlock_t tx_keys_lock;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> tx_keys;
};
static Cache* cache = nullptr;
bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation)
bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag)
{
return cache->get_derivation(key1, key2, derivation);
return cache->get_derivation(key1, key2, output_index, derivation, view_tag);
}
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key)
@@ -253,6 +341,27 @@ bool derive_public_key(const hash& derivation, size_t output_index, const hash&
return cache->get_public_key(derivation, output_index, base, derived_key);
}
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id)
{
cache->get_tx_keys(pub, sec, wallet_spend_key, monero_block_id);
}
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag)
{
constexpr uint8_t salt[] = "view_tag";
constexpr size_t SALT_SIZE = sizeof(salt) - 1;
uint8_t buf[64];
memcpy(buf, salt, SALT_SIZE);
memcpy(buf + SALT_SIZE, derivation.h, HASH_SIZE);
uint8_t* p = buf + SALT_SIZE + HASH_SIZE;
writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; });
hash view_tag_full;
keccak(buf, static_cast<int>(p - buf), view_tag_full.h, HASH_SIZE);
view_tag = view_tag_full.h[0];
}
void init_crypto_cache()
{
if (!cache) {
+5 -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
@@ -20,9 +20,12 @@
namespace p2pool {
void generate_keys(hash& pub, hash& sec);
void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len);
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id);
bool check_keys(const hash& pub, const hash& sec);
bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation);
bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag);
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key);
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag);
void init_crypto_cache();
void destroy_crypto_cache();
+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
@@ -104,7 +104,7 @@ struct parse_wrapper<T, difficulty_type>
if (!from_hex(s[i], d)) {
return false;
}
out_value.hi = (out_value.hi << 4) || (out_value.lo >> 60);
out_value.hi = (out_value.hi << 4) | (out_value.lo >> 60);
out_value.lo = (out_value.lo << 4) | d;
}
+414 -184
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,223 +18,453 @@
#include "common.h"
#include "uv_util.h"
#include "json_rpc_request.h"
#include "llhttp.h"
#include <string>
#include <curl/curl.h>
static constexpr char log_category_prefix[] = "JSONRPCRequest ";
namespace p2pool {
namespace JSONRPCRequest {
JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb)
: m_socket{}
, m_connect{}
, m_write{}
, m_callback(cb)
, m_closeCallback(close_cb)
, m_contentLength(0)
, m_contentLengthHeader(false)
, m_readBufInUse(false)
, m_valid(true)
struct CurlContext
{
m_readBuf[0] = '\0';
CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
~CurlContext();
uv_tcp_init(uv_default_loop_checked(), &m_socket);
uv_tcp_nodelay(&m_socket, 1);
static int socket_func(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp)
{
CurlContext* ctx = reinterpret_cast<CurlContext*>(socketp ? socketp : userp);
return ctx->on_socket(easy, s, action);
}
sockaddr_storage addr;
if (uv_ip4_addr(address, port, reinterpret_cast<sockaddr_in*>(&addr)) != 0) {
const int err = uv_ip6_addr(address, port, reinterpret_cast<sockaddr_in6*>(&addr));
if (err) {
LOGERR(1, "invalid IP address " << address << " or port " << port);
m_valid = false;
static int timer_func(CURLM* multi, long timeout_ms, void* ctx)
{
return reinterpret_cast<CurlContext*>(ctx)->on_timer(multi, timeout_ms);
}
static size_t write_func(const void* buffer, size_t size, size_t count, void* ctx)
{
return reinterpret_cast<CurlContext*>(ctx)->on_write(buffer, size, count);
}
int on_socket(CURL* easy, curl_socket_t s, int action);
int on_timer(CURLM* multi, long timeout_ms);
static void on_timeout(uv_handle_t* req);
size_t on_write(const void* buffer, size_t size, size_t count);
static void curl_perform(uv_poll_t* req, int status, int events);
void check_multi_info();
static void on_close(uv_handle_t* h);
void shutdown();
std::vector<std::pair<curl_socket_t, uv_poll_t*>> m_pollHandles;
CallbackBase* m_callback;
CallbackBase* m_closeCallback;
uv_loop_t* m_loop;
uv_timer_t m_timer;
uv_async_t m_async;
CURLM* m_multiHandle;
CURL* m_handle;
std::string m_url;
std::string m_req;
std::string m_auth;
std::vector<char> m_response;
std::string m_error;
curl_slist* m_headers;
};
CurlContext::CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
: m_callback(cb)
, m_closeCallback(close_cb)
, m_loop(loop)
, m_timer{}
, m_async{}
, m_multiHandle(nullptr)
, m_handle(nullptr)
, m_req(req)
, m_auth(auth)
, m_headers(nullptr)
{
m_pollHandles.reserve(2);
{
char buf[log::Stream::BUF_SIZE + 1];
buf[0] = '\0';
log::Stream s(buf);
s << "http://" << address << ':' << port;
if (!m_req.empty() && (m_req.front() == '/')) {
s << m_req.c_str() << '\0';
m_req.clear();
}
else {
s << "/json_rpc\0";
}
m_url = buf;
}
int err = uv_timer_init(m_loop, &m_timer);
if (err) {
LOGERR(1, "uv_timer_init failed, error " << uv_err_name(err));
throw std::runtime_error("uv_timer_init failed");
}
m_timer.data = this;
err = uv_async_init(m_loop, &m_async, reinterpret_cast<uv_async_cb>(on_timeout));
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
throw std::runtime_error("uv_async_init failed");
}
m_async.data = this;
m_multiHandle = curl_multi_init();
if (!m_multiHandle) {
static constexpr char msg[] = "curl_multi_init() failed";
LOGERR(1, msg);
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
throw std::runtime_error(msg);
}
#define curl_multi_setopt_checked(...) \
do { \
const CURLMcode r = curl_multi_setopt(__VA_ARGS__); \
if (r != CURLM_OK) { \
static constexpr char msg[] = "curl_multi_setopt(" #__VA_ARGS__ ") failed"; \
LOGERR(1, msg << ": " << curl_multi_strerror(r)); \
throw std::runtime_error(msg); \
} \
} while (0)
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_SOCKETFUNCTION, socket_func);
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_SOCKETDATA, this);
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_TIMERFUNCTION, timer_func);
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_TIMERDATA, this);
m_handle = curl_easy_init();
if (!m_handle) {
static constexpr char msg[] = "curl_easy_init() failed";
LOGERR(1, msg);
curl_multi_cleanup(m_multiHandle);
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
throw std::runtime_error(msg);
}
#define curl_easy_setopt_checked(...) \
do { \
const CURLcode r = curl_easy_setopt(__VA_ARGS__); \
if (r != CURLE_OK) { \
static constexpr char msg[] = "curl_easy_setopt(" #__VA_ARGS__ ") failed"; \
LOGERR(1, msg << ": " << curl_easy_strerror(r)); \
throw std::runtime_error(msg); \
} \
} while (0)
curl_easy_setopt_checked(m_handle, CURLOPT_WRITEFUNCTION, write_func);
curl_easy_setopt_checked(m_handle, CURLOPT_WRITEDATA, this);
curl_easy_setopt_checked(m_handle, CURLOPT_URL, m_url.c_str());
curl_easy_setopt_checked(m_handle, CURLOPT_POSTFIELDS, m_req.c_str());
curl_easy_setopt_checked(m_handle, CURLOPT_CONNECTTIMEOUT, 1);
curl_easy_setopt_checked(m_handle, CURLOPT_TIMEOUT, 10);
m_headers = curl_slist_append(m_headers, "Content-Type: application/json");
if (m_headers) {
curl_easy_setopt_checked(m_handle, CURLOPT_HTTPHEADER, m_headers);
}
if (!m_auth.empty()) {
curl_easy_setopt_checked(m_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_ONLY);
curl_easy_setopt_checked(m_handle, CURLOPT_USERPWD, m_auth.c_str());
}
CURLMcode curl_err = curl_multi_add_handle(m_multiHandle, m_handle);
if (curl_err != CURLM_OK) {
LOGERR(1, "curl_multi_add_handle failed, error " << curl_multi_strerror(curl_err));
curl_easy_cleanup(m_handle);
curl_multi_cleanup(m_multiHandle);
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
throw std::runtime_error("curl_multi_add_handle failed");
}
}
CurlContext::~CurlContext()
{
if (m_error.empty() && !m_response.empty()) {
(*m_callback)(m_response.data(), m_response.size());
}
delete m_callback;
if (m_response.empty()) {
if (m_error.empty()) {
m_error = "empty response";
}
else {
m_error += " (empty response)";
}
}
(*m_closeCallback)(m_error.c_str(), m_error.length());
delete m_closeCallback;
curl_slist_free_all(m_headers);
}
int CurlContext::on_socket(CURL* /*easy*/, curl_socket_t s, int action)
{
auto it = std::find_if(m_pollHandles.begin(), m_pollHandles.end(), [s](const auto& value) { return value.first == s; });
switch (action) {
case CURL_POLL_IN:
case CURL_POLL_OUT:
case CURL_POLL_INOUT:
{
uv_poll_t* h = nullptr;
if (it != m_pollHandles.end()) {
h = it->second;
}
else {
h = new uv_poll_t{};
// cppcheck-suppress nullPointer
h->data = this;
const int result = uv_poll_init_socket(m_loop, h, s);
if (result < 0) {
LOGERR(1, "uv_poll_init_socket failed: " << uv_err_name(result));
delete h;
h = nullptr;
}
else {
m_pollHandles.emplace_back(s, h);
}
}
if (h) {
const CURLMcode err = curl_multi_assign(m_multiHandle, s, this);
if (err != CURLM_OK) {
LOGERR(1, "curl_multi_assign(action = " << action << ") failed: " << curl_multi_strerror(err));
}
int events = 0;
if (action != CURL_POLL_IN) events |= UV_WRITABLE;
if (action != CURL_POLL_OUT) events |= UV_READABLE;
const int result = uv_poll_start(h, events, curl_perform);
if (result < 0) {
LOGERR(1, "uv_poll_start failed with error " << uv_err_name(result));
}
}
else {
LOGERR(1, "failed to start polling on socket " << static_cast<int>(s));
}
}
break;
case CURL_POLL_REMOVE:
default:
{
if (it != m_pollHandles.end()) {
uv_poll_t* h = it->second;
m_pollHandles.erase(it);
uv_poll_stop(h);
uv_close(reinterpret_cast<uv_handle_t*>(h), [](uv_handle_t* h) { delete reinterpret_cast<uv_poll_t*>(h); });
}
const CURLMcode err = curl_multi_assign(m_multiHandle, s, nullptr);
if (err != CURLM_OK) {
LOGERR(1, "curl_multi_assign(action = " << action << ") failed: " << curl_multi_strerror(err));
}
}
break;
}
return 0;
}
int CurlContext::on_timer(CURLM* /*multi*/, long timeout_ms)
{
if (timeout_ms < 0) {
uv_timer_stop(&m_timer);
return 0;
}
if (timeout_ms == 0) {
// 0 ms timeout, but we can't just call on_timeout() here - we have to kick the UV loop
const int result = uv_async_send(&m_async);
if (result < 0) {
LOGERR(1, "uv_async_send failed with error " << uv_err_name(result));
// if async call didn't work, try to use the timer with 1 ms timeout
timeout_ms = 1;
}
else {
return 0;
}
}
const int result = uv_timer_start(&m_timer, reinterpret_cast<uv_timer_cb>(on_timeout), timeout_ms, 0);
if (result < 0) {
LOGERR(1, "uv_timer_start failed with error " << uv_err_name(result));
return -1;
}
return 0;
}
void CurlContext::on_timeout(uv_handle_t* req)
{
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
int running_handles = 0;
const CURLMcode err = curl_multi_socket_action(ctx->m_multiHandle, CURL_SOCKET_TIMEOUT, 0, &running_handles);
if (err != CURLM_OK) {
LOGERR(1, "curl_multi_socket_action failed, error " << curl_multi_strerror(err));
}
ctx->check_multi_info();
if (running_handles == 0) {
ctx->shutdown();
}
}
size_t CurlContext::on_write(const void* buffer, size_t size, size_t count)
{
const size_t realsize = size * count;
const char* p = reinterpret_cast<const char*>(buffer);
m_response.insert(m_response.end(), p, p + realsize);
return realsize;
}
void CurlContext::curl_perform(uv_poll_t* req, int status, int events)
{
int flags = 0;
if (status < 0) {
flags |= CURL_CSELECT_ERR;
LOGERR(1, "uv_poll_start returned error " << uv_err_name(status));
}
else {
if (events & UV_READABLE) flags |= CURL_CSELECT_IN;
if (events & UV_WRITABLE) flags |= CURL_CSELECT_OUT;
}
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
int running_handles = 0;
auto it = std::find_if(ctx->m_pollHandles.begin(), ctx->m_pollHandles.end(), [req](const auto& value) { return value.second == req; });
if (it != ctx->m_pollHandles.end()) {
const CURLMcode err = curl_multi_socket_action(ctx->m_multiHandle, it->first, flags, &running_handles);
if (err != CURLM_OK) {
LOGERR(1, "curl_multi_socket_action failed, error " << curl_multi_strerror(err));
}
}
ctx->check_multi_info();
if (running_handles == 0) {
ctx->shutdown();
}
}
void CurlContext::check_multi_info()
{
int pending;
while (CURLMsg* message = curl_multi_info_read(m_multiHandle, &pending)) {
if (message->msg == CURLMSG_DONE) {
if (message->data.result != CURLE_OK) {
m_error = curl_easy_strerror(message->data.result);
}
else {
long http_code = 0;
curl_easy_getinfo(message->easy_handle, CURLINFO_RESPONSE_CODE, &http_code);
if (http_code != 200) {
char buf[32] = {};
log::Stream s(buf);
s << "HTTP error " << static_cast<int>(http_code) << '\0';
m_error = buf;
}
else if (m_response.empty()) {
m_error = "empty response";
}
}
curl_multi_remove_handle(m_multiHandle, m_handle);
curl_easy_cleanup(m_handle);
curl_multi_cleanup(m_multiHandle);
return;
}
}
m_socket.data = this;
m_connect.data = this;
m_write.data = this;
const size_t len = strlen(req);
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";
m_request.resize(s.m_pos);
m_request.insert(m_request.end(), req, req + len);
m_response.reserve(sizeof(m_readBuf));
const int err = uv_tcp_connect(&m_connect, &m_socket, reinterpret_cast<const sockaddr*>(&addr), on_connect);
if (err) {
LOGERR(1, "failed to initiate tcp connection to " << address << ", error " << uv_err_name(err));
m_valid = false;
}
}
void JSONRPCRequest::on_connect(uv_connect_t* req, int status)
void CurlContext::on_close(uv_handle_t* h)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(req->data);
CurlContext* ctx = reinterpret_cast<CurlContext*>(h->data);
h->data = nullptr;
if (status != 0) {
pThis->m_error = uv_err_name(status);
LOGERR(1, "failed to connect, error " << pThis->m_error);
pThis->close();
if (ctx->m_timer.data || ctx->m_async.data) {
return;
}
uv_buf_t buf[1];
buf[0].base = pThis->m_request.data();
buf[0].len = static_cast<uint32_t>(pThis->m_request.size());
uv_write(&pThis->m_write, reinterpret_cast<uv_stream_t*>(&pThis->m_socket), buf, 1, on_write);
delete ctx;
}
void JSONRPCRequest::on_write(uv_write_t* handle, int status)
void CurlContext::shutdown()
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(handle->data);
for (const auto& p : m_pollHandles) {
uv_poll_stop(p.second);
uv_close(reinterpret_cast<uv_handle_t*>(p.second), [](uv_handle_t* h) { delete reinterpret_cast<uv_poll_t*>(h); });
}
m_pollHandles.clear();
if (status != 0) {
pThis->m_error = uv_err_name(status);
LOGERR(1, "failed to send request, error " << pThis->m_error);
pThis->close();
return;
if (m_async.data && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_async))) {
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), on_close);
}
uv_read_start(reinterpret_cast<uv_stream_t*>(&pThis->m_socket), on_alloc, on_read);
}
void JSONRPCRequest::on_alloc(uv_handle_t* handle, size_t /*suggested_size*/, uv_buf_t* buf)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(handle->data);
if (pThis->m_readBufInUse) {
LOGERR(1, "read buffer is already in use");
}
buf->len = sizeof(pThis->m_readBuf);
buf->base = pThis->m_readBuf;
pThis->m_readBufInUse = true;
}
void JSONRPCRequest::on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(stream->data);
pThis->m_readBufInUse = false;
if (nread > 0) {
pThis->on_read(buf->base, nread);
}
else if (nread < 0) {
if (nread != UV_EOF){
pThis->m_error = uv_err_name(static_cast<int>(nread));
LOGERR(1, "failed to read response, error " << pThis->m_error);
}
pThis->close();
if (m_timer.data && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_timer))) {
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), on_close);
}
}
void JSONRPCRequest::on_read(const char* data, size_t size)
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
{
m_response.append(data, size);
static constexpr char headers_end[] = "\r\n\r\n";
if (m_response.find(headers_end) == std::string::npos) {
return;
if (!loop) {
loop = uv_default_loop();
}
llhttp_settings_t settings{};
settings.on_status = [](llhttp_t*, const char* at, size_t length)
{
if ((length == 2) && (!memcmp(at, "Ok", 2) || !memcmp(at, "OK", 2))) {
return 0;
}
return -1;
};
settings.on_header_field = [](llhttp_t* parser, const char* at, size_t length)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(parser->data);
static const char header[] = "Content-Length";
pThis->m_contentLengthHeader = ((length == sizeof(header) - 1) && (memcmp(at, header, length) == 0));
return 0;
};
settings.on_header_value = [](llhttp_t* parser, const char* at, size_t length)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(parser->data);
if (pThis->m_contentLengthHeader) {
uint32_t k = 0;
for (const char* p = at; p < at + length; ++p) {
if ('0' <= *p && *p <= '9') {
k = k * 10 + (*p - '0');
}
else {
return -1;
}
const bool result = CallOnLoop(loop,
[=]()
{
try {
new CurlContext(address, port, req, auth, cb, close_cb, loop);
}
if (!k) {
return -1;
catch (const std::exception& e) {
const char* msg = e.what();
(*close_cb)(msg, strlen(msg));
}
pThis->m_contentLength = k;
}
return 0;
};
});
settings.on_body = [](llhttp_t* parser, const char* at, size_t length)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(parser->data);
if (pThis->m_contentLength && (length >= pThis->m_contentLength) && pThis->m_callback) {
(*pThis->m_callback)(at, length);
delete pThis->m_callback;
pThis->m_callback = nullptr;
}
return 0;
};
llhttp_t parser;
llhttp_init(&parser, HTTP_RESPONSE, &settings);
parser.data = this;
const llhttp_errno result = llhttp_execute(&parser, m_response.c_str(), m_response.length());
if (result != HPE_OK) {
m_error = "failed to parse response";
LOGERR(1, m_error << ", result = " << static_cast<int>(result));
close();
return;
}
if (!m_callback) {
close();
if (!result) {
LOGERR(1, "JSON RPC \"" << req << "\" failed");
}
}
void JSONRPCRequest::close()
{
uv_handle_t* h = reinterpret_cast<uv_handle_t*>(&m_socket);
if (!uv_is_closing(h)) {
uv_close(h, on_close);
}
}
void JSONRPCRequest::on_close(uv_handle_t* handle)
{
JSONRPCRequest* req = static_cast<JSONRPCRequest*>(handle->data);
if (req->m_closeCallback) {
(*req->m_closeCallback)(req->m_error.c_str(), req->m_error.length());
}
delete req;
}
JSONRPCRequest::~JSONRPCRequest()
{
delete m_callback;
delete m_closeCallback;
}
} // namespace JSONRPCRequest
} // namespace p2pool
+25 -69
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,76 +18,32 @@
#pragma once
namespace p2pool {
namespace JSONRPCRequest {
class JSONRPCRequest
struct CallbackBase
{
public:
template<typename T>
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);
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)
{
// 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)));
if (!r->m_valid) {
constexpr char err[] = "internal error";
close_cb(err, sizeof(err) - 1);
delete r;
}
}
private:
struct CallbackBase
{
virtual ~CallbackBase() {}
virtual void operator()(const char* data, size_t size) = 0;
};
template<typename T>
struct Callback : public CallbackBase
{
explicit FORCEINLINE Callback(T&& cb) : m_cb(std::move(cb)) {}
void operator()(const char* data, size_t size) override { m_cb(data, size); }
private:
Callback& operator=(Callback&&) = delete;
T m_cb;
};
JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb);
~JSONRPCRequest();
static void on_connect(uv_connect_t* req, int status);
static void on_write(uv_write_t* handle, int status);
static void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf);
void on_read(const char* data, size_t size);
void close();
static void on_close(uv_handle_t* handle);
uv_tcp_t m_socket;
uv_connect_t m_connect;
uv_write_t m_write;
CallbackBase* m_callback;
CallbackBase* m_closeCallback;
uint32_t m_contentLength;
bool m_contentLengthHeader;
std::vector<char> m_request;
std::string m_response;
char m_readBuf[65536];
bool m_readBufInUse;
bool m_valid;
std::string m_error;
virtual ~CallbackBase() {}
virtual void operator()(const char* data, size_t size) = 0;
};
template<typename T>
struct Callback : public CallbackBase
{
explicit FORCEINLINE Callback(T&& cb) : m_cb(std::move(cb)) {}
void operator()(const char* data, size_t size) override { m_cb(data, size); }
private:
Callback& operator=(Callback&&) = delete;
T m_cb;
};
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
template<typename T, typename U>
FORCEINLINE void call(const std::string& address, int port, const std::string& req, const std::string& auth, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr)
{
Call(address, port, req, auth, new Callback<T>(std::move(cb)), new Callback<U>(std::move(close_cb)), loop);
}
} // namespace JSONRPCRequest
} // namespace p2pool
+34 -63
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,23 @@ 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 THETA(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; \
}
THETA(0);
THETA(1);
THETA(2);
THETA(3);
THETA(4);
// 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);
@@ -85,60 +91,25 @@ NOINLINE void keccakf(uint64_t* st)
st[10] = ROTL64(t, 1);
// Chi
// unrolled loop, where only last iteration is different
int j = 0;
bc[0] = st[j + 0];
bc[1] = st[j + 1];
#define CHI(j) { \
const uint64_t st0 = st[j ]; \
const uint64_t st1 = st[j + 1]; \
const uint64_t st2 = st[j + 2]; \
const uint64_t st3 = st[j + 3]; \
const uint64_t st4 = st[j + 4]; \
st[j ] ^= ~st1 & st2; \
st[j + 1] ^= ~st2 & st3; \
st[j + 2] ^= ~st3 & st4; \
st[j + 3] ^= ~st4 & st0; \
st[j + 4] ^= ~st0 & st1; \
}
st[j + 0] ^= (~st[j + 1]) & st[j + 2];
st[j + 1] ^= (~st[j + 2]) & st[j + 3];
st[j + 2] ^= (~st[j + 3]) & st[j + 4];
st[j + 3] ^= (~st[j + 4]) & bc[0];
st[j + 4] ^= (~bc[0]) & bc[1];
CHI( 0);
CHI( 5);
CHI(10);
CHI(15);
CHI(20);
j = 5;
bc[0] = st[j + 0];
bc[1] = st[j + 1];
st[j + 0] ^= (~st[j + 1]) & st[j + 2];
st[j + 1] ^= (~st[j + 2]) & st[j + 3];
st[j + 2] ^= (~st[j + 3]) & st[j + 4];
st[j + 3] ^= (~st[j + 4]) & bc[0];
st[j + 4] ^= (~bc[0]) & bc[1];
j = 10;
bc[0] = st[j + 0];
bc[1] = st[j + 1];
st[j + 0] ^= (~st[j + 1]) & st[j + 2];
st[j + 1] ^= (~st[j + 2]) & st[j + 3];
st[j + 2] ^= (~st[j + 3]) & st[j + 4];
st[j + 3] ^= (~st[j + 4]) & bc[0];
st[j + 4] ^= (~bc[0]) & bc[1];
j = 15;
bc[0] = st[j + 0];
bc[1] = st[j + 1];
st[j + 0] ^= (~st[j + 1]) & st[j + 2];
st[j + 1] ^= (~st[j + 2]) & st[j + 3];
st[j + 2] ^= (~st[j + 3]) & st[j + 4];
st[j + 3] ^= (~st[j + 4]) & bc[0];
st[j + 4] ^= (~bc[0]) & bc[1];
j = 20;
bc[0] = st[j + 0];
bc[1] = st[j + 1];
bc[2] = st[j + 2];
bc[3] = st[j + 3];
bc[4] = st[j + 4];
st[j + 0] ^= (~bc[1]) & bc[2];
st[j + 1] ^= (~bc[2]) & bc[3];
st[j + 2] ^= (~bc[3]) & bc[4];
st[j + 3] ^= (~bc[4]) & bc[0];
st[j + 4] ^= (~bc[0]) & bc[1];
// Iota
st[0] ^= keccakf_rndc[round];
}
@@ -155,7 +126,7 @@ NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen)
for (; inlen >= rsiz; inlen -= rsiz, in += rsiz) {
for (int i = 0; i < rsizw; i++) {
st[i] ^= ((uint64_t*)in)[i];
st[i] ^= read_unaligned(reinterpret_cast<const uint64_t*>(in) + i);
}
keccakf(st);
}
@@ -169,7 +140,7 @@ NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen)
temp[rsiz - 1] |= 0x80;
for (int i = 0; i < rsizw; i++) {
st[i] ^= ((uint64_t*)temp)[i];
st[i] ^= reinterpret_cast<uint64_t*>(temp)[i];
}
keccakf(st);
+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
+83 -25
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,7 @@
#include "uv_util.h"
#include <ctime>
#include <fstream>
#include <thread>
static constexpr char log_category_prefix[] = "Log ";
static constexpr char log_file_name[] = "p2pool.log";
@@ -28,13 +29,15 @@ namespace p2pool {
namespace log {
int GLOBAL_LOG_LEVEL = 3;
bool CONSOLE_COLORS = true;
#ifndef P2POOL_LOG_DISABLE
static volatile bool stopped = false;
static std::atomic<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
@@ -45,14 +48,14 @@ public:
enum params : int
{
SLOT_SIZE = 1024,
BUF_SIZE = SLOT_SIZE * 16384,
BUF_SIZE = SLOT_SIZE * 8192,
};
FORCEINLINE Worker()
: 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 +66,22 @@ public:
uv_cond_init(&m_cond);
uv_mutex_init(&m_mutex);
uv_thread_create(&m_worker, run_wrapper, this);
do {} while (!worker_started);
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();
}
while (!worker_started) {
std::this_thread::yield();
}
#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,18 +94,21 @@ public:
}
}
FORCEINLINE ~Worker()
~Worker()
{
stop();
try {
stop();
}
catch (...) {
}
}
FORCEINLINE void stop()
{
if (stopped) {
if (stopped.exchange(true)) {
return;
}
stopped = true;
LOGINFO(0, "stopped");
uv_thread_join(&m_worker);
uv_cond_destroy(&m_cond);
@@ -117,6 +133,7 @@ public:
memcpy(p + 1, buf + 1, size - 1);
// Ensure memory order in the writer thread
std::atomic_thread_fence(std::memory_order_seq_cst);
// Mark that everything is written into this log slot
@@ -135,18 +152,26 @@ private:
do {
uv_mutex_lock(&m_mutex);
uv_cond_wait(&m_cond, &m_mutex);
if (m_readPos == m_writePos.load()) {
// Nothing to do, wait for the signal
uv_cond_wait(&m_cond, &m_mutex);
}
uv_mutex_unlock(&m_mutex);
for (uint32_t writePos = m_writePos.load(); m_readPos < writePos; writePos = m_writePos.load()) {
for (uint32_t writePos = m_writePos.load(); m_readPos != writePos; writePos = m_writePos.load()) {
// We have at least one log slot pending, possibly more than one
// Process everything in a loop before reading m_writePos again
do {
char* p = m_buf.data() + (m_readPos % BUF_SIZE);
// Wait until everything is written into this log slot
volatile char& severity = *p;
while (!severity) {}
volatile char& severity = p[0];
while (!severity) {
std::this_thread::yield();
}
// Ensure memory order in the reader thread
std::atomic_thread_fence(std::memory_order_seq_cst);
uint32_t size = static_cast<uint8_t>(p[2]);
size = (size << 8) + static_cast<uint8_t>(p[1]);
@@ -155,6 +180,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);
@@ -162,17 +194,10 @@ private:
fwrite(p, 1, size, (severity == 1) ? stdout : stderr);
#endif
// Reopen the log file if it's been moved (logrotate support)
if (m_logFile.is_open()) {
struct stat buf;
if (stat(log_file_name, &buf) != 0) {
m_logFile.close();
m_logFile.open(log_file_name, std::ios::app | std::ios::binary);
if (c) {
strip_colors(p, size);
}
}
if (m_logFile.is_open()) {
strip_colors(p, size);
if (severity == 1) {
m_logFile.write("NOTICE ", 8);
@@ -185,7 +210,6 @@ private:
}
m_logFile.write(p, size);
m_logFile.flush();
}
}
@@ -195,6 +219,18 @@ private:
m_readPos += SLOT_SIZE;
} while (m_readPos < writePos);
}
// Flush the log file only after all pending log lines have been written
if (m_logFile.is_open()) {
m_logFile.flush();
// Reopen the log file if it's been moved (logrotate support)
struct stat buf;
if (stat(log_file_name, &buf) != 0) {
m_logFile.close();
m_logFile.open(log_file_name, std::ios::app | std::ios::binary);
}
}
} while (!stopped);
}
@@ -240,7 +276,6 @@ static Worker worker;
#endif // P2POOL_LOG_DISABLE
// cppcheck-suppress uninitMemberVar
NOINLINE Writer::Writer(Severity severity) : Stream(m_stackBuf)
{
m_buf[0] = static_cast<char>(severity);
@@ -301,6 +336,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
+118 -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
@@ -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 {
@@ -34,8 +35,10 @@ struct Stream
{
enum params : int { BUF_SIZE = 1024 - 1 };
explicit FORCEINLINE Stream(char* buf) : m_pos(0), m_numberWidth(1), m_buf(buf), m_bufSize(BUF_SIZE) {}
FORCEINLINE Stream(char* buf, size_t size) : m_pos(0), m_numberWidth(1), m_buf(buf), m_bufSize(static_cast<int>(size) - 1) {}
template<size_t N>
explicit FORCEINLINE Stream(char (&buf)[N]) : m_pos(0), m_numberWidth(1), m_buf(buf), m_bufSize(N - 1) {}
FORCEINLINE Stream(void* buf, size_t size) : m_pos(0), m_numberWidth(1), m_buf(reinterpret_cast<char*>(buf)), m_bufSize(static_cast<int>(size) - 1) {}
template<typename T>
struct Entry
@@ -181,7 +184,10 @@ INT_ENTRY(unsigned long)
template<typename T, int base>
struct BasedValue
{
explicit FORCEINLINE BasedValue(T value) : m_value(value) {}
explicit FORCEINLINE BasedValue(T value) : m_value(value)
{
static_assert(std::is_integral<T>::value, "Must be an integer type here");
}
T m_value;
};
@@ -189,7 +195,7 @@ struct BasedValue
template<typename T, int base>
struct Stream::Entry<BasedValue<T, base>>
{
static FORCEINLINE void put(BasedValue<T, base>&& data, Stream* wrapper)
static FORCEINLINE void put(BasedValue<T, base> data, Stream* wrapper)
{
wrapper->writeInt<T, base>(data.m_value);
}
@@ -301,6 +307,7 @@ struct Hashrate
{
FORCEINLINE Hashrate() : m_data(0), m_valid(false) {}
explicit FORCEINLINE Hashrate(uint64_t data) : m_data(data), m_valid(true) {}
FORCEINLINE Hashrate(uint64_t data, bool valid) : m_data(data), m_valid(valid) {}
uint64_t m_data;
bool m_valid;
@@ -308,7 +315,7 @@ struct Hashrate
template<> struct log::Stream::Entry<Hashrate>
{
static NOINLINE void put(Hashrate&& value, Stream* wrapper)
static NOINLINE void put(const Hashrate& value, Stream* wrapper)
{
if (!value.m_valid) {
return;
@@ -353,7 +360,7 @@ struct XMRAmount
template<> struct log::Stream::Entry<XMRAmount>
{
static NOINLINE void put(XMRAmount&& value, Stream* wrapper)
static NOINLINE void put(XMRAmount value, Stream* wrapper)
{
constexpr uint64_t denomination = 1000000000000ULL;
@@ -371,7 +378,7 @@ template<> struct log::Stream::Entry<XMRAmount>
template<> struct log::Stream::Entry<NetworkType>
{
static NOINLINE void put(const NetworkType& value, Stream* wrapper)
static NOINLINE void put(NetworkType value, Stream* wrapper)
{
switch (value) {
case NetworkType::Invalid: *wrapper << "invalid"; break;
@@ -382,6 +389,75 @@ template<> struct log::Stream::Entry<NetworkType>
}
};
struct Duration
{
explicit FORCEINLINE Duration(uint64_t data) : m_data(data) {}
uint64_t m_data;
};
template<> struct log::Stream::Entry<Duration>
{
static NOINLINE void put(Duration value, Stream* wrapper)
{
const uint64_t uptime = value.m_data;
const int64_t s = uptime % 60;
const int64_t m = (uptime / 60) % 60;
const int64_t h = (uptime / 3600) % 24;
const int64_t d = uptime / 86400;
if (d > 0) {
*wrapper << d << "d ";
}
*wrapper << h << "h " << m << "m " << s << 's';
}
};
template<typename T>
struct PadRight
{
FORCEINLINE PadRight(const T& value, int len) : m_value(value), m_len(len) {}
const T& m_value;
int m_len;
// Declare it to make compiler happy
PadRight(const PadRight&);
private:
PadRight& operator=(const PadRight&) = delete;
PadRight& operator=(PadRight&&) = delete;
};
template<typename T> FORCEINLINE PadRight<T> pad_right(const T& value, int len) { return PadRight<T>(value, len); }
template<typename T>
struct log::Stream::Entry<PadRight<T>>
{
static NOINLINE void put(PadRight<T>&& data, Stream* wrapper)
{
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf);
s << data.m_value;
const int len = std::min<int>(data.m_len, log::Stream::BUF_SIZE);
if (s.m_pos < len) {
memset(buf + s.m_pos, ' ', static_cast<size_t>(len) - s.m_pos);
s.m_pos = len;
}
wrapper->writeBuf(buf, s.m_pos);
}
};
void put_rawip(const raw_ip& value, Stream* wrapper);
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&);
@@ -393,16 +469,47 @@ namespace {
#define CONCAT(a, b) CONCAT2(a, b)
#define CONCAT2(a, b) a##b
// This is to check that LOG() call doesn't modify variables in scope, making program behavior dependent on the log level:
//
// int some_func(int& n) { return ++n; }
// ...
// LOGINFO(1, "Some important value: " << some_func(n));
//
// will not compile because the dummy lambda capture uses const-qualified copies of all variables.
//
// The check is "free": compiler will remove it entirely in release builds.
struct DummyStream
{
template<typename T>
FORCEINLINE DummyStream& operator<<(const T&)
{
return *this;
}
};
#define SIDE_EFFECT_CHECK(level, ...) \
do { \
if (0) { \
MSVC_PRAGMA(warning(suppress:26444)) \
[=]() { \
log::DummyStream x; \
x << level << __VA_ARGS__; \
}; \
} \
} while (0)
#ifdef P2POOL_LOG_DISABLE
#define LOGINFO(level, ...)
#define LOGWARN(level, ...)
#define LOGERR(level, ...)
#define LOGINFO(level, ...) SIDE_EFFECT_CHECK(level, __VA_ARGS__)
#define LOGWARN(level, ...) SIDE_EFFECT_CHECK(level, __VA_ARGS__)
#define LOGERR(level, ...) SIDE_EFFECT_CHECK(level, __VA_ARGS__)
#else
#define LOG(level, severity, ...) \
do { \
SIDE_EFFECT_CHECK(level, __VA_ARGS__); \
if (level <= log::GLOBAL_LOG_LEVEL) { \
log::Writer CONCAT(log_wrapper_, __LINE__)(severity); \
CONCAT(log_wrapper_, __LINE__) << log::Gray() << log_category_prefix; \
+33 -8
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,8 +20,9 @@
#include "p2pool.h"
#include "stratum_server.h"
#include "p2p_server.h"
#include <curl/curl.h>
static void usage()
void p2pool_usage()
{
printf("P2Pool %s\n"
"\nUsage:\n\n" \
@@ -33,13 +34,27 @@ 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"
"--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum\n"
"--rpc-login Specify username[:password] required for Monero RPC server\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
@@ -56,26 +71,36 @@ 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;
}
}
int result;
memory_tracking_start();
p2pool::init_crypto_cache();
{
int result = static_cast<int>(curl_global_init_mem(CURL_GLOBAL_ALL, p2pool::malloc_hook, p2pool::free_hook, p2pool::realloc_hook, p2pool::strdup_hook, p2pool::calloc_hook));
if (result != CURLE_OK) {
return result;
}
try {
p2pool::p2pool pool(argc, argv);
result = pool.run();
}
catch (...) {
result = 1;
}
curl_global_cleanup();
p2pool::destroy_crypto_cache();
memory_tracking_stop();
+66 -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
@@ -32,7 +32,7 @@ namespace p2pool {
static bool track_memory = false;
constexpr size_t N = 1048576;
constexpr size_t N = 2097152;
constexpr size_t MAX_FRAMES = 30;
struct TrackedAllocation
@@ -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,59 @@ 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);
}
char* strdup_hook(const char* s) noexcept
{
#ifdef _MSC_VER
char* s1 = _strdup(s);
#else
char* s1 = strdup(s);
#endif
if (s1) {
add_alocation(s1, strlen(s) + 1);
}
return s1;
}
void* realloc_hook(void* ptr, size_t size) noexcept
{
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 +241,34 @@ 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
// cppcheck-suppress functionStatic
void memory_tracking_start() {}
// cppcheck-suppress functionStatic
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); }
char* strdup_hook(const char* s) noexcept
{
#ifdef _MSC_VER
return _strdup(s);
#else
return strdup(s);
#endif
}
}
#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
+234
View File
@@ -0,0 +1,234 @@
/*
* 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 "side_chain.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) <<
"\nShares found = " << m_sharesFound.load()
);
}
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) {
const double block_reward_share_percent = m_pool->side_chain().get_reward_share(m_pool->params().m_wallet) * 100.0;
m_pool->api()->set(p2pool_api::Category::LOCAL, "miner",
[cur_ts, hash_count, dt, block_reward_share_percent, this](log::Stream& s)
{
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
const double time_running = static_cast<double>(duration_cast<milliseconds>(cur_ts - m_startTimestamp).count()) / 1e3;
s << "{\"current_hashrate\":" << hr
<< ",\"total_hashes\":" << m_totalHashes.load()
<< ",\"time_running\":" << time_running
<< ",\"shares_found\":" << m_sharesFound.load()
<< ",\"block_reward_share_percent\":" << block_reward_share_percent
<< ",\"threads\":" << m_threads
<< "}";
});
}
}
void Miner::reset_share_counters()
{
m_totalHashes = 0;
m_sharesFound = 0;
}
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);
}
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
+84
View File
@@ -0,0 +1,84 @@
/*
* 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);
void reset_share_counters();
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;
std::atomic<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;
std::atomic<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];
std::atomic<uint32_t> m_jobIndex;
void run(WorkerData* data);
};
} // namespace p2pool
+618 -173
View File
File diff suppressed because it is too large Load Diff
+48 -15
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>
{
@@ -98,10 +97,12 @@ public:
bool on_peer_list_request(const uint8_t* buf);
bool on_peer_list_response(const uint8_t* buf) const;
bool handle_incoming_block_async(PoolBlock* block);
bool handle_incoming_block_async(const PoolBlock* block);
void handle_incoming_block(p2pool* pool, PoolBlock& block, const uint32_t reset_counter, const raw_ip& addr, std::vector<hash>& missing_blocks);
void post_handle_incoming_block(const uint32_t reset_counter, std::vector<hash>& missing_blocks);
bool is_good() const { return m_handshakeComplete && !m_handshakeInvalid && (m_listenPort >= 0); }
uint64_t m_peerId;
MessageId m_expectedMessage;
uint64_t m_handshakeChallenge;
@@ -109,13 +110,23 @@ 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;
bool m_chainTipBlockRequest;
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);
@@ -123,15 +134,28 @@ public:
uint64_t get_peerId() const { return m_peerId; }
void print_status() override;
void show_peers();
size_t peer_list_size() const { MutexLock lock(m_peerListLock); 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); }
int deserialize_block(const uint8_t* buf, uint32_t size);
const PoolBlock* get_block() const { return m_block; }
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(); }
@@ -139,27 +163,32 @@ 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;
std::vector<uint8_t> m_blockDeserializeBuf;
int m_blockDeserializeResult;
uv_timer_t m_timer;
uint64_t m_timerCounter;
uint64_t m_timerInterval;
uint64_t m_peerId;
uv_mutex_t m_peerListLock;
mutable uv_mutex_t m_peerListLock;
struct Peer
{
@@ -167,10 +196,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;
std::atomic<uint64_t> m_peerListLastSaved;
struct Broadcast
{
@@ -183,8 +214,10 @@ private:
uv_async_t m_broadcastAsync;
std::vector<Broadcast*> m_broadcastQueue;
bool m_lookForMissingBlocks;
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();
+387 -75
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,15 @@
#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 "keccak.h"
#include <thread>
#include <fstream>
@@ -49,26 +54,29 @@ 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();
throw std::exception();
}
bool is_v6;
if (!resolve_host(m_params->m_host, is_v6)) {
LOGERR(1, "resolve_host failed for " << m_params->m_host);
panic();
throw std::exception();
}
hash pub, sec, eph_public_key;
generate_keys(pub, sec);
if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key)) {
uint8_t view_tag;
if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key, view_tag)) {
LOGERR(1, "Invalid wallet address: get_eph_public_key failed");
panic();
throw std::exception();
}
const NetworkType type = m_params->m_wallet.type();
@@ -83,41 +91,89 @@ p2pool::p2pool(int argc, char* argv[])
int err = uv_async_init(uv_default_loop_checked(), &m_submitBlockAsync, on_submit_block);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
throw std::exception();
}
m_submitBlockAsync.data = this;
err = uv_async_init(uv_default_loop_checked(), &m_blockTemplateAsync, on_update_block_template);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
throw std::exception();
}
m_blockTemplateAsync.data = this;
err = uv_async_init(uv_default_loop_checked(), &m_stopAsync, on_stop);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic();
throw std::exception();
}
m_stopAsync.data = this;
err = uv_async_init(uv_default_loop_checked(), &m_restartZMQAsync, on_restart_zmq);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
throw std::exception();
}
m_restartZMQAsync.data = this;
uv_rwlock_init_checked(&m_mainchainLock);
uv_rwlock_init_checked(&m_minerDataLock);
uv_mutex_init_checked(&m_foundBlocksLock);
#ifdef WITH_RANDOMX
uv_mutex_init_checked(&m_minerLock);
#endif
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");
throw std::exception();
}
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);
try {
m_consoleCommands = new ConsoleCommands(this);
}
catch (...) {
LOGERR(1, "Couldn't start console commands handler");
m_consoleCommands = nullptr;
}
}
p2pool::~p2pool()
{
uv_rwlock_destroy(&m_mainchainLock);
uv_rwlock_destroy(&m_minerDataLock);
uv_mutex_destroy(&m_foundBlocksLock);
#ifdef WITH_RANDOMX
uv_mutex_destroy(&m_minerLock);
#endif
uv_mutex_destroy(&m_submitBlockDataLock);
delete m_api;
@@ -129,9 +185,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)
@@ -155,6 +211,17 @@ bool p2pool::get_seed(uint64_t height, hash& seed) const
return true;
}
#ifdef WITH_RANDOMX
void p2pool::print_miner_status()
{
MutexLock lock(m_minerLock);
if (m_miner) {
m_miner->print_status();
}
}
#endif
void p2pool::handle_tx(TxMempoolData& tx)
{
if (!tx.weight || !tx.fee) {
@@ -171,8 +238,10 @@ void p2pool::handle_tx(TxMempoolData& tx)
", fee = " << log::Gray() << static_cast<double>(tx.fee) / 1e6 << " um");
#if TEST_MEMPOOL_PICKING_ALGORITHM
m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet);
m_blockTemplate->update(miner_data(), *m_mempool, &m_params->m_wallet);
#endif
m_zmqLastActive = seconds_since_epoch();
}
void p2pool::handle_miner_data(MinerData& data)
@@ -199,11 +268,16 @@ 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();
m_minerData = data;
data.time_received = std::chrono::high_resolution_clock::now();
{
WriteLock lock(m_minerDataLock);
m_minerData = data;
}
m_updateSeed = true;
update_median_timestamp();
@@ -220,12 +294,50 @@ 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) {
auto it = m_mainchainByHeight.find(h);
if ((it == m_mainchainByHeight.end()) || it->second.difficulty.empty()) {
LOGWARN(3, "Mainchain data for height " << h << " is missing, requesting it from monerod again");
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, m_params->m_rpcPort, buf, m_params->m_rpcLogin,
[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\
@@ -278,8 +390,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 {
@@ -288,6 +405,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)
@@ -301,6 +420,13 @@ void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t e
m_submitBlockData.blob.clear();
}
// If p2pool is stopped, m_submitBlockAsync is most likely already closed
if (m_stopped) {
LOGWARN(0, "p2pool is shutting down, but a block was found. Trying to submit it anyway!");
submit_block();
return;
}
const int err = uv_async_send(&m_submitBlockAsync);
if (err) {
LOGERR(1, "uv_async_send failed, error " << uv_err_name(err));
@@ -318,6 +444,13 @@ void p2pool::submit_block_async(const std::vector<uint8_t>& blob)
m_submitBlockData.blob = blob;
}
// If p2pool is stopped, m_submitBlockAsync is most likely already closed
if (m_stopped) {
LOGWARN(0, "p2pool is shutting down, but a block was found. Trying to submit it anyway!");
submit_block();
return;
}
const int err = uv_async_send(&m_submitBlockAsync);
if (err) {
LOGERR(1, "uv_async_send failed, error " << uv_err_name(err));
@@ -335,7 +468,11 @@ void p2pool::on_stop(uv_async_t* async)
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_submitBlockAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_blockTemplateAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_stopAsync), nullptr);
uv_stop(uv_default_loop());
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_restartZMQAsync), nullptr);
uv_loop_t* loop = uv_default_loop_checked();
delete GetLoopUserData(loop, false);
uv_stop(loop);
}
void p2pool::submit_block() const
@@ -395,7 +532,7 @@ void p2pool::submit_block() const
}
request.append("\"]}");
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, request.c_str(),
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, request, m_params->m_rpcLogin,
[height, diff, template_id, nonce, extra_nonce, is_external](const char* data, size_t size)
{
rapidjson::Document doc;
@@ -447,7 +584,7 @@ void p2pool::submit_block() const
LOGWARN(3, "submit_block (external blob): RPC request failed, error " << log::const_buf(data, size));
}
else {
LOGERR(0, "submit_block (external blob): RPC request failed, error " << log::const_buf(data, size));
LOGERR(0, "submit_block: RPC request failed, error " << log::const_buf(data, size));
}
}
});
@@ -459,8 +596,15 @@ void p2pool::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32
m_blockTemplate->submit_sidechain_block(template_id, nonce, extra_nonce);
}
void p2pool::update_block_template_async()
void p2pool::update_block_template_async(bool is_alternative_block)
{
// If p2pool is stopped, m_blockTemplateAsync is most likely already closed
if (m_stopped) {
return;
}
m_isAlternativeBlock = is_alternative_block;
const int err = uv_async_send(&m_blockTemplateAsync);
if (err) {
LOGERR(1, "uv_async_send failed, error " << uv_err_name(err));
@@ -469,13 +613,25 @@ void p2pool::update_block_template_async()
void p2pool::update_block_template()
{
MinerData data = miner_data();
if (m_updateSeed) {
m_hasher->set_seed_async(m_minerData.seed_hash);
m_hasher->set_seed_async(data.seed_hash);
m_updateSeed = false;
}
m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet);
m_blockTemplate->update(data, *m_mempool, &m_params->m_wallet);
stratum_on_block();
api_update_pool_stats();
#ifdef WITH_RANDOMX
if (m_isAlternativeBlock.exchange(false)) {
MutexLock lock(m_minerLock);
if (m_miner) {
m_miner->reset_share_counters();
}
}
#endif
}
void p2pool::download_block_headers(uint64_t current_height)
@@ -483,7 +639,7 @@ void p2pool::download_block_headers(uint64_t current_height)
const uint64_t seed_height = get_seed_height(current_height);
const uint64_t prev_seed_height = (seed_height > SEEDHASH_EPOCH_BLOCKS) ? (seed_height - SEEDHASH_EPOCH_BLOCKS) : 0;
char buf[log::Stream::BUF_SIZE + 1];
char buf[log::Stream::BUF_SIZE + 1] = {};
log::Stream s(buf);
// First download 2 RandomX seeds
@@ -492,7 +648,7 @@ void p2pool::download_block_headers(uint64_t current_height)
s.m_pos = 0;
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << height << "}}\0";
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin,
[this, prev_seed_height, height](const char* data, size_t size)
{
ChainMain block;
@@ -516,29 +672,44 @@ void p2pool::download_block_headers(uint64_t current_height)
});
}
s.m_pos = 0;
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_headers_range\",\"params\":{\"start_height\":" << current_height - BLOCK_HEADERS_REQUIRED << ",\"end_height\":" << current_height - 1 << "}}\0";
const uint64_t start_height = (current_height > BLOCK_HEADERS_REQUIRED) ? (current_height - BLOCK_HEADERS_REQUIRED) : 0;
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
[this, current_height](const char* data, size_t size)
s.m_pos = 0;
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_headers_range\",\"params\":{\"start_height\":" << start_height << ",\"end_height\":" << current_height - 1 << "}}\0";
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin,
[this, start_height, current_height](const char* data, size_t size)
{
if (parse_block_headers_range(data, size) == BLOCK_HEADERS_REQUIRED) {
if (parse_block_headers_range(data, size) == current_height - start_height) {
update_median_timestamp();
if (m_serversStarted.exchange(1) == 0) {
try {
m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this);
}
catch (const std::exception& e) {
LOGERR(1, "Couldn't start ZMQ reader: exception " << e.what());
panic();
}
m_stratumServer = new StratumServer(this);
m_p2pServer = new P2PServer(this);
#ifdef WITH_RANDOMX
if (m_params->m_minerThreads) {
start_mining(m_params->m_minerThreads);
}
#endif
api_update_network_stats();
}
}
else {
LOGERR(1, "fatal error: couldn't download block headers for heights " << current_height - BLOCK_HEADERS_REQUIRED << " - " << current_height - 1);
LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1);
panic();
}
},
[current_height](const char* data, size_t size)
[start_height, current_height](const char* data, size_t size)
{
if (size > 0) {
LOGERR(1, "fatal error: couldn't download block headers for heights " << current_height - BLOCK_HEADERS_REQUIRED << " - " << current_height - 1 << ", error " << log::const_buf(data, size));
LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1 << ", error " << log::const_buf(data, size));
panic();
}
});
@@ -580,6 +751,7 @@ void p2pool::update_median_timestamp()
uint64_t timestamps[TIMESTAMP_WINDOW];
if (!get_timestamps(timestamps))
{
WriteLock lock(m_minerDataLock);
m_minerData.median_timestamp = 0;
return;
}
@@ -587,33 +759,33 @@ void p2pool::update_median_timestamp()
std::sort(timestamps, timestamps + TIMESTAMP_WINDOW);
// Shift it +1 block compared to Monero's code because we don't have the latest block yet when we receive new miner data
m_minerData.median_timestamp = (timestamps[TIMESTAMP_WINDOW / 2] + timestamps[TIMESTAMP_WINDOW / 2 + 1]) / 2;
LOGINFO(4, "median timestamp updated to " << log::Gray() << m_minerData.median_timestamp);
const uint64_t ts = (timestamps[TIMESTAMP_WINDOW / 2] + timestamps[TIMESTAMP_WINDOW / 2 + 1]) / 2;
LOGINFO(4, "median timestamp updated to " << log::Gray() << ts);
WriteLock lock(m_minerDataLock);
m_minerData.median_timestamp = ts;
}
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;
#ifdef WITH_RANDOMX
{
MutexLock lock(m_minerLock);
if (m_miner) {
m_miner->on_block(*m_blockTemplate);
}
}
#endif
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
if (m_stratumServer) {
m_stratumServer->on_block(*m_blockTemplate);
}
#endif
}
void p2pool::get_info()
{
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_info\"}",
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_info\"}", m_params->m_rpcLogin,
[this](const char* data, size_t size)
{
parse_get_info_rpc(data, size);
@@ -622,8 +794,10 @@ void p2pool::get_info()
{
if (size > 0) {
LOGWARN(1, "get_info RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_info();
if (!m_stopped) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_info();
}
}
});
}
@@ -667,6 +841,10 @@ void p2pool::load_found_blocks()
void p2pool::parse_get_info_rpc(const char* data, size_t size)
{
if (m_stopped) {
return;
}
rapidjson::Document doc;
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
@@ -680,18 +858,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;
@@ -715,7 +897,7 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
void p2pool::get_version()
{
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_version\"}",
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_version\"}", m_params->m_rpcLogin,
[this](const char* data, size_t size)
{
parse_get_version_rpc(data, size);
@@ -724,14 +906,20 @@ void p2pool::get_version()
{
if (size > 0) {
LOGWARN(1, "get_version RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_version();
if (!m_stopped) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_version();
}
}
});
}
void p2pool::parse_get_version_rpc(const char* data, size_t size)
{
if (m_stopped) {
return;
}
rapidjson::Document doc;
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
@@ -761,10 +949,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();
}
@@ -773,7 +965,9 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
void p2pool::get_miner_data()
{
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_miner_data\"}",
m_getMinerDataPending = true;
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_miner_data\"}", m_params->m_rpcLogin,
[this](const char* data, size_t size)
{
parse_get_miner_data_rpc(data, size);
@@ -782,14 +976,31 @@ void p2pool::get_miner_data()
{
if (size > 0) {
LOGWARN(1, "get_miner_data RPC request failed: error " << log::const_buf(data, size) << ", trying again in 1 second");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_miner_data();
if (!m_stopped) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_miner_data();
}
}
else {
m_getMinerDataPending = false;
}
});
}
void p2pool::parse_get_miner_data_rpc(const char* data, size_t size)
{
if (m_stopped) {
return;
}
hash h;
keccak(reinterpret_cast<const uint8_t*>(data), static_cast<int>(size), h.h, HASH_SIZE);
if (h == m_getMinerDataHash) {
LOGWARN(4, "Received a duplicate get_miner_data RPC response, ignoring it");
return;
}
m_getMinerDataHash = h;
rapidjson::Document doc;
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
@@ -935,10 +1146,16 @@ void p2pool::api_update_network_stats()
return;
}
hash prev_id;
{
ReadLock lock(m_minerDataLock);
prev_id = m_minerData.prev_id;
}
ChainMain mainnet_tip;
{
ReadLock lock(m_mainchainLock);
mainnet_tip = m_mainchainByHash[m_minerData.prev_id];
mainnet_tip = m_mainchainByHash[prev_id];
}
m_api->set(p2pool_api::Category::NETWORK, "stats",
@@ -963,7 +1180,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;
@@ -1000,10 +1217,16 @@ void p2pool::api_update_stats_mod()
return;
}
hash prev_id;
{
ReadLock lock(m_minerDataLock);
prev_id = m_minerData.prev_id;
}
ChainMain mainnet_tip;
{
ReadLock lock(m_mainchainLock);
mainnet_tip = m_mainchainByHash[m_minerData.prev_id];
mainnet_tip = m_mainchainByHash[prev_id];
}
time_t last_block_found_time = 0;
@@ -1014,10 +1237,11 @@ void p2pool::api_update_stats_mod()
{
MutexLock lock(m_foundBlocksLock);
if (!m_foundBlocks.empty()) {
last_block_found_time = m_foundBlocks.back().timestamp;
last_block_found_height = m_foundBlocks.back().height;
last_block_found_hash = m_foundBlocks.back().id;
last_block_total_hashes = m_foundBlocks.back().total_hashes;
const FoundBlock& b = m_foundBlocks.back();
last_block_found_time = b.timestamp;
last_block_found_height = b.height;
last_block_found_hash = b.id;
last_block_total_hashes = b.total_hashes;
}
}
@@ -1026,7 +1250,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();
@@ -1043,7 +1267,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 << ':'
@@ -1055,6 +1279,31 @@ 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();
@@ -1118,6 +1367,26 @@ 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();
MutexLock lock(m_minerLock);
m_miner = new Miner(this, threads);
}
void p2pool::stop_mining()
{
MutexLock lock(m_minerLock);
if (m_miner) {
delete m_miner;
m_miner = nullptr;
}
}
#endif
static void on_signal(uv_signal_t* handle, int signum)
{
p2pool* pool = reinterpret_cast<p2pool*>(handle->data);
@@ -1160,6 +1429,10 @@ static bool init_uv_threadpool()
static bool init_signals(p2pool* pool)
{
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
constexpr int signal_names[] = {
SIGHUP,
SIGINT,
@@ -1189,7 +1462,38 @@ static bool init_signals(p2pool* pool)
void p2pool::stop()
{
uv_async_send(&m_stopAsync);
// Can be called only once
if (m_stopped.exchange(true) == false) {
uv_async_send(&m_stopAsync);
}
}
void p2pool::restart_zmq()
{
// If p2pool is stopped, m_restartZMQAsync is most likely already closed
if (m_stopped) {
return;
}
if (!is_main_thread()) {
uv_async_send(&m_restartZMQAsync);
return;
}
if (!m_getMinerDataPending) {
get_miner_data();
}
delete m_ZMQReader;
m_ZMQReader = nullptr;
try {
m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this);
m_zmqLastActive = seconds_since_epoch();
}
catch (const std::exception& e) {
LOGERR(1, "Couldn't restart ZMQ reader: exception " << e.what());
}
}
int p2pool::run()
@@ -1209,12 +1513,17 @@ int p2pool::run()
return 1;
}
// Init default loop user data before running it
uv_loop_t* loop = uv_default_loop_checked();
loop->data = nullptr;
GetLoopUserData(loop);
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();
@@ -1226,6 +1535,9 @@ int p2pool::run()
bkg_jobs_tracker.wait();
#ifdef WITH_RANDOMX
delete m_miner;
#endif
delete m_stratumServer;
delete m_p2pServer;
+56 -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,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
{
@@ -47,16 +48,27 @@ public:
const Params& params() const { return *m_params; }
BlockTemplate& block_template() { return *m_blockTemplate; }
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);
FORCEINLINE MinerData miner_data() const
{
ReadLock lock(m_minerDataLock);
return m_minerData;
}
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
void print_miner_status();
#endif
virtual void handle_tx(TxMempoolData& tx) override;
virtual void handle_miner_data(MinerData& data) override;
virtual void handle_chain_main(ChainMain& data, const char* extra) override;
@@ -65,7 +77,7 @@ public:
void submit_block_async(const std::vector<uint8_t>& blob);
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
void update_block_template_async();
void update_block_template_async(bool is_alternative_block = false);
void update_block_template();
void download_block_headers(uint64_t current_height);
@@ -76,6 +88,15 @@ 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; }
void restart_zmq();
private:
p2pool(const p2pool&) = delete;
p2pool(p2pool&&) = delete;
@@ -83,24 +104,27 @@ private:
static void on_submit_block(uv_async_t* async) { reinterpret_cast<p2pool*>(async->data)->submit_block(); }
static void on_update_block_template(uv_async_t* async) { reinterpret_cast<p2pool*>(async->data)->update_block_template(); }
static void on_stop(uv_async_t*);
static void on_restart_zmq(uv_async_t* async) { reinterpret_cast<p2pool*>(async->data)->restart_zmq(); }
void submit_block() const;
bool m_stopped;
std::atomic<bool> m_stopped;
Params* m_params;
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;
Mempool* m_mempool;
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;
mutable uv_rwlock_t m_minerDataLock;
MinerData m_minerData;
enum { TIMESTAMP_WINDOW = 60 };
bool get_timestamps(uint64_t (&timestamps)[TIMESTAMP_WINDOW]) const;
@@ -125,6 +149,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)
@@ -149,13 +175,18 @@ private:
StratumServer* m_stratumServer = nullptr;
P2PServer* m_p2pServer = nullptr;
#ifdef WITH_RANDOMX
uv_mutex_t m_minerLock;
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 +196,17 @@ private:
uv_async_t m_submitBlockAsync;
uv_async_t m_blockTemplateAsync;
uv_async_t m_stopAsync;
std::atomic<bool> m_isAlternativeBlock{ false };
std::atomic<uint64_t> m_zmqLastActive;
uint64_t m_startTime;
uv_async_t m_restartZMQAsync;
ZMQReader* m_ZMQReader = nullptr;
hash m_getMinerDataHash;
bool m_getMinerDataPending = false;
};
} // namespace p2pool
+50 -32
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,9 @@ 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)
, m_counter(0)
{
if (m_apiPath.empty()) {
LOGERR(1, "api path is empty");
@@ -60,9 +62,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()
@@ -96,7 +103,7 @@ void p2pool_api::on_stop()
uv_close(reinterpret_cast<uv_handle_t*>(&m_dumpToFileAsync), nullptr);
}
void p2pool_api::dump_to_file_async_internal(const Category& category, const char* filename, DumpFileCallbackBase&& callback)
void p2pool_api::dump_to_file_async_internal(Category category, const char* filename, DumpFileCallbackBase&& callback)
{
std::vector<char> buf(16384);
log::Stream s(buf.data(), buf.size());
@@ -109,6 +116,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,20 +129,16 @@ 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);
// cppcheck-suppress accessMoved
m_dumpData.clear();
}
for (auto& it : data) {
// cppcheck-suppress uninitvar
DumpFileWork* work = new DumpFileWork{ {}, {}, {}, it.first, std::move(it.second) };
work->open_req.data = work;
work->write_req.data = work;
work->close_req.data = work;
DumpFileWork* work = new DumpFileWork{ {}, 0, it.first, it.first + std::to_string(m_counter), std::move(it.second) };
work->req.data = work;
++m_counter;
const int flags = O_WRONLY | O_CREAT | O_TRUNC
#ifdef O_BINARY
@@ -142,9 +146,9 @@ void p2pool_api::dump_to_file()
#endif
;
const int result = uv_fs_open(uv_default_loop_checked(), &work->open_req, it.first.c_str(), flags, 0644, on_fs_open);
const int result = uv_fs_open(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), flags, 0644, on_fs_open);
if (result < 0) {
LOGWARN(4, "failed to open " << it.first << ", error " << uv_err_name(result));
LOGWARN(4, "failed to open " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
}
}
@@ -153,11 +157,11 @@ void p2pool_api::dump_to_file()
void p2pool_api::on_fs_open(uv_fs_t* req)
{
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
const int fd = static_cast<int>(req->result);
work->fd = static_cast<int>(req->result);
uv_fs_req_cleanup(req);
if (fd < 0) {
LOGWARN(4, "failed to open " << work->name << ", error " << uv_err_name(fd));
uv_fs_req_cleanup(req);
if (work->fd < 0) {
LOGWARN(4, "failed to open " << work->tmp_name << ", error " << uv_err_name(work->fd));
delete work;
return;
}
@@ -166,15 +170,12 @@ void p2pool_api::on_fs_open(uv_fs_t* req)
buf[0].base = work->buf.data();
buf[0].len = static_cast<uint32_t>(work->buf.size());
const int result = uv_fs_write(uv_default_loop_checked(), &work->write_req, static_cast<uv_file>(fd), buf, 1, 0, on_fs_write);
const int result = uv_fs_write(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), buf, 1, 0, on_fs_write);
if (result < 0) {
LOGWARN(4, "failed to write to " << work->name << ", error " << uv_err_name(result));
uv_fs_req_cleanup(req);
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
return;
}
uv_fs_req_cleanup(req);
}
void p2pool_api::on_fs_write(uv_fs_t* req)
@@ -182,18 +183,17 @@ void p2pool_api::on_fs_write(uv_fs_t* req)
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
if (req->result < 0) {
LOGWARN(4, "failed to write to " << work->name << ", error " << uv_err_name(static_cast<int>(req->result)));
}
const int result = uv_fs_close(uv_default_loop_checked(), &work->close_req, static_cast<uv_file>(work->open_req.result), on_fs_close);
if (result < 0) {
LOGWARN(4, "failed to close " << work->name << ", error " << uv_err_name(result));
uv_fs_req_cleanup(req);
delete work;
return;
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(static_cast<int>(req->result)));
}
uv_fs_req_cleanup(req);
const int result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_close);
if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
return;
}
}
void p2pool_api::on_fs_close(uv_fs_t* req)
@@ -201,7 +201,25 @@ void p2pool_api::on_fs_close(uv_fs_t* req)
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
if (req->result < 0) {
LOGWARN(4, "failed to close " << work->name << ", error " << uv_err_name(static_cast<int>(req->result)));
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(static_cast<int>(req->result)));
}
uv_fs_req_cleanup(req);
const int result = uv_fs_rename(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), work->name.c_str(), on_fs_rename);
if (result < 0) {
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(result));
delete work;
return;
}
}
void p2pool_api::on_fs_rename(uv_fs_t* req)
{
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
if (req->result < 0) {
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(static_cast<int>(req->result)));
}
uv_fs_req_cleanup(req);
+14 -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
@@ -18,26 +18,27 @@
#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,
};
// cppcheck-suppress functionConst
void on_stop();
template<typename T>
void set(const Category& category, const char* filename, T&& callback) { dump_to_file_async_internal(category, filename, DumpFileCallback<T>(std::move(callback))); }
void set(Category category, const char* filename, T&& callback) { dump_to_file_async_internal(category, filename, DumpFileCallback<T>(std::move(callback))); }
private:
void create_dir(const std::string& path);
@@ -45,11 +46,11 @@ private:
static void on_dump_to_file(uv_async_t* async) { reinterpret_cast<p2pool_api*>(async->data)->dump_to_file(); }
struct DumpFileWork {
uv_fs_t open_req;
uv_fs_t write_req;
uv_fs_t close_req;
uv_fs_t req;
int fd;
std::string name;
std::string tmp_name;
std::vector<char> buf;
};
@@ -71,20 +72,24 @@ private:
T m_callback;
};
void dump_to_file_async_internal(const Category& category, const char* filename, DumpFileCallbackBase&& callback);
void dump_to_file_async_internal(Category category, const char* filename, DumpFileCallbackBase&& callback);
void dump_to_file();
static void on_fs_open(uv_fs_t* req);
static void on_fs_write(uv_fs_t* req);
static void on_fs_close(uv_fs_t* req);
static void on_fs_rename(uv_fs_t* req);
std::string m_apiPath;
std::string m_networkPath;
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;
uint64_t m_counter;
};
} // namespace p2pool
+75 -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,125 @@
#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 (strcmp(argv[i], "--no-autodiff") == 0) {
m_autoDiff = false;
ok = true;
}
if ((strcmp(argv[i], "--rpc-login") == 0) && (i + 1 < argc)) {
m_rpcLogin = argv[++i];
ok = true;
}
if (!ok) {
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
p2pool_usage();
throw std::exception();
}
}
@@ -80,16 +151,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
+14 -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,19 @@ 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;
bool m_autoDiff = true;
std::string m_rpcLogin;
};
} // namespace p2pool
+57 -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
@@ -53,14 +53,14 @@ PoolBlock::PoolBlock()
, m_invalid(false)
, m_broadcasted(false)
, m_wantBroadcast(false)
, m_localTimestamp(time(nullptr))
, m_precalculated(false)
, m_localTimestamp(seconds_since_epoch())
{
uv_mutex_init_checked(&m_lock);
m_mainChainData.reserve(48 * 1024);
m_outputs.reserve(2048);
m_transactions.reserve(256);
m_tmpInts.reserve(m_transactions.capacity() * HASH_SIZE);
m_sideChainData.reserve(512);
m_uncles.reserve(8);
m_tmpTxExtra.reserve(80);
@@ -110,14 +110,14 @@ 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_precalculated = b.m_precalculated;
m_localTimestamp = time(nullptr);
m_localTimestamp = seconds_since_epoch();
if (lock_result == 0) {
uv_mutex_unlock(&b.m_lock);
@@ -159,8 +159,12 @@ void PoolBlock::serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, c
for (TxOutput& output : m_outputs) {
writeVarint(output.m_reward, m_mainChainData);
m_mainChainData.push_back(TXOUT_TO_KEY);
m_mainChainData.push_back(output.m_txType);
m_mainChainData.insert(m_mainChainData.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
m_mainChainData.push_back(output.m_viewTag);
}
}
m_mainChainOutputsBlobSize = static_cast<int>(m_mainChainData.size()) - m_mainChainOutputsOffset;
@@ -227,7 +231,25 @@ 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)
void PoolBlock::reset_offchain_data()
{
// Defaults for off-chain variables
m_tmpTxExtra.clear();
m_depth = 0;
m_verified = false;
m_invalid = false;
m_broadcasted = false;
m_wantBroadcast = false;
m_precalculated = false;
m_localTimestamp = seconds_since_epoch();
}
bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash)
{
alignas(8) uint8_t hashes[HASH_SIZE * 3];
@@ -276,28 +298,50 @@ 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) {
const TxOutput& out = m_outputs[i];
hash eph_public_key;
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
if (w.get_eph_public_key_with_view_tag(m_txkeySec, i, eph_public_key, out.m_viewTag) && (eph_public_key == out.m_ephPublicKey)) {
return out.m_reward;
}
}
else {
uint8_t view_tag;
if (w.get_eph_public_key(m_txkeySec, i, eph_public_key, view_tag) && (eph_public_key == out.m_ephPublicKey)) {
return out.m_reward;
}
}
}
return 0;
}
} // namespace p2pool
+21 -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
@@ -28,7 +28,7 @@
namespace p2pool {
class RandomX_Hasher;
class RandomX_Hasher_Base;
class SideChain;
/*
@@ -81,11 +81,13 @@ struct PoolBlock
struct TxOutput
{
FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey() {}
FORCEINLINE TxOutput(uint64_t r, const hash& k) : m_reward(r), m_ephPublicKey(k) {}
FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey(), m_txType(0), m_viewTag(0) {}
FORCEINLINE TxOutput(uint64_t r, const hash& k, uint8_t tx_type, uint8_t view_tag) : m_reward(r), m_ephPublicKey(k), m_txType(tx_type), m_viewTag(view_tag) {}
uint64_t m_reward;
hash m_ephPublicKey;
uint8_t m_txType;
uint8_t m_viewTag;
};
std::vector<TxOutput> m_outputs;
@@ -121,23 +123,32 @@ 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;
bool m_verified;
bool m_invalid;
bool m_broadcasted;
bool m_wantBroadcast;
mutable bool m_broadcasted;
mutable bool m_wantBroadcast;
time_t m_localTimestamp;
bool m_precalculated;
uint64_t m_localTimestamp;
void serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash);
void serialize_sidechain_data();
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);
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop);
void reset_offchain_data();
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;
// Both tx types are allowed by Monero consensus during v15 because it needs to process pre-fork mempool transactions,
// but P2Pool can switch to using only TXOUT_TO_TAGGED_KEY for miner payouts starting from v15
FORCEINLINE uint8_t get_tx_type() const { return (m_majorVersion < HARDFORK_VIEW_TAGS_VERSION) ? TXOUT_TO_KEY : TXOUT_TO_TAGGED_KEY; }
};
} // namespace p2pool
+25 -44
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
@@ -23,7 +23,7 @@ namespace p2pool {
// Since data here can come from external and possibly malicious sources, check everything
// Only the syntax (i.e. the serialized block binary format) and the keccak hash are checked here
// Semantics must also be checked elsewhere before accepting the block (PoW, reward split between miners, difficulty calculation and so on)
int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechain)
int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop)
{
try {
// Sanity check
@@ -46,29 +46,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
#define READ_BYTE(x) do { if (!read_byte(x)) return __LINE__; } while (0)
#define EXPECT_BYTE(value) do { uint8_t tmp; READ_BYTE(tmp); if (tmp != (value)) return __LINE__; } while (0)
auto read_varint = [&data, data_end](auto& b) -> bool
{
uint64_t result = 0;
int k = 0;
while (data < data_end) {
if (k >= static_cast<int>(sizeof(b)) * 8) {
return false;
}
const uint64_t cur_byte = *(data++);
result |= (cur_byte & 0x7F) << k;
k += 7;
if ((cur_byte & 0x80) == 0) {
b = result;
return true;
}
}
return false;
};
#define READ_VARINT(x) do { if (!read_varint(x)) return __LINE__; } while(0)
#define READ_VARINT(x) do { data = readVarint(data, data_end, x); if (!data) return __LINE__; } while(0)
auto read_buf = [&data, data_end](void* buf, size_t size) -> bool
{
@@ -121,7 +99,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
if (num_outputs > 0) {
// Outputs are in the buffer, just read them
// Each output is at least 34 bytes, exit early if there's not enough data left
// 1 byte for reward, 1 byte for TXOUT_TO_KEY, 32 bytes for eph_pub_key
// 1 byte for reward, 1 byte for tx_type, 32 bytes for eph_pub_key
constexpr uint64_t MIN_OUTPUT_SIZE = 34;
if (num_outputs > std::numeric_limits<uint64_t>::max() / MIN_OUTPUT_SIZE) return __LINE__;
@@ -130,15 +108,23 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
m_outputs.clear();
m_outputs.reserve(num_outputs);
const uint8_t expected_tx_type = get_tx_type();
for (uint64_t i = 0; i < num_outputs; ++i) {
TxOutput t;
READ_VARINT(t.m_reward);
total_reward += t.m_reward;
EXPECT_BYTE(TXOUT_TO_KEY);
EXPECT_BYTE(expected_tx_type);
t.m_txType = expected_tx_type;
READ_BUF(t.m_ephPublicKey.h, HASH_SIZE);
if (expected_tx_type == TXOUT_TO_TAGGED_KEY) {
READ_BYTE(t.m_viewTag);
}
m_outputs.emplace_back(std::move(t));
}
@@ -187,7 +173,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);
@@ -201,7 +187,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
const int sidechain_hash_offset = static_cast<int>((data - data_begin) + outputs_blob_size_diff);
READ_BUF(m_sidechainId.h, HASH_SIZE);
// cppcheck-suppress duplicateExpression
if (static_cast<uint64_t>(data - tx_extra_begin) != tx_extra_size) return __LINE__;
EXPECT_BYTE(0);
@@ -244,6 +229,15 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
return __LINE__;
}
// Enforce deterministic tx keys starting from v15
if (m_majorVersion >= HARDFORK_VIEW_TAGS_VERSION) {
hash pub, sec;
get_tx_keys(pub, sec, spend_pub_key, m_prevId);
if ((pub != m_txkeyPub) || (sec != m_txkeySec)) {
return __LINE__;
}
}
READ_BUF(m_parent.h, HASH_SIZE);
uint64_t num_uncles;
@@ -278,7 +272,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
return __LINE__;
}
if ((num_outputs == 0) && !sidechain.get_outputs_blob(this, total_reward, outputs_blob)) {
if ((num_outputs == 0) && !sidechain.get_outputs_blob(this, total_reward, outputs_blob, loop)) {
return __LINE__;
}
@@ -339,20 +333,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
return __LINE__;
}
// Defaults for off-chain variables
m_tmpTxExtra.clear();
m_tmpInts.clear();
m_depth = 0;
m_verified = false;
m_invalid = false;
m_broadcasted = false;
m_wantBroadcast = false;
m_localTimestamp = time(nullptr);
reset_offchain_data();
return 0;
}
+141 -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
@@ -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);
@@ -172,7 +180,6 @@ void RandomX_Hasher::set_seed(const hash& seed)
}
{
// cppcheck-suppress unreadVariable
ON_SCOPE_LEAVE([this]() { uv_rwlock_wrunlock(&m_cacheLock); });
if (m_stopped.load()) {
@@ -216,6 +223,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 +278,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 +287,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,11 +316,16 @@ 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) {
// cppcheck-suppress unreadVariable
ON_SCOPE_LEAVE([this]() { uv_rwlock_rdunlock(&m_datasetLock); });
if (m_stopped.load()) {
@@ -347,5 +366,111 @@ 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_loop{}
, m_loopThread{}
{
int err = uv_loop_init(&m_loop);
if (err) {
LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
panic();
}
// Init loop user data before running it
GetLoopUserData(&m_loop);
uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
uv_async_init(&m_loop, &m_kickTheLoopAsync, nullptr);
m_shutdownAsync.data = this;
uv_mutex_init_checked(&m_requestMutex);
uv_mutex_init_checked(&m_condMutex);
uv_cond_init_checked(&m_cond);
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\":\"\"}}\0";
volatile int result = 0;
volatile bool done = false;
const Params& params = m_pool->params();
JSONRPCRequest::call(params.m_host, params.m_rpcPort, buf, params.m_rpcLogin,
[&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
+66 -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,47 @@ 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);
delete GetLoopUserData(&server->m_loop, false);
}
};
} // namespace p2pool
+663 -199
View File
File diff suppressed because it is too large Load Diff
+57 -24
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,12 @@
#include "uv_util.h"
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <thread>
namespace p2pool {
class p2pool;
class P2PServer;
struct DifficultyData;
struct PoolBlock;
class Wallet;
@@ -32,32 +32,34 @@ class Wallet;
struct MinerShare
{
FORCEINLINE MinerShare() : m_weight(0), m_wallet(nullptr) {}
FORCEINLINE MinerShare(uint64_t w, Wallet* x) : m_weight(w), m_wallet(x) {}
FORCEINLINE MinerShare(uint64_t w, const Wallet* x) : m_weight(w), m_wallet(x) {}
uint64_t m_weight;
Wallet* m_wallet;
const Wallet* m_wallet;
};
class SideChain
class SideChain : public nocopy_nomove
{
public:
SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr);
~SideChain();
void fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares);
void fill_sidechain_data(PoolBlock& block, const Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares) const;
bool block_seen(const PoolBlock& block);
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);
void get_missing_blocks(std::vector<hash>& missing_blocks) const;
bool has_block(const hash& id);
PoolBlock* find_block(const hash& id) const;
void watch_mainchain_block(const ChainMain& data, const hash& possible_id);
bool get_block_blob(const hash& id, std::vector<uint8_t>& blob);
bool get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob);
bool get_block_blob(const hash& id, std::vector<uint8_t>& blob) const;
bool get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob, uv_loop_t* loop) const;
void print_status();
void print_status(bool obtain_sidechain_lock = true) const;
double get_reward_share(const Wallet& w) const;
// Consensus ID can be used to spawn independent P2Pools with their own sidechains
// It's never sent over the network to avoid revealing it to the possible man in the middle
@@ -65,41 +67,51 @@ public:
const std::vector<uint8_t>& consensus_id() const { return m_consensusId; }
uint64_t chain_window_size() const { return m_chainWindowSize; }
NetworkType network_type() const { return m_networkType; }
const difficulty_type& difficulty() const { return m_curDifficulty; }
FORCEINLINE difficulty_type difficulty() const { ReadLock lock(m_curDifficultyLock); return m_curDifficulty; }
difficulty_type total_hashes() const;
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:
bool get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) const;
bool get_difficulty(PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const;
bool get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares) const;
bool get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const;
bool get_wallets(const PoolBlock* tip, std::vector<const Wallet*>& wallets) const;
void verify_loop(PoolBlock* block);
void verify(PoolBlock* block);
void update_chain_tip(PoolBlock* block);
PoolBlock* get_parent(const PoolBlock* block);
void update_chain_tip(const PoolBlock* block);
PoolBlock* get_parent(const PoolBlock* block) const;
// Checks if "candidate" has longer (higher difficulty) chain than "block"
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate);
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative);
void update_depths(PoolBlock* block);
void prune_old_blocks();
bool load_config(const std::string& filename);
bool check_config();
mutable uv_mutex_t m_sidechainLock;
PoolBlock* m_chainTip;
mutable uv_rwlock_t m_sidechainLock;
std::atomic<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;
uv_mutex_t m_seenWalletsLock;
unordered_map<hash, uint64_t> m_seenWallets;
uint64_t m_seenWalletsLastPruneTime;
uv_mutex_t m_seenBlocksLock;
unordered_set<hash> m_seenBlocks;
std::vector<DifficultyData> m_difficultyData;
@@ -111,11 +123,32 @@ private:
uint64_t m_unclePenalty;
std::vector<uint8_t> m_consensusId;
std::string m_consensusIdDisplayStr;
mutable uv_rwlock_t m_curDifficultyLock;
difficulty_type m_curDifficulty;
ChainMain m_watchBlock;
hash m_watchBlockSidechainId;
struct PrecalcJob
{
const PoolBlock* b;
std::vector<const Wallet*> wallets;
};
uv_cond_t m_precalcJobsCond;
uv_mutex_t m_precalcJobsMutex;
std::vector<PrecalcJob*> m_precalcJobs;
std::vector<std::thread> m_precalcWorkers;
unordered_set<size_t>* m_uniquePrecalcInputs;
std::atomic<bool> m_precalcFinished;
void launch_precalc(const PoolBlock* block);
void precalc_worker();
void finish_precalc();
};
} // namespace p2pool

Some files were not shown because too many files have changed in this diff Show More