Compare commits

...
211 Commits
Author SHA1 Message Date
SChernykh 2b61c1d90f p2pool v2.7 2023-01-03 15:53:03 +01:00
SChernykh baf5a64c51 Add high fee transactions immediately 2023-01-02 14:32:13 +01:00
SChernykh 260564cff1 Check for NO_COLOR before logging starts 2023-01-01 22:12:16 +01:00
SChernykh 248f74d948 Updated libzmq 2023-01-01 15:55:50 +01:00
SChernykh 724a9e49df Updated curl to 7.87.0 2023-01-01 15:26:35 +01:00
SChernykh 726224b253 Support NO_COLOR environment variable 2023-01-01 11:47:13 +01:00
SChernykh 421e087d81 Fixed UV thread pool size 2022-12-26 19:52:15 +01:00
SChernykh a1ec97a221 Fixed submit_block error logging 2022-12-25 16:23:57 +01:00
SChernykh d151ed9354 Miner: fixed share counter reset logic 2022-12-22 21:53:04 +01:00
SChernykh 7502481f26 Fixed cppcheck issues 2022-12-22 21:00:59 +01:00
SChernykh 76e7e4956d Miner: fixed share counting during initial sync 2022-12-22 20:53:23 +01:00
SChernykh 15d25bca27 Fixed data race 2022-12-22 20:30:20 +01:00
SChernykh dffc8bcea0 Fixed cppcheck issues 2022-12-22 14:08:49 +01:00
SChernykh a61966ed61 Don't count shares that were found during sync 2022-12-22 11:48:50 +01:00
SChernykh e6d77a40e2 Check block major version in deserialize() 2022-12-19 10:29:29 +01:00
SChernykh edcb933874 Call get_miner_data again after startup is done
Startup sequence can take a while, so miner data can change - new transactions added, or a new block found by the network.
2022-12-18 11:46:18 +01:00
SChernykh be18ad4177 Made payout messages more visible 2022-12-18 11:06:38 +01:00
SChernykh 07a5b191bb Fixes for different BSD variants 2022-12-17 12:56:32 +01:00
SChernykh da12b329be Check the block id returned by BLOCK_RESPONSE 2022-12-14 14:24:09 +01:00
SChernykh 3902612c4b P2PServer: show more data for connected peers 2022-12-09 11:31:05 +01:00
SChernykh 9bce415523 Added alternative command line parameter names 2022-12-03 21:21:02 +01:00
SChernykh 3e4f877ec0 Display software name in peers list 2022-12-02 15:43:20 +01:00
SChernykh 1b601343ff Updated recommended command lines 2022-11-30 14:19:44 +01:00
SChernykh 77ee46e153 Updated RandomX 2022-11-30 10:40:25 +01:00
SChernykh 357c1a20df Updated recommended monerod command line 2022-11-29 11:33:55 +01:00
SChernykh 67bbfea87d p2pool v2.6 2022-11-28 12:44:30 +01:00
SChernykh 220c6939b1 Ignore shares from peers who have lagging monerod
More than 10 seconds lag is unacceptable
2022-11-28 08:29:38 +01:00
SChernykh fd27ea0e8f Console: check for arguments when required 2022-11-25 21:58:25 +01:00
SChernykh 4bb1982806 Added BlockTemplate tests 2022-11-24 21:38:15 +01:00
SChernykh 9283677a2b Sync test: log more data in the end 2022-11-24 11:06:18 +01:00
SChernykh d4180a5084 Fixed Coverity issue 2022-11-24 08:43:13 +01:00
SChernykh 2721b16c46 Miner: fixed data race 2022-11-24 08:04:53 +01:00
SChernykh f1ff6a21ee Revert "Broadcast uncle blocks at the same height"
This reverts commit 530079b8a5.
2022-11-23 17:59:28 +01:00
SChernykh 530079b8a5 Broadcast uncle blocks at the same height
To reduce orphan block rate
2022-11-23 17:55:11 +01:00
SChernykh 2ca428bbbb BlockTemplate: non-ambiguous transaction order
Different nodes could pick different transactions with the same fee/byte which hurted compact broadcasts efficiency
2022-11-22 23:05:17 +01:00
SChernykh 8a27a8cce4 Added a safety check to block template update 2022-11-22 15:23:32 +01:00
SChernykh 7eec4d742c P2PServer: removed an unnecessary lock 2022-11-22 09:57:50 +01:00
SChernykh 3f66975e1b Optimized transaction picking algorithm 2022-11-21 17:09:56 +01:00
SChernykh b7f8c973f4 Refined transaction picking algorithm 2022-11-21 08:08:31 +01:00
SChernykh 98e51feb46 Updated libzmq 2022-11-18 20:23:14 +01:00
SChernykh 68fccd2edb Updated FAQ 2022-11-17 10:51:26 +01:00
SChernykh 72e414b2b7 Fixed struct alignment 2022-11-16 18:48:46 +01:00
SChernykh 79a31ce57c SideChain: reduce lock scope in get_outputs_blob 2022-11-16 14:23:13 +01:00
SChernykh 488ed8e562 Added more 128 bit calculations 2022-11-16 10:36:46 +01:00
SChernykh b3bce1651b SideChain: use full 128 bits in get_difficulty 2022-11-15 16:39:39 +01:00
SChernykh e9a2e4b076 Added html 2022-11-15 14:16:34 +01:00
SChernykh cc2f031a32 Fixed a race when stopping logger 2022-11-15 11:07:50 +01:00
SChernykh d0eea1bf91 Fixed libuv init/shutdown sequence 2022-11-14 23:35:59 +01:00
SChernykh 53ba29b288 Optimized crypto cache memory allocation 2022-11-13 17:30:47 +01:00
SChernykh c5f3a03d1e Improved peers display 2022-11-10 19:56:14 +01:00
SChernykh da9cdf7df3 Send our P2Pool version to other peers 2022-11-10 14:31:02 +01:00
SChernykh 8a7f1b1b3a Log shares found by other nodes 2022-11-10 08:22:31 +01:00
SChernykh aa14620a50 Merge branch 'compact_broadcast' 2022-11-10 07:56:30 +01:00
SChernykh 1fa6a7c11a Fixed mouse wheel scrolling on Windows 2022-11-09 23:46:10 +01:00
SChernykh 25806b67a2 Added protocol version negotiation
Protocol version 1.1: compact block broadcasts
2022-11-09 22:56:12 +01:00
SChernykh 47ff7e228e Added code to deserialize compact blobs 2022-11-09 16:21:31 +01:00
SChernykh c135787620 Prepare compact blob for block broadcasts
Reference transactions by index in the parent block instead of storing full 32 bytes per transaction
2022-11-09 12:40:45 +01:00
SChernykh bbc3d44526 P2PServer: tidy up PEER_LIST_RESPONSE messages 2022-11-08 16:36:09 +01:00
SChernykh 2e747beda6 Added sidechain height to SHARE FOUND log 2022-11-08 14:27:03 +01:00
SChernykh b9a9be2795 Update robin-hood-hashing 2022-11-04 10:35:25 +01:00
SChernykh 19414b061b Refactored BackgroundJobTracker 2022-11-04 10:20:32 +01:00
SChernykh 74b5ab1798 Force update block template after 30 seconds
To get new transactions in
2022-11-04 07:47:07 +01:00
SChernykh 83cda110aa P2PServer: tweaked invalid timestamp messages 2022-11-03 21:19:48 +01:00
SChernykh 6f87546492 Update README.md 2022-11-03 19:55:25 +01:00
SChernykh 1f7c89a95c Fixed Coverity issues 2022-11-03 19:24:15 +01:00
SChernykh 0df11d24f5 P2PServer: tweaked invalid timestamp logic 2022-11-03 16:14:56 +01:00
SChernykh a6c27a1e3f P2PServer: checks timestamps only for new blocks 2022-11-03 15:20:38 +01:00
SChernykh 0a5fa12ce5 Update wallet_tests.cpp 2022-11-03 14:29:57 +01:00
SChernykh c090b0cf62 P2PServer: tweaked invalid timestamp logic 2022-11-03 13:52:29 +01:00
SChernykh 45660e3d96 Show wallet address in error messages and status 2022-11-03 11:38:43 +01:00
SChernykh 732190bb11 P2PServer: sanity check for timestamps 2022-11-03 08:51:06 +01:00
SChernykh b667f88d0e Updated cppzmq 2022-11-02 15:10:16 +01:00
SChernykh 255d312ae0 SideChain: log blocks with the same id 2022-11-02 12:49:12 +01:00
SChernykh cc92ae7998 Retry get_block_headers_range RPC call if it fails 2022-11-02 11:36:29 +01:00
SChernykh ae9905e4c7 CI: removed Ubuntu 18.04 2022-11-02 09:45:59 +01:00
SChernykh 38469c26ff Updated curl to 7.86.0 2022-11-02 08:47:54 +01:00
SChernykh 07b460147e P2PServer: added missing time check 2022-11-01 16:07:13 +01:00
SChernykh 6cf1b13500 p2pool v2.5 2022-10-31 17:39:33 +01:00
SChernykh 4a8d4ab8d9 Updated github actions 2022-10-28 14:32:49 +02:00
SChernykh 65a9574e52 Added --p2p-external-port command line parameter 2022-10-28 14:06:26 +02:00
SChernykh 6eacb677a4 Update README.md 2022-10-20 10:36:52 +02:00
SChernykh d081c8ea74 TCPServer: fixed data race during shutdown 2022-10-18 18:41:58 +02:00
SChernykh 0342e7ffb5 Enabled parallel compilation 2022-10-13 21:41:08 +02:00
SChernykh c029e5c39d Added macro for debug builds 2022-10-13 11:21:40 +02:00
SChernykh a917a9493c Randomize extra_nonce
To guarantee that hashing blobs will be different between different p2pool nodes, even if they mine to the same wallet address and don't have transactions to shuffle within the block template.
2022-10-11 11:33:58 +02:00
SChernykh 02405bb8ff Workaround for duplicate sidechain IDs
Place transactions in the block template in random order, so two different p2pool nodes mining to the same wallet will get different sidechain IDs with high probability if there's more than 2-3 transactions in mempool.
2022-10-10 21:50:33 +02:00
SChernykh d8bb85a1d8 Fix: api didn't always save total hashes 2022-10-09 18:17:01 +02:00
SChernykh fd6b2360aa Fix: uv_close was called from wrong thread 2022-10-07 16:02:08 +02:00
SChernykhandGitHub 3e655961e9 Merge pull request #206 from SChernykh/reduce_memory
Reduced memory usage
2022-10-06 20:01:55 +03:00
SChernykh 077837054b Optimized TxOutput struct 2022-10-04 21:03:56 +02:00
SChernykh 45674ef554 Better cache cleanup 2022-10-04 20:04:41 +02:00
SChernykh eabf856dbd Removed redundant m_sideChainData 2022-10-04 15:49:58 +02:00
SChernykh 32ea0f2cf6 Clear crypto cache more often 2022-10-04 14:37:55 +02:00
SChernykh 47f40c0be3 Fixed curl include directory 2022-10-03 20:05:33 +02:00
SChernykh 6a66615f37 Removed redundant m_mainChainData 2022-10-03 18:42:32 +02:00
SChernykh 314a1ba39b p2pool v2.4 2022-09-30 17:52:33 +02:00
SChernykh 2daa7f9fea Fixed memory leak 2022-09-28 22:43:38 +02:00
SChernykh f0d576e1b0 Reduced memory usage for each PoolBlock 2022-09-28 16:50:30 +02:00
SChernykh be76c58264 Fixed MSYS2 build 2022-09-26 01:02:26 +02:00
SChernykh 8245f232d4 Updated dependencies 2022-09-25 19:45:52 +02:00
SChernykh 7468af0779 TCPServer: fix incoming connection counting 2022-09-22 11:28:38 +02:00
SChernykh 38a6d3e549 Limit max in/out peers to 450
To avoid hitting default open files limit (1024)
2022-09-20 10:39:55 +02:00
SChernykh 3022588e35 Run tests in debug config on Windows 2022-09-14 18:30:59 +02:00
SChernykh b81f1c4975 Require monerod v0.18 2022-09-14 14:45:10 +02:00
SChernykh ed78e9df6e More reliable file load/save 2022-09-13 18:48:00 +02:00
SChernykh f044149004 P2PServer fix: reset ping time on disconnect 2022-09-13 16:14:54 +02:00
SChernykh f126eb7611 P2PServer: added more logging 2022-09-13 11:12:08 +02:00
SChernykh afa9cf371e P2PServer: sync from the fastest peer if possible 2022-09-13 10:51:15 +02:00
SChernykh f813cf6d36 Fix: possible use after free on shutdown
Console commands could access destroyed objects
2022-09-08 16:16:00 +02:00
SChernykh 3dcd3dbb7f Update README.md 2022-09-08 13:45:06 +02:00
SChernykh d80a06bc8e Added more error logging 2022-09-08 09:18:30 +02:00
SChernykh 947178f10a StratumServer: improved warning message 2022-09-07 08:59:04 +02:00
SChernykh e92cc7d50c Fix: don't use proxy for incoming connections 2022-09-06 15:59:05 +02:00
SChernykh 989a28a8ca TCPServer small fixes 2022-09-05 15:14:10 +02:00
SChernykh e4dd46b498 Added --no-dns command line parameter 2022-09-04 18:10:14 +02:00
SChernykh c49e8d4770 Merge branch 'proxy' 2022-09-04 09:57:32 +02:00
SChernykh 86156f3fec SOCKS5 proxy support 2022-09-03 22:35:38 +02:00
SChernykh fec826b10e Changed spammy warning to level 5 2022-09-01 10:09:23 +02:00
SChernykh 229b07391b CI: build for aarch64 on all Ubuntu versions 2022-08-31 09:49:45 +02:00
SChernykh e157477b6d Create COMMAND_LINE.MD 2022-08-30 23:22:55 +02:00
SChernykh d40bb61da8 p2pool v2.3 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
SChernykhandGitHub 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
SChernykhandGitHub 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 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 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
SChernykhandGitHub 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
SChernykhandGitHub 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
SChernykhandGitHub 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
SChernykhandGitHub 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
SChernykhandGitHub 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
120 changed files with 7680 additions and 18062 deletions
+2
View File
@@ -0,0 +1,2 @@
**/build
**/docker-compose
+139 -36
View File
@@ -1,10 +1,6 @@
name: C/C++ CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
on: [push, pull_request]
jobs:
build-ubuntu:
@@ -14,18 +10,18 @@ jobs:
strategy:
matrix:
config:
- {os: ubuntu-latest, c: gcc-11, cpp: g++-11}
- {os: ubuntu-18.04, c: gcc, cpp: g++}
- {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 add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install -y git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev ${{ matrix.config.c }} ${{ matrix.config.cpp }}
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
uses: actions/checkout@v3
with:
submodules: true
@@ -34,7 +30,7 @@ jobs:
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
make -j2
make -j$(nproc)
- name: Build tests
run: |
@@ -42,38 +38,115 @@ jobs:
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
make -j2
make -j$(nproc)
- name: Run tests
run: cd tests/build && ./p2pool_tests
- name: Archive binary
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool-${{ matrix.config.os }}
path: build/p2pool
build-ubuntu-aarch64:
build-ubuntu-static-libs:
runs-on: ubuntu-latest
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 gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu
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
uses: actions/checkout@v3
with:
submodules: true
- name: Build libcurl
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j$(nproc)
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++
cmake .. -DBUILD_TESTING=OFF
make -j$(nproc)
- name: Build libzmq
run: |
cd external/src/libzmq
mkdir build
cd build
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j$(nproc)
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -DSTATIC_LIBS=ON
make -j$(nproc)
- name: Build tests
run: |
cd tests
mkdir build
cd build
cmake .. -DSTATIC_LIBS=ON
make -j$(nproc)
- name: Run tests
run: cd tests/build && ./p2pool_tests
- name: Archive binary
uses: actions/upload-artifact@v3
with:
name: p2pool-ubuntu-static-libs
path: build/p2pool
build-ubuntu-aarch64:
runs-on: ${{ matrix.config.os }}
strategy:
matrix:
config:
- {os: ubuntu-20.04}
- {os: ubuntu-22.04}
steps:
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y git build-essential cmake autoconf gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: true
- name: Build libcurl
run: |
cd external/src/curl
autoreconf -fi
./configure --host=aarch64-linux-gnu --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j$(nproc)
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DBUILD_TESTING=OFF
make -j$(nproc)
- name: Build libzmq
@@ -92,9 +165,9 @@ jobs:
make -j$(nproc)
- name: Archive binary
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool-ubuntu-latest-aarch64
name: p2pool-${{ matrix.config.os }}-aarch64
path: build/p2pool
build-windows-msys2:
@@ -107,7 +180,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: recursive
@@ -115,22 +188,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-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: |
@@ -138,7 +234,7 @@ jobs:
./p2pool_tests.exe
- name: Archive binary
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool-msys2.exe
path: build/p2pool.exe
@@ -156,7 +252,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: recursive
@@ -176,15 +272,15 @@ jobs:
mkdir build
cd build
cmake .. -G "${{ matrix.config.vs }}"
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool_tests.vcxproj
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Debug p2pool_tests.vcxproj
- name: Run tests
run: |
cd tests/build/Release
cd tests/build/Debug
./p2pool_tests.exe
- name: Archive binary
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}.exe
path: build/Release/p2pool.exe
@@ -195,23 +291,30 @@ jobs:
strategy:
matrix:
os: [macos-10.15, macos-11]
os: [macos-11, macos-12]
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: recursive
- name: Install dependencies
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake autoconf libtool automake
- name: Build libcurl
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j3
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake ..
cmake .. -DBUILD_TESTING=OFF
make -j3
- name: Build libzmq
@@ -219,7 +322,7 @@ jobs:
cd external/src/libzmq
mkdir build
cd build
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j3
- name: Build p2pool
@@ -243,7 +346,7 @@ jobs:
./p2pool_tests
- name: Archive binary
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool-${{ matrix.os }}
path: build/p2pool
+5 -8
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,16 +37,16 @@ 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
uses: actions/checkout@v3
with:
submodules: true
# 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
+3 -7
View File
@@ -1,10 +1,6 @@
name: cppcheck
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
on: [push, pull_request]
jobs:
cppcheck-ubuntu:
@@ -13,7 +9,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: true
@@ -39,7 +35,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: true
+4 -6
View File
@@ -10,9 +10,7 @@ name: Microsoft C++ Code Analysis
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
schedule:
- cron: '40 10 * * 0'
@@ -28,7 +26,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: true
@@ -40,7 +38,7 @@ jobs:
# run: cmake --build ${{ env.build }}
- name: Initialize MSVC Code Analysis
uses: microsoft/msvc-code-analysis-action@47ecec99bf7ce0399ed85cbb5012b6f24eab5959
uses: microsoft/msvc-code-analysis-action@24c285ab36952c9e9182f4b78dfafbac38a7e5ee
# Provide a unique ID to access the sarif output path
id: run-analysis
with:
@@ -51,13 +49,13 @@ jobs:
# Upload SARIF file to GitHub Code Scanning Alerts
- name: Upload SARIF to GitHub
uses: github/codeql-action/upload-sarif@v1
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
uses: actions/upload-artifact@v3
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@v3
with:
submodules: true
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -DDEV_TEST_SYNC=ON -DCMAKE_C_COMPILER=gcc-12 -DCMAKE_CXX_COMPILER=g++-12
make -j$(nproc)
- name: Run p2pool
timeout-minutes: 15
run: |
cd build
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 6
- name: Archive p2pool.log
uses: actions/upload-artifact@v3
with:
name: p2pool_ubuntu.log
path: build/p2pool.log
sync-test-macos:
runs-on: macos-12
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: recursive
- name: Install dependencies
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake autoconf libtool automake libuv zmq libpgm curl
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -DDEV_TEST_SYNC=ON
make -j3
- name: Run p2pool
timeout-minutes: 15
run: |
cd build
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 6
- name: Archive p2pool.log
uses: actions/upload-artifact@v3
with:
name: p2pool_macos.log
path: build/p2pool.log
sync-test-windows:
runs-on: windows-2022
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup cmake
uses: lukka/get-cmake@latest
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -G "Visual Studio 17 2022" -DDEV_TEST_SYNC=ON
& "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\msbuild" /m /p:Configuration=Debug p2pool.vcxproj
- name: Run p2pool
timeout-minutes: 15
run: |
cd build/Debug
./p2pool.exe --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 6
- name: Archive p2pool.log
uses: actions/upload-artifact@v3
with:
name: p2pool_windows.log
path: build/Debug/p2pool.log
+3
View File
@@ -19,3 +19,6 @@
[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
+81 -22
View File
@@ -1,15 +1,15 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 3.6)
project(p2pool)
option(STATIC_BINARY "Build static binary" OFF)
option(STATIC_LIBS "Link libuv and libzmq statically" OFF)
option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned off, p2pool will rely on monerod for verifying RandomX hashes" ON)
option(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_EQUAL "3.6.0")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
endif()
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
if (WITH_RANDOMX)
add_definitions(-DWITH_RANDOMX)
@@ -17,11 +17,15 @@ if (WITH_RANDOMX)
set(LIBS randomx)
endif()
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
external/src/hardforks/hardforks.h
src/block_cache.h
src/block_template.h
src/common.h
@@ -52,9 +56,7 @@ 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
external/src/hardforks/hardforks.cpp
src/block_cache.cpp
src/block_template.cpp
src/console_commands.cpp
@@ -83,13 +85,30 @@ if (WITH_RANDOMX)
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()
if ((CMAKE_CXX_COMPILER_ID MATCHES MSVC) OR STATIC_BINARY OR STATIC_LIBS)
set(UV_INCLUDE_DIR external/src/libuv/include)
set(ZMQ_INCLUDE_DIR external/src/libzmq/include)
else()
find_path(UV_INCLUDE_DIR NAMES uv.h PATH_SUFFIXES "include")
find_path(ZMQ_INCLUDE_DIR NAMES zmq.h PATH_SUFFIXES "include")
endif()
include_directories(src)
include_directories(external/src)
include_directories(external/src/cryptonote)
include_directories(external/src/libuv/include)
include_directories(${UV_INCLUDE_DIR})
include_directories(external/src/cppzmq)
include_directories(external/src/libzmq/include)
include_directories(external/src/llhttp)
include_directories(${ZMQ_INCLUDE_DIR})
if (WITH_RANDOMX)
include_directories(external/src/RandomX/src)
endif()
@@ -98,6 +117,10 @@ include_directories(external/src/robin-hood-hashing/src/include)
if (WIN32)
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(LIBS ${LIBS} bcrypt)
endif()
add_definitions(-DCURL_STATICLIB)
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(LIBS ${LIBS} pthread)
elseif (NOT APPLE)
@@ -109,11 +132,27 @@ if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
find_library(ZMQ_LIBRARY NAMES libzmq-v142-mt-s-4_3_5 PATHS "external/lib/libzmq/Release")
find_library(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")
add_definitions(-D_DISABLE_VECTOR_ANNOTATION)
add_definitions(-D_DISABLE_STRING_ANNOTATION)
elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
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()
@@ -132,21 +171,22 @@ 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)
include(CheckSymbolExists)
set(CMAKE_REQUIRED_FLAGS "${GENERAL_FLAGS}")
check_symbol_exists(pthread_cancel pthread.h HAVE_PTHREAD_CANCEL)
if (HAVE_PTHREAD_CANCEL)
add_definitions(/DHAVE_PTHREAD_CANCEL)
endif()
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)
check_cxx_source_compiles("#include <sched.h>\nint main(){sched_param param;return sched_setscheduler(0, SCHED_IDLE, &param);}" HAVE_SCHED)
if (HAVE_BUILTIN_CLZLL)
add_definitions(/DHAVE_BUILTIN_CLZLL)
@@ -156,26 +196,45 @@ if (HAVE_BITSCANREVERSE64)
add_definitions(/DHAVE_BITSCANREVERSE64)
endif()
if (HAVE_SCHED)
add_definitions(/DHAVE_SCHED)
endif()
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
if (STATIC_BINARY OR STATIC_LIBS)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME})
if (WIN32)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} "${CMAKE_PROJECT_NAME}.exe")
else()
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME})
endif()
if (WITH_RANDOMX)
set(STATIC_LIBS randomx)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
if (WIN32)
set(STATIC_LIBS ${STATIC_LIBS} ws2_32 iphlpapi userenv psapi wldap32)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(STATIC_LIBS ${STATIC_LIBS} bcrypt)
endif()
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(STATIC_LIBS ${STATIC_LIBS} pthread)
elseif (NOT APPLE)
elseif (APPLE)
find_library(FOUNDATION_LIB Foundation)
find_library(CORE_FOUNDATION_LIB CoreFoundation)
find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration)
set(STATIC_LIBS ${STATIC_LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB})
else()
set(STATIC_LIBS ${STATIC_LIBS} pthread dl)
endif()
target_link_libraries(${CMAKE_PROJECT_NAME}
"${CMAKE_SOURCE_DIR}/external/src/libzmq/build/lib/libzmq.a"
"${CMAKE_SOURCE_DIR}/external/src/libuv/build/libuv_a.a"
"${CMAKE_SOURCE_DIR}/external/src/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()
+6 -3
View File
@@ -1,4 +1,4 @@
FROM ubuntu:latest
FROM ubuntu:20.04
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
@@ -12,7 +12,8 @@ RUN set -e && \
libsodium-dev \
libpgm-dev \
libnorm-dev \
libgss-dev
libgss-dev \
libcurl4-openssl-dev
ADD . /usr/src/p2pool
WORKDIR /usr/src/p2pool
@@ -24,7 +25,7 @@ RUN git submodule update --init --recursive && \
# ---
FROM ubuntu:latest
FROM ubuntu:20.04
COPY --from=0 /usr/src/p2pool/build/p2pool /
RUN set -e && \
@@ -32,6 +33,7 @@ RUN set -e && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
libzmq5 \
libuv1 \
libcurl4 \
&& \
apt-get clean
@@ -43,6 +45,7 @@ USER p2pool
EXPOSE 3333
EXPOSE 37889
EXPOSE 37888
VOLUME /home/p2pool/.p2pool
+62 -34
View File
@@ -7,6 +7,8 @@ Pool status and monitoring pages can be found at https://p2pool.io/, https://p2p
### Build Status
![C/C++ CI](https://github.com/SChernykh/p2pool/actions/workflows/c-cpp.yml/badge.svg)
![test-sync](https://github.com/SChernykh/p2pool/actions/workflows/test-sync.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)
@@ -66,34 +68,37 @@ First you need to find a pool share. This share will stay in PPLNS window for 21
## Monero version support
Monero will undergo a network upgrade on July 16th, 2022 (block 2,668,888). In order to continue mining after that date, you must update both Monero and P2Pool software to the latest available versions as soon as they are released.
Monero network upgrade happened on August 13th, 2022 (block 2,688,888). In order to continue mining, you must update both Monero and P2Pool software to the latest available versions as soon as they are released.
|Monero protocol version|Required Monero software version|Required P2Pool version
|-|-|-|
|v14 (active until July 16th, 2022)|v0.17.3.0 or newer|v1.0 or newer
|v15, v16 (active after July 16th, 2022)|v0.18.0.0 or newer|v2.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.
- In order to mine on P2Pool, a synced Monero node using monerod v0.18.0.0 or newer is required. If you don't currently have one, you can download the official Monero binaries, start `monerod` on your PC and wait until it's fully synced. Advanced Monero node setup instructions are [here](https://sethforprivacy.com/guides/run-a-monero-node-advanced/).
- It is highly recommended that you create a separate restricted user account (in your OS) for mining. While P2Pool has been battle-tested for a long time now, any software may have unknown bugs/vulnerabilities.
- You have to use a primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
- Starting from P2Pool v1.7, you can add the `--mini` parameter to your P2Pool command to connect to the **p2pool-mini** sidechain. Note that it will also change the default p2p port from 37889 to 37888.
- You can add the `--mini` parameter to your P2Pool command to connect to the **p2pool-mini** sidechain. Note that it will also change the default p2p port from 37889 to 37888.
- Check that ports 18080 (Monero p2p port) and 37889/37888 (P2Pool/P2Pool mini p2p port) are open in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine.
- You can connect multiple miners to the same P2Pool node. The more the better!
- The below steps assume that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup.
- The steps below assume that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup.
- It is highly recommended to create a new mainnet wallet for P2Pool mining because **wallet addresses are public on P2Pool**.
**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/)
- [Official Monero CLI and GUI v0.18.0.0 and newer](https://www.getmonero.org/downloads/)
- [Monerujo v3.0.2 "Fluorine Fermi" and newer](https://www.monerujo.io/)
- [Cake Wallet v4.4.5 and newer](https://cakewallet.com/)
- [Monero.com by Cake Wallet](https://monero.com/)
- [Feather Wallet v1.0.0 and newer](https://featherwallet.org/)
- [Feather Wallet v2.1.0 and newer](https://featherwallet.org/)
- [MyMonero](https://mymonero.com/)
### GUI for P2Pool
- [Gupax](https://github.com/hinto-janaiyo/gupax) project (currently in development) aims to provide an easy to use GUI to configure and run monerod/p2pool/xmrig
### GNU/Linux
1. Download the latest P2Pool binaries [here](https://github.com/SChernykh/p2pool/releases/latest).
@@ -106,9 +111,19 @@ 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
./monerod --zmq-pub tcp://127.0.0.1:18083 --out-peers 64 --in-peers 32 --add-priority-node=node.supportxmr.com:18080 --add-priority-node=nodes.hashvault.pro:18080 --disable-dns-checkpoints --enable-dns-blocklist
```
**Note:** The `--zmq-pub` option is required for P2Pool to work properly.
**Note:**
The `--zmq-pub` option is required for P2Pool to work properly.
`--out-peers 64 --in-peers 32` is needed to (1) have many connections to other nodes and (2) limit incoming connection count because it can grow uncontrollably and cause problems when it goes above 1000 (open files limit in Linux). If your network connection's **upload** bandwidth is less than **10 Mbit**, use `--out-peers 16 --in-peers 8` instead.
`--add-priority-node=node.supportxmr.com:18080 --add-priority-node=nodes.hashvault.pro:18080` is needed to have guaranteed good working nodes in your connected peers.
`--disable-dns-checkpoints` is needed to avoid periodical lags when DNS is updated (it's not needed when mining)
`--enable-dns-blocklist` is needed to ban known bad nodes
6. Start P2Pool with the following command/options:
```
@@ -157,21 +172,32 @@ nocreate
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.
6. Create "Monero" folder inside extracted P2Pool folder and copy Monero binaries there.
7. Open a command prompt and navigate to the folder where you extracted P2Pool.
**Note:** *When running the below commands, Windows Firewall may prompt to allow connections, click "Allow" if prompted.*
7. Start `monerod` with the following command/options:
8. Start `monerod` with the following command/options:
```
.\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
.\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --out-peers 64 --in-peers 32 --add-priority-node=node.supportxmr.com:18080 --add-priority-node=nodes.hashvault.pro:18080 --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:
**Note:**
The `--zmq-pub` option is required for P2Pool to work properly.
`--out-peers 64 --in-peers 32` is needed to (1) have many connections to other nodes and (2) limit incoming connection count because it can grow uncontrollably and cause problems when it goes above 1000 (open files limit in Linux). If your network connection's **upload** bandwidth is less than **10 Mbit**, use `--out-peers 16 --in-peers 8` instead.
`--add-priority-node=node.supportxmr.com:18080 --add-priority-node=nodes.hashvault.pro:18080` is needed to have guaranteed good working nodes in your connected peers.
`--disable-dns-checkpoints` is needed to avoid periodical lags when DNS is updated (it's not needed when mining)
`--enable-dns-blocklist` is needed to ban known bad nodes
9. Start P2Pool with the following command/options:
```
.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS
.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS --mini
```
9. Wait until the initial P2Pool sync is finished (shouldn't take more than 5-10 minutes).
10. Start XMRig with the following command/options:
10. Wait until the initial P2Pool sync is finished (shouldn't take more than 5-10 minutes).
11. Start XMRig with the following command/options:
```
.\xmrig.exe -o 127.0.0.1:3333
```
@@ -180,17 +206,17 @@ nocreate
```
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`.
12. XMRig should connect and start mining!
13. *(Optional but highly recommended)* You can create a Quickstart by creating a batch (.bat) file with the following contents and placing it in your P2Pool directory along with `xmrig.exe`.
```
@ECHO OFF
start cmd /k %~dp0\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
start cmd /k %~dp0\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --out-peers 64 --in-peers 32 --add-priority-node=node.supportxmr.com:18080 --add-priority-node=nodes.hashvault.pro:18080 --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
start cmd /k %~dp0\p2pool.exe --wallet YOUR_WALLET_ADDRESS --mini
ECHO Wait until the daemon shows fully synced before continuing. This can take some time.
PAUSE
%~dp0\xmrig.exe -u x+30000 -o 127.0.0.1
%~dp0\xmrig.exe -o 127.0.0.1
```
## Build instructions
@@ -200,7 +226,7 @@ Please see the relevant instructions for your platform:
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on Ubuntu 20.04:
```
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
@@ -208,9 +234,11 @@ cmake ..
make -j$(nproc)
```
### Arch Linux [AUR](https://wiki.archlinux.org/title/Arch_User_Repository)
### [Arch Linux](https://archlinux.org/packages/community/x86_64/p2pool/)
Make the package: [p2pool-git](https://aur.archlinux.org/packages/p2pool-git/)
```
pacman -S p2pool
```
### [Nix/NixOS](https://nixos.org)
@@ -224,12 +252,12 @@ 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
nix run git+https://github.com/SChernykh/p2pool?ref=master&submodules=1 -- --help
```
### Windows
@@ -251,7 +279,7 @@ Alternatively, you can select "Clone a repository" within the GUI, then select "
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on your Mac:
```
brew update && brew install git cmake libuv zmq libpgm
brew update && brew install git cmake libuv zmq libpgm curl
git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool
mkdir build && cd build
@@ -263,7 +291,7 @@ make -j$(sysctl -n hw.logicalcpu)
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on FreeBSD:
```
pkg install git cmake libuv libzmq4
pkg install git cmake libuv libzmq4 curl
git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool
mkdir build && cd build
@@ -275,7 +303,7 @@ make
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
pkg install git build-essential cmake libuv libzmq libcurl
git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool
mkdir build && cd build
+3 -4
View File
@@ -7,7 +7,7 @@ set(CMAKE_C_STANDARD_REQUIRED ON)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(GENERAL_FLAGS "-pthread")
set(WARNING_FLAGS "-Wall -Wextra -Werror")
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} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
@@ -26,7 +26,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
endif()
endif()
elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
set(GENERAL_FLAGS "")
set(GENERAL_FLAGS "/MP")
set(WARNING_FLAGS "/Wall /WX /sdl")
set(SECURITY_FLAGS "/GS /guard:cf")
set(OPTIMIZATION_FLAGS "/O2 /Oi /Ob2 /Ot /DNDEBUG /GL")
@@ -39,10 +39,9 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
set(GENERAL_FLAGS "-pthread")
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal -Werror")
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} ${GENERAL_FLAGS} ${WARNING_FLAGS} ${OPTIMIZATION_FLAGS}")
+1 -1
View File
@@ -4,7 +4,7 @@
../external/src/libuv/include/
../external/src/cppzmq/
../external/src/libzmq/include/
../external/src/llhttp/
../external/src/curl/include
../external/src/RandomX/src/
../external/src/rapidjson/include
../external/src/robin-hood-hashing/src/include
+1
View File
@@ -1,3 +1,4 @@
unmatchedSuppression
missingIncludeSystem
unusedFunction
useStlAlgorithm
-42
View File
@@ -1,42 +0,0 @@
##
# Monero Wallet Address for mining rewards (replace this with your own address)
WALLET_ADDRESS="44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg"
##
# p2pool settings
#
# Use p2pool-mini (uncomment to enable)
#P2POOL_MINI="--mini"
#
# Which port to listen for miner connections
P2POOL_STRATUM_PORT=3333
#
# How much logging - (Less) 0 - 6 (More)
P2POOL_LOGLEVEL=2
##
# Monero Node Settings
#
# Version of Monero to build. Must be v0.17.3.0 or later for p2pool support. "latest" pulls the most recent release tag
MONERO_GIT_TAG=latest
#
# Limit the size of the blockchain on disk (comment to disable)
PRUNE_NODE="--prune-blockchain"
#
# Other monerod commandline options
MONERO_EXTRA_OPTIONS=""
##
# Xmrig Miner Settings
#
# Submit shares at a lower fixed difficulty to show mining progress (comment to disable, edit to customize)
FIXED_MINING_DIFFICULTY="-u x+500000"
#
# set process priority (0 idle, 2 normal to 5 highest)
MINING_CPU_PRIORITY="--cpu-priority=2"
#
# Other XMRIG commandline options
# See: https://xmrig.com/docs/miner/command-line-options
XMRIG_EXTRA_OPTIONS=""
+20 -15
View File
@@ -8,6 +8,8 @@ Run your own <b>Monero Node + P2Pool + XMRig</b> in Docker
[Install Docker](https://docs.docker.com/engine/install/)
[Install Docker 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
@@ -16,37 +18,40 @@ git clone --recursive https://github.com/SChernykh/p2pool
#### Configure your Monero address for mining rewards
```
cd p2pool/docker-compose
vi .env
./configure
```
**WALLET_ADDRESS** is the only setting that needs to be updated in that file
Make sure to set your own monero **Wallet Address**. The default is to donate mining to P2Pool development.
#### Build the docker containers
```
docker-compose build
docker compose build --no-cache
```
#### Run the node, pool, and CPU miner
#### Run the node, pool, and CPU miner (or updated configuration)
```
docker-compose up
docker compose up
```
#### Optional
* Open ports 18080 (Monero p2p port) and 37889 (P2Pool p2p port) or 37888 (P2Pool-mini p2p port) in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine
* An XMRig CPU miner is included by default, but you can connect additional miners to this same p2pool node using port 3333
* An XMRig CPU miner is included by default, but you can connect additional miners to this same p2pool node using port 3333 (or alternate if configured) when you set it as "exposed" in the configuration
* Configure your kernel for maximum mining performance: [XMRig RandomX Optimization Guide](https://xmrig.com/docs/miner/randomx-optimization-guide)
* Small miners can mine on p2pool-mini by editing the .env file
* Many optional configurations and customizations are available by running './configure'
#### Other usefull commands
* You can **run everythng in the background** by adding the "-d" argument to the "docker-compose up" command: ```docker-compose up -d```
* You can **stop everything** with CTRL-C or ```docker-compose down```
* You can see logs when running in the background for with the "docker logs" command: ```docker logs -f p2pool-xmrig``` or ```docker logs -f p2pool-p2pool``` or ```docker logs -f p2pool-monero```
* You can pause mining with: ```docker-compose pause xmrig``` and resume mining with: ```docker-compose unpause xmrig```
* You can disable mining with: ```docker-compose stop xmrig``` and re-enable mining with: ```docker-compose start xmrig```
* 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
Stop and remove all containers: ```docker-compose down```
Remove the p2pool data: ```docker volume rm p2pool```
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": ""
}
+186
View File
@@ -0,0 +1,186 @@
---
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
--out-peers 32
--in-peers 16
--add-priority-node=nodes.hashvault.pro:18080
--add-priority-node=node.supportxmr.com:18080
--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": ""
}
+89 -28
View File
@@ -7,22 +7,86 @@ networks:
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=${MONERO_GIT_TAG}
- 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
@@ -32,37 +96,31 @@ services:
--zmq-pub tcp://0.0.0.0:18083
--disable-dns-checkpoints
--enable-dns-blocklist
--out-peers 32
--in-peers 16
--add-priority-node=nodes.hashvault.pro:18080
--add-priority-node=node.supportxmr.com:18080
--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
${PRUNE_NODE}
${MONERO_EXTRA_OPTIONS}
--log-level=0
--prune-blockchain
--fast-block-sync=0
p2pool:
image: p2pool:latest
build: ../
container_name: p2pool-p2pool
networks:
- p2pool
ports:
- ${P2POOL_STRATUM_PORT}:3333/tcp
- 37888:37888/tcp
- 37889:37889/tcp
volumes:
- p2pool:/home/p2pool/.p2pool:rw
- /dev/null:/home/p2pool/.p2pool/p2pool.log:rw
- /dev/hugepages:/dev/hugepages:rw
depends_on:
- monero
restart: unless-stopped
command: >-
--host monero
--wallet ${WALLET_ADDRESS}
--loglevel ${P2POOL_LOGLEVEL}
${P2POOL_MINI}
xmrig:
image: xmrig:latest
@@ -79,8 +137,11 @@ services:
- p2pool
restart: unless-stopped
command: >-
${FIXED_MINING_DIFFICULTY}
${MINING_CPU_PRIORITY}
${XMRIG_EXTRA_OPTIONS}
--randomx-1gb-pages
-o p2pool:3333
-u p2pool
--cpu-max-threads-hint=100
--cpu-priority=2
+31 -51
View File
@@ -1,79 +1,59 @@
FROM ubuntu:latest
# Multistage docker build, requires docker 17.05
# builder stage
FROM ubuntu:20.04 as builder
ARG MONERO_GIT_TAG="latest"
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
git \
ca-certificates \
automake \
autotools-dev \
bsdmainutils \
build-essential \
pkg-config \
cmake \
libuv1-dev \
libzmq3-dev \
libsodium-dev \
libpgm-dev \
libnorm-dev \
libgss-dev \
libssl-dev \
libzmq3-dev \
libunbound-dev \
libsodium-dev \
libunwind8-dev \
liblzma-dev \
libreadline6-dev \
libldns-dev \
libexpat1-dev \
libpgm-dev \
qttools5-dev-tools \
libhidapi-dev \
libusb-1.0-0-dev \
libprotobuf-dev \
protobuf-compiler \
libudev-dev \
libboost-chrono-dev \
libboost-date-time-dev \
libboost-filesystem-dev \
libboost-locale-dev \
libboost-program-options-dev \
libboost-regex-dev \
libboost-serialization-dev \
libboost-system-dev \
libboost-thread-dev \
ca-certificates \
ccache \
doxygen \
graphviz
cmake \
curl \
git \
libtool \
pkg-config \
gperf
WORKDIR /usr/src
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 release-static -j$(nproc)
make -j$(nproc) depends target=$(contrib/depends/config.guess)
# ---
FROM ubuntu:latest
# runtime stage
FROM ubuntu:20.04
COPY --from=0 /usr/src/monero/build/Linux/*/release/bin/* /
COPY --from=0 /src/monero/build/*/release/bin /usr/local/bin/
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
libgssapi-krb5-2 \
&& \
apt-get clean
ca-certificates \
netcat \
&& \
apt-get clean && \
rm -rf /var/lib/apt
RUN groupadd -r monero -g 1000 && \
useradd -u 1000 -r -g monero -s /sbin/nologin -c "monero node user" monero
RUN mkdir -p /home/monero/.bitmonero && \
chown monero.monero /home/monero /home/monero/.bitmonero
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
WORKDIR /home/monero
ENTRYPOINT ["/monerod"]
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>
+2 -2
View File
@@ -1,4 +1,4 @@
FROM ubuntu:latest
FROM ubuntu:20.04
RUN set -e && \
apt-get update -q -y --no-install-recommends && \
@@ -20,7 +20,7 @@ RUN git clone https://github.com/xmrig/xmrig.git && \
# ---
FROM ubuntu:latest
FROM ubuntu:20.04
COPY --from=0 /usr/src/xmrig/build/xmrig /
+33
View File
@@ -0,0 +1,33 @@
### P2Pool command line options
```
--wallet Wallet address to mine to. Subaddresses and integrated addresses are not supported!
--host IP address of your Monero node, default is 127.0.0.1
--rpc-port monerod RPC API port number, default is 18081
--zmq-port monerod ZMQ pub port number, default is 18083 (same port as in monerod's "--zmq-pub" command line parameter)
--stratum Comma-separated list of IP:port for stratum server to listen on
--p2p Comma-separated list of IP:port for p2p server to listen on
--addpeers Comma-separated list of IP:port of other p2pool nodes to connect to
--light-mode Don't allocate RandomX dataset, saves 2GB of RAM
--loglevel Verbosity of the log, integer number between 0 and 6
--config Name of the p2pool config file
--data-api Path to the p2pool JSON data (use it in tandem with an external web-server)
--local-api Enable /local/ path in api path for Stratum Server and built-in miner statistics
--stratum-api An alias for --local-api
--no-cache Disable p2pool.cache
--no-color Disable colors in console output
--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes
--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 450)
--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 450)
--start-mining N Start built-in miner using N threads (any value between 1 and 64)
--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from 37889 to 37888
--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum
--rpc-login Specify username[:password] required for Monero RPC server
--socks5 Specify IP:port of a SOCKS5 proxy to use for outgoing connections
--no-dns disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too)
--p2p-external-port port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections
```
### Example command line
p2pool.exe --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:3333 --p2p 0.0.0.0:37889
Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

+305
View File
@@ -0,0 +1,305 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>P2Pool - Decentralized Monero mining pool</title>
<link rel="shortcut icon" type="image/png" href="favicon-32x32.png"/>
<meta name="viewport" content="width=device-width, initial-scale=0.5">
<style>
a {
color: #e09000;
-ms-touch-action: none;
touch-action: none;
}
a:visited {
color: #808080;
-ms-touch-action: none;
touch-action: none;
}
body {
background-color: #383838;
color: #e0e0e0;
font-family: Arial, sans-serif;
font-size: 1.5rem;
}
table {
color: #c0c0c0;
border-spacing:15px 5px;
font-size: 1.5rem;
}
table.menu_page {
width: 100%;
margin-left: auto;
margin-right: auto;
}
details > summary {
padding: 0.25rem;
font-size: 2.5rem;
-ms-touch-action: none;
touch-action: none;
}
details > ul > li > span {
color: #e09000;
}
</style>
<script>
function elapsedTimeStr(ts1, ts2, short_form) {
let s = ts2 - ts1;
if (s < 0) s = 0;
let m = Math.floor(s / 60) % 60;
let h = Math.floor(s / 3600) % 24;
let d = Math.floor(s / 86400);
if (short_form) {
if (s >= 86400) return d + 'd';
if (s >= 3600) return h + 'h';
return m + 'm';
}
else {
let result = m + 'm';
if (s >= 3600) result = h + 'h ' + result;
if (s >= 86400) result = d + 'd ' + h + 'h';
return result;
}
}
function set_effort(el, value) {
el.innerHTML = value.toFixed(2) + '%';
el.style = (value < 100.0) ? "color:#00C000;" : (value < 200.0) ? "color:#E0E000;" : "color:#FF0000;";
}
async function load_data() {
init_menu();
window.onresize = init_menu;
let pool_stats_req = await fetch('api/pool/stats');
let pool_blocks_req = await fetch('api/pool/blocks');
let network_stats_req = await fetch('api/network/stats');
let pool_stats = JSON.parse(await pool_stats_req.text());
let pool_blocks = JSON.parse(await pool_blocks_req.text());
let network_stats = JSON.parse(await network_stats_req.text());
let pool_hashrate = pool_stats.pool_statistics.hashRate / 1e6;
document.getElementById("pool_hashrate").innerHTML = pool_hashrate.toFixed(3) + ' MH/s';
document.getElementById("pool_miners").innerHTML = pool_stats.pool_statistics.miners;
let ts = Math.floor(Date.now() / 1000);
let n = pool_stats.pool_statistics.lastBlockFound;
document.getElementById("pool_last_block").innerHTML = n ? ('<a href="/explorer/block/' + n + '" target="_blank">' + n + '</a> (' + elapsedTimeStr(pool_stats.pool_statistics.lastBlockFoundTime, ts, 0) + ' ago)') : 'N/A';
document.getElementById("pool_total_blocks").innerHTML = pool_stats.pool_statistics.totalBlocksFound;
let t = document.getElementById('menu_blocks');
let cur_effort = (pool_stats.pool_statistics.totalHashes - ((pool_blocks.length > 0) ? pool_blocks[0].totalHashes : 0)) * 100.0 / network_stats.difficulty;
set_effort(document.getElementById("current_effort"), (cur_effort >= 0) ? cur_effort : 0);
let total_effort = 0.0;
let num_blocks_with_effort = 0;
for (let i = 0, m = Math.min(pool_blocks.length, 50); i < m; ++i) {
if (t.rows.length <= i + 1) t.insertRow(-1);
let row = t.rows[i + 1];
while (row.cells.length < 3) row.insertCell(-1);
let height = row.cells[0];
let effort = row.cells[1];
let age = row.cells[2];
n = pool_blocks[i].height;
height.innerHTML = '<a href="/explorer/block/' + pool_blocks[i].hash + '" target="_blank">' + n + '</a>';
if ((i + 1 < pool_blocks.length) && (pool_blocks[i + 1].totalHashes > 0)) {
let value = (pool_blocks[i].totalHashes - pool_blocks[i + 1].totalHashes) * 100.0 / pool_blocks[i].difficulty;
total_effort += value;
++num_blocks_with_effort;
set_effort(effort, value);
}
else {
effort.innerHTML = "N/A";
}
age.innerHTML = elapsedTimeStr(pool_blocks[i].ts, ts, 1);
}
if (num_blocks_with_effort > 0) {
let average_effort = total_effort / num_blocks_with_effort;
set_effort(document.getElementById("average_effort"), average_effort);
}
let network_hashrate = network_stats.difficulty / 120e6;
document.getElementById("monero_hashrate").innerHTML = network_hashrate.toFixed(3) + ' MH/s';
document.getElementById("monero_last_block").innerHTML = network_stats.height + ' (' + elapsedTimeStr(network_stats.timestamp, ts, 0) + ' ago)';
}
const menu = ['pool', 'blocks', 'help', 'faq'];
function init_menu()
{
let is_mobile = false;
if (navigator.userAgentData) {
is_mobile = navigator.userAgentData.mobile;
}
else {
const mobile_preg = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i;
is_mobile = mobile_preg.test(navigator.userAgent) || mobile_preg.test(navigator.platform);
}
let e = document.getElementById('main_menu');
if (!is_mobile) {
e.style.cursor = "pointer";
document.querySelectorAll("details > summary").forEach(el => { el.style.cursor = "pointer"; });
}
e.style.width = (window.innerWidth > 1600) ? "1600px" : "100%";
menu.forEach(element => {
let e2 = document.getElementById('menu_' + element);
e2.style.width = e.style.width;
});
}
function toggle_menu(name) {
if (!name) {
name = 'pool';
}
menu.forEach(element => {
let e = document.getElementById('menu_' + element);
let b = document.getElementById('button_' + element);
if (element == name) {
e.style.display = 'block';
b.style.backgroundColor = '#646464';
window.location.hash = '#' + name;
}
else {
e.style.display = 'none';
b.style.backgroundColor = '#404040';
}
});
}
</script>
</head>
<body onload="load_data();setInterval(load_data, 2000);toggle_menu(window.location.hash.substr(1));">
<div style="font-size:3rem;text-align:center;">P2Pool</div><div style="font-size:1.1rem;text-align:center;">Decentralized Monero mining pool</div>
<br>
<table id="main_menu" style="width:100%;margin-left:auto;margin-right:auto;table-layout:fixed;text-align:center;-ms-touch-action:none;touch-action:none;"><tr>
<td onclick="toggle_menu('pool')" id="button_pool">Pool</td>
<td onclick="toggle_menu('blocks')" id="button_blocks">Blocks</td>
<td onclick="toggle_menu('help')" id="button_help">Get started</td>
<td onclick="toggle_menu('faq')" id="button_faq">FAQ</td>
</tr></table>
<br>
<table id="menu_pool" class="menu_page">
<tr><td></td><td style="width:17rem;">Pool</td><td>Network</td></tr>
<tr><td style="padding-bottom: 1rem;"></td><td></td><td></td></tr>
<tr><td>Hashrate</td><td><span id="pool_hashrate">N/A</span></td><td><span id="monero_hashrate">N/A</span></td></tr>
<tr><td>Miners</td><td><span id="pool_miners">N/A</span></td><td></td></tr>
<tr><td>Last block</td><td><span id="pool_last_block">N/A</span></td><td><span id="monero_last_block">N/A</span></td></tr>
<tr><td>Total blocks</td><td><span id="pool_total_blocks">N/A</span></td><td></td></tr>
<tr><td>Current effort</td><td><span id="current_effort">N/A</span></td><td></td></tr>
<tr><td>Average effort<br><div style="text-align:center;font-size:0.9rem">last 50 blocks</div></td><td><span id="average_effort">N/A</span></td><td></td></tr>
<tr><td>&nbsp;</td><td></td><td></td></tr>
<tr><td>Fee</td><td>0%</td><td></td></tr>
<tr><td>Min payout</td><td>0.0003 XMR</td><td></td></tr>
<tr><td>Payout scheme</td><td><a href="https://en.wikipedia.org/wiki/Mining_pool#Pay-per-last-N-shares" target="_blank">PPLNS</a></td><td></td></tr>
<tr><td>&nbsp;</td><td></td><td></td></tr>
<tr><td>Statistics</td><td><a href="https://p2pool.observer/" target="_blank">P2Pool observer</a></td><td></td></tr>
</table>
<table id="menu_blocks" class="menu_page">
<tr><td style="padding-right:5rem;">Height</td><td style="padding-right:5rem;">Effort</td><td>Age</td></tr>
</table>
<table id="menu_help" class="menu_page"><tr><td>
<details><summary>P2Pool video guide</summary>
<ul>
<li>A detailed <a href="https://www.youtube.com/watch?v=NbxbRu-2GWI" target="_blank">video guide</a> to install P2Pool and start mining
</ul>
</details>
<details><summary>Setup Monero wallet</summary>
Wallets with confirmed P2Pool support<br>
<span style="font-size:0.9rem;color:#e09000;">You might not see P2Pool payouts in other wallets if they're not updated!</span>
<ul>
<li>Official <a href="https://www.getmonero.org/downloads/" target="_blank">Monero CLI and GUI v0.18.1.0</a> and newer
<li><a href="https://play.google.com/store/apps/details?id=com.m2049r.xmrwallet" target="_blank">Monerujo v3.0.2 "Fluorine Fermi"</a> and newer
<li><a href="https://play.google.com/store/apps/details?id=com.cakewallet.cake_wallet" target="_blank">Cake Wallet v4.4.5</a> and newer
<li><a href="https://featherwallet.org/" target="_blank">Feather Wallet v2.1.0</a> and newer
<li><a href="https://mymonero.com/" target="_blank">MyMonero</a>
</ul>
It's highly recommended to create a new wallet for mining because wallet addresses are public on p2pool. You have to use the primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
</details>
<details><summary>Setup Monero node</summary>
<ul>
<li>Download the latest Monero <a href="https://www.getmonero.org/downloads/" target="_blank">release</a>
<li>[Optional] Open port <span>18080</span> (Monero p2p port) in your firewall to ensure better connectivity
<li>Run <span>./monerod --zmq-pub tcp://127.0.0.1:18083 --out-peers 64 --in-peers 32 --add-priority-node=node.supportxmr.com:18080 --add-priority-node=nodes.hashvault.pro:18080 --disable-dns-checkpoints --enable-dns-blocklist</span>
<li>If your network connection's upload bandwidth is less than 10 Mbit, use <span>--out-peers 16 --in-peers 8</span> instead.
<li>Wait until it's fully synchronized. If you didn't run Monero node before, it can take up to several days to synchronize (5-6 hours on a modern PC with fast SSD and fast Internet connection). You can add <span>--prune-blockchain</span> argument to the command line to run a pruned node (3-4 times less disk usage)
</ul></details>
<details><summary>Setup P2Pool node</summary>
<ul>
<li>Download the latest P2Pool <a href="https://github.com/SChernykh/p2pool/releases/latest" target="_blank">release</a>
<li>[Optional] Open port <span>37889</span> (P2Pool port) or <span>37888</span> (P2Pool mini port) in your firewall to ensure better connectivity
<li>Run <span>./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS</span>
<li>It should take no more than 5-10 minutes to synchronize
</ul></details>
<details><summary>Miner software</summary>
<ul>
<li><a href="https://xmrig.com/" target="_blank">XMRig</a> is recommended. <a href="https://xmrig.com/proxy" target="_blank">XMRig Proxy</a> is fully supported.
<li>Once P2Pool sync is complete, XMRig should be able to connect to the stratum server on port <span>3333</span>
<li>Run <span>./xmrig -o 127.0.0.1:3333</span>. Note that you don't need to specify wallet address for xmrig. Wallet address set in xmrig config will be ignored!
<li>To set custom fixed difficulty for your miner (for example, <span>10000</span>), run <span>./xmrig -u x+10000 -o 127.0.0.1:3333</span>
</ul></details>
<details><summary>Checking your progress</summary>
<ul>
<li>You can enter your wallet address at <a href="https://p2pool.observer/" target="_blank">P2Pool Observer</a>
<li>P2Pool Observer for P2Pool mini can be found <a href="https://mini.p2pool.observer/" target="_blank">here</a>
<li>You will find your wallet's statistics there only after you've mined at least 1 P2Pool share
</ul>
</details>
<details><summary>Help and support</summary>
P2Pool is a decentralized community, there is no dedicated support. You can reach and get help from other miners on:
<ul>
<li>IRC channels: <span>#monero-pools</span> and <span>#p2pool-log</span> (<span>#p2pool-mini</span> for P2Pool mini) on <a href="https://libera.chat/" target="_blank">Libera.Chat</a>
<li>Reddit: <a href="https://reddit.com/r/MoneroMining/" target="_blank">r/MoneroMining</a>
</ul></details>
</td></tr></table>
<table id="menu_faq" class="menu_page"><tr><td>
<ul>
<li><span style="font-size:2rem;color:#e09000;">What are system requirements to run P2Pool?</span>
<ul><li>P2Pool needs 0.5 GB disc space and 2.6 GB RAM to run (0.6 GB RAM in light mode). Add this to monerod requirements if you run both on the same machine. Add <span style="color:#808080;">--light-mode</span> to p2pool command line to reduce the memory usage to 0.6 GB, or <span style="color:#808080;">--no-randomx --no-cache</span> to reduce the memory usage to less than 100 MB.</ul><br>
<li><span style="font-size:2rem;color:#e09000;">Does P2Pool work with XMRig Proxy?</span>
<ul><li>Yes, P2Pool fully supports <a href="https://xmrig.com/proxy" target="_blank">XMRig Proxy</a>.</ul><br>
<li><span style="font-size:2rem;color:#e09000;">Does monerod have to be run on the same machine as P2Pool?</span>
<ul><li>No, you can run them on different machines and even connect multiple p2pool instances to a single server running monerod</ul><br>
<li><span style="font-size:2rem;color:#e09000;">Does P2Pool work with pruned Monero node?</span>
<ul><li>Yes, you can use pruned Monero node to mine on P2Pool</ul><br>
<li><span style="font-size:2rem;color:#e09000;">Does P2Pool require incoming connections to work properly?</span>
<ul><li>P2Pool (and Monero) can work with only outgoing connections, but it's recommended to open ports 18080 (Monero) and 37889/37888 (P2Pool/P2Pool mini) for better connectivity with the rest of the network.</ul><br>
<li><span style="font-size:2rem;color:#e09000;">I have been mining for several hours and still got no rewards?</span>
<ul><li>P2Pool doesn't work exactly like regular pools - there's no "pending balance" here. You need to find a pool share first and then wait for P2Pool to find a Monero block. You'll get a payout to your XMR wallet as soon as a block is found and you have shares in PPLNS window. It can take several days to a week to find a share if your hashrate is low. Type "status" command in P2Pool console or visit <a href="https://p2pool.observer/" target="_blank">P2Pool observer</a> to check your progress.</ul><br>
<li><span style="font-size:2rem;color:#e09000;">I am mining but I can't find any shares</span>
<ul><li>First check for any warnings and errors in P2Pool console and logs. If everything works fine, you can check the <a href="https://p2pool.observer/calculate-share-time" target="_blank">average share time calculator</a> to get an idea how often you should find a share with your hashrate. If this time is more than 1 day, it's recommended to switch to P2Pool mini by adding <span style="color:#808080;">--mini</span> to P2Pool command line.</ul><br>
<li><span style="font-size:2rem;color:#e09000;">I get an error "monerod RPC ... is incompatible, update to RPC >= v3.8"</span>
<ul><li>Use monerod v0.18.0.0 or newer</ul><br>
<li><span style="font-size:2rem;color:#e09000;">I get warnings "peer ... is ahead on mainchain (height ..., your height ...). Is your monerod stuck or lagging?"</span>
<ul><li>Make sure you add these arguments to monerod command line: <span style="color:#808080;">--zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist</span></ul><br>
</ul>
</td></tr></table>
<br><br>
<div style="position:fixed;width:100%;bottom:0;background-color:#383838;font-size:1rem;text-align:center;color:#b0b0b0;">
<a href="api/" target="_blank">Pool API</a>&nbsp;|&nbsp;<a href="https://github.com/SChernykh/p2pool" target="_blank">Source</a>&nbsp;|&nbsp;<a href="http://yucmgsbw7nknw7oi3bkuwudvc657g2xcqahhbjyewazusyytapqo4xid.onion/" target="_blank">Onion URL</a><br>
Donate to support the project:<br><a href="monero:44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg?tx_description=Donation%20to%20P2Pool" style="font-size:0.9rem;color:#b0b0b0;">44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg</a><br>
&nbsp;
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

+305
View File
@@ -0,0 +1,305 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>P2Pool mini - Decentralized Monero mining pool</title>
<link rel="shortcut icon" type="image/png" href="favicon-32x32.png"/>
<meta name="viewport" content="width=device-width, initial-scale=0.5">
<style>
a {
color: #e09000;
-ms-touch-action: none;
touch-action: none;
}
a:visited {
color: #808080;
-ms-touch-action: none;
touch-action: none;
}
body {
background-color: #383838;
color: #e0e0e0;
font-family: Arial, sans-serif;
font-size: 1.5rem;
}
table {
color: #c0c0c0;
border-spacing:15px 5px;
font-size: 1.5rem;
}
table.menu_page {
width: 100%;
margin-left: auto;
margin-right: auto;
}
details > summary {
padding: 0.25rem;
font-size: 2.5rem;
-ms-touch-action: none;
touch-action: none;
}
details > ul > li > span {
color: #e09000;
}
</style>
<script>
function elapsedTimeStr(ts1, ts2, short_form) {
let s = ts2 - ts1;
if (s < 0) s = 0;
let m = Math.floor(s / 60) % 60;
let h = Math.floor(s / 3600) % 24;
let d = Math.floor(s / 86400);
if (short_form) {
if (s >= 86400) return d + 'd';
if (s >= 3600) return h + 'h';
return m + 'm';
}
else {
let result = m + 'm';
if (s >= 3600) result = h + 'h ' + result;
if (s >= 86400) result = d + 'd ' + h + 'h';
return result;
}
}
function set_effort(el, value) {
el.innerHTML = value.toFixed(2) + '%';
el.style = (value < 100.0) ? "color:#00C000;" : (value < 200.0) ? "color:#E0E000;" : "color:#FF0000;";
}
async function load_data() {
init_menu();
window.onresize = init_menu;
let pool_stats_req = await fetch('api/pool/stats');
let pool_blocks_req = await fetch('api/pool/blocks');
let network_stats_req = await fetch('api/network/stats');
let pool_stats = JSON.parse(await pool_stats_req.text());
let pool_blocks = JSON.parse(await pool_blocks_req.text());
let network_stats = JSON.parse(await network_stats_req.text());
let pool_hashrate = pool_stats.pool_statistics.hashRate / 1e6;
document.getElementById("pool_hashrate").innerHTML = pool_hashrate.toFixed(3) + ' MH/s';
document.getElementById("pool_miners").innerHTML = pool_stats.pool_statistics.miners;
let ts = Math.floor(Date.now() / 1000);
let n = pool_stats.pool_statistics.lastBlockFound;
document.getElementById("pool_last_block").innerHTML = n ? ('<a href="/explorer/block/' + n + '" target="_blank">' + n + '</a> (' + elapsedTimeStr(pool_stats.pool_statistics.lastBlockFoundTime, ts, 0) + ' ago)') : 'N/A';
document.getElementById("pool_total_blocks").innerHTML = pool_stats.pool_statistics.totalBlocksFound;
let t = document.getElementById('menu_blocks');
let cur_effort = (pool_stats.pool_statistics.totalHashes - ((pool_blocks.length > 0) ? pool_blocks[0].totalHashes : 0)) * 100.0 / network_stats.difficulty;
set_effort(document.getElementById("current_effort"), (cur_effort >= 0) ? cur_effort : 0);
let total_effort = 0.0;
let num_blocks_with_effort = 0;
for (let i = 0, m = Math.min(pool_blocks.length, 50); i < m; ++i) {
if (t.rows.length <= i + 1) t.insertRow(-1);
let row = t.rows[i + 1];
while (row.cells.length < 3) row.insertCell(-1);
let height = row.cells[0];
let effort = row.cells[1];
let age = row.cells[2];
n = pool_blocks[i].height;
height.innerHTML = '<a href="/explorer/block/' + pool_blocks[i].hash + '" target="_blank">' + n + '</a>';
if ((i + 1 < pool_blocks.length) && (pool_blocks[i + 1].totalHashes > 0)) {
let value = (pool_blocks[i].totalHashes - pool_blocks[i + 1].totalHashes) * 100.0 / pool_blocks[i].difficulty;
total_effort += value;
++num_blocks_with_effort;
set_effort(effort, value);
}
else {
effort.innerHTML = "N/A";
}
age.innerHTML = elapsedTimeStr(pool_blocks[i].ts, ts, 1);
}
if (num_blocks_with_effort > 0) {
let average_effort = total_effort / num_blocks_with_effort;
set_effort(document.getElementById("average_effort"), average_effort);
}
let network_hashrate = network_stats.difficulty / 120e6;
document.getElementById("monero_hashrate").innerHTML = network_hashrate.toFixed(3) + ' MH/s';
document.getElementById("monero_last_block").innerHTML = network_stats.height + ' (' + elapsedTimeStr(network_stats.timestamp, ts, 0) + ' ago)';
}
const menu = ['pool', 'blocks', 'help', 'faq'];
function init_menu()
{
let is_mobile = false;
if (navigator.userAgentData) {
is_mobile = navigator.userAgentData.mobile;
}
else {
const mobile_preg = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i;
is_mobile = mobile_preg.test(navigator.userAgent) || mobile_preg.test(navigator.platform);
}
let e = document.getElementById('main_menu');
if (!is_mobile) {
e.style.cursor = "pointer";
document.querySelectorAll("details > summary").forEach(el => { el.style.cursor = "pointer"; });
}
e.style.width = (window.innerWidth > 1600) ? "1600px" : "100%";
menu.forEach(element => {
let e2 = document.getElementById('menu_' + element);
e2.style.width = e.style.width;
});
}
function toggle_menu(name) {
if (!name) {
name = 'pool';
}
menu.forEach(element => {
let e = document.getElementById('menu_' + element);
let b = document.getElementById('button_' + element);
if (element == name) {
e.style.display = 'block';
b.style.backgroundColor = '#646464';
window.location.hash = '#' + name;
}
else {
e.style.display = 'none';
b.style.backgroundColor = '#404040';
}
});
}
</script>
</head>
<body onload="load_data();setInterval(load_data, 2000);toggle_menu(window.location.hash.substr(1));">
<div style="font-size:3rem;text-align:center;">P2Pool mini</div><div style="font-size:1.1rem;text-align:center;">Decentralized Monero mining pool</div>
<br>
<table id="main_menu" style="width:100%;margin-left:auto;margin-right:auto;table-layout:fixed;text-align:center;-ms-touch-action:none;touch-action:none;"><tr>
<td onclick="toggle_menu('pool')" id="button_pool">Pool</td>
<td onclick="toggle_menu('blocks')" id="button_blocks">Blocks</td>
<td onclick="toggle_menu('help')" id="button_help">Get started</td>
<td onclick="toggle_menu('faq')" id="button_faq">FAQ</td>
</tr></table>
<br>
<table id="menu_pool" class="menu_page">
<tr><td></td><td style="width:17rem;">Pool</td><td>Network</td></tr>
<tr><td style="padding-bottom: 1rem;"></td><td></td><td></td></tr>
<tr><td>Hashrate</td><td><span id="pool_hashrate">N/A</span></td><td><span id="monero_hashrate">N/A</span></td></tr>
<tr><td>Miners</td><td><span id="pool_miners">N/A</span></td><td></td></tr>
<tr><td>Last block</td><td><span id="pool_last_block">N/A</span></td><td><span id="monero_last_block">N/A</span></td></tr>
<tr><td>Total blocks</td><td><span id="pool_total_blocks">N/A</span></td><td></td></tr>
<tr><td>Current effort</td><td><span id="current_effort">N/A</span></td><td></td></tr>
<tr><td>Average effort<br><div style="text-align:center;font-size:0.9rem">last 50 blocks</div></td><td><span id="average_effort">N/A</span></td><td></td></tr>
<tr><td>&nbsp;</td><td></td><td></td></tr>
<tr><td>Fee</td><td>0%</td><td></td></tr>
<tr><td>Min payout</td><td>0.0003 XMR</td><td></td></tr>
<tr><td>Payout scheme</td><td><a href="https://en.wikipedia.org/wiki/Mining_pool#Pay-per-last-N-shares" target="_blank">PPLNS</a></td><td></td></tr>
<tr><td>&nbsp;</td><td></td><td></td></tr>
<tr><td>Statistics</td><td><a href="https://mini.p2pool.observer/" target="_blank">P2Pool observer</a></td><td></td></tr>
</table>
<table id="menu_blocks" class="menu_page">
<tr><td style="padding-right:5rem;">Height</td><td style="padding-right:5rem;">Effort</td><td>Age</td></tr>
</table>
<table id="menu_help" class="menu_page"><tr><td>
<details><summary>P2Pool video guide</summary>
<ul>
<li>A detailed <a href="https://www.youtube.com/watch?v=NbxbRu-2GWI" target="_blank">video guide</a> to install P2Pool and start mining
</ul>
</details>
<details><summary>Setup Monero wallet</summary>
Wallets with confirmed P2Pool support<br>
<span style="font-size:0.9rem;color:#e09000;">You might not see P2Pool payouts in other wallets if they're not updated!</span>
<ul>
<li>Official <a href="https://www.getmonero.org/downloads/" target="_blank">Monero CLI and GUI v0.18.1.0</a> and newer
<li><a href="https://play.google.com/store/apps/details?id=com.m2049r.xmrwallet" target="_blank">Monerujo v3.0.2 "Fluorine Fermi"</a> and newer
<li><a href="https://play.google.com/store/apps/details?id=com.cakewallet.cake_wallet" target="_blank">Cake Wallet v4.4.5</a> and newer
<li><a href="https://featherwallet.org/" target="_blank">Feather Wallet v2.1.0</a> and newer
<li><a href="https://mymonero.com/" target="_blank">MyMonero</a>
</ul>
It's highly recommended to create a new wallet for mining because wallet addresses are public on p2pool. You have to use the primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
</details>
<details><summary>Setup Monero node</summary>
<ul>
<li>Download the latest Monero <a href="https://www.getmonero.org/downloads/" target="_blank">release</a>
<li>[Optional] Open port <span>18080</span> (Monero p2p port) in your firewall to ensure better connectivity
<li>Run <span>./monerod --zmq-pub tcp://127.0.0.1:18083 --out-peers 64 --in-peers 32 --add-priority-node=node.supportxmr.com:18080 --add-priority-node=nodes.hashvault.pro:18080 --disable-dns-checkpoints --enable-dns-blocklist</span>
<li>If your network connection's upload bandwidth is less than 10 Mbit, use <span>--out-peers 16 --in-peers 8</span> instead.
<li>Wait until it's fully synchronized. If you didn't run Monero node before, it can take up to several days to synchronize (5-6 hours on a modern PC with fast SSD and fast Internet connection). You can add <span>--prune-blockchain</span> argument to the command line to run a pruned node (3-4 times less disk usage)
</ul></details>
<details><summary>Setup P2Pool node</summary>
<ul>
<li>Download the latest P2Pool <a href="https://github.com/SChernykh/p2pool/releases/latest" target="_blank">release</a>
<li>[Optional] Open port <span>37889</span> (P2Pool port) or <span>37888</span> (P2Pool mini port) in your firewall to ensure better connectivity
<li>Run <span>./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS --mini</span>
<li>It should take no more than 5-10 minutes to synchronize
</ul></details>
<details><summary>Miner software</summary>
<ul>
<li><a href="https://xmrig.com/" target="_blank">XMRig</a> is recommended. <a href="https://xmrig.com/proxy" target="_blank">XMRig Proxy</a> is fully supported.
<li>Once P2Pool sync is complete, XMRig should be able to connect to the stratum server on port <span>3333</span>
<li>Run <span>./xmrig -o 127.0.0.1:3333</span>. Note that you don't need to specify wallet address for xmrig. Wallet address set in xmrig config will be ignored!
<li>To set custom fixed difficulty for your miner (for example, <span>10000</span>), run <span>./xmrig -u x+10000 -o 127.0.0.1:3333</span>
</ul></details>
<details><summary>Checking your progress</summary>
<ul>
<li>You can enter your wallet address at <a href="https://p2pool.observer/" target="_blank">P2Pool Observer</a>
<li>P2Pool Observer for P2Pool mini can be found <a href="https://mini.p2pool.observer/" target="_blank">here</a>
<li>You will find your wallet's statistics there only after you've mined at least 1 P2Pool share
</ul>
</details>
<details><summary>Help and support</summary>
P2Pool is a decentralized community, there is no dedicated support. You can reach and get help from other miners on:
<ul>
<li>IRC channels: <span>#monero-pools</span> and <span>#p2pool-log</span> (<span>#p2pool-mini</span> for P2Pool mini) on <a href="https://libera.chat/" target="_blank">Libera.Chat</a>
<li>Reddit: <a href="https://reddit.com/r/MoneroMining/" target="_blank">r/MoneroMining</a>
</ul></details>
</td></tr></table>
<table id="menu_faq" class="menu_page"><tr><td>
<ul>
<li><span style="font-size:2rem;color:#e09000;">What are system requirements to run P2Pool?</span>
<ul><li>P2Pool needs 0.5 GB disc space and 2.6 GB RAM to run (0.6 GB RAM in light mode). Add this to monerod requirements if you run both on the same machine. Add <span style="color:#808080;">--light-mode</span> to p2pool command line to reduce the memory usage to 0.6 GB, or <span style="color:#808080;">--no-randomx --no-cache</span> to reduce the memory usage to less than 100 MB.</ul><br>
<li><span style="font-size:2rem;color:#e09000;">Does P2Pool work with XMRig Proxy?</span>
<ul><li>Yes, P2Pool fully supports <a href="https://xmrig.com/proxy" target="_blank">XMRig Proxy</a>.</ul><br>
<li><span style="font-size:2rem;color:#e09000;">Does monerod have to be run on the same machine as P2Pool?</span>
<ul><li>No, you can run them on different machines and even connect multiple p2pool instances to a single server running monerod</ul><br>
<li><span style="font-size:2rem;color:#e09000;">Does P2Pool work with pruned Monero node?</span>
<ul><li>Yes, you can use pruned Monero node to mine on P2Pool</ul><br>
<li><span style="font-size:2rem;color:#e09000;">Does P2Pool require incoming connections to work properly?</span>
<ul><li>P2Pool (and Monero) can work with only outgoing connections, but it's recommended to open ports 18080 (Monero) and 37889/37888 (P2Pool/P2Pool mini) for better connectivity with the rest of the network.</ul><br>
<li><span style="font-size:2rem;color:#e09000;">I have been mining for several hours and still got no rewards?</span>
<ul><li>P2Pool doesn't work exactly like regular pools - there's no "pending balance" here. You need to find a pool share first and then wait for P2Pool to find a Monero block. You'll get a payout to your XMR wallet as soon as a block is found and you have shares in PPLNS window. It can take several days to a week to find a share if your hashrate is low. Type "status" command in P2Pool console or visit <a href="https://mini.p2pool.observer/" target="_blank">P2Pool observer</a> to check your progress.</ul><br>
<li><span style="font-size:2rem;color:#e09000;">I am mining but I can't find any shares</span>
<ul><li>First check for any warnings and errors in P2Pool console and logs. If everything works fine, you can check the <a href="https://p2pool.observer/calculate-share-time" target="_blank">average share time calculator</a> to get an idea how often you should find a share with your hashrate. If this time is more than 1 day, it's recommended to switch to P2Pool mini by adding <span style="color:#808080;">--mini</span> to P2Pool command line.</ul><br>
<li><span style="font-size:2rem;color:#e09000;">I get an error "monerod RPC ... is incompatible, update to RPC >= v3.8"</span>
<ul><li>Use monerod v0.18.0.0 or newer</ul><br>
<li><span style="font-size:2rem;color:#e09000;">I get warnings "peer ... is ahead on mainchain (height ..., your height ...). Is your monerod stuck or lagging?"</span>
<ul><li>Make sure you add these arguments to monerod command line: <span style="color:#808080;">--zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist</span></ul><br>
</ul>
</td></tr></table>
<br><br>
<div style="position:fixed;width:100%;bottom:0;background-color:#383838;font-size:1rem;text-align:center;color:#b0b0b0;">
<a href="api/" target="_blank">Pool API</a>&nbsp;|&nbsp;<a href="https://github.com/SChernykh/p2pool" target="_blank">Source</a>&nbsp;|&nbsp;<a href="http://yucmgsbw7nknw7oi3bkuwudvc657g2xcqahhbjyewazusyytapqo4xid.onion/mini/" target="_blank">Onion URL</a><br>
Donate to support the project:<br><a href="monero:44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg?tx_description=Donation%20to%20P2Pool" style="font-size:0.9rem;color:#b0b0b0;">44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg</a><br>
&nbsp;
</div>
</body>
</html>
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.
Vendored Submodule
+1
Submodule external/src/curl added at c12fb3ddaf
+132
View File
@@ -0,0 +1,132 @@
// Copyright (c) 2014-2022, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifdef _MSC_VER
#pragma warning(disable : 4514 4820)
#endif
#include "hardforks.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "blockchain.hardforks"
const hardfork_t mainnet_hard_forks[] = {
// version 1 from the start of the blockchain
{ 1, 1, 0, 1341378000 },
// version 2 starts from block 1009827, which is on or around the 20th of March, 2016. Fork time finalised on 2015-09-20. No fork voting occurs for the v2 fork.
{ 2, 1009827, 0, 1442763710 },
// version 3 starts from block 1141317, which is on or around the 24th of September, 2016. Fork time finalised on 2016-03-21.
{ 3, 1141317, 0, 1458558528 },
// version 4 starts from block 1220516, which is on or around the 5th of January, 2017. Fork time finalised on 2016-09-18.
{ 4, 1220516, 0, 1483574400 },
// version 5 starts from block 1288616, which is on or around the 15th of April, 2017. Fork time finalised on 2017-03-14.
{ 5, 1288616, 0, 1489520158 },
// version 6 starts from block 1400000, which is on or around the 16th of September, 2017. Fork time finalised on 2017-08-18.
{ 6, 1400000, 0, 1503046577 },
// version 7 starts from block 1546000, which is on or around the 6th of April, 2018. Fork time finalised on 2018-03-17.
{ 7, 1546000, 0, 1521303150 },
// version 8 starts from block 1685555, which is on or around the 18th of October, 2018. Fork time finalised on 2018-09-02.
{ 8, 1685555, 0, 1535889547 },
// version 9 starts from block 1686275, which is on or around the 19th of October, 2018. Fork time finalised on 2018-09-02.
{ 9, 1686275, 0, 1535889548 },
// version 10 starts from block 1788000, which is on or around the 9th of March, 2019. Fork time finalised on 2019-02-10.
{ 10, 1788000, 0, 1549792439 },
// version 11 starts from block 1788720, which is on or around the 10th of March, 2019. Fork time finalised on 2019-02-15.
{ 11, 1788720, 0, 1550225678 },
// version 12 starts from block 1978433, which is on or around the 30th of November, 2019. Fork time finalised on 2019-10-18.
{ 12, 1978433, 0, 1571419280 },
{ 13, 2210000, 0, 1598180817 },
{ 14, 2210720, 0, 1598180818 },
{ 15, 2688888, 0, 1656629117 },
{ 16, 2689608, 0, 1656629118 },
};
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
const uint64_t mainnet_hard_fork_version_1_till = 1009826;
const hardfork_t testnet_hard_forks[] = {
// version 1 from the start of the blockchain
{ 1, 1, 0, 1341378000 },
// version 2 starts from block 624634, which is on or around the 23rd of November, 2015. Fork time finalised on 2015-11-20. No fork voting occurs for the v2 fork.
{ 2, 624634, 0, 1445355000 },
// versions 3-5 were passed in rapid succession from September 18th, 2016
{ 3, 800500, 0, 1472415034 },
{ 4, 801219, 0, 1472415035 },
{ 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6
{ 6, 971400, 0, 1501709789 },
{ 7, 1057027, 0, 1512211236 },
{ 8, 1057058, 0, 1533211200 },
{ 9, 1057778, 0, 1533297600 },
{ 10, 1154318, 0, 1550153694 },
{ 11, 1155038, 0, 1550225678 },
{ 12, 1308737, 0, 1569582000 },
{ 13, 1543939, 0, 1599069376 },
{ 14, 1544659, 0, 1599069377 },
{ 15, 1982800, 0, 1652727000 },
{ 16, 1983520, 0, 1652813400 },
};
const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
const uint64_t testnet_hard_fork_version_1_till = 624633;
const hardfork_t stagenet_hard_forks[] = {
// version 1 from the start of the blockchain
{ 1, 1, 0, 1341378000 },
// versions 2-7 in rapid succession from March 13th, 2018
{ 2, 32000, 0, 1521000000 },
{ 3, 33000, 0, 1521120000 },
{ 4, 34000, 0, 1521240000 },
{ 5, 35000, 0, 1521360000 },
{ 6, 36000, 0, 1521480000 },
{ 7, 37000, 0, 1521600000 },
{ 8, 176456, 0, 1537821770 },
{ 9, 177176, 0, 1537821771 },
{ 10, 269000, 0, 1550153694 },
{ 11, 269720, 0, 1550225678 },
{ 12, 454721, 0, 1571419280 },
{ 13, 675405, 0, 1598180817 },
{ 14, 676125, 0, 1598180818 },
{ 15, 1151000, 0, 1656629117 },
{ 16, 1151720, 0, 1656629118 },
};
const size_t num_stagenet_hard_forks = sizeof(stagenet_hard_forks) / sizeof(stagenet_hard_forks[0]);
+52
View File
@@ -0,0 +1,52 @@
// Copyright (c) 2014-2022, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <stdint.h>
#include <time.h>
struct hardfork_t
{
uint8_t version;
uint64_t height;
uint8_t threshold;
time_t time;
hardfork_t(uint8_t version, uint64_t height, uint8_t threshold, time_t time): version(version), height(height), threshold(threshold), time(time) {}
};
extern const hardfork_t mainnet_hard_forks[];
extern const uint64_t mainnet_hard_fork_version_1_till;
extern const size_t num_mainnet_hard_forks;
extern const hardfork_t testnet_hard_forks[];
extern const uint64_t testnet_hard_fork_version_1_till;
extern const size_t num_testnet_hard_forks;
extern const hardfork_t stagenet_hard_forks[];
extern const size_t num_stagenet_hard_forks;
-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 = ''
+16 -7
View File
@@ -145,7 +145,7 @@ struct BlockCache::Impl : public nocopy_nomove
#else
// Not implemented on other platforms
void flush() {}
void flush() { m_data = nullptr; }
#endif
uint8_t* m_data = nullptr;
@@ -155,6 +155,7 @@ BlockCache::BlockCache()
: m_impl(new Impl())
, m_flushRunning(0)
, m_storeIndex(0)
, m_loadingStarted(0)
{
}
@@ -165,8 +166,11 @@ BlockCache::~BlockCache()
void BlockCache::store(const PoolBlock& block)
{
const size_t n1 = block.m_mainChainData.size();
const size_t n2 = block.m_sideChainData.size();
const std::vector<uint8_t> mainchain_data = block.serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = block.serialize_sidechain_data();
const size_t n1 = mainchain_data.size();
const size_t n2 = sidechain_data.size();
if (!m_impl->m_data || (sizeof(uint32_t) + n1 + n2 > BLOCK_SIZE)) {
return;
@@ -175,8 +179,8 @@ void BlockCache::store(const PoolBlock& block)
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);
memcpy(data + sizeof(uint32_t) + n1, block.m_sideChainData.data(), n2);
memcpy(data + sizeof(uint32_t), mainchain_data.data(), n1);
memcpy(data + sizeof(uint32_t) + n1, sidechain_data.data(), n2);
}
void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
@@ -185,6 +189,11 @@ void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
return;
}
// Can be only called once
if (m_loadingStarted.exchange(1)) {
return;
}
LOGINFO(1, "loading cached blocks");
PoolBlock block;
@@ -198,7 +207,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(), false) == 0) {
server.add_cached_block(block);
++blocks_loaded;
}
@@ -209,7 +218,7 @@ void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
void BlockCache::flush()
{
if (m_flushRunning.exchange(1) == 0) {
if (m_flushRunning.exchange(1)) {
m_impl->flush();
m_flushRunning.store(0);
}
+1
View File
@@ -38,6 +38,7 @@ private:
Impl* m_impl;
std::atomic<uint32_t> m_flushRunning;
std::atomic<uint32_t> m_storeIndex;
std::atomic<uint32_t> m_loadingStarted;
};
} // namespace p2pool
+172 -118
View File
@@ -34,11 +34,16 @@
static constexpr char log_category_prefix[] = "BlockTemplate ";
// Max P2P message size (128 KB) minus BLOCK_RESPONSE header (5 bytes)
static constexpr size_t MAX_BLOCK_TEMPLATE_SIZE = 128 * 1024 - (1 + sizeof(uint32_t));
namespace p2pool {
BlockTemplate::BlockTemplate(p2pool* pool)
: m_pool(pool)
BlockTemplate::BlockTemplate(SideChain* sidechain, RandomX_Hasher_Base* hasher)
: m_sidechain(sidechain)
, m_hasher(hasher)
, m_templateId(0)
, m_lastUpdated(seconds_since_epoch())
, m_blockHeaderSize(0)
, m_minerTxOffsetInTemplate(0)
, m_minerTxSize(0)
@@ -50,9 +55,15 @@ BlockTemplate::BlockTemplate(p2pool* pool)
, m_difficulty{}
, m_seedHash{}
, m_timestamp(0)
, m_txkeyPub{}
, m_txkeySec{}
, m_poolBlockTemplate(new PoolBlock())
, m_finalReward(0)
, m_rng(RandomDeviceSeed::instance)
{
// Diffuse the initial state in case it has low quality
m_rng.discard(10000);
uv_rwlock_init_checked(&m_lock);
m_blockHeader.reserve(64);
@@ -64,7 +75,8 @@ BlockTemplate::BlockTemplate(p2pool* pool)
m_merkleTreeMainBranch.reserve(HASH_SIZE * 10);
m_mempoolTxs.reserve(1024);
m_mempoolTxsOrder.reserve(1024);
m_shares.reserve(m_pool->side_chain().chain_window_size() * 2);
m_mempoolTxsOrder2.reserve(1024);
m_shares.reserve(m_sidechain->chain_window_size() * 2);
for (size_t i = 0; i < array_size(&BlockTemplate::m_oldTemplates); ++i) {
m_oldTemplates[i] = new BlockTemplate(*this);
@@ -73,8 +85,6 @@ BlockTemplate::BlockTemplate(p2pool* pool)
#if TEST_MEMPOOL_PICKING_ALGORITHM
m_knapsack.reserve(512 * 309375);
#endif
update_tx_keys();
}
BlockTemplate::~BlockTemplate()
@@ -104,8 +114,10 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
WriteLock lock(m_lock);
m_pool = b.m_pool;
m_sidechain = b.m_sidechain;
m_hasher = b.m_hasher;
m_templateId = b.m_templateId;
m_lastUpdated = b.m_lastUpdated.load();
m_blockTemplateBlob = b.m_blockTemplateBlob;
m_merkleTreeMainBranch = b.m_merkleTreeMainBranch;
m_blockHeaderSize = b.m_blockHeaderSize;
@@ -131,8 +143,11 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
m_rewards.clear();
m_mempoolTxs.clear();
m_mempoolTxsOrder.clear();
m_mempoolTxsOrder2.clear();
m_shares.clear();
m_rng = b.m_rng;
#if TEST_MEMPOOL_PICKING_ALGORITHM
m_knapsack.clear();
#endif
@@ -171,6 +186,14 @@ static FORCEINLINE uint64_t get_block_reward(uint64_t base_reward, uint64_t medi
return reward + fees;
}
void BlockTemplate::shuffle_tx_order()
{
const int64_t n = static_cast<int64_t>(m_mempoolTxsOrder.size());
for (int64_t i = n - 1; i > 0; --i) {
std::swap(m_mempoolTxsOrder[i], m_mempoolTxsOrder[m_rng() % (i + 1)]);
}
}
void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet)
{
if (data.major_version > HARDFORK_SUPPORTED_VERSION) {
@@ -189,6 +212,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
}
++m_templateId;
m_lastUpdated = seconds_since_epoch();
// When block template generation fails for any reason
auto use_old_template = [this]() {
@@ -197,58 +221,12 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
*this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)];
};
get_tx_keys(m_txkeyPub, m_txkeySec, miner_wallet->spend_public_key(), data.prev_id);
m_height = data.height;
m_difficulty = data.difficulty;
m_seedHash = data.seed_hash;
// Only choose transactions that were received 10 or more seconds ago
size_t total_mempool_transactions;
{
m_mempoolTxs.clear();
ReadLock mempool_lock(mempool.m_lock);
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);
}
}
}
// Safeguard for busy mempool moments
// If the block template gets too big, nodes won't be able to send and receive it because of p2p packet size limit
// Select 1000 transactions with the highest fee per byte
if (m_mempoolTxs.size() > 1000) {
std::nth_element(m_mempoolTxs.begin(), m_mempoolTxs.begin() + 1000, m_mempoolTxs.end(),
[](const TxMempoolData& tx_a, const TxMempoolData& tx_b)
{
return tx_a.fee * tx_b.weight > tx_b.fee * tx_a.weight;
});
m_mempoolTxs.resize(1000);
}
LOGINFO(4, "mempool has " << total_mempool_transactions << " transactions, taking " << m_mempoolTxs.size() << " transactions from it");
const uint64_t base_reward = get_base_reward(data.already_generated_coins);
uint64_t total_tx_fees = 0;
uint64_t total_tx_weight = 0;
for (const TxMempoolData& tx : m_mempoolTxs) {
total_tx_fees += tx.fee;
total_tx_weight += tx.weight;
}
const uint64_t max_reward = base_reward + total_tx_fees;
LOGINFO(3, "base reward = " << log::Gray() << log::XMRAmount(base_reward) << log::NoColor() <<
", " << log::Gray() << m_mempoolTxs.size() << log::NoColor() <<
" transactions, fees = " << log::Gray() << log::XMRAmount(total_tx_fees) << log::NoColor() <<
", weight = " << log::Gray() << total_tx_weight);
m_blockHeader.clear();
m_poolBlockTemplate->m_verified = false;
@@ -280,7 +258,71 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_blockHeaderSize = m_blockHeader.size();
m_pool->side_chain().fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares);
m_sidechain->fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares);
// Only choose transactions that were received 10 or more seconds ago, or high fee (>= 0.006 XMR) transactions
size_t total_mempool_transactions;
{
m_mempoolTxs.clear();
ReadLock mempool_lock(mempool.m_lock);
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) || (it.second.fee >= HIGH_FEE_VALUE)) {
m_mempoolTxs.emplace_back(it.second);
}
}
}
// Safeguard for busy mempool moments
// If the block template gets too big, nodes won't be able to send and receive it because of p2p packet size limit
// Calculate how many transactions we can take
{
PoolBlock* b = m_poolBlockTemplate;
b->m_transactions.clear();
b->m_transactions.resize(1);
b->m_outputs.clear();
// Block template size without coinbase outputs and transactions (add 1+1 more bytes for output and tx count if they go above 128)
size_t k = b->serialize_mainchain_data().size() + b->serialize_sidechain_data().size() + 2;
// a rough estimation of outputs' size
// all outputs have <= 5 bytes for each output's reward, and up to 18 outputs can have 6 bytes for output's reward
k += m_shares.size() * (5 /* reward */ + 1 /* tx_type */ + HASH_SIZE /* stealth address */ + 1 /* viewtag */) + 18;
const size_t max_transactions = (MAX_BLOCK_TEMPLATE_SIZE > k) ? ((MAX_BLOCK_TEMPLATE_SIZE - k) / HASH_SIZE) : 0;
if (max_transactions == 0) {
m_mempoolTxs.clear();
}
else if (m_mempoolTxs.size() > max_transactions) {
std::nth_element(m_mempoolTxs.begin(), m_mempoolTxs.begin() + max_transactions, m_mempoolTxs.end());
m_mempoolTxs.resize(max_transactions);
}
LOGINFO(4, "mempool has " << total_mempool_transactions << " transactions, taking " << m_mempoolTxs.size() << " transactions from it");
}
const uint64_t base_reward = get_base_reward(data.already_generated_coins);
uint64_t total_tx_fees = 0;
uint64_t total_tx_weight = 0;
for (const TxMempoolData& tx : m_mempoolTxs) {
total_tx_fees += tx.fee;
total_tx_weight += tx.weight;
}
const uint64_t max_reward = base_reward + total_tx_fees;
LOGINFO(3, "base reward = " << log::Gray() << log::XMRAmount(base_reward) << log::NoColor() <<
", " << log::Gray() << m_mempoolTxs.size() << log::NoColor() <<
" transactions, fees = " << log::Gray() << log::XMRAmount(total_tx_fees) << log::NoColor() <<
", weight = " << log::Gray() << total_tx_weight);
if (!SideChain::split_reward(max_reward, m_shares, m_rewards)) {
use_old_template();
return;
@@ -313,16 +355,22 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
// if a block doesn't get into the penalty zone, just pick all transactions
if (total_tx_weight + miner_tx_weight <= data.median_weight) {
m_numTransactionHashes = 0;
final_fees = 0;
final_weight = miner_tx_weight;
m_transactionHashes.assign(HASH_SIZE, 0);
for (const TxMempoolData& tx : m_mempoolTxs) {
m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
++m_numTransactionHashes;
shuffle_tx_order();
m_numTransactionHashes = m_mempoolTxsOrder.size();
m_transactionHashes.assign(HASH_SIZE, 0);
m_transactionHashesSet.clear();
m_transactionHashesSet.reserve(m_mempoolTxsOrder.size());
for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
const TxMempoolData& tx = m_mempoolTxs[m_mempoolTxsOrder[i]];
if (!m_transactionHashesSet.insert(tx.id).second) {
LOGERR(1, "Added transaction " << tx.id << " twice. Fix the code!");
continue;
}
m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
final_fees += tx.fee;
final_weight += tx.weight;
}
@@ -337,19 +385,14 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
// Sometimes it even finds the optimal solution
// Sort all transactions by fee per byte (highest to lowest)
std::sort(m_mempoolTxsOrder.begin(), m_mempoolTxsOrder.end(),
[this](int a, int b)
{
const TxMempoolData& tx_a = m_mempoolTxs[a];
const TxMempoolData& tx_b = m_mempoolTxs[b];
return tx_a.fee * tx_b.weight > tx_b.fee * tx_a.weight;
});
std::sort(m_mempoolTxsOrder.begin(), m_mempoolTxsOrder.end(), [this](int a, int b) { return m_mempoolTxs[a] < m_mempoolTxs[b]; });
final_reward = base_reward;
final_fees = 0;
final_weight = miner_tx_weight;
for (int i = 0; i < static_cast<int>(m_mempoolTxsOrder.size());) {
m_mempoolTxsOrder2.clear();
for (int i = 0; i < static_cast<int>(m_mempoolTxsOrder.size()); ++i) {
const TxMempoolData& tx = m_mempoolTxs[m_mempoolTxsOrder[i]];
int k = -1;
@@ -363,8 +406,10 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
// Try replacing other transactions when we are above the limit
if (final_weight + tx.weight > data.median_weight) {
for (int j = 0; j < i; ++j) {
const TxMempoolData& prev_tx = m_mempoolTxs[m_mempoolTxsOrder[j]];
// Don't check more than 100 transactions deep because they have higher and higher fee/byte
const int n = static_cast<int>(m_mempoolTxsOrder2.size());
for (int j = n - 1, j1 = std::max<int>(0, n - 100); j >= j1; --j) {
const TxMempoolData& prev_tx = m_mempoolTxs[m_mempoolTxsOrder2[j]];
const uint64_t reward2 = get_block_reward(base_reward, data.median_weight, final_fees + tx.fee - prev_tx.fee, final_weight + tx.weight - prev_tx.weight);
if (reward2 > final_reward) {
// If replacing some other transaction increases the reward even more, remember it
@@ -377,29 +422,35 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
if (k == i) {
// Simply adding this tx improves the reward
m_mempoolTxsOrder2.push_back(m_mempoolTxsOrder[i]);
final_fees += tx.fee;
final_weight += tx.weight;
++i;
continue;
}
if (k >= 0) {
else if (k >= 0) {
// Replacing another tx with this tx improves the reward
const TxMempoolData& prev_tx = m_mempoolTxs[m_mempoolTxsOrder[k]];
const TxMempoolData& prev_tx = m_mempoolTxs[m_mempoolTxsOrder2[k]];
m_mempoolTxsOrder2[k] = m_mempoolTxsOrder[i];
final_fees += tx.fee - prev_tx.fee;
final_weight += tx.weight - prev_tx.weight;
}
m_mempoolTxsOrder.erase(m_mempoolTxsOrder.begin() + ((k >= 0) ? k : i));
}
m_mempoolTxsOrder = m_mempoolTxsOrder2;
final_fees = 0;
final_weight = miner_tx_weight;
shuffle_tx_order();
m_numTransactionHashes = m_mempoolTxsOrder.size();
m_transactionHashes.assign(HASH_SIZE, 0);
m_transactionHashesSet.clear();
m_transactionHashesSet.reserve(m_mempoolTxsOrder.size());
for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
const TxMempoolData& tx = m_mempoolTxs[m_mempoolTxsOrder[i]];
if (!m_transactionHashesSet.insert(tx.id).second) {
LOGERR(1, "Added transaction " << tx.id << " twice. Fix the code!");
continue;
}
m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
final_fees += tx.fee;
final_weight += tx.weight;
@@ -526,15 +577,16 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
m_poolBlockTemplate->serialize_mainchain_data(0, 0, m_poolBlockTemplate->m_sidechainId);
#if POOL_BLOCK_DEBUG
if (m_poolBlockTemplate->m_mainChainData != m_blockTemplateBlob) {
const std::vector<uint8_t> mainchain_data = m_poolBlockTemplate->serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
if (mainchain_data != m_blockTemplateBlob) {
LOGERR(1, "serialize_mainchain_data() has a bug, fix it! ");
LOGERR(1, "m_poolBlockTemplate->m_mainChainData.size() = " << m_poolBlockTemplate->m_mainChainData.size());
LOGERR(1, "m_poolBlockTemplate->m_mainChainData.size() = " << mainchain_data.size());
LOGERR(1, "m_blockTemplateBlob.size() = " << m_blockTemplateBlob.size());
for (size_t i = 0, n = std::min(m_poolBlockTemplate->m_mainChainData.size(), m_blockTemplateBlob.size()); i < n; ++i) {
if (m_poolBlockTemplate->m_mainChainData[i] != m_blockTemplateBlob[i]) {
for (size_t i = 0, n = std::min(mainchain_data.size(), m_blockTemplateBlob.size()); i < n; ++i) {
if (mainchain_data[i] != m_blockTemplateBlob[i]) {
LOGERR(1, "m_poolBlockTemplate->m_mainChainData is different at offset " << i);
break;
}
@@ -543,10 +595,10 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
{
std::vector<uint8_t> buf = m_blockTemplateBlob;
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end());
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain());
const int result = check.deserialize(buf.data(), buf.size(), *m_sidechain, nullptr, false);
if (result != 0) {
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
}
@@ -572,9 +624,11 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_blockHeader.clear();
m_minerTxExtra.clear();
m_transactionHashes.clear();
m_transactionHashesSet.clear();
m_rewards.clear();
m_mempoolTxs.clear();
m_mempoolTxsOrder.clear();
m_mempoolTxsOrder2.clear();
}
#if TEST_MEMPOOL_PICKING_ALGORITHM
@@ -710,7 +764,7 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
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, tx_type, view_tag);
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key, view_tag);
}
if (tx_type == TXOUT_TO_TAGGED_KEY) {
@@ -743,7 +797,7 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
const uint64_t corrected_extra_nonce_size = EXTRA_NONCE_SIZE + max_reward_amounts_weight - reward_amounts_weight;
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");
LOGWARN(5, "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");
@@ -781,9 +835,10 @@ hash BlockTemplate::calc_sidechain_hash() const
const int sidechain_hash_offset = static_cast<int>(m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize) + 2;
const int blob_size = static_cast<int>(m_blockTemplateBlob.size());
const std::vector<uint8_t>& consensus_id = m_pool->side_chain().consensus_id();
const std::vector<uint8_t>& consensus_id = m_sidechain->consensus_id();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
keccak_custom([this, sidechain_hash_offset, blob_size, consensus_id](int offset) -> uint8_t {
keccak_custom([this, sidechain_hash_offset, blob_size, consensus_id, &sidechain_data](int offset) -> uint8_t {
uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(m_nonceOffset));
if (k < NONCE_SIZE) {
return 0;
@@ -804,15 +859,15 @@ hash BlockTemplate::calc_sidechain_hash() const
}
const int side_chain_data_offsset = offset - blob_size;
const int side_chain_data_size = static_cast<int>(m_poolBlockTemplate->m_sideChainData.size());
const int side_chain_data_size = static_cast<int>(sidechain_data.size());
if (side_chain_data_offsset < side_chain_data_size) {
return m_poolBlockTemplate->m_sideChainData[side_chain_data_offsset];
return sidechain_data[side_chain_data_offsset];
}
const int consensus_id_offset = side_chain_data_offsset - side_chain_data_size;
return consensus_id[consensus_id_offset];
},
static_cast<int>(m_blockTemplateBlob.size() + m_poolBlockTemplate->m_sideChainData.size() + consensus_id.size()), sidechain_hash.h, HASH_SIZE);
static_cast<int>(m_blockTemplateBlob.size() + sidechain_data.size() + consensus_id.size()), sidechain_hash.h, HASH_SIZE);
return sidechain_hash;
}
@@ -908,11 +963,13 @@ void BlockTemplate::calc_merkle_tree_main_branch()
}
}
bool BlockTemplate::get_difficulties(const uint32_t template_id, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const
bool BlockTemplate::get_difficulties(const uint32_t template_id, uint64_t& height, uint64_t& sidechain_height, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const
{
ReadLock lock(m_lock);
if (template_id == m_templateId) {
height = m_height;
sidechain_height = m_poolBlockTemplate->m_sidechainHeight;
mainchain_difficulty = m_difficulty;
sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
return true;
@@ -921,7 +978,7 @@ bool BlockTemplate::get_difficulties(const uint32_t template_id, difficulty_type
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 old->get_difficulties(template_id, height, sidechain_height, mainchain_difficulty, sidechain_difficulty);
}
return false;
@@ -950,11 +1007,12 @@ uint32_t BlockTemplate::get_hashing_blob(const uint32_t template_id, uint32_t ex
return 0;
}
uint32_t BlockTemplate::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 BlockTemplate::get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, uint64_t& sidechain_height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const
{
ReadLock lock(m_lock);
height = m_height;
sidechain_height = m_poolBlockTemplate->m_sidechainHeight;
difficulty = m_difficulty;
sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
seed_hash = m_seedHash;
@@ -1057,13 +1115,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);
@@ -1071,36 +1122,39 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
if (template_id == m_templateId) {
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, EXTRA_NONCE_SIZE);
SideChain& side_chain = m_pool->side_chain();
#if POOL_BLOCK_DEBUG
{
std::vector<uint8_t> buf = m_poolBlockTemplate->m_mainChainData;
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end());
std::vector<uint8_t> buf = m_poolBlockTemplate->serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
memcpy(buf.data() + m_nonceOffset, &nonce, NONCE_SIZE);
memcpy(buf.data() + m_extraNonceOffsetInTemplate, &extra_nonce, EXTRA_NONCE_SIZE);
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), side_chain);
const int result = check.deserialize(buf.data(), buf.size(), *m_sidechain, nullptr, false);
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(), 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)) {
LOGERR(1, "Sidechain block has wrong PoW. Fix it! ");
if (m_hasher) {
hash pow_hash;
if (!check.get_pow_hash(m_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)) {
LOGERR(1, "Sidechain block has wrong PoW. Fix it! ");
}
}
}
#endif
m_poolBlockTemplate->m_verified = true;
if (!side_chain.block_seen(*m_poolBlockTemplate)) {
if (!m_sidechain->block_seen(*m_poolBlockTemplate)) {
m_poolBlockTemplate->m_wantBroadcast = true;
side_chain.add_block(*m_poolBlockTemplate);
m_sidechain->add_block(*m_poolBlockTemplate);
}
return;
}
+16 -6
View File
@@ -23,7 +23,8 @@
namespace p2pool {
class p2pool;
class SideChain;
class RandomX_Hasher_Base;
class Mempool;
class Wallet;
struct PoolBlock;
@@ -32,22 +33,22 @@ struct MinerShare;
class BlockTemplate
{
public:
explicit BlockTemplate(p2pool* pool);
explicit BlockTemplate(SideChain* sidechain, RandomX_Hasher_Base* hasher);
~BlockTemplate();
BlockTemplate(const BlockTemplate& b);
BlockTemplate& operator=(const BlockTemplate& b);
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet);
uint64_t last_updated() const { return m_lastUpdated.load(); }
bool get_difficulties(const uint32_t template_id, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
bool get_difficulties(const uint32_t template_id, uint64_t& height, uint64_t& sidechain_height, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
uint32_t get_hashing_blob(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const;
uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, uint64_t& sidechain_height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
uint32_t get_hashing_blobs(uint32_t extra_nonce_start, uint32_t count, std::vector<uint8_t>& blobs, uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
std::vector<uint8_t> get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const;
void update_tx_keys();
FORCEINLINE uint64_t height() const { return m_height; }
FORCEINLINE difficulty_type difficulty() const { return m_difficulty; }
@@ -55,9 +56,11 @@ public:
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
FORCEINLINE const std::vector<MinerShare>& shares() const { return m_shares; }
FORCEINLINE const PoolBlock* pool_block_template() const { return m_poolBlockTemplate; }
private:
p2pool* m_pool;
SideChain* m_sidechain;
RandomX_Hasher_Base* m_hasher;
private:
int create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run);
@@ -70,6 +73,7 @@ private:
mutable uv_rwlock_t m_lock;
uint32_t m_templateId;
std::atomic<uint64_t> m_lastUpdated;
std::vector<uint8_t> m_blockTemplateBlob;
std::vector<uint8_t> m_merkleTreeMainBranch;
@@ -102,11 +106,17 @@ private:
std::vector<uint8_t> m_blockHeader;
std::vector<uint8_t> m_minerTxExtra;
std::vector<uint8_t> m_transactionHashes;
unordered_set<hash> m_transactionHashesSet;
std::vector<uint64_t> m_rewards;
std::vector<TxMempoolData> m_mempoolTxs;
std::vector<int> m_mempoolTxsOrder;
std::vector<int> m_mempoolTxsOrder2;
std::vector<MinerShare> m_shares;
std::mt19937_64 m_rng;
void shuffle_tx_order();
#if TEST_MEMPOOL_PICKING_ALGORITHM
void fill_optimal_knapsack(const MinerData& data, uint64_t base_reward, uint64_t miner_tx_weight, uint64_t& best_reward, uint64_t& final_fees, uint64_t& final_weight);
+119 -6
View File
@@ -19,7 +19,7 @@
#ifdef _MSC_VER
#pragma warning(disable : 4005 4061 4365 4464 4625 4626 4668 4710 4711 4804 4820 5039 5045 5220 5246)
#pragma warning(disable : 4005 4061 4324 4365 4464 4619 4625 4626 4668 4710 4711 4804 4820 5039 5045 5220 5246 5264)
#define FORCEINLINE __forceinline
#define NOINLINE __declspec(noinline)
#define LIKELY(expression) expression
@@ -81,6 +81,14 @@
#endif
#ifndef __has_feature
#define __has_feature(x) 0
#endif
#if defined(_DEBUG) || defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) || defined(__SANITIZE_THREAD__) || __has_feature(thread_sanitizer)
#define P2POOL_DEBUGGING 1
#endif
namespace p2pool {
constexpr size_t HASH_SIZE = 32;
@@ -101,6 +109,8 @@ constexpr uint8_t TX_EXTRA_MERGE_MINING_TAG = 3;
#ifdef _MSC_VER
#define umul128 _umul128
#define udiv128 _udiv128
FORCEINLINE uint64_t shiftleft128(uint64_t lo, uint64_t hi, uint64_t shift) { return __shiftleft128(lo, hi, static_cast<unsigned char>(shift)); }
FORCEINLINE uint64_t shiftright128(uint64_t lo, uint64_t hi, uint64_t shift) { return __shiftright128(lo, hi, static_cast<unsigned char>(shift)); }
#else
FORCEINLINE uint64_t umul128(uint64_t a, uint64_t b, uint64_t* hi)
{
@@ -118,13 +128,16 @@ FORCEINLINE uint64_t udiv128(uint64_t hi, uint64_t lo, uint64_t divisor, uint64_
return result;
}
FORCEINLINE uint64_t shiftleft128(uint64_t lo, uint64_t hi, uint64_t shift) { return (hi << shift) | (lo >> (64 - shift)); }
FORCEINLINE uint64_t shiftright128(uint64_t lo, uint64_t hi, uint64_t shift) { return (hi << (64 - shift)) | (lo >> shift); }
#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); }
template<typename T> constexpr FORCEINLINE T round_up(T a, size_t granularity) { return static_cast<T>(((a + (granularity - static_cast<size_t>(1))) / granularity) * granularity); }
struct hash
struct alignas(uint64_t) hash
{
alignas(8) uint8_t h[HASH_SIZE];
uint8_t h[HASH_SIZE];
FORCEINLINE hash() : h{} {}
@@ -166,7 +179,11 @@ struct hash
static_assert(sizeof(hash) == HASH_SIZE, "struct hash has invalid size, check your compiler options");
static_assert(std::is_standard_layout<hash>::value, "struct hash is not a POD, check your compiler options");
struct difficulty_type
struct
#ifdef __GNUC__
alignas(unsigned __int128)
#endif
difficulty_type
{
FORCEINLINE difficulty_type() : lo(0), hi(0) {}
FORCEINLINE difficulty_type(uint64_t a, uint64_t b) : lo(a), hi(b) {}
@@ -189,6 +206,47 @@ struct difficulty_type
return *this;
}
FORCEINLINE difficulty_type& operator+=(uint64_t b) { return operator+=(difficulty_type{ b, 0 }); }
FORCEINLINE difficulty_type& operator-=(const difficulty_type& b)
{
#ifdef _MSC_VER
_subborrow_u64(_subborrow_u64(0, lo, b.lo, &lo), hi, b.hi, &hi);
#elif __GNUC__
*reinterpret_cast<unsigned __int128*>(this) -= *reinterpret_cast<const unsigned __int128*>(&b);
#else
const uint64_t t = b.lo;
const uint64_t carry = (lo < t) ? 1 : 0;
lo -= t;
hi -= b.hi + carry;
#endif
return *this;
}
FORCEINLINE difficulty_type& operator-=(uint64_t b) { return operator-=(difficulty_type{ b, 0 }); }
FORCEINLINE difficulty_type& operator*=(const uint64_t b)
{
uint64_t t;
lo = umul128(lo, b, &t);
hi = t + hi * b;
return *this;
}
FORCEINLINE difficulty_type& operator/=(const uint64_t b)
{
const uint64_t t = hi;
hi = t / b;
uint64_t r;
lo = udiv128(t % b, lo, b, &r);
return *this;
}
difficulty_type& operator/=(difficulty_type b);
FORCEINLINE bool operator<(const difficulty_type& other) const
{
if (hi < other.hi) return true;
@@ -236,12 +294,58 @@ struct difficulty_type
static_assert(sizeof(difficulty_type) == sizeof(uint64_t) * 2, "struct difficulty_type has invalid size, check your compiler options");
static_assert(std::is_standard_layout<difficulty_type>::value, "struct difficulty_type is not a POD, check your compiler options");
difficulty_type operator+(const difficulty_type& a, const difficulty_type& b);
template<typename T>
FORCEINLINE difficulty_type operator+(const difficulty_type& a, const T& b)
{
difficulty_type result = a;
result += b;
return result;
}
template<typename T>
FORCEINLINE difficulty_type operator-(const difficulty_type& a, const T& b)
{
difficulty_type result = a;
result -= b;
return result;
}
FORCEINLINE difficulty_type operator*(const difficulty_type& a, uint64_t b)
{
difficulty_type result = a;
result *= b;
return result;
}
template<typename T>
FORCEINLINE difficulty_type operator/(const difficulty_type& a, const T& b)
{
difficulty_type result = a;
result /= b;
return result;
}
struct TxMempoolData
{
FORCEINLINE TxMempoolData() : id(), blob_size(0), weight(0), fee(0), time_received(0) {}
FORCEINLINE bool operator<(const TxMempoolData& tx) const
{
const uint64_t a = fee * tx.weight;
const uint64_t b = tx.fee * weight;
// Prefer transactions with higher fee/byte
if (a > b) return true;
if (a < b) return false;
// If fee/byte is the same, prefer smaller transactions (they give smaller penalty when going above the median block size limit)
if (weight < tx.weight) return true;
if (weight > tx.weight) return false;
// If two transactions have exactly the same fee and weight, just order them by id
return id < tx.id;
}
hash id;
uint64_t blob_size;
uint64_t weight;
@@ -325,6 +429,12 @@ struct raw_ip
return (*this == localhost_ipv4) || (*this == localhost_ipv6);
}
FORCEINLINE bool is_ipv4_prefix() const
{
alignas(8) static constexpr uint8_t ipv4_prefix[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff };
return memcmp(data, ipv4_prefix, sizeof(ipv4_prefix)) == 0;
}
};
static_assert(sizeof(raw_ip) == 16, "struct raw_ip has invalid size");
@@ -333,6 +443,9 @@ 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
+140 -79
View File
@@ -27,35 +27,61 @@
#include "side_chain.h"
#include <iostream>
#ifdef HAVE_PTHREAD_CANCEL
#include <pthread.h>
#endif
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);
#elif defined HAVE_PTHREAD_CANCEL
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");
}
@@ -67,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;
@@ -91,8 +117,8 @@ static cmd cmds[] = {
{ 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 },
{ STRCONST("outpeers"), "<N>", "set maximum number of outgoing connections", do_outpeers },
{ STRCONST("inpeers"), "<N>", "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 },
@@ -101,16 +127,15 @@ static cmd cmds[] = {
{ STRCNULL, NULL, NULL, NULL }
};
static int do_help(p2pool * /* m_pool */, const char * /* args */)
static void do_help(p2pool * /* m_pool */, const char * /* args */)
{
LOGINFO(0, "List of commands");
for (int i = 0; cmds[i].name.len; ++i) {
LOGINFO(0, cmds[i].name.str << " " << cmds[i].arg << "\t" << cmds[i].descr);
LOGINFO(0, log::pad_right(cmds[i].name.str, 20) << log::pad_right(cmds[i].arg, 12) << 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()) {
@@ -123,51 +148,45 @@ static int do_status(p2pool *m_pool, const char * /* args */)
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 = 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);
m_pool->p2p_server()->connect_to_peers_async(args);
}
return 0;
}
static int do_droppeers(p2pool *m_pool, const char * /* args */)
static void do_droppeers(p2pool *m_pool, const char * /* args */)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->drop_connections();
m_pool->p2p_server()->drop_connections_async();
}
return 0;
}
static int do_showpeers(p2pool* m_pool, const char* /* args */)
static void do_showpeers(p2pool* m_pool, const char* /* args */)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->show_peers();
m_pool->p2p_server()->show_peers_async();
}
return 0;
}
static int do_showworkers(p2pool* m_pool, const char* /* args */)
static void do_showworkers(p2pool* m_pool, const char* /* args */)
{
if (m_pool->stratum_server()) {
m_pool->stratum_server()->show_workers();
}
return 0;
}
static int do_showbans(p2pool* m_pool, const char* /* args */)
static void do_showbans(p2pool* m_pool, const char* /* args */)
{
if (m_pool->stratum_server()) {
m_pool->stratum_server()->print_bans();
@@ -175,87 +194,129 @@ static int do_showbans(p2pool* m_pool, const char* /* args */)
if (m_pool->p2p_server()) {
m_pool->p2p_server()->print_bans();
}
return 0;
}
static int do_outpeers(p2pool* m_pool, const char* args)
static void do_outpeers(p2pool* m_pool, const char* args)
{
if (m_pool->p2p_server()) {
m_pool->p2p_server()->set_max_outgoing_peers(strtoul(args, nullptr, 10));
LOGINFO(0, "max outgoing peers set to " << m_pool->p2p_server()->max_outgoing_peers());
}
return 0;
}
static int do_inpeers(p2pool* m_pool, const char* args)
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());
}
return 0;
}
#ifdef WITH_RANDOMX
static int do_start_mining(p2pool* m_pool, const char* args)
static void do_start_mining(p2pool* m_pool, const char* args)
{
uint32_t threads = strtoul(args, nullptr, 10);
threads = std::min(std::max(threads, 1u), 64u);
m_pool->start_mining(threads);
return 0;
}
static int do_stop_mining(p2pool* m_pool, const char* /*args*/)
static void do_stop_mining(p2pool* m_pool, const char* /*args*/)
{
m_pool->stop_mining();
return 0;
}
#endif
static int do_exit(p2pool *m_pool, const char * /* args */)
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) : "";
// Skip spaces
while ((args[0] == ' ') || (args[0] == '\t')) {
++args;
}
// Check if an argument is required
if (strlen(c->arg) && !strlen(args)) {
LOGWARN(0, c->name.str << " requires arguments");
do_help(nullptr, nullptr);
break;
}
c->func(pThis->m_pool, args);
break;
}
}
if (!c->name.len) {
LOGWARN(0, "Unknown command " << command.c_str());
do_help(nullptr, nullptr);
}
k = command.find_first_not_of("\r\n", k + 1);
command.erase(0, k);
} while (true);
}
else if (nread < 0) {
LOGWARN(4, "read error " << uv_err_name(static_cast<int>(nread)));
}
pThis->m_readBufInUse = false;
}
void ConsoleCommands::loop(void* data)
{
LOGINFO(1, "event loop started");
ConsoleCommands* pThis = static_cast<ConsoleCommands*>(data);
int err = uv_run(&pThis->m_loop, UV_RUN_DEFAULT);
if (err) {
LOGWARN(1, "uv_run returned " << err);
}
err = uv_loop_close(&pThis->m_loop);
if (err) {
LOGWARN(1, "uv_loop_close returned error " << uv_err_name(err));
}
LOGINFO(1, "event loop stopped");
}
} // namespace p2pool
+22 -4
View File
@@ -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
+190 -43
View File
@@ -24,6 +24,10 @@ 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 {
@@ -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"
@@ -131,54 +154,71 @@ static FORCEINLINE void derivation_to_scalar(const hash& derivation, size_t outp
hash_to_scalar(data, static_cast<int>(p - data), res);
}
class Cache
class Cache : public nocopy_nomove
{
public:
Cache()
: derivations(new DerivationsMap())
, public_keys(new PublicKeysMap())
, tx_keys(new TxKeysMap())
{
uv_mutex_init_checked(&m);
uv_rwlock_init_checked(&derivations_lock);
uv_rwlock_init_checked(&public_keys_lock);
uv_rwlock_init_checked(&tx_keys_lock);
}
~Cache()
{
uv_mutex_destroy(&m);
delete derivations;
delete public_keys;
delete tx_keys;
uv_rwlock_destroy(&derivations_lock);
uv_rwlock_destroy(&public_keys_lock);
uv_rwlock_destroy(&tx_keys_lock);
}
bool get_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag)
{
std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)> index;
std::array<uint8_t, HASH_SIZE * 2> index;
memcpy(index.data(), key1.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE, key2.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t));
derivation = {};
{
MutexLock lock(m);
auto it = derivations.find(index);
if (it != derivations.end()) {
derivation = it->second.derivation;
view_tag = it->second.view_tag;
return true;
ReadLock lock(derivations_lock);
auto it = derivations->find(index);
if (it != derivations->end()) {
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, DerivationEntry{ derivation, view_tag } );
WriteLock lock(derivations_lock);
DerivationEntry& entry = derivations->emplace(index, DerivationEntry{ derivation, { 0xFFFFFFFFUL, 0xFFFFFFFFUL }, {} }).first->second;
entry.add_view_tag(static_cast<uint32_t>(output_index << 8) | view_tag);
}
return true;
@@ -192,9 +232,9 @@ public:
memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t));
{
MutexLock lock(m);
auto it = public_keys.find(index);
if (it != public_keys.end()) {
ReadLock lock(public_keys_lock);
auto it = public_keys->find(index);
if (it != public_keys->end()) {
derived_key = it->second;
return true;
}
@@ -219,32 +259,132 @@ public:
ge_tobytes(derived_key.h, &point5);
{
MutexLock lock(m);
public_keys.emplace(index, derived_key);
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);
delete derivations;
derivations = new DerivationsMap();
derivations->reserve(5000);
}
{
WriteLock lock(public_keys_lock);
delete public_keys;
public_keys = new PublicKeysMap();
public_keys->reserve(5000);
}
{
WriteLock lock(tx_keys_lock);
delete tx_keys;
tx_keys = new TxKeysMap();
tx_keys->reserve(50);
}
}
private:
struct DerivationEntry
{
hash derivation;
// cppcheck-suppress unusedStructMember
uint8_t view_tag;
hash m_derivation;
uint32_t m_viewTags1[2] = { 0xFFFFFFFFUL, 0xFFFFFFFFUL };
std::vector<uint32_t> m_viewTags2;
FORCEINLINE bool find_view_tag(size_t output_index, uint8_t& view_tag) const
{
#define ITER(i) do { \
const uint32_t k = m_viewTags1[i]; \
if ((k >> 8) == output_index) { \
view_tag = static_cast<uint8_t>(k); \
return true; \
} \
} while(0)
ITER(0);
ITER(1);
#undef ITER
for (const uint32_t k : m_viewTags2) {
if ((k >> 8) == output_index) {
view_tag = static_cast<uint8_t>(k);
return true;
}
}
return false;
}
FORCEINLINE void add_view_tag(uint32_t k)
{
#define ITER(i) do { \
const uint32_t t = m_viewTags1[i]; \
if (t == 0xFFFFFFFFUL) { \
m_viewTags1[i] = k; \
return; \
} \
else if (t == k) { \
return; \
} \
} while (0)
ITER(0);
ITER(1);
#undef ITER
if (std::find(m_viewTags2.begin(), m_viewTags2.end(), k) == m_viewTags2.end()) {
m_viewTags2.emplace_back(k);
}
}
};
uv_mutex_t m;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, DerivationEntry> derivations;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, DerivationEntry> DerivationsMap;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> PublicKeysMap;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> TxKeysMap;
uv_rwlock_t derivations_lock;
DerivationsMap* derivations;
uv_rwlock_t public_keys_lock;
PublicKeysMap* public_keys;
uv_rwlock_t tx_keys_lock;
TxKeysMap* tx_keys;
};
static Cache* cache = nullptr;
@@ -259,6 +399,11 @@ bool derive_public_key(const hash& derivation, size_t output_index, const hash&
return cache->get_public_key(derivation, output_index, base, derived_key);
}
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";
@@ -292,7 +437,9 @@ void destroy_crypto_cache()
void clear_crypto_cache()
{
cache->clear();
if (cache) {
cache->clear();
}
}
} // namespace p2pool
+2
View File
@@ -20,6 +20,8 @@
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, 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);
+1 -1
View File
@@ -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;
}
+429 -201
View File
@@ -18,235 +18,463 @@
#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, uv_loop_t* loop)
: 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, const std::string& proxy, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
~CurlContext();
uv_tcp_init(loop ? loop : uv_default_loop_checked(), &m_socket);
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::vector<char> m_response;
std::string m_error;
curl_slist* m_headers;
};
CurlContext::CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
: m_callback(cb)
, m_closeCallback(close_cb)
, m_loop(loop)
, m_timer{}
, m_async{}
, m_multiHandle(nullptr)
, m_handle(nullptr)
, m_req(req)
, m_headers(nullptr)
{
m_pollHandles.reserve(2);
{
char buf[log::Stream::BUF_SIZE + 1];
buf[0] = '\0';
log::Stream s(buf);
s << "http://" << address << ':' << port;
if (!m_req.empty() && (m_req.front() == '/')) {
s << m_req.c_str() << '\0';
m_req.clear();
}
else {
s << "/json_rpc\0";
}
m_url = buf;
}
int err = uv_timer_init(m_loop, &m_timer);
if (err) {
LOGERR(1, "uv_timer_init failed, error " << uv_err_name(err));
throw std::runtime_error("uv_timer_init failed");
}
m_timer.data = this;
err = uv_async_init(m_loop, &m_async, reinterpret_cast<uv_async_cb>(on_timeout));
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
throw std::runtime_error("uv_async_init failed");
}
m_async.data = this;
m_multiHandle = curl_multi_init();
if (!m_multiHandle) {
static constexpr char msg[] = "curl_multi_init() failed";
LOGERR(1, msg);
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
throw std::runtime_error(msg);
}
#define curl_multi_setopt_checked(...) \
do { \
const CURLMcode r = curl_multi_setopt(__VA_ARGS__); \
if (r != CURLM_OK) { \
static constexpr char msg[] = "curl_multi_setopt(" #__VA_ARGS__ ") failed"; \
LOGERR(1, msg << ": " << curl_multi_strerror(r)); \
throw std::runtime_error(msg); \
} \
} while (0)
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_SOCKETFUNCTION, socket_func);
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_SOCKETDATA, this);
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_TIMERFUNCTION, timer_func);
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_TIMERDATA, this);
m_handle = curl_easy_init();
if (!m_handle) {
static constexpr char msg[] = "curl_easy_init() failed";
LOGERR(1, msg);
curl_multi_cleanup(m_multiHandle);
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
throw std::runtime_error(msg);
}
#define curl_easy_setopt_checked(...) \
do { \
const CURLcode r = curl_easy_setopt(__VA_ARGS__); \
if (r != CURLE_OK) { \
static constexpr char msg[] = "curl_easy_setopt(" #__VA_ARGS__ ") failed"; \
LOGERR(1, msg << ": " << curl_easy_strerror(r)); \
throw std::runtime_error(msg); \
} \
} while (0)
curl_easy_setopt_checked(m_handle, CURLOPT_WRITEFUNCTION, write_func);
curl_easy_setopt_checked(m_handle, CURLOPT_WRITEDATA, this);
const int timeout = proxy.empty() ? 1 : 5;
curl_easy_setopt_checked(m_handle, CURLOPT_URL, m_url.c_str());
curl_easy_setopt_checked(m_handle, CURLOPT_POSTFIELDS, m_req.c_str());
curl_easy_setopt_checked(m_handle, CURLOPT_CONNECTTIMEOUT, timeout);
curl_easy_setopt_checked(m_handle, CURLOPT_TIMEOUT, timeout * 10);
m_headers = curl_slist_append(m_headers, "Content-Type: application/json");
if (m_headers) {
curl_easy_setopt_checked(m_handle, CURLOPT_HTTPHEADER, m_headers);
}
if (!auth.empty()) {
curl_easy_setopt_checked(m_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_ONLY);
curl_easy_setopt_checked(m_handle, CURLOPT_USERPWD, auth.c_str());
}
if (!proxy.empty()) {
if (is_localhost(address)) {
LOGINFO(5, "not using proxy to connect to localhost address " << log::Gray() << address);
}
else {
curl_easy_setopt_checked(m_handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
curl_easy_setopt_checked(m_handle, CURLOPT_PROXY, proxy.c_str());
}
}
CURLMcode curl_err = curl_multi_add_handle(m_multiHandle, m_handle);
if (curl_err != CURLM_OK) {
LOGERR(1, "curl_multi_add_handle failed, error " << curl_multi_strerror(curl_err));
curl_easy_cleanup(m_handle);
curl_multi_cleanup(m_multiHandle);
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
throw std::runtime_error("curl_multi_add_handle failed");
}
}
CurlContext::~CurlContext()
{
if (m_error.empty() && !m_response.empty()) {
(*m_callback)(m_response.data(), m_response.size());
}
delete m_callback;
if (m_response.empty()) {
if (m_error.empty()) {
m_error = "empty response";
}
else {
m_error += " (empty response)";
}
}
(*m_closeCallback)(m_error.c_str(), m_error.length());
delete m_closeCallback;
curl_slist_free_all(m_headers);
}
int CurlContext::on_socket(CURL* /*easy*/, curl_socket_t s, int action)
{
auto it = std::find_if(m_pollHandles.begin(), m_pollHandles.end(), [s](const auto& value) { return value.first == s; });
switch (action) {
case CURL_POLL_IN:
case CURL_POLL_OUT:
case CURL_POLL_INOUT:
{
uv_poll_t* h = nullptr;
if (it != m_pollHandles.end()) {
h = it->second;
}
else {
h = new uv_poll_t{};
// cppcheck-suppress nullPointer
h->data = this;
const int result = uv_poll_init_socket(m_loop, h, s);
if (result < 0) {
LOGERR(1, "uv_poll_init_socket failed: " << uv_err_name(result));
delete h;
h = nullptr;
}
else {
m_pollHandles.emplace_back(s, h);
}
}
if (h) {
const CURLMcode err = curl_multi_assign(m_multiHandle, s, this);
if (err != CURLM_OK) {
LOGERR(1, "curl_multi_assign(action = " << action << ") failed: " << curl_multi_strerror(err));
}
int events = 0;
if (action != CURL_POLL_IN) events |= UV_WRITABLE;
if (action != CURL_POLL_OUT) events |= UV_READABLE;
const int result = uv_poll_start(h, events, curl_perform);
if (result < 0) {
LOGERR(1, "uv_poll_start failed with error " << uv_err_name(result));
}
}
else {
LOGERR(1, "failed to start polling on socket " << static_cast<int>(s));
}
}
break;
case CURL_POLL_REMOVE:
default:
{
if (it != m_pollHandles.end()) {
uv_poll_t* h = it->second;
m_pollHandles.erase(it);
uv_poll_stop(h);
uv_close(reinterpret_cast<uv_handle_t*>(h), [](uv_handle_t* h) { delete reinterpret_cast<uv_poll_t*>(h); });
}
const CURLMcode err = curl_multi_assign(m_multiHandle, s, nullptr);
if (err != CURLM_OK) {
LOGERR(1, "curl_multi_assign(action = " << action << ") failed: " << curl_multi_strerror(err));
}
}
break;
}
return 0;
}
int CurlContext::on_timer(CURLM* /*multi*/, long timeout_ms)
{
if (timeout_ms < 0) {
uv_timer_stop(&m_timer);
return 0;
}
if (timeout_ms == 0) {
// 0 ms timeout, but we can't just call on_timeout() here - we have to kick the UV loop
const int result = uv_async_send(&m_async);
if (result < 0) {
LOGERR(1, "uv_async_send failed with error " << uv_err_name(result));
// if async call didn't work, try to use the timer with 1 ms timeout
timeout_ms = 1;
}
else {
return 0;
}
}
const int result = uv_timer_start(&m_timer, reinterpret_cast<uv_timer_cb>(on_timeout), timeout_ms, 0);
if (result < 0) {
LOGERR(1, "uv_timer_start failed with error " << uv_err_name(result));
return -1;
}
return 0;
}
void CurlContext::on_timeout(uv_handle_t* req)
{
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
int running_handles = 0;
const CURLMcode err = curl_multi_socket_action(ctx->m_multiHandle, CURL_SOCKET_TIMEOUT, 0, &running_handles);
if (err != CURLM_OK) {
LOGERR(1, "curl_multi_socket_action failed, error " << curl_multi_strerror(err));
}
ctx->check_multi_info();
if (running_handles == 0) {
ctx->shutdown();
}
}
size_t CurlContext::on_write(const void* buffer, size_t size, size_t count)
{
const size_t realsize = size * count;
const char* p = reinterpret_cast<const char*>(buffer);
m_response.insert(m_response.end(), p, p + realsize);
return realsize;
}
void CurlContext::curl_perform(uv_poll_t* req, int status, int events)
{
int flags = 0;
if (status < 0) {
flags |= CURL_CSELECT_ERR;
LOGERR(1, "uv_poll_start returned error " << uv_err_name(status));
}
else {
if (events & UV_READABLE) flags |= CURL_CSELECT_IN;
if (events & UV_WRITABLE) flags |= CURL_CSELECT_OUT;
}
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
int running_handles = 0;
auto it = std::find_if(ctx->m_pollHandles.begin(), ctx->m_pollHandles.end(), [req](const auto& value) { return value.second == req; });
if (it != ctx->m_pollHandles.end()) {
const CURLMcode err = curl_multi_socket_action(ctx->m_multiHandle, it->first, flags, &running_handles);
if (err != CURLM_OK) {
LOGERR(1, "curl_multi_socket_action failed, error " << curl_multi_strerror(err));
}
}
ctx->check_multi_info();
if (running_handles == 0) {
ctx->shutdown();
}
}
void CurlContext::check_multi_info()
{
int pending;
while (CURLMsg* message = curl_multi_info_read(m_multiHandle, &pending)) {
if (message->msg == CURLMSG_DONE) {
if (message->data.result != CURLE_OK) {
m_error = curl_easy_strerror(message->data.result);
}
else {
long http_code = 0;
curl_easy_getinfo(message->easy_handle, CURLINFO_RESPONSE_CODE, &http_code);
if (http_code != 200) {
char buf[32] = {};
log::Stream s(buf);
s << "HTTP error " << static_cast<int>(http_code) << '\0';
m_error = buf;
}
else if (m_response.empty()) {
m_error = "empty response";
}
}
curl_multi_remove_handle(m_multiHandle, m_handle);
curl_easy_cleanup(m_handle);
curl_multi_cleanup(m_multiHandle);
return;
}
}
}
m_socket.data = this;
m_connect.data = this;
m_write.data = this;
void CurlContext::on_close(uv_handle_t* h)
{
CurlContext* ctx = reinterpret_cast<CurlContext*>(h->data);
h->data = nullptr;
const char* uri = "/json_rpc";
size_t len = req ? strlen(req) : 0;
if (!len) {
LOGERR(1, "Empty JSONRPCRequest, fix the code!");
m_valid = false;
if (ctx->m_timer.data || ctx->m_async.data) {
return;
}
if (req[0] == '/') {
uri = req;
len = 0;
delete ctx;
}
void CurlContext::shutdown()
{
for (const auto& p : m_pollHandles) {
uv_poll_stop(p.second);
uv_close(reinterpret_cast<uv_handle_t*>(p.second), [](uv_handle_t* h) { delete reinterpret_cast<uv_poll_t*>(h); });
}
m_pollHandles.clear();
if (m_async.data && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_async))) {
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), on_close);
}
m_request.reserve(std::max<size_t>(len + 128, log::Stream::BUF_SIZE + 1));
m_request.resize(log::Stream::BUF_SIZE + 1);
log::Stream s(m_request.data(), m_request.size());
s << "POST " << uri << " HTTP/1.1\nContent-Type: application/json\nContent-Length: " << len << "\n\n";
m_request.resize(s.m_pos);
m_request.insert(m_request.end(), req, req + len);
m_response.reserve(sizeof(m_readBuf));
const int err = uv_tcp_connect(&m_connect, &m_socket, reinterpret_cast<const sockaddr*>(&addr), on_connect);
if (err) {
LOGERR(1, "failed to initiate tcp connection to " << address << ", error " << uv_err_name(err));
m_valid = false;
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_connect(uv_connect_t* req, int status)
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(req->data);
if (status != 0) {
pThis->m_error = uv_err_name(status);
LOGERR(1, "failed to connect, error " << pThis->m_error);
pThis->close();
return;
if (!loop) {
loop = uv_default_loop();
}
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);
}
void JSONRPCRequest::on_write(uv_write_t* handle, int status)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(handle->data);
if (status != 0) {
pThis->m_error = uv_err_name(status);
LOGERR(1, "failed to send request, error " << pThis->m_error);
pThis->close();
return;
}
uv_read_start(reinterpret_cast<uv_stream_t*>(&pThis->m_socket), on_alloc, on_read);
}
void JSONRPCRequest::on_alloc(uv_handle_t* handle, size_t /*suggested_size*/, uv_buf_t* buf)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(handle->data);
if (pThis->m_readBufInUse) {
LOGERR(1, "read buffer is already in use");
}
buf->len = sizeof(pThis->m_readBuf);
buf->base = pThis->m_readBuf;
pThis->m_readBufInUse = true;
}
void JSONRPCRequest::on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(stream->data);
pThis->m_readBufInUse = false;
if (nread > 0) {
pThis->on_read(buf->base, nread);
}
else if (nread < 0) {
if (nread != UV_EOF){
pThis->m_error = uv_err_name(static_cast<int>(nread));
LOGERR(1, "failed to read response, error " << pThis->m_error);
}
pThis->close();
}
}
void JSONRPCRequest::on_read(const char* data, size_t size)
{
m_response.append(data, size);
static constexpr char headers_end[] = "\r\n\r\n";
if (m_response.find(headers_end) == std::string::npos) {
return;
}
llhttp_settings_t settings{};
settings.on_status = [](llhttp_t*, const char* at, size_t length)
{
if ((length == 2) && (!memcmp(at, "Ok", 2) || !memcmp(at, "OK", 2))) {
return 0;
}
return -1;
};
settings.on_header_field = [](llhttp_t* parser, const char* at, size_t length)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(parser->data);
static const char header[] = "Content-Length";
pThis->m_contentLengthHeader = ((length == sizeof(header) - 1) && (memcmp(at, header, length) == 0));
return 0;
};
settings.on_header_value = [](llhttp_t* parser, const char* at, size_t length)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(parser->data);
if (pThis->m_contentLengthHeader) {
uint32_t k = 0;
for (const char* p = at; p < at + length; ++p) {
if ('0' <= *p && *p <= '9') {
k = k * 10 + (*p - '0');
}
else {
return -1;
}
const bool result = CallOnLoop(loop,
[=]()
{
try {
new CurlContext(address, port, req, auth, proxy, 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
+24 -70
View File
@@ -18,78 +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, nullptr);
if (!r->m_valid) {
delete r;
}
}
template<typename T, typename U>
static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr)
{
// It will be deleted in one of the tcp callbacks eventually
CallbackBase* close_callback = new Callback<U>(std::move(close_cb));
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), close_callback, loop);
if (!r->m_valid) {
constexpr char err[] = "internal error";
(*close_callback)(err, sizeof(err) - 1);
delete r;
}
}
private:
struct CallbackBase
{
virtual ~CallbackBase() {}
virtual void operator()(const char* data, size_t size) = 0;
};
template<typename T>
struct Callback : public CallbackBase
{
explicit FORCEINLINE Callback(T&& cb) : m_cb(std::move(cb)) {}
void operator()(const char* data, size_t size) override { m_cb(data, size); }
private:
Callback& operator=(Callback&&) = delete;
T m_cb;
};
JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
~JSONRPCRequest();
static void on_connect(uv_connect_t* req, int status);
static void on_write(uv_write_t* handle, int status);
static void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf);
void on_read(const char* data, size_t size);
// cppcheck-suppress functionConst
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, const std::string& proxy, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
template<typename T, typename U>
FORCEINLINE void call(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr)
{
Call(address, port, req, auth, proxy, new Callback<T>(std::move(cb)), new Callback<U>(std::move(close_cb)), loop);
}
} // namespace JSONRPCRequest
} // namespace p2pool
+76 -15
View File
@@ -17,9 +17,15 @@
#include "common.h"
#include "uv_util.h"
#include "wallet.h"
#include <ctime>
#include <fstream>
#include <thread>
#include <stdlib.h>
#ifdef _MSC_VER
#pragma warning(disable : 4996)
#endif
static constexpr char log_category_prefix[] = "Log ";
static constexpr char log_file_name[] = "p2pool.log";
@@ -33,9 +39,6 @@ bool CONSOLE_COLORS = true;
#ifndef P2POOL_LOG_DISABLE
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);
@@ -54,6 +57,8 @@ public:
FORCEINLINE Worker()
: m_writePos(0)
, m_readPos(0)
, m_started{ false }
, m_stopped(false)
{
set_main_thread();
@@ -73,25 +78,31 @@ public:
abort();
}
while (!worker_started) {
while (m_started.load() == false) {
std::this_thread::yield();
}
#ifdef _WIN32
SetConsoleMode(hStdIn, ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_EXTENDED_FLAGS);
DWORD dwConsoleMode;
if (GetConsoleMode(hStdIn, &dwConsoleMode)) {
SetConsoleMode(hStdIn, dwConsoleMode & ~ENABLE_QUICK_EDIT_MODE);
}
if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
SetConsoleMode(hStdOut, dwConsoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
#endif
const char* no_color = getenv("NO_COLOR");
if (no_color && *no_color) {
CONSOLE_COLORS = false;
}
LOGINFO(0, "started");
if (!m_logFile.is_open()) {
LOGERR(0, "failed to open " << log_file_name);
}
init_uv_threadpool();
}
~Worker()
@@ -105,16 +116,25 @@ public:
FORCEINLINE void stop()
{
if (stopped.exchange(true)) {
return;
{
MutexLock lock(m_mutex);
if (m_stopped) {
return;
}
m_stopped = true;
LOGINFO(0, "stopped");
}
LOGINFO(0, "stopped");
uv_thread_join(&m_worker);
uv_cond_destroy(&m_cond);
uv_mutex_destroy(&m_mutex);
uv_loop_close(uv_default_loop());
#if ((UV_VERSION_MAJOR > 1) || ((UV_VERSION_MAJOR == 1) && (UV_VERSION_MINOR >= 38)))
uv_library_shutdown();
#endif
m_logFile.close();
}
@@ -143,17 +163,47 @@ public:
uv_cond_signal(&m_cond);
}
private:
static void init_uv_threadpool()
{
#ifdef _MSC_VER
#define putenv _putenv
#endif
const uint32_t N = std::min(std::max(std::thread::hardware_concurrency(), 4U), 8U);
char buf[40] = {};
log::Stream s(buf);
s << "UV_THREADPOOL_SIZE=" << N << '\0';
int err = putenv(buf);
if (err != 0) {
err = errno;
LOGWARN(0, "Couldn't set UV thread pool size to " << N << " threads, putenv returned error " << err);
}
static uv_work_t dummy;
err = uv_queue_work(uv_default_loop_checked(), &dummy, [](uv_work_t*) {}, nullptr);
if (err) {
LOGERR(0, "init_uv_threadpool: uv_queue_work failed, error " << uv_err_name(err));
}
}
private:
static void run_wrapper(void* arg) { reinterpret_cast<Worker*>(arg)->run(); }
NOINLINE void run()
{
worker_started = true;
m_started.exchange(true);
do {
uv_mutex_lock(&m_mutex);
if (m_readPos == m_writePos.load()) {
// Nothing to do, wait for the signal
// Nothing to do, wait for the signal or exit if stopped
if (m_stopped) {
uv_mutex_unlock(&m_mutex);
return;
}
uv_cond_wait(&m_cond, &m_mutex);
}
uv_mutex_unlock(&m_mutex);
@@ -217,7 +267,7 @@ private:
severity = '\0';
m_readPos += SLOT_SIZE;
} while (m_readPos < writePos);
} while (m_readPos != writePos);
}
// Flush the log file only after all pending log lines have been written
@@ -231,7 +281,7 @@ private:
m_logFile.open(log_file_name, std::ios::app | std::ios::binary);
}
}
} while (!stopped);
} while (1);
}
static FORCEINLINE void strip_colors(char* buf, uint32_t& size)
@@ -269,6 +319,9 @@ private:
uv_mutex_t m_mutex;
uv_thread_t m_worker;
std::atomic<bool> m_started;
bool m_stopped;
std::ofstream m_logFile;
};
@@ -278,6 +331,7 @@ static Worker worker;
NOINLINE Writer::Writer(Severity severity) : Stream(m_stackBuf)
{
m_stackBuf[BUF_SIZE] = '\0';
m_buf[0] = static_cast<char>(severity);
m_pos = 3;
@@ -336,7 +390,7 @@ NOINLINE void Stream::writeCurrentTime()
m_numberWidth = 1;
}
NOINLINE void put_rawip(const raw_ip& value, Stream* wrapper)
NOINLINE void Stream::Entry<raw_ip>::put(const raw_ip& value, Stream* wrapper)
{
const char* addr_str;
char addr_str_buf[64];
@@ -359,6 +413,13 @@ NOINLINE void put_rawip(const raw_ip& value, Stream* wrapper)
}
}
NOINLINE void Stream::Entry<Wallet>::put(const Wallet& w, Stream* wrapper)
{
char buf[Wallet::ADDRESS_LENGTH];
w.encode(buf);
wrapper->writeBuf(buf, Wallet::ADDRESS_LENGTH);
}
} // namespace log
} // namespace p2pool
+4 -6
View File
@@ -19,6 +19,8 @@
namespace p2pool {
class Wallet;
namespace log {
extern int GLOBAL_LOG_LEVEL;
@@ -451,12 +453,8 @@ struct log::Stream::Entry<PadRight<T>>
}
};
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); }
};
template<> struct log::Stream::Entry<raw_ip> { static NOINLINE void put(const raw_ip& value, Stream* wrapper); };
template<> struct log::Stream::Entry<Wallet> { static NOINLINE void put(const Wallet& w, Stream* wrapper); };
namespace {
template<log::Severity severity> void apply_severity(log::Stream&);
+14 -4
View File
@@ -20,6 +20,7 @@
#include "p2pool.h"
#include "stratum_server.h"
#include "p2p_server.h"
#include <curl/curl.h>
void p2pool_usage()
{
@@ -41,11 +42,15 @@ void p2pool_usage()
"--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"
"--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 450)\n"
"--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 450)\n"
"--start-mining N Start built-in miner using N threads (any value between 1 and 64)\n"
"--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d\n"
"--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum\n"
"--rpc-login Specify username[:password] required for Monero RPC server\n"
"--socks5 Specify IP:port of a SOCKS5 proxy to use for outgoing connections\n"
"--no-dns disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too)\n"
"--p2p-external-port port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections\n"
"--help Show this help message\n\n"
"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",
@@ -80,12 +85,15 @@ int main(int argc, char* argv[])
}
}
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();
@@ -94,6 +102,8 @@ int main(int argc, char* argv[])
result = 1;
}
curl_global_cleanup();
p2pool::destroy_crypto_cache();
memory_tracking_stop();
+117 -34
View File
@@ -32,15 +32,48 @@ namespace p2pool {
static bool track_memory = false;
constexpr size_t N = 1048576;
constexpr size_t MAX_FRAMES = 30;
constexpr size_t N = 1 << 22;
constexpr size_t MAX_FRAMES = 29;
struct TrackedAllocation
{
void* p;
void* stack_trace[MAX_FRAMES];
uint64_t allocated_size;
uint32_t thread_id;
uint32_t allocated_size;
FORCEINLINE bool operator<(const TrackedAllocation& rhs) { return memcmp(stack_trace, rhs.stack_trace, sizeof(stack_trace)) < 0; }
FORCEINLINE bool operator==(const TrackedAllocation& rhs) { return memcmp(stack_trace, rhs.stack_trace, sizeof(stack_trace)) == 0; }
void print(HANDLE h) const
{
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = {};
PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
IMAGEHLP_LINE64 line{};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
for (size_t j = 0; j < MAX_FRAMES; ++j) {
const DWORD64 address = reinterpret_cast<DWORD64>(stack_trace[j]);
DWORD64 t1 = 0;
DWORD t2 = 0;
if (SymFromAddr(h, address, &t1, pSymbol) && SymGetLineFromAddr64(h, address, &t2, &line)) {
const char* s = line.FileName;
const char* file_name = nullptr;
while (*s) {
if ((*s == '\\') || (*s == '/')) {
file_name = s + 1;
}
++s;
}
printf("%-25s %s (line %lu)\n", file_name ? file_name : line.FileName, pSymbol->Name, line.LineNumber);
}
}
printf("\n");
}
};
static_assert(sizeof(TrackedAllocation) == 256, "");
@@ -51,15 +84,62 @@ uint32_t first[N];
uint32_t next[N];
TrackedAllocation allocations[N];
uint32_t num_allocations = 0;
uint64_t total_allocated = 0;
uint32_t cur_allocation_index = 1;
void show_top_10()
{
TrackedAllocation* buf = reinterpret_cast<TrackedAllocation*>(VirtualAlloc(nullptr, sizeof(TrackedAllocation) * N, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
if (!buf) {
return;
}
const HANDLE h = GetCurrentProcess();
{
p2pool::MutexLock lock(allocation_lock);
TrackedAllocation* end = buf;
for (size_t i = 0; i < N; ++i) {
if (allocations[i].allocated_size) {
*(end++) = allocations[i];
}
}
std::sort(buf, end);
TrackedAllocation* prev = buf;
for (TrackedAllocation* p = buf + 1; p < end; ++p) {
if (*p == *prev) {
prev->allocated_size += p->allocated_size;
}
else {
++prev;
*prev = *p;
}
}
end = prev + 1;
std::sort(buf, end, [](const auto& a, const auto& b) { return a.allocated_size > b.allocated_size; });
printf("%I64u total bytes allocated\n", total_allocated);
for (TrackedAllocation* p = buf; (p < buf + 10) && (p < end); ++p) {
printf("%I64u bytes allocated at:\n", p->allocated_size);
p->print(h);
}
}
VirtualFree(buf, 0, MEM_RELEASE);
}
FORCEINLINE static void add_alocation(void* p, size_t size)
{
if (!track_memory) {
return;
}
void* stack_trace[MAX_FRAMES];
void* stack_trace[MAX_FRAMES] = {};
DWORD hash;
CaptureStackBackTrace(1, MAX_FRAMES, stack_trace, &hash);
@@ -74,6 +154,7 @@ FORCEINLINE static void add_alocation(void* p, size_t size)
// Make N two times bigger if this triggers
__debugbreak();
}
total_allocated += size;
for (uint64_t i = cur_allocation_index;; i = (i + 1) & (N - 1)) {
if (i && !allocations[i].allocated_size) {
@@ -105,6 +186,7 @@ FORCEINLINE static void remove_allocation(void* p)
for (uint32_t prev = 0, k = first[index]; k != 0; prev = k, k = next[k]) {
if (allocations[k].p == p) {
total_allocated -= allocations[k].allocated_size;
allocations[k].allocated_size = 0;
if (prev) {
next[prev] = next[k];
@@ -144,6 +226,19 @@ void free_hook(void* p) noexcept
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);
@@ -168,6 +263,8 @@ void* calloc_hook(size_t count, size_t size) noexcept
void memory_tracking_start()
{
SymInitialize(GetCurrentProcess(), NULL, TRUE);
using namespace p2pool;
uv_replace_allocator(malloc_hook, realloc_hook, calloc_hook, free_hook);
@@ -183,47 +280,24 @@ void memory_tracking_stop()
uv_mutex_destroy(&allocation_lock);
const HANDLE h = GetCurrentProcess();
SymInitialize(h, NULL, TRUE);
uint64_t total_leaks = 0;
for (uint32_t i = 0; i < N; ++i) {
if (allocations[i].allocated_size) {
total_leaks += allocations[i].allocated_size;
const TrackedAllocation& t = allocations[i];
if (t.allocated_size) {
total_leaks += t.allocated_size;
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = {};
PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
IMAGEHLP_LINE64 line{};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
printf("Memory leak detected, %u bytes allocated by thread %u at:\n", allocations[i].allocated_size, allocations[i].thread_id);
for (size_t j = 0; j < MAX_FRAMES; ++j) {
const DWORD64 address = reinterpret_cast<DWORD64>(allocations[i].stack_trace[j]);
DWORD64 t1 = 0;
DWORD t2 = 0;
if (SymFromAddr(h, address, &t1, pSymbol) && SymGetLineFromAddr64(h, address, &t2, &line)) {
const char* s = line.FileName;
const char* file_name = nullptr;
while (*s) {
if ((*s == '\\') || (*s == '/')) {
file_name = s + 1;
}
++s;
}
printf("%-25s %s (line %lu)\n", file_name ? file_name : line.FileName, pSymbol->Name, line.LineNumber);
}
}
printf("\n");
printf("Memory leak detected, %I64u bytes allocated at %p by thread %u:\n", t.allocated_size, t.p, t.thread_id);
t.print(h);
}
}
if (total_leaks > 0) {
printf("%I64u bytes leaked\n\n", total_leaks);
}
SymCleanup(h);
}
NOINLINE void* operator new(size_t n) { return p2pool::allocate(n); }
@@ -248,5 +322,14 @@ void* realloc_hook(void* ptr, size_t size) noexcept { return realloc(ptr, size);
void* calloc_hook(size_t count, size_t size) noexcept { return calloc(count, size); }
void 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
View File
@@ -21,6 +21,8 @@
namespace p2pool {
constexpr uint64_t HIGH_FEE_VALUE = 6000000000ULL;
class p2pool;
class Mempool : public nocopy_nomove
+18 -9
View File
@@ -25,6 +25,7 @@
#include "params.h"
#include "p2pool_api.h"
#include "side_chain.h"
#include "p2p_server.h"
#include <thread>
static constexpr char log_category_prefix[] = "Miner ";
@@ -40,12 +41,14 @@ Miner::Miner(p2pool* pool, uint32_t threads)
, 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 }
{
std::random_device rd;
m_extraNonce = static_cast<uint32_t>(rd());
on_block(m_pool->block_template());
m_minerThreads.reserve(threads);
@@ -80,8 +83,9 @@ void Miner::print_status()
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)
"\nThreads = " << m_threads <<
"\nHashrate = " << log::Hashrate(hr) <<
"\nShares found = " << m_sharesFound.load()
);
}
@@ -90,7 +94,7 @@ 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);
j.m_blobSize = block.get_hashing_blob(m_extraNonce, j.m_blob, j.m_height, j.m_sidechainHeight, j.m_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
const uint32_t hash_count = 0 - m_nonce.exchange(0);
m_jobIndex = next_index;
@@ -111,7 +115,7 @@ void Miner::on_block(const BlockTemplate& block)
const double time_running = static_cast<double>(duration_cast<milliseconds>(cur_ts - m_startTimestamp).count()) / 1e3;
s << "{\"current_hashrate\":" << hr
<< ",\"total_hashes\":" << m_totalHashes
<< ",\"total_hashes\":" << m_totalHashes.load()
<< ",\"time_running\":" << time_running
<< ",\"shares_found\":" << m_sharesFound.load()
<< ",\"block_reward_share_percent\":" << block_reward_share_percent
@@ -121,6 +125,12 @@ void Miner::on_block(const BlockTemplate& block)
}
}
void Miner::reset_share_counters()
{
m_totalHashes = 0;
m_sharesFound = 0;
}
void Miner::run(void* data)
{
WorkerData* d = static_cast<WorkerData*>(data);
@@ -196,15 +206,14 @@ void Miner::run(WorkerData* data)
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");
LOGINFO(0, log::Green() << "worker thread " << data->m_index << '/' << data->m_count << " found a mainchain block at height " << j.m_height << ", submitting it");
m_pool->submit_block_async(j.m_templateId, j.m_nonce, j.m_extraNonce);
m_pool->block_template().update_tx_keys();
}
if (j.m_sidechainDiff.check_pow(h)) {
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << j.m_height << ", diff " << j.m_sidechainDiff << ", worker thread " << data->m_index << '/' << data->m_count);
m_pool->submit_sidechain_block(j.m_templateId, j.m_nonce, j.m_extraNonce);
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << j.m_height << ", sidechain height " << j.m_sidechainHeight << ", diff " << j.m_sidechainDiff << ", worker thread " << data->m_index << '/' << data->m_count);
++m_sharesFound;
m_pool->submit_sidechain_block(j.m_templateId, j.m_nonce, j.m_extraNonce);
}
std::this_thread::yield();
+4 -2
View File
@@ -33,6 +33,7 @@ public:
void print_status();
void on_block(const BlockTemplate& block);
void reset_share_counters();
private:
static void run(void* data);
@@ -55,9 +56,9 @@ private:
std::atomic<uint32_t> m_nonce;
std::chrono::high_resolution_clock::time_point m_nonceTimestamp;
const uint32_t m_extraNonce;
uint32_t m_extraNonce;
uint64_t m_totalHashes;
std::atomic<uint64_t> m_totalHashes;
std::atomic<uint32_t> m_sharesFound;
struct Job
@@ -68,6 +69,7 @@ private:
difficulty_type m_diff = {};
difficulty_type m_sidechainDiff = {};
uint64_t m_height = 0;
uint64_t m_sidechainHeight = 0;
size_t m_nonceOffset = 0;
uint32_t m_nonce = 0;
uint32_t m_extraNonce = 0;
+504 -216
View File
File diff suppressed because it is too large Load Diff
+55 -12
View File
@@ -18,6 +18,7 @@
#pragma once
#include "tcp_server.h"
#include <list>
namespace p2pool {
@@ -30,6 +31,11 @@ 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;
static constexpr uint32_t PROTOCOL_VERSION_1_0 = 0x00010000UL;
static constexpr uint32_t PROTOCOL_VERSION_1_1 = 0x00010001UL;
static constexpr uint32_t SUPPORTED_PROTOCOL_VERSION = PROTOCOL_VERSION_1_1;
class P2PServer : public TCPServer<P2P_BUF_SIZE, P2P_BUF_SIZE>
{
public:
@@ -42,6 +48,7 @@ public:
BLOCK_BROADCAST = 5,
PEER_LIST_REQUEST = 6,
PEER_LIST_RESPONSE = 7,
BLOCK_BROADCAST_COMPACT = 8,
};
explicit P2PServer(p2pool *pool);
@@ -51,6 +58,7 @@ public:
void clear_cached_blocks();
void store_in_cache(const PoolBlock& block);
void connect_to_peers_async(const char* peer_list);
void connect_to_peers(const std::string& peer_list);
void on_connect_failed(bool is_v6, const raw_ip& ip, int port) override;
@@ -92,16 +100,23 @@ public:
void on_after_handshake(uint8_t* &p);
bool on_listen_port(const uint8_t* buf);
bool on_block_request(const uint8_t* buf);
bool on_block_response(const uint8_t* buf, uint32_t size);
bool on_block_broadcast(const uint8_t* buf, uint32_t size);
bool on_block_response(const uint8_t* buf, uint32_t size, const hash& expected_id);
bool on_block_broadcast(const uint8_t* buf, uint32_t size, bool compact);
bool on_peer_list_request(const uint8_t* buf);
bool on_peer_list_response(const uint8_t* buf) const;
bool on_peer_list_response(const uint8_t* buf);
bool handle_incoming_block_async(PoolBlock* block);
bool handle_incoming_block_async(const PoolBlock* block, uint64_t max_time_delta = 0);
void handle_incoming_block(p2pool* pool, PoolBlock& block, const uint32_t reset_counter, const raw_ip& addr, std::vector<hash>& missing_blocks);
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); }
const char* software_name() const;
uint64_t m_peerId;
uint64_t m_connectedTime;
uint64_t m_broadcastMaxHeight;
MessageId m_expectedMessage;
uint64_t m_handshakeChallenge;
bool m_handshakeSolutionSent;
@@ -114,10 +129,14 @@ public:
uint64_t m_nextOutgoingPeerListRequest;
std::chrono::high_resolution_clock::time_point m_lastPeerListRequestTime;
int m_peerListPendingRequests;
uint32_t m_protocolVersion;
uint32_t m_SoftwareVersion;
uint32_t m_SoftwareID;
int64_t m_pingTime;
int m_blockPendingRequests;
bool m_chainTipBlockRequest;
std::list<hash> m_blockPendingRequests;
uint64_t m_lastAlive;
uint64_t m_lastBroadcastTimestamp;
@@ -127,19 +146,24 @@ public:
std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 };
};
void broadcast(const PoolBlock& block);
void broadcast(const PoolBlock& block, const PoolBlock* parent);
uint64_t get_random64();
uint64_t get_peerId() const { return m_peerId; }
void print_status() override;
void show_peers();
void show_peers_async();
size_t peer_list_size() const { MutexLock lock(m_peerListLock); return m_peerList.size(); }
int listen_port() const override;
uint32_t max_outgoing_peers() const { return m_maxOutgoingPeers; }
uint32_t max_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); }
void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 450U); }
void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 450U); }
int deserialize_block(const uint8_t* buf, uint32_t size, bool compact);
const PoolBlock* get_block() const { return m_block; }
private:
p2pool* m_pool;
@@ -159,8 +183,10 @@ private:
void flush_cache();
void download_missing_blocks();
void check_zmq();
void check_block_template();
void update_peer_connections();
void update_peer_list();
void send_peer_list_request(P2PClient* client, uint64_t cur_time);
void save_peer_list_async();
void save_peer_list();
void load_peer_list();
@@ -169,11 +195,12 @@ private:
void remove_peer_from_list(P2PClient* client);
void remove_peer_from_list(const raw_ip& ip);
uv_mutex_t m_rngLock;
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;
@@ -200,6 +227,7 @@ private:
{
std::vector<uint8_t> blob;
std::vector<uint8_t> pruned_blob;
std::vector<uint8_t> compact_blob;
std::vector<hash> ancestor_hashes;
};
@@ -207,11 +235,26 @@ private:
uv_async_t m_broadcastAsync;
std::vector<Broadcast*> m_broadcastQueue;
uv_mutex_t m_missingBlockRequestsLock;
bool m_lookForMissingBlocks;
unordered_set<std::pair<uint64_t, uint64_t>> m_missingBlockRequests;
P2PClient* m_fastestPeer;
static void on_broadcast(uv_async_t* handle) { reinterpret_cast<P2PServer*>(handle->data)->on_broadcast(); }
void on_broadcast();
uv_mutex_t m_connectToPeersLock;
uv_async_t m_connectToPeersAsync;
std::string m_connectToPeersData;
static void on_connect_to_peers(uv_async_t* handle);
uv_async_t m_showPeersAsync;
static void on_show_peers(uv_async_t* handle) { reinterpret_cast<P2PServer*>(handle->data)->show_peers(); }
void show_peers();
void on_shutdown() override;
};
} // namespace p2pool
+179 -72
View File
@@ -64,10 +64,18 @@ p2pool::p2pool(int argc, char* argv[])
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);
throw std::exception();
if (m_params->m_socks5Proxy.empty()) {
if (m_params->m_dns) {
bool is_v6;
if (!resolve_host(m_params->m_host, is_v6)) {
LOGERR(1, "resolve_host failed for " << m_params->m_host);
throw std::exception();
}
}
else if (m_params->m_host.find_first_not_of("0123456789.:") != std::string::npos) {
LOGERR(1, "Can't resolve hostname " << m_params->m_host << " with DNS disabled");
throw std::exception();
}
}
hash pub, sec, eph_public_key;
@@ -154,9 +162,16 @@ p2pool::p2pool(int argc, char* argv[])
m_hasher = new RandomX_Hasher_RPC(this);
#endif
m_blockTemplate = new BlockTemplate(this);
m_blockTemplate = new BlockTemplate(m_sideChain, m_hasher);
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()
@@ -175,7 +190,6 @@ p2pool::~p2pool()
delete m_blockTemplate;
delete m_mempool;
delete m_params;
delete m_consoleCommands;
}
bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result)
@@ -230,6 +244,11 @@ void p2pool::handle_tx(TxMempoolData& tx)
", weight = " << log::Gray() << tx.weight << log::NoColor() <<
", fee = " << log::Gray() << static_cast<double>(tx.fee) / 1e6 << " um");
if (tx.fee >= HIGH_FEE_VALUE) {
LOGINFO(4, "high fee tx received: " << log::LightBlue() << tx.id << log::NoColor() << ", " << log::XMRAmount(tx.fee) << " - updating block template");
update_block_template_async();
}
#if TEST_MEMPOOL_PICKING_ALGORITHM
m_blockTemplate->update(miner_data(), *m_mempool, &m_params->m_wallet);
#endif
@@ -287,6 +306,11 @@ void p2pool::handle_miner_data(MinerData& data)
"\n---------------------------------------------------------------------------------------------------------------"
);
// Tx secret keys from all miners change every block, so cache can be cleared here
if (m_sideChain->precalcFinished()) {
clear_crypto_cache();
}
if (!is_main_thread()) {
update_block_template_async();
}
@@ -311,11 +335,11 @@ void p2pool::handle_miner_data(MinerData& data)
}
for (uint64_t h : missing_heights) {
char buf[log::Stream::BUF_SIZE + 1];
char buf[log::Stream::BUF_SIZE + 1] = {};
log::Stream s(buf);
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << h << "}}\0";
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin, m_params->m_socks5Proxy,
[this, h](const char* data, size_t size)
{
ChainMain block;
@@ -386,11 +410,17 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
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);
const Wallet& w = params().m_wallet;
const uint64_t payout = block->get_payout(w);
if (payout) {
LOGINFO(0, log::LightCyan() << "You received a payout of " << log::LightGreen() << log::XMRAmount(payout) << log::LightCyan() << " in block " << log::LightGreen() << data.height);
LOGINFO(0, log::LightCyan() << "Your wallet " << log::LightGreen() << w << log::LightCyan() << " got a payout of " << log::LightGreen() << log::XMRAmount(payout) << log::LightCyan() << " in block " << log::LightGreen() << data.height);
}
api_update_block_found(&data);
else {
LOGINFO(0, log::LightCyan() << "Your wallet " << log::LightYellow() << w << log::LightCyan() << " didn't get a payout in block " << log::LightYellow() << data.height << log::LightCyan() << " because you had no shares in PPLNS window");
}
api_update_block_found(&data, block);
}
else {
side_chain().watch_mainchain_block(data, sidechain_id);
@@ -413,13 +443,20 @@ 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));
}
}
void p2pool::submit_block_async(const std::vector<uint8_t>& blob)
void p2pool::submit_block_async(std::vector<uint8_t>&& blob)
{
{
MutexLock lock(m_submitBlockDataLock);
@@ -427,7 +464,14 @@ void p2pool::submit_block_async(const std::vector<uint8_t>& blob)
m_submitBlockData.template_id = 0;
m_submitBlockData.nonce = 0;
m_submitBlockData.extra_nonce = 0;
m_submitBlockData.blob = blob;
m_submitBlockData.blob = std::move(blob);
}
// If p2pool is stopped, m_submitBlockAsync is most likely already closed
if (m_stopped) {
LOGWARN(0, "p2pool is shutting down, but a block was found. Trying to submit it anyway!");
submit_block();
return;
}
const int err = uv_async_send(&m_submitBlockAsync);
@@ -436,10 +480,14 @@ void p2pool::submit_block_async(const std::vector<uint8_t>& blob)
}
}
bool init_signals(p2pool* pool, bool init);
void p2pool::on_stop(uv_async_t* async)
{
p2pool* pool = reinterpret_cast<p2pool*>(async->data);
delete pool->m_consoleCommands;
if (pool->m_api) {
pool->m_api->on_stop();
}
@@ -448,7 +496,12 @@ void p2pool::on_stop(uv_async_t* async)
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_blockTemplateAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_stopAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_restartZMQAsync), nullptr);
uv_stop(uv_default_loop());
init_signals(pool, false);
uv_loop_t* loop = uv_default_loop_checked();
delete GetLoopUserData(loop, false);
loop->data = nullptr;
}
void p2pool::submit_block() const
@@ -508,7 +561,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, m_params->m_socks5Proxy,
[height, diff, template_id, nonce, extra_nonce, is_external](const char* data, size_t size)
{
rapidjson::Document doc;
@@ -527,8 +580,8 @@ void p2pool::submit_block() const
const char* error_msg = nullptr;
auto it = doc.FindMember("message");
if (it != doc.MemberEnd() && it->value.IsString()) {
auto it = err.FindMember("message");
if (it != err.MemberEnd() && it->value.IsString()) {
error_msg = it->value.GetString();
}
@@ -560,7 +613,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));
}
}
});
@@ -572,8 +625,17 @@ 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;
}
if (is_alternative_block) {
m_isAlternativeBlock = true;
}
const int err = uv_async_send(&m_blockTemplateAsync);
if (err) {
LOGERR(1, "uv_async_send failed, error " << uv_err_name(err));
@@ -591,6 +653,16 @@ void p2pool::update_block_template()
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)
@@ -598,7 +670,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
@@ -607,7 +679,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, m_params->m_socks5Proxy,
[this, prev_seed_height, height](const char* data, size_t size)
{
ChainMain block;
@@ -618,14 +690,14 @@ void p2pool::download_block_headers(uint64_t current_height)
}
}
else {
LOGERR(1, "fatal error: couldn't download block header for height " << height);
LOGERR(1, "fatal error: couldn't download block header for seed height " << height);
panic();
}
},
[height](const char* data, size_t size)
{
if (size > 0) {
LOGERR(1, "fatal error: couldn't download block header for height " << height << ", error " << log::const_buf(data, size));
LOGERR(1, "fatal error: couldn't download block header for seed height " << height << ", error " << log::const_buf(data, size));
panic();
}
});
@@ -636,14 +708,14 @@ 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\":" << start_height << ",\"end_height\":" << current_height - 1 << "}}\0";
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin, m_params->m_socks5Proxy,
[this, start_height, current_height](const char* data, size_t size)
{
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);
m_ZMQReader = new ZMQReader(m_params->m_host, m_params->m_zmqPort, m_params->m_socks5Proxy, this);
}
catch (const std::exception& e) {
LOGERR(1, "Couldn't start ZMQ reader: exception " << e.what());
@@ -658,18 +730,19 @@ void p2pool::download_block_headers(uint64_t current_height)
}
#endif
api_update_network_stats();
get_miner_data();
}
}
else {
LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1);
panic();
LOGERR(1, "Couldn't download block headers for heights " << start_height << " - " << current_height - 1);
download_block_headers(current_height);
}
},
[start_height, current_height](const char* data, size_t size)
[this, start_height, current_height](const char* data, size_t size)
{
if (size > 0) {
LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1 << ", error " << log::const_buf(data, size));
panic();
LOGERR(1, "Couldn't download block headers for heights " << start_height << " - " << current_height - 1 << ", error " << log::const_buf(data, size));
download_block_headers(current_height);
}
});
}
@@ -744,7 +817,7 @@ void p2pool::stratum_on_block()
void p2pool::get_info()
{
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_info\"}",
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_info\"}", m_params->m_rpcLogin, m_params->m_socks5Proxy,
[this](const char* data, size_t size)
{
parse_get_info_rpc(data, size);
@@ -753,8 +826,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();
}
}
});
}
@@ -770,34 +845,39 @@ void p2pool::load_found_blocks()
return;
}
while (!f.eof()) {
while (f.good()) {
time_t timestamp;
f >> timestamp;
if (f.eof()) break;
if (!f.good()) break;
uint64_t height;
f >> height;
if (f.eof()) break;
if (!f.good()) break;
hash id;
f >> id;
if (f.eof()) break;
if (!f.good()) break;
difficulty_type block_difficulty;
f >> block_difficulty;
if (f.eof()) break;
if (!f.good()) break;
difficulty_type cumulative_difficulty;
f >> cumulative_difficulty;
if (!f.good() && !f.eof()) break;
m_foundBlocks.emplace_back(timestamp, height, id, block_difficulty, cumulative_difficulty);
}
api_update_block_found(nullptr);
api_update_block_found(nullptr, nullptr);
}
void p2pool::parse_get_info_rpc(const char* data, size_t size)
{
if (m_stopped) {
return;
}
rapidjson::Document doc;
doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size);
@@ -850,7 +930,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, m_params->m_socks5Proxy,
[this](const char* data, size_t size)
{
parse_get_version_rpc(data, size);
@@ -859,14 +939,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);
@@ -896,14 +982,16 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
return;
}
const uint64_t required = m_params->m_disableRandomX ? 0x30009 : 0x30008;
constexpr uint64_t major = 3;
constexpr uint64_t minor = 10;
constexpr uint64_t required = (major << 16) | minor;
if (version < required) {
const uint64_t version_hi = version >> 16;
const uint64_t version_lo = version & 65535;
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)");
LOGERR(1, "monerod RPC v" << version_hi << '.' << version_lo << " is incompatible, update to RPC >= v" << required_version_hi << '.' << required_version_lo << " (Monero v0.18.0.0 or newer)");
panic();
}
@@ -912,9 +1000,12 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
void p2pool::get_miner_data()
{
if (m_getMinerDataPending) {
return;
}
m_getMinerDataPending = true;
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_miner_data\"}",
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_miner_data\"}", m_params->m_rpcLogin, m_params->m_socks5Proxy,
[this](const char* data, size_t size)
{
parse_get_miner_data_rpc(data, size);
@@ -923,8 +1014,10 @@ 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;
@@ -934,6 +1027,10 @@ void p2pool::get_miner_data()
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) {
@@ -985,7 +1082,9 @@ void p2pool::parse_get_miner_data_rpc(const char* data, size_t size)
}
handle_miner_data(minerData);
download_block_headers(minerData.height);
if (m_serversStarted.load() == 0) {
download_block_headers(minerData.height);
}
}
bool p2pool::parse_block_header(const char* data, size_t size, ChainMain& c)
@@ -1245,22 +1344,22 @@ void p2pool::cleanup_mainchain_data(uint64_t height)
}
}
void p2pool::api_update_block_found(const ChainMain* data)
void p2pool::api_update_block_found(const ChainMain* data, const PoolBlock* block)
{
clear_crypto_cache();
if (!m_api) {
return;
}
const time_t cur_time = time(nullptr);
const difficulty_type total_hashes = m_sideChain->total_hashes();
const difficulty_type total_hashes = block ? block->m_cumulativeDifficulty : m_sideChain->total_hashes();
difficulty_type diff;
if (data && get_difficulty_at_height(data->height, diff)) {
std::ofstream f(FOUND_BLOCKS_FILE, std::ios::app);
if (f.is_open()) {
f << cur_time << ' ' << data->height << ' ' << data->id << ' ' << diff << ' ' << total_hashes << '\n';
f.flush();
f.close();
}
}
@@ -1362,13 +1461,7 @@ static void on_signal(uv_signal_t* handle, int signum)
pool->stop();
}
static bool init_uv_threadpool()
{
static uv_work_t dummy;
return (uv_queue_work(uv_default_loop_checked(), &dummy, [](uv_work_t*) {}, nullptr) == 0);
}
static bool init_signals(p2pool* pool)
bool init_signals(p2pool* pool, bool init)
{
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
@@ -1388,6 +1481,14 @@ static bool init_signals(p2pool* pool)
static uv_signal_t signals[array_size(signal_names)];
if (!init) {
for (size_t i = 0; i < array_size(signals); ++i) {
uv_signal_stop(&signals[i]);
uv_close(reinterpret_cast<uv_handle_t*>(&signals[i]), nullptr);
}
return true;
}
for (size_t i = 0; i < array_size(signal_names); ++i) {
uv_signal_init(uv_default_loop_checked(), &signals[i]);
signals[i].data = pool;
@@ -1403,25 +1504,31 @@ 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();
}
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_ZMQReader = new ZMQReader(m_params->m_host, m_params->m_zmqPort, m_params->m_socks5Proxy, this);
m_zmqLastActive = seconds_since_epoch();
}
catch (const std::exception& e) {
@@ -1431,21 +1538,21 @@ void p2pool::restart_zmq()
int p2pool::run()
{
if (!m_params->ok()) {
if (!m_params->valid()) {
LOGERR(1, "Invalid or missing command line. Try \"p2pool --help\".");
return 1;
}
if (!init_uv_threadpool()) {
LOGERR(1, "failed to start UV thread pool");
return 1;
}
if (!init_signals(this)) {
if (!init_signals(this, true)) {
LOGERR(1, "failed to initialize signal handlers");
return 1;
}
// Init default loop user data before running it
uv_loop_t* loop = uv_default_loop_checked();
loop->data = nullptr;
GetLoopUserData(loop);
try {
get_info();
load_found_blocks();
+9 -6
View File
@@ -33,8 +33,9 @@ class Miner;
class ConsoleCommands;
class p2pool_api;
class ZMQReader;
struct PoolBlock;
class p2pool : public MinerCallbackHandler
class p2pool : public MinerCallbackHandler, public nocopy_nomove
{
public:
p2pool(int argc, char* argv[]);
@@ -74,17 +75,17 @@ public:
virtual void handle_chain_main(ChainMain& data, const char* extra) override;
void submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
void submit_block_async(const std::vector<uint8_t>& blob);
void submit_block_async(std::vector<uint8_t>&& blob);
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
void 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);
bool chainmain_get_by_hash(const hash& id, ChainMain& data) const;
void api_update_block_found(const ChainMain* data);
void api_update_block_found(const ChainMain* data, const PoolBlock* block);
bool get_difficulty_at_height(uint64_t height, difficulty_type& diff);
@@ -108,7 +109,7 @@ private:
void submit_block() const;
bool m_stopped;
std::atomic<bool> m_stopped;
Params* m_params;
@@ -142,7 +143,7 @@ private:
void get_miner_data();
void parse_get_miner_data_rpc(const char* data, size_t size);
bool parse_block_header(const char* data, size_t size, ChainMain& result);
bool parse_block_header(const char* data, size_t size, ChainMain& c);
uint32_t parse_block_headers_range(const char* data, size_t size);
void api_update_network_stats();
@@ -197,6 +198,8 @@ private:
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;
+42 -28
View File
@@ -28,7 +28,9 @@ static constexpr char log_category_prefix[] = "P2Pool API ";
namespace p2pool {
p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats): m_apiPath(api_path)
p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats)
: m_apiPath(api_path)
, m_counter(0)
{
if (m_apiPath.empty()) {
LOGERR(1, "api path is empty");
@@ -101,7 +103,7 @@ void p2pool_api::on_stop()
uv_close(reinterpret_cast<uv_handle_t*>(&m_dumpToFileAsync), nullptr);
}
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());
@@ -131,14 +133,12 @@ void p2pool_api::dump_to_file()
{
MutexLock lock(m_dumpDataLock);
data = std::move(m_dumpData);
m_dumpData.clear();
}
for (auto& it : data) {
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
@@ -146,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;
}
}
@@ -157,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;
}
@@ -170,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)
@@ -186,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)
@@ -205,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);
+8 -5
View File
@@ -38,7 +38,7 @@ public:
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);
@@ -46,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;
};
@@ -72,11 +72,12 @@ 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;
@@ -87,6 +88,8 @@ private:
unordered_map<std::string, std::vector<char>> m_dumpData;
uv_async_t m_dumpToFileAsync;
uint64_t m_counter;
};
} // namespace p2pool
+28 -7
View File
@@ -35,12 +35,12 @@ Params::Params(int argc, char* argv[])
}
if ((strcmp(argv[i], "--rpc-port") == 0) && (i + 1 < argc)) {
m_rpcPort = strtoul(argv[++i], nullptr, 10);
m_rpcPort = std::min(std::max(strtoul(argv[++i], nullptr, 10), 1UL), 65535UL);
ok = true;
}
if ((strcmp(argv[i], "--zmq-port") == 0) && (i + 1 < argc)) {
m_zmqPort = strtoul(argv[++i], nullptr, 10);
m_zmqPort = std::min(std::max(strtoul(argv[++i], nullptr, 10), 1UL), 65535UL);
ok = true;
}
@@ -105,13 +105,13 @@ Params::Params(int argc, char* argv[])
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);
if ((!strcmp(argv[i], "--out-peers") || !strcmp(argv[i], "--outpeers")) && (i + 1 < argc)) {
m_maxOutgoingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 450UL);
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);
if ((!strcmp(argv[i], "--in-peers") || !strcmp(argv[i], "--inpeers")) && (i + 1 < argc)) {
m_maxIncomingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 450UL);
ok = true;
}
@@ -130,6 +130,27 @@ Params::Params(int argc, char* argv[])
ok = true;
}
if ((strcmp(argv[i], "--rpc-login") == 0) && (i + 1 < argc)) {
m_rpcLogin = argv[++i];
ok = true;
}
if ((strcmp(argv[i], "--socks5") == 0) && (i + 1 < argc)) {
m_socks5Proxy = argv[++i];
ok = true;
}
if (strcmp(argv[i], "--no-dns") == 0) {
m_dns = false;
disable_resolve_host = true;
ok = true;
}
if ((strcmp(argv[i], "--p2p-external-port") == 0) && (i + 1 < argc)) {
m_p2pExternalPort = std::min(std::max(strtoul(argv[++i], nullptr, 10), 1UL), 65535UL);
ok = true;
}
if (!ok) {
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
p2pool_usage();
@@ -148,7 +169,7 @@ Params::Params(int argc, char* argv[])
}
}
bool Params::ok() const
bool Params::valid() const
{
return !m_host.empty() && m_rpcPort && m_zmqPort && m_wallet.valid();
}
+6 -2
View File
@@ -25,7 +25,7 @@ struct Params
{
Params(int argc, char* argv[]);
bool ok() const;
bool valid() const;
std::string m_host = "127.0.0.1";
uint32_t m_rpcPort = 18081;
@@ -45,10 +45,14 @@ struct Params
bool m_disableRandomX = true;
#endif
uint32_t m_maxOutgoingPeers = 10;
uint32_t m_maxIncomingPeers = 1000;
uint32_t m_maxIncomingPeers = 450;
uint32_t m_minerThreads = 0;
bool m_mini = false;
bool m_autoDiff = true;
std::string m_rpcLogin;
std::string m_socks5Proxy;
bool m_dns = true;
uint32_t m_p2pExternalPort = 0;
};
} // namespace p2pool
+142 -86
View File
@@ -29,11 +29,7 @@ static constexpr char log_category_prefix[] = "PoolBlock ";
namespace p2pool {
PoolBlock::PoolBlock()
: m_mainChainHeaderSize(0)
, m_mainChainMinerTxSize(0)
, m_mainChainOutputsOffset(0)
, m_mainChainOutputsBlobSize(0)
, m_majorVersion(0)
: m_majorVersion(0)
, m_minorVersion(0)
, m_timestamp(0)
, m_prevId{}
@@ -53,16 +49,10 @@ PoolBlock::PoolBlock()
, m_invalid(false)
, m_broadcasted(false)
, m_wantBroadcast(false)
, 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_sideChainData.reserve(512);
m_uncles.reserve(8);
m_tmpTxExtra.reserve(80);
}
PoolBlock::PoolBlock(const PoolBlock& b)
@@ -83,11 +73,11 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
LOGERR(1, "operator= uv_mutex_trylock failed. Fix the code!");
}
m_mainChainData = b.m_mainChainData;
m_mainChainHeaderSize = b.m_mainChainHeaderSize;
m_mainChainMinerTxSize = b.m_mainChainMinerTxSize;
m_mainChainOutputsOffset = b.m_mainChainOutputsOffset;
m_mainChainOutputsBlobSize = b.m_mainChainOutputsBlobSize;
#if POOL_BLOCK_DEBUG
m_mainChainDataDebug = b.m_mainChainDataDebug;
m_sideChainDataDebug = b.m_sideChainDataDebug;
#endif
m_majorVersion = b.m_majorVersion;
m_minorVersion = b.m_minorVersion;
m_timestamp = b.m_timestamp;
@@ -99,7 +89,6 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_extraNonceSize = b.m_extraNonceSize;
m_extraNonce = b.m_extraNonce;
m_transactions = b.m_transactions;
m_sideChainData = b.m_sideChainData;
m_minerWallet = b.m_minerWallet;
m_txkeySec = b.m_txkeySec;
m_parent = b.m_parent;
@@ -108,12 +97,12 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_difficulty = b.m_difficulty;
m_cumulativeDifficulty = b.m_cumulativeDifficulty;
m_sidechainId = b.m_sidechainId;
m_tmpTxExtra = b.m_tmpTxExtra;
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 = seconds_since_epoch();
@@ -129,104 +118,164 @@ PoolBlock::~PoolBlock()
uv_mutex_destroy(&m_lock);
}
void PoolBlock::serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash)
std::vector<uint8_t> PoolBlock::serialize_mainchain_data(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const
{
MutexLock lock(m_lock);
return serialize_mainchain_data_nolock(header_size, miner_tx_size, outputs_offset, outputs_blob_size);
}
m_mainChainData.clear();
std::vector<uint8_t> PoolBlock::serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const
{
std::vector<uint8_t> data;
data.reserve(128 + m_outputs.size() * 39 + m_transactions.size() * HASH_SIZE);
// Header
m_mainChainData.push_back(m_majorVersion);
m_mainChainData.push_back(m_minorVersion);
writeVarint(m_timestamp, m_mainChainData);
m_mainChainData.insert(m_mainChainData.end(), m_prevId.h, m_prevId.h + HASH_SIZE);
m_mainChainData.insert(m_mainChainData.end(), reinterpret_cast<uint8_t*>(&nonce), reinterpret_cast<uint8_t*>(&nonce) + NONCE_SIZE);
data.push_back(m_majorVersion);
data.push_back(m_minorVersion);
writeVarint(m_timestamp, data);
data.insert(data.end(), m_prevId.h, m_prevId.h + HASH_SIZE);
data.insert(data.end(), reinterpret_cast<const uint8_t*>(&m_nonce), reinterpret_cast<const uint8_t*>(&m_nonce) + NONCE_SIZE);
m_mainChainHeaderSize = m_mainChainData.size();
const size_t header_size0 = data.size();
if (header_size) {
*header_size = header_size0;
}
// Miner tx
m_mainChainData.push_back(TX_VERSION);
writeVarint(m_txinGenHeight + MINER_REWARD_UNLOCK_TIME, m_mainChainData);
m_mainChainData.push_back(1);
m_mainChainData.push_back(TXIN_GEN);
writeVarint(m_txinGenHeight, m_mainChainData);
data.push_back(TX_VERSION);
writeVarint(m_txinGenHeight + MINER_REWARD_UNLOCK_TIME, data);
data.push_back(1);
data.push_back(TXIN_GEN);
writeVarint(m_txinGenHeight, data);
m_mainChainOutputsOffset = static_cast<int>(m_mainChainData.size());
const int outputs_offset0 = static_cast<int>(data.size());
if (outputs_offset) {
*outputs_offset = outputs_offset0;
}
writeVarint(m_outputs.size(), m_mainChainData);
writeVarint(m_outputs.size(), data);
for (TxOutput& output : m_outputs) {
writeVarint(output.m_reward, m_mainChainData);
m_mainChainData.push_back(output.m_txType);
m_mainChainData.insert(m_mainChainData.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
const uint8_t tx_type = get_tx_type();
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
m_mainChainData.push_back(output.m_viewTag);
for (const TxOutput& output : m_outputs) {
writeVarint(output.m_reward, data);
data.push_back(tx_type);
data.insert(data.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
if (tx_type == TXOUT_TO_TAGGED_KEY) {
data.push_back(static_cast<uint8_t>(output.m_viewTag));
}
}
m_mainChainOutputsBlobSize = static_cast<int>(m_mainChainData.size()) - m_mainChainOutputsOffset;
m_tmpTxExtra.clear();
m_tmpTxExtra.push_back(TX_EXTRA_TAG_PUBKEY);
m_tmpTxExtra.insert(m_tmpTxExtra.end(), m_txkeyPub.h, m_txkeyPub.h + HASH_SIZE);
m_tmpTxExtra.push_back(TX_EXTRA_NONCE);
writeVarint(m_extraNonceSize, m_tmpTxExtra);
m_extraNonce = extra_nonce;
m_tmpTxExtra.insert(m_tmpTxExtra.end(), reinterpret_cast<uint8_t*>(&m_extraNonce), reinterpret_cast<uint8_t*>(&m_extraNonce) + EXTRA_NONCE_SIZE);
if (m_extraNonceSize > EXTRA_NONCE_SIZE) {
m_tmpTxExtra.insert(m_tmpTxExtra.end(), m_extraNonceSize - EXTRA_NONCE_SIZE, 0);
if (outputs_blob_size) {
*outputs_blob_size = static_cast<int>(data.size()) - outputs_offset0;
}
m_tmpTxExtra.push_back(TX_EXTRA_MERGE_MINING_TAG);
writeVarint(HASH_SIZE, m_tmpTxExtra);
m_tmpTxExtra.insert(m_tmpTxExtra.end(), sidechain_hash.h, sidechain_hash.h + HASH_SIZE);
uint8_t tx_extra[128];
uint8_t* p = tx_extra;
writeVarint(m_tmpTxExtra.size(), m_mainChainData);
m_mainChainData.insert(m_mainChainData.end(), m_tmpTxExtra.begin(), m_tmpTxExtra.end());
*(p++) = TX_EXTRA_TAG_PUBKEY;
memcpy(p, m_txkeyPub.h, HASH_SIZE);
p += HASH_SIZE;
m_tmpTxExtra.clear();
uint64_t extra_nonce_size = m_extraNonceSize;
if (extra_nonce_size > EXTRA_NONCE_MAX_SIZE) {
LOGERR(1, "extra nonce size is too large (" << extra_nonce_size << "), fix the code!");
extra_nonce_size = EXTRA_NONCE_MAX_SIZE;
}
m_mainChainData.push_back(0);
*(p++) = TX_EXTRA_NONCE;
*(p++) = static_cast<uint8_t>(extra_nonce_size);
m_mainChainMinerTxSize = m_mainChainData.size() - m_mainChainHeaderSize;
memcpy(p, &m_extraNonce, EXTRA_NONCE_SIZE);
p += EXTRA_NONCE_SIZE;
if (extra_nonce_size > EXTRA_NONCE_SIZE) {
memset(p, 0, extra_nonce_size - EXTRA_NONCE_SIZE);
p += extra_nonce_size - EXTRA_NONCE_SIZE;
}
writeVarint(m_transactions.size() - 1, m_mainChainData);
const uint8_t* data = reinterpret_cast<const uint8_t*>(m_transactions.data());
m_mainChainData.insert(m_mainChainData.end(), data + HASH_SIZE, data + m_transactions.size() * HASH_SIZE);
*(p++) = TX_EXTRA_MERGE_MINING_TAG;
*(p++) = HASH_SIZE;
memcpy(p, m_sidechainId.h, HASH_SIZE);
p += HASH_SIZE;
writeVarint(static_cast<size_t>(p - tx_extra), data);
data.insert(data.end(), tx_extra, p);
data.push_back(0);
if (miner_tx_size) {
*miner_tx_size = data.size() - header_size0;
}
writeVarint(m_transactions.size() - 1, data);
const uint8_t* t = reinterpret_cast<const uint8_t*>(m_transactions.data());
data.insert(data.end(), t + HASH_SIZE, t + m_transactions.size() * HASH_SIZE);
#if POOL_BLOCK_DEBUG
if (!m_mainChainDataDebug.empty() && (data != m_mainChainDataDebug)) {
LOGERR(1, "serialize_mainchain_data() has a bug, fix it!");
panic();
}
#endif
return data;
}
void PoolBlock::serialize_sidechain_data()
std::vector<uint8_t> PoolBlock::serialize_sidechain_data() const
{
std::vector<uint8_t> data;
MutexLock lock(m_lock);
m_sideChainData.clear();
m_sideChainData.reserve((m_uncles.size() + 4) * HASH_SIZE + 11);
data.reserve((m_uncles.size() + 4) * HASH_SIZE + 20);
const hash& spend = m_minerWallet.spend_public_key();
const hash& view = m_minerWallet.view_public_key();
m_sideChainData.insert(m_sideChainData.end(), spend.h, spend.h + HASH_SIZE);
m_sideChainData.insert(m_sideChainData.end(), view.h, view.h + HASH_SIZE);
m_sideChainData.insert(m_sideChainData.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE);
m_sideChainData.insert(m_sideChainData.end(), m_parent.h, m_parent.h + HASH_SIZE);
data.insert(data.end(), spend.h, spend.h + HASH_SIZE);
data.insert(data.end(), view.h, view.h + HASH_SIZE);
data.insert(data.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE);
data.insert(data.end(), m_parent.h, m_parent.h + HASH_SIZE);
writeVarint(m_uncles.size(), m_sideChainData);
writeVarint(m_uncles.size(), data);
for (const hash& id : m_uncles) {
m_sideChainData.insert(m_sideChainData.end(), id.h, id.h + HASH_SIZE);
data.insert(data.end(), id.h, id.h + HASH_SIZE);
}
writeVarint(m_sidechainHeight, m_sideChainData);
writeVarint(m_sidechainHeight, data);
writeVarint(m_difficulty.lo, m_sideChainData);
writeVarint(m_difficulty.hi, m_sideChainData);
writeVarint(m_difficulty.lo, data);
writeVarint(m_difficulty.hi, data);
writeVarint(m_cumulativeDifficulty.lo, m_sideChainData);
writeVarint(m_cumulativeDifficulty.hi, m_sideChainData);
writeVarint(m_cumulativeDifficulty.lo, data);
writeVarint(m_cumulativeDifficulty.hi, data);
#if POOL_BLOCK_DEBUG
if (!m_sideChainDataDebug.empty() && (data != m_sideChainDataDebug)) {
LOGERR(1, "serialize_sidechain_data() has a bug, fix it!");
panic();
}
#endif
return data;
}
void PoolBlock::reset_offchain_data()
{
// Defaults for off-chain variables
m_depth = 0;
m_verified = false;
m_invalid = false;
m_broadcasted = false;
m_wantBroadcast = false;
m_precalculated = false;
m_localTimestamp = seconds_since_epoch();
}
bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash)
@@ -249,16 +298,19 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
{
MutexLock lock(m_lock);
if (!m_mainChainHeaderSize || !m_mainChainMinerTxSize || (m_mainChainData.size() < m_mainChainHeaderSize + m_mainChainMinerTxSize)) {
size_t header_size, miner_tx_size;
const std::vector<uint8_t> mainchain_data = serialize_mainchain_data_nolock(&header_size, &miner_tx_size, nullptr, nullptr);
if (!header_size || !miner_tx_size || (mainchain_data.size() < header_size + miner_tx_size)) {
LOGERR(1, "tried to calculate PoW of uninitialized block");
return false;
}
blob_size = m_mainChainHeaderSize;
memcpy(blob, m_mainChainData.data(), blob_size);
blob_size = header_size;
memcpy(blob, mainchain_data.data(), blob_size);
uint8_t* miner_tx = m_mainChainData.data() + m_mainChainHeaderSize;
keccak(miner_tx, static_cast<int>(m_mainChainMinerTxSize) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE);
const uint8_t* miner_tx = mainchain_data.data() + header_size;
keccak(miner_tx, static_cast<int>(miner_tx_size) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE);
count = m_transactions.size();
uint8_t* h = reinterpret_cast<uint8_t*>(m_transactions.data());
@@ -304,12 +356,16 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
uint64_t PoolBlock::get_payout(const Wallet& w) const
{
const uint8_t tx_type = get_tx_type();
for (size_t i = 0, n = m_outputs.size(); i < n; ++i) {
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)) {
if (tx_type == TXOUT_TO_TAGGED_KEY) {
uint8_t view_tag;
const uint8_t expected_view_tag = out.m_viewTag;
if (w.get_eph_public_key(m_txkeySec, i, eph_public_key, view_tag, &expected_view_tag) && (eph_public_key == out.m_ephPublicKey)) {
return out.m_reward;
}
}
+33 -21
View File
@@ -63,13 +63,12 @@ struct PoolBlock
mutable uv_mutex_t m_lock;
// Monero block template
std::vector<uint8_t> m_mainChainData;
size_t m_mainChainHeaderSize;
size_t m_mainChainMinerTxSize;
int m_mainChainOutputsOffset;
int m_mainChainOutputsBlobSize;
#if POOL_BLOCK_DEBUG
std::vector<uint8_t> m_mainChainDataDebug;
std::vector<uint8_t> m_sideChainDataDebug;
#endif
// Monero block template
uint8_t m_majorVersion;
uint8_t m_minorVersion;
uint64_t m_timestamp;
@@ -81,15 +80,16 @@ struct PoolBlock
struct TxOutput
{
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) {}
FORCEINLINE TxOutput() : m_ephPublicKey(), m_reward(0), m_viewTag(0) {}
FORCEINLINE TxOutput(uint64_t r, const hash& k, uint8_t view_tag) : m_ephPublicKey(k), m_reward(r), m_viewTag(view_tag) {}
uint64_t m_reward;
hash m_ephPublicKey;
uint8_t m_txType;
uint8_t m_viewTag;
uint64_t m_reward : 56;
uint64_t m_viewTag : 8;
};
static_assert(sizeof(TxOutput) == sizeof(hash) + sizeof(uint64_t), "TxOutput bit packing didn't work with this compiler, fix the code!");
std::vector<TxOutput> m_outputs;
hash m_txkeyPub;
@@ -99,9 +99,6 @@ struct PoolBlock
// All block transaction hashes including the miner transaction hash at index 0
std::vector<hash> m_transactions;
// Side-chain data
std::vector<uint8_t> m_sideChainData;
// Miner's wallet
Wallet m_minerWallet{ nullptr };
@@ -122,22 +119,25 @@ struct PoolBlock
hash m_sidechainId;
// Just temporary stuff, not a part of the block
std::vector<uint8_t> m_tmpTxExtra;
uint64_t m_depth;
bool m_verified;
bool m_invalid;
bool m_broadcasted;
bool m_wantBroadcast;
mutable bool m_broadcasted;
mutable bool m_wantBroadcast;
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();
std::vector<uint8_t> serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr) const;
std::vector<uint8_t> serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const;
std::vector<uint8_t> serialize_sidechain_data() const;
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact);
void reset_offchain_data();
int deserialize(const uint8_t* data, size_t size, SideChain& sidechain);
bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash);
uint64_t get_payout(const Wallet& w) const;
@@ -145,6 +145,18 @@ struct PoolBlock
// 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; }
typedef std::array<uint8_t, HASH_SIZE + NONCE_SIZE + EXTRA_NONCE_SIZE> full_id;
FORCEINLINE full_id get_full_id() const
{
full_id key;
uint8_t* p = key.data();
memcpy(p, m_sidechainId.h, HASH_SIZE);
memcpy(p + HASH_SIZE, &m_nonce, NONCE_SIZE);
memcpy(p + HASH_SIZE + NONCE_SIZE, &m_extraNonce, EXTRA_NONCE_SIZE);
return key;
}
};
} // namespace p2pool
+122 -62
View File
@@ -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, bool compact)
{
try {
// Sanity check
@@ -75,8 +75,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
const int nonce_offset = static_cast<int>(data - data_begin);
READ_BUF(&m_nonce, NONCE_SIZE);
m_mainChainHeaderSize = data - data_begin;
EXPECT_BYTE(TX_VERSION);
uint64_t unlock_height;
@@ -86,15 +84,17 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
EXPECT_BYTE(TXIN_GEN);
READ_VARINT(m_txinGenHeight);
if (m_majorVersion != sidechain.network_major_version(m_txinGenHeight)) return __LINE__;
if (unlock_height != m_txinGenHeight + MINER_REWARD_UNLOCK_TIME) return __LINE__;
std::vector<uint8_t> outputs_blob;
m_mainChainOutputsOffset = static_cast<int>(data - data_begin);
const int outputs_offset = static_cast<int>(data - data_begin);
uint64_t num_outputs;
READ_VARINT(num_outputs);
uint64_t total_reward = 0;
int outputs_blob_size;
if (num_outputs > 0) {
// Outputs are in the buffer, just read them
@@ -105,31 +105,32 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
if (num_outputs > std::numeric_limits<uint64_t>::max() / MIN_OUTPUT_SIZE) return __LINE__;
if (static_cast<uint64_t>(data_end - data) < num_outputs * MIN_OUTPUT_SIZE) return __LINE__;
m_outputs.clear();
m_outputs.reserve(num_outputs);
m_outputs.resize(num_outputs);
m_outputs.shrink_to_fit();
const uint8_t expected_tx_type = get_tx_type();
for (uint64_t i = 0; i < num_outputs; ++i) {
TxOutput t;
TxOutput& t = m_outputs[i];
READ_VARINT(t.m_reward);
total_reward += t.m_reward;
uint64_t reward;
READ_VARINT(reward);
t.m_reward = reward;
total_reward += reward;
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);
uint8_t view_tag;
READ_BYTE(view_tag);
t.m_viewTag = view_tag;
}
m_outputs.emplace_back(std::move(t));
}
m_mainChainOutputsBlobSize = static_cast<int>(data - data_begin) - m_mainChainOutputsOffset;
outputs_blob.assign(data_begin + m_mainChainOutputsOffset, data);
outputs_blob_size = static_cast<int>(data - data_begin) - outputs_offset;
outputs_blob.assign(data_begin + outputs_offset, data);
}
else {
// Outputs are not in the buffer and must be calculated from sidechain data
@@ -144,7 +145,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
return __LINE__;
}
m_mainChainOutputsBlobSize = static_cast<int>(tmp);
outputs_blob_size = static_cast<int>(tmp);
}
// Technically some p2pool node could keep stuffing block with transactions until reward is less than 0.6 XMR
@@ -153,13 +154,12 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
return __LINE__;
}
const int outputs_actual_blob_size = static_cast<int>(data - data_begin) - m_mainChainOutputsOffset;
if (m_mainChainOutputsBlobSize < outputs_actual_blob_size) {
const int outputs_actual_blob_size = static_cast<int>(data - data_begin) - outputs_offset;
if (outputs_blob_size < outputs_actual_blob_size) {
return __LINE__;
}
const int outputs_blob_size_diff = m_mainChainOutputsBlobSize - outputs_actual_blob_size;
const int outputs_blob_size_diff = outputs_blob_size - outputs_actual_blob_size;
uint64_t tx_extra_size;
READ_VARINT(tx_extra_size);
@@ -191,29 +191,66 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
EXPECT_BYTE(0);
m_mainChainMinerTxSize = (data - data_begin) + outputs_blob_size_diff - m_mainChainHeaderSize;
uint64_t num_transactions;
READ_VARINT(num_transactions);
if (num_transactions > std::numeric_limits<uint64_t>::max() / HASH_SIZE) return __LINE__;
if (static_cast<uint64_t>(data_end - data) < num_transactions * HASH_SIZE) return __LINE__;
const int transactions_offset = static_cast<int>(data - data_begin);
m_transactions.resize(1);
m_transactions.reserve(num_transactions + 1);
std::vector<uint64_t> parent_indices;
if (compact) {
if (static_cast<uint64_t>(data_end - data) < num_transactions) return __LINE__;
for (uint64_t i = 0; i < num_transactions; ++i) {
hash id;
READ_BUF(id.h, HASH_SIZE);
m_transactions.emplace_back(std::move(id));
m_transactions.resize(1);
parent_indices.resize(1);
// limit reserved memory size because we can't check "num_transactions" properly here
const uint64_t k = std::min<uint64_t>(num_transactions + 1, 256);
m_transactions.reserve(k);
parent_indices.reserve(k);
for (uint64_t i = 0; i < num_transactions; ++i) {
uint64_t parent_index;
READ_VARINT(parent_index);
hash id;
if (parent_index == 0) {
READ_BUF(id.h, HASH_SIZE);
}
m_transactions.emplace_back(id);
parent_indices.emplace_back(parent_index);
}
}
else {
if (num_transactions > std::numeric_limits<uint64_t>::max() / HASH_SIZE) return __LINE__;
if (static_cast<uint64_t>(data_end - data) < num_transactions * HASH_SIZE) return __LINE__;
m_transactions.resize(1);
m_transactions.reserve(num_transactions + 1);
for (uint64_t i = 0; i < num_transactions; ++i) {
hash id;
READ_BUF(id.h, HASH_SIZE);
m_transactions.emplace_back(id);
}
}
m_mainChainData.reserve((data - data_begin) + outputs_blob_size_diff);
m_mainChainData.assign(data_begin, data_begin + m_mainChainOutputsOffset);
m_mainChainData.insert(m_mainChainData.end(), m_mainChainOutputsBlobSize, 0);
m_mainChainData.insert(m_mainChainData.end(), data_begin + m_mainChainOutputsOffset + outputs_actual_blob_size, data);
const int transactions_actual_blob_size = static_cast<int>(data - data_begin) - transactions_offset;
const int transactions_blob_size = static_cast<int>(num_transactions) * HASH_SIZE;
const int transactions_blob_size_diff = transactions_blob_size - transactions_actual_blob_size;
m_transactions.shrink_to_fit();
#if POOL_BLOCK_DEBUG
m_mainChainDataDebug.reserve((data - data_begin) + outputs_blob_size_diff + transactions_blob_size_diff);
m_mainChainDataDebug.assign(data_begin, data_begin + outputs_offset);
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), outputs_blob_size, 0);
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), data_begin + outputs_offset + outputs_actual_blob_size, data_begin + transactions_offset);
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), transactions_blob_size, 0);
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), data_begin + transactions_offset + transactions_actual_blob_size, data);
const uint8_t* sidechain_data_begin = data;
#endif
hash spend_pub_key;
hash view_pub_key;
@@ -229,8 +266,34 @@ 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);
if (compact) {
const PoolBlock* parent = sidechain.find_block(m_parent);
if (!parent) {
return __LINE__;
}
for (uint64_t i = 1, n = m_transactions.size(); i < n; ++i) {
const uint64_t parent_index = parent_indices[i];
if (parent_index) {
if (parent_index >= parent->m_transactions.size()) {
return __LINE__;
}
m_transactions[i] = parent->m_transactions[parent_index];
}
}
}
uint64_t num_uncles;
READ_VARINT(num_uncles);
@@ -243,7 +306,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
for (uint64_t i = 0; i < num_uncles; ++i) {
hash id;
READ_BUF(id.h, HASH_SIZE);
m_uncles.emplace_back(std::move(id));
m_uncles.emplace_back(id);
}
READ_VARINT(m_sidechainHeight);
@@ -263,20 +326,26 @@ 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__;
}
if (static_cast<int>(outputs_blob.size()) != m_mainChainOutputsBlobSize) {
if (static_cast<int>(outputs_blob.size()) != outputs_blob_size) {
return __LINE__;
}
memcpy(m_mainChainData.data() + m_mainChainOutputsOffset, outputs_blob.data(), m_mainChainOutputsBlobSize);
const uint8_t* transactions_blob = reinterpret_cast<uint8_t*>(m_transactions.data() + 1);
#if POOL_BLOCK_DEBUG
memcpy(m_mainChainDataDebug.data() + outputs_offset, outputs_blob.data(), outputs_blob_size);
memcpy(m_mainChainDataDebug.data() + transactions_offset + outputs_blob_size_diff, transactions_blob, transactions_blob_size);
#endif
hash check;
const std::vector<uint8_t>& consensus_id = sidechain.consensus_id();
const int data_size = static_cast<int>((data_end - data_begin) + outputs_blob_size_diff + transactions_blob_size_diff);
keccak_custom(
[this, nonce_offset, extra_nonce_offset, sidechain_hash_offset, data_begin, data_end, &consensus_id, &outputs_blob, outputs_blob_size_diff](int offset) -> uint8_t
[nonce_offset, extra_nonce_offset, sidechain_hash_offset, data_begin, data_size, &consensus_id, &outputs_blob, outputs_blob_size_diff, outputs_offset, outputs_blob_size, transactions_blob, transactions_blob_size_diff, transactions_offset, transactions_blob_size](int offset) -> uint8_t
{
uint32_t k = static_cast<uint32_t>(offset - nonce_offset);
if (k < NONCE_SIZE) {
@@ -293,30 +362,33 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
return 0;
}
const int data_size = static_cast<int>((data_end - data_begin) + outputs_blob_size_diff);
if (offset < data_size) {
if (offset < m_mainChainOutputsOffset) {
if (offset < outputs_offset) {
return data_begin[offset];
}
else if (offset < m_mainChainOutputsOffset + m_mainChainOutputsBlobSize) {
const int tmp = offset - m_mainChainOutputsOffset;
return outputs_blob[tmp];
else if (offset < outputs_offset + outputs_blob_size) {
return outputs_blob[offset - outputs_offset];
}
else {
else if (offset < transactions_offset + outputs_blob_size_diff) {
return data_begin[offset - outputs_blob_size_diff];
}
else if (offset < transactions_offset + outputs_blob_size_diff + transactions_blob_size) {
return transactions_blob[offset - (transactions_offset + outputs_blob_size_diff)];
}
return data_begin[offset - outputs_blob_size_diff - transactions_blob_size_diff];
}
offset -= data_size;
return consensus_id[offset];
return consensus_id[offset - data_size];
},
static_cast<int>(size + outputs_blob_size_diff + consensus_id.size()), check.h, HASH_SIZE);
static_cast<int>(size + outputs_blob_size_diff + transactions_blob_size_diff + consensus_id.size()), check.h, HASH_SIZE);
if (check != m_sidechainId) {
return __LINE__;
}
m_sideChainData.assign(sidechain_data_begin, data_end);
#if POOL_BLOCK_DEBUG
m_sideChainDataDebug.assign(sidechain_data_begin, data_end);
#endif
}
catch (std::exception& e) {
const char* msg = e.what();
@@ -324,19 +396,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
return __LINE__;
}
// Defaults for off-chain variables
m_tmpTxExtra.clear();
m_depth = 0;
m_verified = false;
m_invalid = false;
m_broadcasted = false;
m_wantBroadcast = false;
m_localTimestamp = seconds_since_epoch();
reset_offchain_data();
return 0;
}
+26 -14
View File
@@ -141,7 +141,7 @@ void RandomX_Hasher::set_seed_async(const hash& seed)
const int err = uv_queue_work(uv_default_loop_checked(), &work->req,
[](uv_work_t* req)
{
bkg_jobs_tracker.start("RandomX_Hasher::set_seed_async");
BACKGROUND_JOB_START(RandomX_Hasher::set_seed_async);
Work* work = reinterpret_cast<Work*>(req->data);
if (!work->pool->stopped()) {
work->hasher->set_seed(work->seed);
@@ -150,7 +150,7 @@ void RandomX_Hasher::set_seed_async(const hash& seed)
[](uv_work_t* req, int)
{
delete reinterpret_cast<Work*>(req->data);
bkg_jobs_tracker.stop("RandomX_Hasher::set_seed_async");
BACKGROUND_JOB_STOP(RandomX_Hasher::set_seed_async);
}
);
@@ -370,6 +370,7 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*
RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
: m_pool(pool)
, m_loop{}
, m_loopThread{}
{
int err = uv_loop_init(&m_loop);
@@ -378,18 +379,17 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
panic();
}
uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
uv_async_init(&m_loop, &m_kickTheLoopAsync, nullptr);
// Init loop user data before running it
GetLoopUserData(&m_loop);
uv_async_init_checked(&m_loop, &m_shutdownAsync, on_shutdown);
uv_async_init_checked(&m_loop, &m_kickTheLoopAsync, nullptr);
m_shutdownAsync.data = this;
uv_mutex_init_checked(&m_requestMutex);
uv_mutex_init_checked(&m_condMutex);
err = uv_cond_init(&m_cond);
if (err) {
LOGERR(1, "failed to create cond, error " << uv_err_name(err));
panic();
}
uv_cond_init_checked(&m_cond);
err = uv_thread_create(&m_loopThread, loop, this);
if (err) {
@@ -413,9 +413,19 @@ RandomX_Hasher_RPC::~RandomX_Hasher_RPC()
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);
int err = uv_run(&hasher->m_loop, UV_RUN_DEFAULT);
if (err) {
LOGWARN(1, "uv_run returned " << err);
}
err = uv_loop_close(&hasher->m_loop);
if (err) {
LOGWARN(1, "uv_loop_close returned error " << uv_err_name(err));
}
LOGINFO(1, "event loop stopped");
}
@@ -426,17 +436,19 @@ bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t h
const uint8_t* data = reinterpret_cast<const uint8_t*>(data_ptr);
const uint8_t major_version = data[0];
char buf[log::Stream::BUF_SIZE + 1];
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\":\"\"}}";
",\"seed_hash\":\"\"}}\0";
volatile int result = 0;
volatile bool done = false;
JSONRPCRequest::call(m_pool->params().m_host.c_str(), m_pool->params().m_rpcPort, buf,
const Params& params = m_pool->params();
JSONRPCRequest::call(params.m_host, params.m_rpcPort, buf, params.m_rpcLogin, params.m_socks5Proxy,
[&result, &h](const char* data, size_t size)
{
rapidjson::Document doc;
+3 -1
View File
@@ -100,7 +100,7 @@ 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;
bool calculate(const void* data_ptr, size_t size, uint64_t height, const hash& seed, hash& h) override;
private:
static void loop(void* data);
@@ -122,6 +122,8 @@ private:
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);
}
};
+471 -145
View File
@@ -32,26 +32,23 @@
#include "stratum_server.h"
#include "params.h"
#include "json_parsers.h"
#include "crypto.h"
#include "hardforks/hardforks.h"
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include <fstream>
#include <iterator>
#include <numeric>
// Only uncomment it to debug issues with uncle/orphan blocks
//#define DEBUG_BROADCAST_DELAY_MS 100
#ifdef DEBUG_BROADCAST_DELAY_MS
#include <thread>
#endif
static constexpr char log_category_prefix[] = "SideChain ";
constexpr uint64_t MIN_DIFFICULTY = 100000;
constexpr size_t UNCLE_BLOCK_DEPTH = 3;
static constexpr uint64_t MIN_DIFFICULTY = 100000;
static constexpr size_t UNCLE_BLOCK_DEPTH = 3;
static_assert(1 <= UNCLE_BLOCK_DEPTH && UNCLE_BLOCK_DEPTH <= 10, "Invalid UNCLE_BLOCK_DEPTH");
static constexpr uint64_t MONERO_BLOCK_TIME = 120;
namespace p2pool {
static constexpr uint8_t default_consensus_id[HASH_SIZE] = { 34,175,126,231,181,11,104,146,227,153,218,107,44,108,68,39,178,81,4,212,169,4,142,0,177,110,157,240,68,7,249,24 };
@@ -68,6 +65,7 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
, m_chainWindowSize(2160)
, m_unclePenalty(20)
, m_curDifficulty(m_minDifficulty)
, m_precalcFinished(false)
{
LOGINFO(1, log::LightCyan() << "network type = " << m_networkType);
@@ -159,14 +157,45 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
memset(buf + 8, '*', HASH_SIZE * 2 - 16);
m_consensusIdDisplayStr.assign(buf);
LOGINFO(1, "consensus ID = " << log::LightCyan() << m_consensusIdDisplayStr.c_str());
uv_cond_init_checked(&m_precalcJobsCond);
uv_mutex_init_checked(&m_precalcJobsMutex);
m_precalcJobs.reserve(16);
uint32_t numThreads = std::thread::hardware_concurrency();
// Leave 1 CPU core free from worker threads
if (numThreads > 1) {
--numThreads;
}
// Use between 1 and 8 threads
if (numThreads < 1) numThreads = 1;
// Don't limit thread count when debugging because debug builds are slow
#ifndef P2POOL_DEBUGGING
if (numThreads > 8) numThreads = 8;
#endif
LOGINFO(4, "running " << numThreads << " pre-calculation workers");
m_precalcWorkers.reserve(numThreads);
for (uint32_t i = 0; i < numThreads; ++i) {
m_precalcWorkers.emplace_back(&SideChain::precalc_worker, this);
}
m_uniquePrecalcInputs = new unordered_set<size_t>();
}
SideChain::~SideChain()
{
finish_precalc();
uv_rwlock_destroy(&m_sidechainLock);
uv_mutex_destroy(&m_seenWalletsLock);
uv_mutex_destroy(&m_seenBlocksLock);
uv_rwlock_destroy(&m_curDifficultyLock);
for (const auto& it : m_blocksById) {
delete it.second;
}
@@ -287,7 +316,7 @@ P2PServer* SideChain::p2pServer() const
return m_pool ? m_pool->p2p_server() : nullptr;
}
bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) const
bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares) const
{
shares.clear();
shares.reserve(m_chainWindowSize * 2);
@@ -295,9 +324,9 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
// Collect shares from each block in the PPLNS window, starting from the "tip"
uint64_t block_depth = 0;
PoolBlock* cur = tip;
const PoolBlock* cur = tip;
do {
MinerShare cur_share{ cur->m_difficulty.lo, &cur->m_minerWallet };
MinerShare cur_share{ cur->m_difficulty, &cur->m_minerWallet };
for (const hash& uncle_id : cur->m_uncles) {
auto it = m_blocksById.find(uncle_id);
@@ -315,14 +344,9 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
}
// Take some % of uncle's weight into this share
uint64_t product[2];
product[0] = umul128(uncle->m_difficulty.lo, m_unclePenalty, &product[1]);
uint64_t rem;
const uint64_t uncle_penalty = udiv128(product[1], product[0], 100, &rem);
const difficulty_type uncle_penalty = uncle->m_difficulty * m_unclePenalty / 100;
cur_share.m_weight += uncle_penalty;
shares.emplace_back(uncle->m_difficulty.lo - uncle_penalty, &uncle->m_minerWallet);
shares.emplace_back(uncle->m_difficulty - uncle_penalty, &uncle->m_minerWallet);
}
shares.push_back(cur_share);
@@ -345,7 +369,7 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
}
cur = it->second;
} while (block_depth < m_chainWindowSize);
} while (true);
// Combine shares with the same wallet addresses
std::sort(shares.begin(), shares.end(), [](const auto& a, const auto& b) { return *a.m_wallet < *b.m_wallet; });
@@ -369,6 +393,50 @@ bool SideChain::get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) cons
return true;
}
bool SideChain::get_wallets(const PoolBlock* tip, std::vector<const Wallet*>& wallets) const
{
// Collect wallets from each block in the PPLNS window, starting from the "tip"
wallets.clear();
wallets.reserve(m_chainWindowSize * 2);
uint64_t block_depth = 0;
const PoolBlock* cur = tip;
do {
wallets.push_back(&cur->m_minerWallet);
for (const hash& uncle_id : cur->m_uncles) {
auto it = m_blocksById.find(uncle_id);
if (it == m_blocksById.end()) {
return false;
}
// Skip uncles which are already out of PPLNS window
if (tip->m_sidechainHeight - it->second->m_sidechainHeight < m_chainWindowSize) {
wallets.push_back(&it->second->m_minerWallet);
}
}
++block_depth;
if ((block_depth >= m_chainWindowSize) || (cur->m_sidechainHeight == 0)) {
break;
}
auto it = m_blocksById.find(cur->m_parent);
if (it == m_blocksById.end()) {
return false;
}
cur = it->second;
} while (true);
// Remove duplicates
std::sort(wallets.begin(), wallets.end(), [](const Wallet* a, const Wallet* b) { return *a < *b; });
wallets.erase(std::unique(wallets.begin(), wallets.end(), [](const Wallet* a, const Wallet* b) { return *a == *b; }), wallets.end());
return true;
}
bool SideChain::block_seen(const PoolBlock& block)
{
// Check if it's some old block
@@ -381,21 +449,19 @@ bool SideChain::block_seen(const PoolBlock& block)
// Check if it was received before
MutexLock lock(m_seenBlocksLock);
return !m_seenBlocks.insert(block.m_sidechainId).second;
return !m_seenBlocks.insert(block.get_full_id()).second;
}
void SideChain::unsee_block(const PoolBlock& block)
{
MutexLock lock(m_seenBlocksLock);
m_seenBlocks.erase(block.m_sidechainId);
m_seenBlocks.erase(block.get_full_id());
}
extern const char* BLOCK_FOUND;
bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks)
{
if (block.m_difficulty < m_minDifficulty) {
LOGWARN(3, "add_external_block: block has invalid difficulty " << block.m_difficulty << ", expected >= " << m_minDifficulty);
LOGWARN(3, "add_external_block: block mined by " << block.m_minerWallet << " has invalid difficulty " << block.m_difficulty << ", expected >= " << m_minDifficulty);
return false;
}
@@ -428,7 +494,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
LOGINFO(4, "add_external_block: height = " << block.m_sidechainHeight << ", id = " << block.m_sidechainId << ", mainchain height = " << block.m_txinGenHeight);
if (too_low_diff) {
LOGWARN(4, "add_external_block: block has too low difficulty " << block.m_difficulty << ", expected >= ~" << expected_diff << ". Ignoring it.");
LOGWARN(4, "add_external_block: block mined by " << block.m_minerWallet << " has too low difficulty " << block.m_difficulty << ", expected >= ~" << expected_diff << ". Ignoring it.");
return true;
}
@@ -436,7 +502,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
ChainMain data;
if (m_pool->chainmain_get_by_hash(block.m_prevId, data)) {
if (data.height + 1 != block.m_txinGenHeight) {
LOGWARN(3, "add_external_block: wrong mainchain height " << block.m_txinGenHeight << ", expected " << data.height + 1);
LOGWARN(3, "add_external_block mined by " << block.m_minerWallet << ": wrong mainchain height " << block.m_txinGenHeight << ", expected " << data.height + 1);
return false;
}
}
@@ -446,7 +512,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
hash seed;
if (!m_pool->get_seed(block.m_txinGenHeight, seed)) {
LOGWARN(3, "add_external_block: couldn't get seed hash for mainchain height " << block.m_txinGenHeight);
LOGWARN(3, "add_external_block mined by " << block.m_minerWallet << ": couldn't get seed hash for mainchain height " << block.m_txinGenHeight);
unsee_block(block);
return false;
}
@@ -462,7 +528,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
MinerData miner_data = m_pool->miner_data();
if ((block.m_prevId == miner_data.prev_id) && miner_data.difficulty.check_pow(pow_hash)) {
LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero network, submitting it");
m_pool->submit_block_async(block.m_mainChainData);
m_pool->submit_block_async(block.serialize_mainchain_data());
}
else {
difficulty_type diff;
@@ -471,12 +537,12 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
}
else if (diff.check_pow(pow_hash)) {
LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero height " << block.m_txinGenHeight << ", submitting it");
m_pool->submit_block_async(block.m_mainChainData);
m_pool->submit_block_async(block.serialize_mainchain_data());
}
}
if (!block.m_difficulty.check_pow(pow_hash)) {
LOGWARN(3, "add_external_block: not enough PoW for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight);
LOGWARN(3, "add_external_block mined by " << block.m_minerWallet << ": not enough PoW for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight);
return false;
}
@@ -501,15 +567,19 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
data = m_watchBlock;
block_found = true;
const uint64_t payout = block.get_payout(m_pool->params().m_wallet);
const Wallet& w = m_pool->params().m_wallet;
const uint64_t payout = block.get_payout(w);
if (payout) {
LOGINFO(0, log::LightCyan() << "You received a payout of " << log::LightGreen() << log::XMRAmount(payout) << log::LightCyan() << " in block " << log::LightGreen() << data.height);
LOGINFO(0, log::LightCyan() << "Your wallet " << log::LightGreen() << w << log::LightCyan() << " got a payout of " << log::LightGreen() << log::XMRAmount(payout) << log::LightCyan() << " in block " << log::LightGreen() << data.height);
}
else {
LOGINFO(0, log::LightCyan() << "Your wallet " << log::LightYellow() << w << log::LightCyan() << " didn't get a payout in block " << log::LightYellow() << data.height << log::LightCyan() << " because you had no shares in PPLNS window");
}
}
}
if (block_found) {
m_pool->api_update_block_found(&data);
m_pool->api_update_block_found(&data, &block);
}
add_block(block);
@@ -534,10 +604,20 @@ void SideChain::add_block(const PoolBlock& block)
auto result = m_blocksById.insert({ new_block->m_sidechainId, new_block });
if (!result.second) {
LOGWARN(3, "add_block: trying to add the same block twice, id = "
<< new_block->m_sidechainId << ", sidechain height = "
<< new_block->m_sidechainHeight << ", height = "
<< new_block->m_txinGenHeight);
const PoolBlock* old_block = result.first->second;
LOGWARN(3, "add_block: trying to add the same block twice:"
<< "\nnew block id = " << new_block->m_sidechainId
<< ", sidechain height = " << new_block->m_sidechainHeight
<< ", height = " << new_block->m_txinGenHeight
<< ", nonce = " << new_block->m_nonce
<< ", extra_nonce = " << new_block->m_extraNonce
<< "\nold block id = " << old_block->m_sidechainId
<< ", sidechain height = " << old_block->m_sidechainHeight
<< ", height = " << old_block->m_txinGenHeight
<< ", nonce = " << old_block->m_nonce
<< ", extra_nonce = " << old_block->m_extraNonce
);
delete new_block;
return;
@@ -545,6 +625,9 @@ void SideChain::add_block(const PoolBlock& block)
m_blocksByHeight[new_block->m_sidechainHeight].push_back(new_block);
// Pre-calculate eph_public_keys during initial sync
launch_precalc(new_block);
update_depths(new_block);
if (new_block->m_verified) {
@@ -607,50 +690,122 @@ bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob) const
return false;
}
blob.reserve(block->m_mainChainData.size() + block->m_sideChainData.size());
blob = block->serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = block->serialize_sidechain_data();
blob.insert(blob.end(), sidechain_data.begin(), sidechain_data.end());
blob = block->m_mainChainData;
blob.insert(blob.end(), block->m_sideChainData.begin(), block->m_sideChainData.end());
return true;
}
bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob) const
bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob, uv_loop_t* loop) const
{
blob.clear();
ReadLock lock(m_sidechainLock);
auto it = m_blocksById.find(block->m_sidechainId);
if (it != m_blocksById.end()) {
PoolBlock* b = it->second;
const size_t n = b->m_outputs.size();
blob.reserve(n * 39 + 64);
writeVarint(n, blob);
for (const PoolBlock::TxOutput& output : b->m_outputs) {
writeVarint(output.m_reward, blob);
blob.emplace_back(output.m_txType);
blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
blob.emplace_back(output.m_viewTag);
}
}
block->m_outputs = b->m_outputs;
return true;
}
std::vector<MinerShare> tmpShares;
std::vector<uint64_t> tmpRewards;
{
ReadLock lock(m_sidechainLock);
if (!get_shares(block, tmpShares) || !split_reward(total_reward, tmpShares, tmpRewards) || (tmpRewards.size() != tmpShares.size())) {
return false;
auto it = m_blocksById.find(block->m_sidechainId);
if (it != m_blocksById.end()) {
PoolBlock* b = it->second;
const size_t n = b->m_outputs.size();
blob.reserve(n * 39 + 64);
writeVarint(n, blob);
const uint8_t tx_type = b->get_tx_type();
for (const PoolBlock::TxOutput& output : b->m_outputs) {
writeVarint(output.m_reward, blob);
blob.emplace_back(tx_type);
blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
if (tx_type == TXOUT_TO_TAGGED_KEY) {
blob.emplace_back(static_cast<uint8_t>(output.m_viewTag));
}
}
block->m_outputs = b->m_outputs;
return true;
}
if (!get_shares(block, tmpShares) || !split_reward(total_reward, tmpShares, tmpRewards) || (tmpRewards.size() != tmpShares.size())) {
return false;
}
}
const size_t n = tmpShares.size();
// Helper jobs call get_eph_public_key with indices in descending order
// Current thread will process indices in ascending order so when they meet, everything will be cached
std::atomic<int> counter{ 0 };
std::atomic<int> num_helper_jobs_finished{ 0 };
int num_helper_jobs_started = 0;
if (loop) {
uint32_t HELPER_JOBS_COUNT = std::thread::hardware_concurrency();
// this thread will also be running, so reduce helper job count by 1
if (HELPER_JOBS_COUNT > 0) {
--HELPER_JOBS_COUNT;
}
// No more than 8 helper jobs because our UV worker thread pool has 8 threads
if (HELPER_JOBS_COUNT > 8) {
HELPER_JOBS_COUNT = 8;
}
struct Work
{
uv_work_t req;
const std::vector<MinerShare>& tmpShares;
const hash& txkeySec;
std::atomic<int>& counter;
std::atomic<int>& num_helper_jobs_finished;
// Fix MSVC warnings
Work() = delete;
Work& operator=(Work&&) = delete;
};
counter = static_cast<int>(n) - 1;
num_helper_jobs_started = HELPER_JOBS_COUNT;
for (size_t i = 0; i < HELPER_JOBS_COUNT; ++i) {
Work* w = new Work{ {}, tmpShares, block->m_txkeySec, counter, num_helper_jobs_finished };
w->req.data = w;
const int err = uv_queue_work(loop, &w->req,
[](uv_work_t* req)
{
Work* work = reinterpret_cast<Work*>(req->data);
hash eph_public_key;
int index;
while ((index = work->counter.fetch_sub(1)) >= 0) {
uint8_t view_tag;
if (!work->tmpShares[index].m_wallet->get_eph_public_key(work->txkeySec, static_cast<size_t>(index), eph_public_key, view_tag)) {
LOGWARN(6, "get_eph_public_key failed at index " << index);
}
}
++work->num_helper_jobs_finished;
},
[](uv_work_t* req, int /*status*/)
{
delete reinterpret_cast<Work*>(req->data);
});
if (err) {
LOGERR(1, "get_outputs_blob: uv_queue_work failed, error " << uv_err_name(err));
--num_helper_jobs_started;
delete w;
}
}
}
blob.reserve(n * 39 + 64);
writeVarint(n, blob);
@@ -662,6 +817,13 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
hash eph_public_key;
for (size_t i = 0; i < n; ++i) {
// stop helper jobs when they meet with current thread
const int c = counter.load();
if ((c >= 0) && (static_cast<int>(i) >= c)) {
// this will cause all helper jobs to finish immediately
counter = -1;
}
writeVarint(tmpRewards[i], blob);
blob.emplace_back(tx_type);
@@ -676,26 +838,38 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
blob.emplace_back(view_tag);
}
block->m_outputs.emplace_back(tmpRewards[i], eph_public_key, tx_type, view_tag);
block->m_outputs.emplace_back(tmpRewards[i], eph_public_key, view_tag);
}
block->m_outputs.shrink_to_fit();
if (loop) {
// this will cause all helper jobs to finish immediately
counter = -1;
while (num_helper_jobs_finished < num_helper_jobs_started) {
std::this_thread::yield();
}
}
return true;
}
void SideChain::print_status() const
void SideChain::print_status(bool obtain_sidechain_lock) const
{
std::vector<hash> blocks_in_window;
blocks_in_window.reserve(m_chainWindowSize * 9 / 8);
const difficulty_type diff = difficulty();
ReadLock lock(m_sidechainLock);
if (obtain_sidechain_lock) uv_rwlock_rdlock(&m_sidechainLock);
ON_SCOPE_LEAVE([this, obtain_sidechain_lock]() { if (obtain_sidechain_lock) uv_rwlock_rdunlock(&m_sidechainLock); });
uint64_t rem;
uint64_t pool_hashrate = udiv128(diff.hi, diff.lo, m_targetBlockTime, &rem);
difficulty_type network_diff = m_pool->miner_data().difficulty;
uint64_t network_hashrate = udiv128(network_diff.hi, network_diff.lo, 120, &rem);
uint64_t network_hashrate = udiv128(network_diff.hi, network_diff.lo, MONERO_BLOCK_TIME, &rem);
const PoolBlock* tip = m_chainTip;
@@ -769,13 +943,16 @@ void SideChain::print_status() const
}
const Wallet& w = m_pool->params().m_wallet;
const uint8_t tx_type = tip->get_tx_type();
hash eph_public_key;
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
const PoolBlock::TxOutput& out = tip->m_outputs[i];
if (!your_reward) {
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
if (tx_type == TXOUT_TO_TAGGED_KEY) {
uint8_t view_tag;
const uint8_t expected_view_tag = out.m_viewTag;
if (w.get_eph_public_key(tip->m_txkeySec, i, eph_public_key, view_tag, &expected_view_tag) && (out.m_ephPublicKey == eph_public_key)) {
your_reward = out.m_reward;
}
}
@@ -826,6 +1003,7 @@ void SideChain::print_status() const
"\nSide chain hashrate = " << log::Hashrate(pool_hashrate) <<
(hashrate_est ? "\nYour hashrate (pool-side) = " : "") << (hashrate_est ? log::Hashrate(hashrate_est) : log::Hashrate()) <<
"\nPPLNS window = " << total_blocks_in_window << " blocks (+" << total_uncles_in_window << " uncles, " << total_orphans << " orphans)" <<
"\nYour wallet address = " << m_pool->params().m_wallet <<
"\nYour shares = " << our_blocks_in_window_total << " blocks (+" << our_uncles_in_window_total << " uncles, " << our_orphans << " orphans)"
<< our_blocks_in_window_chart << our_uncles_in_window_chart <<
"\nBlock reward share = " << block_share << "% (" << log::XMRAmount(your_reward) << ')'
@@ -841,12 +1019,15 @@ double SideChain::get_reward_share(const Wallet& w) const
const PoolBlock* tip = m_chainTip;
if (tip) {
const uint8_t tx_type = tip->get_tx_type();
hash eph_public_key;
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
const PoolBlock::TxOutput& out = tip->m_outputs[i];
if (!reward) {
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
if (tx_type == TXOUT_TO_TAGGED_KEY) {
uint8_t view_tag;
const uint8_t expected_view_tag = out.m_viewTag;
if (w.get_eph_public_key(tip->m_txkeySec, i, eph_public_key, view_tag, &expected_view_tag) && (out.m_ephPublicKey == eph_public_key)) {
reward = out.m_reward;
}
}
@@ -864,6 +1045,37 @@ double SideChain::get_reward_share(const Wallet& w) const
return total_reward ? (static_cast<double>(reward) / static_cast<double>(total_reward)) : 0.0;
}
uint64_t SideChain::network_major_version(uint64_t height) const
{
const hardfork_t* hard_forks;
size_t num_hard_forks;
switch (m_networkType)
{
case NetworkType::Mainnet:
default:
hard_forks = mainnet_hard_forks;
num_hard_forks = num_mainnet_hard_forks;
break;
case NetworkType::Testnet:
hard_forks = testnet_hard_forks;
num_hard_forks = num_testnet_hard_forks;
break;
case NetworkType::Stagenet:
hard_forks = stagenet_hard_forks;
num_hard_forks = num_stagenet_hard_forks;
break;
}
uint64_t result = 1;
for (size_t i = 1; (i < num_hard_forks) && (height >= hard_forks[i].height); ++i) {
result = hard_forks[i].version;
}
return result;
}
difficulty_type SideChain::total_hashes() const
{
const PoolBlock* tip = m_chainTip;
@@ -912,9 +1124,9 @@ bool SideChain::split_reward(uint64_t reward, const std::vector<MinerShare>& sha
{
const size_t num_shares = shares.size();
const uint64_t total_weight = std::accumulate(shares.begin(), shares.end(), 0ULL, [](uint64_t a, const MinerShare& b) { return a + b.m_weight; });
const difficulty_type total_weight = std::accumulate(shares.begin(), shares.end(), difficulty_type(), [](const difficulty_type& a, const MinerShare& b) { return a + b.m_weight; });
if (total_weight == 0) {
if (total_weight.empty()) {
LOGERR(1, "total_weight is 0. Check the code!");
return false;
}
@@ -923,18 +1135,14 @@ bool SideChain::split_reward(uint64_t reward, const std::vector<MinerShare>& sha
rewards.reserve(num_shares);
// Each miner gets a proportional fraction of the block reward
uint64_t w = 0;
difficulty_type w;
uint64_t reward_given = 0;
for (uint64_t i = 0; i < num_shares; ++i) {
w += shares[i].m_weight;
uint64_t hi;
const uint64_t lo = umul128(w, reward, &hi);
uint64_t rem;
const uint64_t next_value = udiv128(hi, lo, total_weight, &rem);
rewards.emplace_back(next_value - reward_given);
reward_given = next_value;
const difficulty_type next_value = w * reward / total_weight;
rewards.emplace_back(next_value.lo - reward_given);
reward_given = next_value.lo;
}
// Double check that we gave out the exact amount
@@ -946,11 +1154,11 @@ bool SideChain::split_reward(uint64_t reward, const std::vector<MinerShare>& sha
return true;
}
bool SideChain::get_difficulty(PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const
bool SideChain::get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const
{
difficultyData.clear();
PoolBlock* cur = tip;
const PoolBlock* cur = tip;
uint64_t oldest_timestamp = std::numeric_limits<uint64_t>::max();
uint64_t block_depth = 0;
@@ -1029,20 +1237,7 @@ bool SideChain::get_difficulty(PoolBlock* tip, std::vector<DifficultyData>& diff
}
}
// This is correct as long as the difference between two 128-bit difficulties is less than 2^64, even if it wraps
const uint64_t delta_diff = diff2.lo - diff1.lo;
uint64_t product[2];
product[0] = umul128(delta_diff, m_targetBlockTime, &product[1]);
if (product[1] >= delta_t) {
LOGERR(1, "calculated difficulty is too high for block at height = " << tip->m_sidechainHeight << ", id = " << tip->m_sidechainId << ", mainchain height = " << tip->m_txinGenHeight);
return false;
}
uint64_t rem;
curDifficulty.lo = udiv128(product[1], product[0], delta_t, &rem);
curDifficulty.hi = 0;
curDifficulty = (diff2 - diff1) * m_targetBlockTime / delta_t;
if (curDifficulty < m_minDifficulty) {
curDifficulty = m_minDifficulty;
@@ -1078,7 +1273,7 @@ void SideChain::verify_loop(PoolBlock* block)
if (block->m_invalid) {
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
", id = " << block->m_sidechainId <<
", mainchain height = " << block->m_txinGenHeight << " is invalid");
", mainchain height = " << block->m_txinGenHeight << ", mined by " << block->m_minerWallet << " is invalid");
}
else {
LOGINFO(3, "verified block at height = " << block->m_sidechainHeight <<
@@ -1105,7 +1300,10 @@ void SideChain::verify_loop(PoolBlock* block)
if (block->m_wantBroadcast && !block->m_broadcasted) {
block->m_broadcasted = true;
if (server && (block->m_depth < UNCLE_BLOCK_DEPTH)) {
server->broadcast(*block);
if (m_pool && (block->m_minerWallet == m_pool->params().m_wallet)) {
LOGINFO(0, log::Green() << "SHARE ADDED: height = " << block->m_sidechainHeight << ", id = " << block->m_sidechainId << ", mainchain height = " << block->m_txinGenHeight);
}
server->broadcast(*block, get_parent(block));
}
}
@@ -1396,6 +1594,8 @@ void SideChain::verify(PoolBlock* block)
return;
}
const uint8_t tx_type = block->get_tx_type();
for (size_t i = 0, n = rewards.size(); i < n; ++i) {
const PoolBlock::TxOutput& out = block->m_outputs[i];
@@ -1419,7 +1619,7 @@ void SideChain::verify(PoolBlock* block)
return;
}
if ((out.m_txType == TXOUT_TO_TAGGED_KEY) && (out.m_viewTag != view_tag)) {
if ((tx_type == TXOUT_TO_TAGGED_KEY) && (out.m_viewTag != view_tag)) {
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
", id = " << block->m_sidechainId <<
", mainchain height = " << block->m_txinGenHeight <<
@@ -1442,7 +1642,7 @@ void SideChain::verify(PoolBlock* block)
block->m_invalid = false;
}
void SideChain::update_chain_tip(PoolBlock* block)
void SideChain::update_chain_tip(const PoolBlock* block)
{
if (!block->m_verified || block->m_invalid) {
LOGERR(1, "trying to update chain tip to an unverified or invalid block, fix the code!");
@@ -1460,7 +1660,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
if (is_longer_chain(tip, block, is_alternative)) {
difficulty_type diff;
if (get_difficulty(block, m_difficultyData, diff)) {
m_chainTip = block;
m_chainTip = const_cast<PoolBlock*>(block);
{
WriteLock lock(m_curDifficultyLock);
m_curDifficulty = diff;
@@ -1472,12 +1672,16 @@ void SideChain::update_chain_tip(PoolBlock* block)
block->m_wantBroadcast = true;
if (m_pool) {
m_pool->update_block_template_async();
m_pool->update_block_template_async(is_alternative);
// Reset stratum share counters when switching to an alternative chain to avoid confusion
StratumServer* s = m_pool->stratum_server();
if (s && is_alternative) {
s->reset_share_counters();
if (is_alternative) {
StratumServer* s = m_pool->stratum_server();
if (s) {
s->reset_share_counters();
}
// Also clear cache because it has data from all old blocks now
clear_crypto_cache();
LOGINFO(0, log::LightCyan() << "SYNCHRONIZED");
}
}
@@ -1498,36 +1702,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
if (p2pServer() && block->m_wantBroadcast && !block->m_broadcasted) {
block->m_broadcasted = true;
#ifdef DEBUG_BROADCAST_DELAY_MS
struct Work
{
uv_work_t req;
P2PServer* server;
PoolBlock* block;
};
Work* work = new Work{};
work->req.data = work;
work->server = p2pServer();
work->block = block;
const int err = uv_queue_work(uv_default_loop(), &work->req,
[](uv_work_t*)
{
num_running_jobs.fetch_add(1);
std::this_thread::sleep_for(std::chrono::milliseconds(DEBUG_BROADCAST_DELAY_MS));
},
[](uv_work_t* req, int)
{
Work* work = reinterpret_cast<Work*>(req->data);
work->server->broadcast(*work->block);
delete reinterpret_cast<Work*>(req->data);
num_running_jobs.fetch_sub(1);
});
if (err) {
LOGERR(1, "update_chain_tip: uv_queue_work failed, error " << uv_err_name(err));
}
#else
p2pServer()->broadcast(*block);
#endif
p2pServer()->broadcast(*block, get_parent(block));
}
}
@@ -1579,6 +1754,7 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
}
}
// cppcheck-suppress knownConditionTrueFalse
while (block_ancestor && candidate_ancestor) {
if (block_ancestor->m_parent == candidate_ancestor->m_parent) {
// If they are really on the same chain, we can just compare cumulative difficulties
@@ -1599,6 +1775,7 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
const PoolBlock* new_chain = candidate;
uint64_t candidate_mainchain_height = 0;
uint64_t candidate_mainchain_min_height = 0;
hash mainchain_prev_id;
for (uint64_t i = 0; (i < m_chainWindowSize) && (old_chain || new_chain); ++i) {
@@ -1608,6 +1785,7 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
}
if (new_chain) {
candidate_mainchain_min_height = candidate_mainchain_min_height ? std::min(candidate_mainchain_min_height, new_chain->m_txinGenHeight) : new_chain->m_txinGenHeight;
candidate_total_diff += new_chain->m_difficulty;
ChainMain data;
@@ -1631,6 +1809,12 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
return false;
}
const uint64_t limit = m_chainWindowSize * 4 * m_targetBlockTime / MONERO_BLOCK_TIME;
if (candidate_mainchain_min_height + limit < data.height) {
LOGWARN(3, "received a longer alternative chain but it's stale: min height " << candidate_mainchain_min_height << ", must be >= " << (data.height - limit));
return false;
}
LOGINFO(3, "received a longer alternative chain: height " <<
log::Gray() << block->m_sidechainHeight << log::NoColor() << " -> " <<
log::Gray() << candidate->m_sidechainHeight << log::NoColor() << ", cumulative difficulty " <<
@@ -1705,7 +1889,7 @@ void SideChain::update_depths(PoolBlock* block)
void SideChain::prune_old_blocks()
{
// Leave 2 minutes worth of spare blocks in addition to 2xPPLNS window for lagging nodes which need to sync
const uint64_t prune_distance = m_chainWindowSize * 2 + 120 / m_targetBlockTime;
const uint64_t prune_distance = m_chainWindowSize * 2 + MONERO_BLOCK_TIME / m_targetBlockTime;
// Remove old blocks from alternative unconnected chains after long enough time
const uint64_t cur_time = seconds_since_epoch();
@@ -1760,6 +1944,9 @@ void SideChain::prune_old_blocks()
if (p2pServer()) {
p2pServer()->clear_cached_blocks();
}
// Pre-calc workers are not needed anymore
finish_precalc();
}
}
@@ -1778,9 +1965,18 @@ void SideChain::get_missing_blocks(std::vector<hash>& missing_blocks) const
missing_blocks.push_back(b.second->m_parent);
}
int num_missing_uncles = 0;
for (const hash& h : b.second->m_uncles) {
if (!h.empty() && (m_blocksById.find(h) == m_blocksById.end())) {
missing_blocks.push_back(h);
// Get no more than 2 first missing uncles at a time from each block
// Blocks with more than 2 uncles are very rare and they will be processed in several steps
++num_missing_uncles;
if (num_missing_uncles >= 2) {
break;
}
}
}
}
@@ -1828,7 +2024,7 @@ bool SideChain::load_config(const std::string& filename)
return true;
}
bool SideChain::check_config()
bool SideChain::check_config() const
{
if (m_poolName.empty()) {
LOGERR(1, "name can't be empty");
@@ -1845,8 +2041,8 @@ bool SideChain::check_config()
return false;
}
if ((m_targetBlockTime < 1) || (m_targetBlockTime > 120)) {
LOGERR(1, "block_time is invalid (must be between 1 and 120)");
if ((m_targetBlockTime < 1) || (m_targetBlockTime > MONERO_BLOCK_TIME)) {
LOGERR(1, "block_time is invalid (must be between 1 and " << MONERO_BLOCK_TIME << ")");
return false;
}
@@ -1877,4 +2073,134 @@ bool SideChain::check_config()
return true;
}
void SideChain::launch_precalc(const PoolBlock* block)
{
if (m_precalcFinished) {
return;
}
for (int h = UNCLE_BLOCK_DEPTH - 1; h >= 0; --h) {
auto it = m_blocksByHeight.find(block->m_sidechainHeight + m_chainWindowSize + h - 1);
if (it == m_blocksByHeight.end()) {
continue;
}
for (PoolBlock* b : it->second) {
if (b->m_precalculated) {
continue;
}
std::vector<const Wallet*> wallets;
if (get_wallets(b, wallets)) {
b->m_precalculated = true;
PrecalcJob* job = new PrecalcJob{ b, std::move(wallets) };
{
MutexLock lock2(m_precalcJobsMutex);
m_precalcJobs.push_back(job);
}
uv_cond_signal(&m_precalcJobsCond);
}
}
}
}
void SideChain::precalc_worker()
{
do {
PrecalcJob* job;
{
MutexLock lock(m_precalcJobsMutex);
if (m_precalcFinished) {
return;
}
while (m_precalcJobs.empty()) {
uv_cond_wait(&m_precalcJobsCond, &m_precalcJobsMutex);
if (m_precalcFinished) {
return;
}
}
job = m_precalcJobs.back();
m_precalcJobs.pop_back();
// Filter out duplicate inputs for get_eph_public_key()
uint8_t t[HASH_SIZE * 2 + sizeof(size_t)];
memcpy(t, job->b->m_txkeySec.h, HASH_SIZE);
for (size_t i = 0, n = job->wallets.size(); i < n; ++i) {
memcpy(t + HASH_SIZE, job->wallets[i]->view_public_key().h, HASH_SIZE);
memcpy(t + HASH_SIZE * 2, &i, sizeof(i));
if (!m_uniquePrecalcInputs->insert(robin_hood::hash_bytes(t, array_size(t))).second) {
job->wallets[i] = nullptr;
}
}
}
for (size_t i = 0, n = job->wallets.size(); i < n; ++i) {
if (job->wallets[i]) {
hash eph_public_key;
uint8_t view_tag;
job->wallets[i]->get_eph_public_key(job->b->m_txkeySec, i, eph_public_key, view_tag);
}
}
delete job;
} while (true);
}
void SideChain::finish_precalc()
{
if (m_precalcFinished.exchange(true)) {
return;
}
try
{
{
MutexLock lock(m_precalcJobsMutex);
for (PrecalcJob* job : m_precalcJobs) {
delete job;
}
m_precalcJobs.clear();
m_precalcJobs.shrink_to_fit();
uv_cond_broadcast(&m_precalcJobsCond);
}
for (std::thread& t : m_precalcWorkers) {
t.join();
}
m_precalcWorkers.clear();
m_precalcWorkers.shrink_to_fit();
delete m_uniquePrecalcInputs;
m_uniquePrecalcInputs = nullptr;
uv_mutex_destroy(&m_precalcJobsMutex);
uv_cond_destroy(&m_precalcJobsCond);
// Also clear cache because it has data from all old blocks now
clear_crypto_cache();
LOGINFO(4, "pre-calculation workers stopped");
}
catch (const std::exception& e)
{
LOGERR(1, "exception in finish_precalc(): " << e.what());
}
#ifdef DEV_TEST_SYNC
if (m_pool) {
LOGINFO(0, log::LightGreen() << "[DEV] Synchronization finished successfully, stopping P2Pool now");
print_status(false);
P2PServer* server = m_pool->p2p_server();
if (server) {
server->print_status();
server->print_bans();
server->show_peers_async();
}
m_pool->stop();
}
#endif
}
} // namespace p2pool
+36 -15
View File
@@ -18,26 +18,25 @@
#pragma once
#include "uv_util.h"
#include "pool_block.h"
#include <map>
#include <thread>
namespace p2pool {
class p2pool;
class P2PServer;
struct DifficultyData;
struct PoolBlock;
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() : m_weight(), m_wallet(nullptr) {}
FORCEINLINE MinerShare(const difficulty_type& w, const Wallet* x) : m_weight(w), m_wallet(x) {}
uint64_t m_weight;
Wallet* m_wallet;
difficulty_type m_weight;
const Wallet* m_wallet;
};
class SideChain
class SideChain : public nocopy_nomove
{
public:
SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr);
@@ -55,9 +54,9 @@ public:
void watch_mainchain_block(const ChainMain& data, const hash& possible_id);
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) const;
bool get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob, uv_loop_t* loop) const;
void print_status() const;
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
@@ -66,6 +65,7 @@ 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; }
uint64_t network_major_version(uint64_t height) const;
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; }
@@ -75,6 +75,7 @@ public:
bool is_mini() const;
const PoolBlock* chainTip() const { return m_chainTip; }
bool precalcFinished() const { return m_precalcFinished.load(); }
static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards);
@@ -84,11 +85,12 @@ private:
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);
void update_chain_tip(const PoolBlock* block);
PoolBlock* get_parent(const PoolBlock* block) const;
// Checks if "candidate" has longer (higher difficulty) chain than "block"
@@ -97,7 +99,7 @@ private:
void prune_old_blocks();
bool load_config(const std::string& filename);
bool check_config();
bool check_config() const;
mutable uv_rwlock_t m_sidechainLock;
std::atomic<PoolBlock*> m_chainTip;
@@ -109,7 +111,7 @@ private:
uint64_t m_seenWalletsLastPruneTime;
uv_mutex_t m_seenBlocksLock;
unordered_set<hash> m_seenBlocks;
unordered_set<PoolBlock::full_id> m_seenBlocks;
std::vector<DifficultyData> m_difficultyData;
@@ -128,6 +130,25 @@ private:
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
+100 -53
View File
@@ -31,7 +31,11 @@ static constexpr uint64_t MIN_DIFF = 1000;
static constexpr uint64_t AUTO_DIFF_TARGET_TIME = 30;
// Use short target format (4 bytes) for diff <= 4 million
static constexpr uint64_t TARGET_4_BYTES_LIMIT = std::numeric_limits<uint64_t>::max() / 4000000 + 1;
static constexpr uint64_t TARGET_4_BYTES_LIMIT = std::numeric_limits<uint64_t>::max() / 4000001;
static constexpr int32_t BAD_SHARE_POINTS = -5;
static constexpr int32_t GOOD_SHARE_POINTS = 1;
static constexpr int32_t BAN_THRESHOLD_POINTS = -15;
#include "tcp_server.inl"
@@ -41,7 +45,6 @@ StratumServer::StratumServer(p2pool* pool)
: TCPServer(StratumClient::allocate)
, m_pool(pool)
, m_autoDiff(pool->params().m_autoDiff)
, m_extraNonce(0)
, m_rng(RandomDeviceSeed::instance)
, m_cumulativeHashes(0)
, m_cumulativeHashesAtLastShare(0)
@@ -56,6 +59,8 @@ StratumServer::StratumServer(p2pool* pool)
// Diffuse the initial state in case it has low quality
m_rng.discard(10000);
m_extraNonce = static_cast<uint32_t>(m_rng());
m_hashrateData[0] = { seconds_since_epoch(), 0 };
uv_mutex_init_checked(&m_blobsQueueLock);
@@ -80,8 +85,6 @@ StratumServer::StratumServer(p2pool* pool)
StratumServer::~StratumServer()
{
uv_close(reinterpret_cast<uv_handle_t*>(&m_blobsAsync), nullptr);
shutdown_tcp();
uv_mutex_destroy(&m_blobsQueueLock);
@@ -102,9 +105,12 @@ void StratumServer::on_block(const BlockTemplate& block)
LOGINFO(4, "no clients connected");
return;
}
m_extraNonce.exchange(num_connections);
const uint32_t extra_nonce_start = static_cast<uint32_t>(get_random64());
m_extraNonce.exchange(extra_nonce_start + num_connections);
BlobsData* blobs_data = new BlobsData{};
blobs_data->m_extraNonceStart = extra_nonce_start;
difficulty_type difficulty;
difficulty_type sidechain_difficulty;
@@ -114,7 +120,7 @@ void StratumServer::on_block(const BlockTemplate& block)
// Even if they do, they'll be added to the beginning of the list and will get their block template in on_login()
// We'll iterate through the list backwards so when we get to the beginning and run out of extra_nonce values, it'll be only new clients left
blobs_data->m_numClientsExpected = num_connections;
blobs_data->m_blobSize = block.get_hashing_blobs(0, num_connections, blobs_data->m_blobs, blobs_data->m_height, difficulty, sidechain_difficulty, blobs_data->m_seedHash, nonce_offset, blobs_data->m_templateId);
blobs_data->m_blobSize = block.get_hashing_blobs(extra_nonce_start, num_connections, blobs_data->m_blobs, blobs_data->m_height, difficulty, sidechain_difficulty, blobs_data->m_seedHash, nonce_offset, blobs_data->m_templateId);
// Integrity checks
if (blobs_data->m_blobSize < 76) {
@@ -132,7 +138,7 @@ void StratumServer::on_block(const BlockTemplate& block)
// Get first 8 bytes of the Merkle root hash from each blob
for (size_t i = 0; i < num_connections; ++i) {
blob_hashes.emplace_back(*reinterpret_cast<const uint64_t*>(data + i * size + 43));
blob_hashes.emplace_back(read_unaligned(reinterpret_cast<const uint64_t*>(data + i * size + 43)));
}
// Find duplicates
@@ -236,14 +242,14 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
const uint32_t extra_nonce = m_extraNonce.fetch_add(1);
uint8_t hashing_blob[128];
uint64_t height;
uint64_t height, sidechain_height;
difficulty_type difficulty;
difficulty_type sidechain_difficulty;
hash seed_hash;
size_t nonce_offset;
uint32_t template_id;
const size_t blob_size = m_pool->block_template().get_hashing_blob(extra_nonce, hashing_blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset, template_id);
const size_t blob_size = m_pool->block_template().get_hashing_blob(extra_nonce, hashing_blob, height, sidechain_height, difficulty, sidechain_difficulty, seed_hash, nonce_offset, template_id);
uint64_t target = std::max(difficulty.target(), sidechain_difficulty.target());
@@ -251,6 +257,10 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
LOGINFO(5, "client " << log::Gray() << static_cast<char*>(client->m_addrString) << " set custom difficulty " << client->m_customDiff);
target = std::max(target, client->m_customDiff.target());
}
else if (m_autoDiff) {
// Limit autodiff to 4000000 for maximum compatibility
target = std::max(target, TARGET_4_BYTES_LIMIT);
}
if (get_custom_user(login, client->m_customUser)) {
const char* s = client->m_customUser;
@@ -353,9 +363,10 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
if (found) {
BlockTemplate& block = m_pool->block_template();
uint64_t height, sidechain_height;
difficulty_type mainchain_diff, sidechain_diff;
if (!block.get_difficulties(template_id, mainchain_diff, sidechain_diff)) {
if (!block.get_difficulties(template_id, height, sidechain_height, mainchain_diff, sidechain_diff)) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
return send(client,
[id](void* buf, size_t buf_size)
@@ -368,9 +379,8 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
if (mainchain_diff.check_pow(resultHash)) {
const char* s = client->m_customUser;
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << " found a mainchain block, submitting it");
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << " found a mainchain block at height " << height << ", submitting it");
m_pool->submit_block_async(template_id, nonce, extra_nonce);
block.update_tx_keys();
}
SubmittedShare* share;
@@ -384,7 +394,8 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
}
if (target >= TARGET_4_BYTES_LIMIT) {
target = (target >> 32) << 32;
// "Low diff share" fix: adjust target to the same value as XMRig would use
target = std::numeric_limits<uint64_t>::max() / (std::numeric_limits<uint32_t>::max() / (target >> 32));
}
share->m_req.data = share;
@@ -400,12 +411,22 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
share->m_target = target;
share->m_resultHash = resultHash;
share->m_sidechainDifficulty = sidechain_diff;
share->m_mainchainHeight = height;
share->m_sidechainHeight = sidechain_height;
share->m_effort = -1.0;
share->m_timestamp = seconds_since_epoch();
uint64_t rem;
share->m_hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 1;
share->m_highEnoughDifficulty = sidechain_diff.check_pow(resultHash);
// Don't count shares that were found during sync
const SideChain& side_chain = m_pool->side_chain();
const PoolBlock* tip = side_chain.chainTip();
if (tip && (sidechain_height + side_chain.chain_window_size() < tip->m_sidechainHeight)) {
share->m_highEnoughDifficulty = false;
}
update_auto_diff(client, share->m_timestamp, share->m_hashes);
// If this share is below sidechain difficulty, process it in this thread because it'll be quick
@@ -428,7 +449,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
return true;
}
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a share with invalid job id");
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a share with invalid job id " << job_id << " (latest job sent has id " << client->m_perConnectionJobId << ')');
const bool result = send(client,
[id](void* buf, size_t buf_size)
@@ -499,6 +520,8 @@ void StratumServer::show_workers()
void StratumServer::reset_share_counters()
{
WriteLock lock(m_hashrateDataLock);
m_cumulativeHashes = 0;
m_cumulativeHashesAtLastShare = 0;
m_cumulativeFoundSharesDiff = 0.0;
@@ -511,6 +534,8 @@ void StratumServer::print_stratum_status() const
int64_t dt_15m, dt_1h, dt_24h;
uint64_t hashes_since_last_share;
double average_effort;
int shares_found;
{
ReadLock lock(m_hashrateDataLock);
@@ -532,24 +557,26 @@ void StratumServer::print_stratum_status() const
hashes_24h = head.m_cumulativeHashes - tail_24h.m_cumulativeHashes;
dt_24h = static_cast<int64_t>(head.m_timestamp - tail_24h.m_timestamp);
average_effort = 0.0;
const double diff = m_cumulativeFoundSharesDiff;
if (diff > 0.0) {
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / diff;
}
shares_found = m_totalFoundShares;
}
const uint64_t hashrate_15m = (dt_15m > 0) ? (hashes_15m / dt_15m) : 0;
const uint64_t hashrate_1h = (dt_1h > 0) ? (hashes_1h / dt_1h ) : 0;
const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0;
double average_effort = 0.0;
const double diff = m_cumulativeFoundSharesDiff;
if (diff > 0.0) {
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / diff;
}
LOGINFO(0, "status" <<
"\nHashrate (15m est) = " << log::Hashrate(hashrate_15m) <<
"\nHashrate (1h est) = " << log::Hashrate(hashrate_1h) <<
"\nHashrate (24h est) = " << log::Hashrate(hashrate_24h) <<
"\nTotal hashes = " << total_hashes <<
"\nShares found = " << m_totalFoundShares <<
"\nShares found = " << shares_found <<
"\nAverage effort = " << average_effort << '%' <<
"\nCurrent effort = " << static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double() << '%' <<
"\nConnections = " << m_numConnections.load() << " (" << m_numIncomingConnections.load() << " incoming)"
@@ -610,14 +637,14 @@ void StratumServer::update_auto_diff(StratumClient* client, const uint64_t times
if (k >= N) {
// Full window
const uint64_t dt = t2 - t1;
const uint16_t dt = t2 - t1;
client->m_autoDiff.lo = std::max<uint64_t>((client->m_autoDiffWindowHashes * AUTO_DIFF_TARGET_TIME) / (dt ? dt : 1), MIN_DIFF);
client->m_autoDiff.hi = 0;
}
else if (k >= 10) {
// Partial window
const uint64_t h0 = hash_uncompress(client->m_autoDiffData[0].m_hashes);
const uint64_t dt = client->m_autoDiffData[k].m_timestamp - client->m_autoDiffData[0].m_timestamp;
const uint16_t dt = client->m_autoDiffData[k].m_timestamp - client->m_autoDiffData[0].m_timestamp;
client->m_autoDiff.lo = std::max<uint64_t>(((client->m_autoDiffWindowHashes - h0) * AUTO_DIFF_TARGET_TIME) / (dt ? dt : 1), MIN_DIFF);
client->m_autoDiff.hi = 0;
@@ -653,9 +680,10 @@ void StratumServer::on_blobs_ready()
// Only send the latest blob
BlobsData* data = blobs_queue.back();
const uint32_t extra_nonce_start = data->m_extraNonceStart;
size_t numClientsProcessed = 0;
uint32_t extra_nonce = 0;
uint32_t num_sent = 0;
const uint64_t cur_time = seconds_since_epoch();
{
@@ -674,18 +702,21 @@ void StratumServer::on_blobs_ready()
continue;
}
if (extra_nonce >= data->m_numClientsExpected) {
if (num_sent >= data->m_numClientsExpected) {
// We don't have any more extra_nonce values available
continue;
}
uint8_t* hashing_blob = data->m_blobs.data() + extra_nonce * data->m_blobSize;
uint8_t* hashing_blob = data->m_blobs.data() + num_sent * data->m_blobSize;
uint64_t target = data->m_target;
if (client->m_customDiff.lo) {
target = std::max(target, client->m_customDiff.target());
}
else if (m_autoDiff) {
// Limit autodiff to 4000000 for maximum compatibility
target = std::max(target, TARGET_4_BYTES_LIMIT);
if (client->m_autoDiff.lo) {
const uint32_t k = client->m_autoDiffIndex;
const uint16_t elapsed_time = static_cast<uint16_t>(cur_time) - client->m_autoDiffData[(k - 1) % StratumClient::AUTO_DIFF_SIZE].m_timestamp;
@@ -712,7 +743,7 @@ void StratumServer::on_blobs_ready()
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE];
saved_job.job_id = job_id;
saved_job.extra_nonce = extra_nonce;
saved_job.extra_nonce = extra_nonce_start + num_sent;
saved_job.template_id = data->m_templateId;
saved_job.target = target;
}
@@ -738,7 +769,7 @@ void StratumServer::on_blobs_ready()
});
if (result) {
++extra_nonce;
++num_sent;
}
else {
client->close();
@@ -751,7 +782,7 @@ void StratumServer::on_blobs_ready()
}
}
LOGINFO(3, "sent new job to " << extra_nonce << '/' << numClientsProcessed << " clients");
LOGINFO(3, "sent new job to " << num_sent << '/' << numClientsProcessed << " clients");
}
void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp)
@@ -789,7 +820,7 @@ void StratumServer::on_share_found(uv_work_t* req)
{
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
if (share->m_highEnoughDifficulty) {
bkg_jobs_tracker.start("StratumServer::on_share_found");
BACKGROUND_JOB_START(StratumServer::on_share_found);
}
StratumClient* client = share->m_client;
@@ -833,19 +864,24 @@ void StratumServer::on_share_found(uv_work_t* req)
if (pow_hash != share->m_resultHash) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " submitted a share with invalid PoW");
share->m_result = SubmittedShare::Result::INVALID_POW;
client->m_score += BAD_SHARE_POINTS;
return;
}
const uint64_t n = server->m_cumulativeHashes + hashes;
client->m_score += GOOD_SHARE_POINTS;
const double diff = sidechain_difficulty.to_double();
const double effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
server->m_cumulativeHashesAtLastShare = n;
{
WriteLock lock(server->m_hashrateDataLock);
server->m_cumulativeFoundSharesDiff += diff;
++server->m_totalFoundShares;
const uint64_t n = server->m_cumulativeHashes + hashes;
share->m_effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
server->m_cumulativeHashesAtLastShare = n;
server->m_cumulativeFoundSharesDiff += diff;
++server->m_totalFoundShares;
}
const char* s = client->m_customUser;
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << ", effort " << effort << '%');
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
}
@@ -861,19 +897,23 @@ void StratumServer::on_share_found(uv_work_t* req)
else {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a low diff share");
share->m_result = SubmittedShare::Result::LOW_DIFF;
client->m_score += BAD_SHARE_POINTS;
}
}
void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
{
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
StratumClient* client = share->m_client;
if (share->m_highEnoughDifficulty) {
bkg_jobs_tracker.stop("StratumServer::on_share_found");
const char* s = client->m_customUser;
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << share->m_mainchainHeight << ", sidechain height " << share->m_sidechainHeight << ", diff " << share->m_sidechainDifficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << ", effort " << share->m_effort << '%');
BACKGROUND_JOB_STOP(StratumServer::on_share_found);
}
ON_SCOPE_LEAVE([share]() { share->m_server->m_submittedSharesPool.push_back(share); });
StratumClient* client = share->m_client;
StratumServer* server = share->m_server;
const bool bad_share = (share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW);
@@ -903,7 +943,7 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
return s.m_pos;
});
if (bad_share) {
if (bad_share && (client->m_score <= BAN_THRESHOLD_POINTS)) {
client->ban(DEFAULT_BAN_TIME);
client->close();
}
@@ -916,6 +956,11 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
}
}
void StratumServer::on_shutdown()
{
uv_close(reinterpret_cast<uv_handle_t*>(&m_blobsAsync), nullptr);
}
StratumServer::StratumClient::StratumClient()
: m_rpcId(0)
, m_perConnectionJobId(0)
@@ -927,6 +972,7 @@ StratumServer::StratumClient::StratumClient()
, m_customDiff{}
, m_autoDiff{}
, m_customUser{}
, m_score(0)
{
}
@@ -946,6 +992,8 @@ void StratumServer::StratumClient::reset()
m_customDiff = {};
m_autoDiff = {};
m_customUser[0] = '\0';
m_score = 0;
}
bool StratumServer::StratumClient::on_connect()
@@ -1035,12 +1083,9 @@ bool StratumServer::StratumClient::process_request(char* data, uint32_t /*size*/
LOGINFO(6, "incoming keepalive from " << log::Gray() << static_cast<char*>(m_addrString));
return true;
}
else {
LOGWARN(4, "client " << static_cast<char*>(m_addrString) << " invalid JSON request (unknown method)");
return false;
}
return true;
LOGWARN(4, "client " << static_cast<char*>(m_addrString) << " invalid JSON request (unknown method)");
return false;
}
bool StratumServer::StratumClient::process_login(rapidjson::Document& doc, uint32_t id)
@@ -1157,6 +1202,8 @@ void StratumServer::api_update_local_stats(uint64_t timestamp)
int64_t dt_15m, dt_1h, dt_24h;
uint64_t hashes_since_last_share;
double average_effort;
int shares_found;
{
ReadLock lock(m_hashrateDataLock);
@@ -1178,20 +1225,20 @@ void StratumServer::api_update_local_stats(uint64_t timestamp)
hashes_24h = head.m_cumulativeHashes - tail_24h.m_cumulativeHashes;
dt_24h = static_cast<int64_t>(head.m_timestamp - tail_24h.m_timestamp);
average_effort = 0.0;
const double diff = m_cumulativeFoundSharesDiff;
if (diff > 0.0) {
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / diff;
}
shares_found = m_totalFoundShares;
}
const uint64_t hashrate_15m = (dt_15m > 0) ? (hashes_15m / dt_15m) : 0;
const uint64_t hashrate_1h = (dt_1h > 0) ? (hashes_1h / dt_1h ) : 0;
const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0;
double average_effort = 0.0;
const double diff = m_cumulativeFoundSharesDiff;
if (diff > 0.0) {
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / diff;
}
int shares_found = m_totalFoundShares;
double current_effort = static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double();
int connections = m_numConnections;

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