Compare commits

..
174 Commits
Author SHA1 Message Date
wowario 157678705f auto initialize submodules 2023-01-27 14:01:31 +03:00
wowario 6741396687 wow 2023-01-27 14:01:22 +03:00
wowario 186e323380 add RandomWOW 2023-01-27 12:08:39 +03:00
wowario 5238897273 remove RandomX submodule 2023-01-27 11:52:15 +03:00
SChernykh 8876af53d8 SideChain: fixed potential division by 0 2023-01-26 21:42:15 +01:00
SChernykh 70f78ebfd1 cmake required version fixes 2023-01-26 20:19:57 +01:00
SChernykh be434329d1 Removed dl dependency 2023-01-26 15:46:43 +01:00
SChernykh 67f091b843 CI: added FreeBSD build 2023-01-26 12:56:46 +01:00
SChernykh 33a592ee52 Fixed v2 signaling 2023-01-25 14:43:55 +01:00
SChernykh a574e68bb7 StratumServer: check for bans before checking shares 2023-01-25 11:36:52 +01:00
SChernykh 001c9e937d Fixed use of destroyed object 2023-01-24 08:19:13 +01:00
SChernykh f01bd88edd Fixed PPLNS window calculation 2023-01-24 07:59:36 +01:00
SChernykh 8700edb330 Miner: added m_sharesFailed to api data 2023-01-23 16:17:17 +01:00
SChernykh 9b686673ae Fixed m_sharesFailed initialization 2023-01-23 13:13:08 +01:00
SChernykh c841a2d2a9 Set new testnet hardfork date 2023-01-22 16:24:36 +01:00
SChernykh 5e373b5304 Miner: count failed shares 2023-01-22 09:28:23 +01:00
SChernykh d332f6cf23 Fixed PPLNS window display in status 2023-01-21 23:09:57 +01:00
SChernykh 1c4806e0d7 Fixed m_txkeySecSeed in block template update 2023-01-21 22:48:51 +01:00
SChernykh f5ac485ac8 TCPServer: optimized internal write buffers 2023-01-20 17:03:59 +01:00
SChernykh 595196b5ec Reduced transaction time-to-include delay
Based on data from https://rucknium.me/posts/monero-pool-transaction-delay/
> 95 percent all transactions arrived at all five Monero nodes within a five-second interval.
2023-01-20 13:14:58 +01:00
SChernykh 85d177d394 Miner: update extra_nonce on each new block 2023-01-19 11:22:22 +01:00
SChernykh 4ee8890166 Fixed status command output 2023-01-19 00:04:35 +01:00
SChernykh 5e8858c40e Fixed warning level 2023-01-18 20:57:45 +01:00
SChernykh 420cb71990 Fixed min difficulty setting 2023-01-18 16:16:35 +01:00
SChernykh 451778d704 Optimized RNG usage 2023-01-17 16:24:15 +01:00
SChernykh c855bb769a PoolBlock: removed unnecessary mutex 2023-01-17 13:20:09 +01:00
SChernykh f95d56d1eb Fixed wrong type in uv_walk 2023-01-17 08:15:53 +01:00
SChernykh fe5e209eb1 Don't limit min difficulty on testnet 2023-01-16 18:34:48 +01:00
SChernykh f206e4b334 Testnet fork on January 21st 2023-01-16 15:36:21 +01:00
SChernykh 5bd518da4b Optimized BlockTemplate::update() 2023-01-16 11:17:41 +01:00
SChernykh ec8e40907d Fixed a crash in status command 2023-01-15 22:20:06 +01:00
SChernykh e828709090 Optimized SideChain::get_outputs_blob() 2023-01-15 22:00:44 +01:00
SChernykh d4329ae594 Show file:line where panic() was called from 2023-01-14 12:37:08 +01:00
SChernykh 2c4c2b8469 Fixed a rare crash on exit 2023-01-13 18:25:04 +01:00
SChernykh 14f667de74 Support dynamic PPLNS window in status 2023-01-13 17:43:56 +01:00
SChernykh 578baeae96 API: added pplnsWeight to pool stats 2023-01-13 16:01:35 +01:00
SChernykh 1ad3fdfd46 Added more errors checks to p2pool_api 2023-01-13 15:16:06 +01:00
SChernykh 5eaf5d1f51 Update README.md 2023-01-13 10:29:12 +01:00
SChernykh 8e7df958b8 Use extra_nonce to signal hardfork readiness 2023-01-12 10:19:29 +01:00
SChernykh 4f2ad9a93c Optimized get_shares 2023-01-10 22:17:17 +01:00
SChernykh 850eb8e6e0 Fixed submit_block 2023-01-09 22:27:06 +01:00
SChernykh 47f551c046 Randomize output order for P2Pool payouts 2023-01-09 20:07:11 +01:00
SChernykh ae6747c82d Initialize tx key from previous blocks 2023-01-09 16:14:33 +01:00
SChernykh 1bd04c2840 Initialize tx key from random seed 2023-01-09 11:28:47 +01:00
SChernykh 11e4c459c9 pub/sec tx key cleanup 2023-01-09 01:15:06 +01:00
SChernykh 647319b731 Added missing check to update_chain_tip 2023-01-09 00:33:37 +01:00
SChernykh e0913f9068 Speed up calc_sidechain_hash 2023-01-08 23:29:54 +01:00
SChernykh af0fa872ae Set sidechain extra (duplicate shares fix) 2023-01-08 21:12:09 +01:00
SChernykh 1ce8f28105 Added sidechain extra 2023-01-08 17:07:28 +01:00
SChernykh a525f34fec Speed up calc_miner_tx_hash a lot
Cache keccak state and update it for new extra_nonce in O(1) time instead of O(N). It reduces the number `keccakf` calls from 30-150 to just 2-3. Time to generate 10,000 hashing blobs is reduced from 0.75s to 0.03s.
2023-01-08 14:17:44 +01:00
SChernykh 632f3faac5 Refactored keccak interface
- Allow calculating keccak hash in steps
- Only allow 32 and 200 byte final hashes
2023-01-08 13:06:20 +01:00
SChernykh 4f34c4466a Dynamic PPLNS window 2023-01-08 00:47:03 +01:00
SChernykh ccc5117172 Track shares that failed to be added 2023-01-07 15:50:02 +01:00
SChernykh b346b93285 Updated copyright 2023-01-04 13:07:55 +01:00
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
87 changed files with 5069 additions and 1830 deletions
+86 -20
View File
@@ -21,7 +21,7 @@ jobs:
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 }} 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 - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: true submodules: true
@@ -44,7 +44,7 @@ jobs:
run: cd tests/build && ./p2pool_tests run: cd tests/build && ./p2pool_tests
- name: Archive binary - name: Archive binary
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: p2pool-${{ matrix.config.os }} name: p2pool-${{ matrix.config.os }}
path: build/p2pool path: build/p2pool
@@ -62,7 +62,7 @@ jobs:
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 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 - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: true submodules: true
@@ -70,7 +70,7 @@ jobs:
run: | run: |
cd external/src/curl cd external/src/curl
autoreconf -fi autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares ./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) make -j$(nproc)
- name: Build libuv - name: Build libuv
@@ -108,14 +108,20 @@ jobs:
run: cd tests/build && ./p2pool_tests run: cd tests/build && ./p2pool_tests
- name: Archive binary - name: Archive binary
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: p2pool-ubuntu-static-libs name: p2pool-ubuntu-static-libs
path: build/p2pool path: build/p2pool
build-ubuntu-aarch64: build-ubuntu-aarch64:
runs-on: ubuntu-22.04 runs-on: ${{ matrix.config.os }}
strategy:
matrix:
config:
- {os: ubuntu-20.04}
- {os: ubuntu-22.04}
steps: steps:
- name: Install dependencies - name: Install dependencies
@@ -124,7 +130,7 @@ jobs:
sudo apt install -y git build-essential cmake autoconf gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu sudo apt install -y git build-essential cmake autoconf gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: true submodules: true
@@ -132,7 +138,7 @@ jobs:
run: | run: |
cd external/src/curl cd external/src/curl
autoreconf -fi autoreconf -fi
./configure --host=aarch64-linux-gnu --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares ./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) make -j$(nproc)
- name: Build libuv - name: Build libuv
@@ -159,9 +165,9 @@ jobs:
make -j$(nproc) make -j$(nproc)
- name: Archive binary - name: Archive binary
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: p2pool-ubuntu-latest-aarch64 name: p2pool-${{ matrix.config.os }}-aarch64
path: build/p2pool path: build/p2pool
build-windows-msys2: build-windows-msys2:
@@ -174,7 +180,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
@@ -188,7 +194,7 @@ jobs:
run: | run: |
cd external/src/curl cd external/src/curl
autoreconf -fi autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares ./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) make -j$(nproc)
- name: Build libuv - name: Build libuv
@@ -228,7 +234,7 @@ jobs:
./p2pool_tests.exe ./p2pool_tests.exe
- name: Archive binary - name: Archive binary
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: p2pool-msys2.exe name: p2pool-msys2.exe
path: build/p2pool.exe path: build/p2pool.exe
@@ -246,7 +252,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
@@ -266,15 +272,15 @@ jobs:
mkdir build mkdir build
cd build cd build
cmake .. -G "${{ matrix.config.vs }}" 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 - name: Run tests
run: | run: |
cd tests/build/Release cd tests/build/Debug
./p2pool_tests.exe ./p2pool_tests.exe
- name: Archive binary - name: Archive binary
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}.exe name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}.exe
path: build/Release/p2pool.exe path: build/Release/p2pool.exe
@@ -289,7 +295,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
@@ -300,7 +306,7 @@ jobs:
run: | run: |
cd external/src/curl cd external/src/curl
autoreconf -fi autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares ./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 make -j3
- name: Build libuv - name: Build libuv
@@ -340,7 +346,67 @@ jobs:
./p2pool_tests ./p2pool_tests
- name: Archive binary - name: Archive binary
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: p2pool-${{ matrix.os }} name: p2pool-${{ matrix.os }}
path: build/p2pool path: build/p2pool
build-freebsd:
timeout-minutes: 30
runs-on: ${{ matrix.os.host }}
strategy:
matrix:
os:
- name: freebsd
architecture: x86-64
version: '12.4'
host: macos-12
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build p2pool
uses: cross-platform-actions/action@v0.10.0
with:
operating_system: ${{ matrix.os.name }}
architecture: ${{ matrix.os.architecture }}
version: ${{ matrix.os.version }}
shell: bash
run: |
sudo pkg install -y cmake autoconf automake libtool
cd external/src/curl
autoreconf -fi
./configure --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
cd ../libuv
mkdir build
cd build
cmake .. -DBUILD_TESTING=OFF
make -j3
cd ../../libzmq
mkdir build
cd build
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j3
cd ../../../..
mkdir build
cd build
cmake .. -DSTATIC_BINARY=ON
make -j3
cd ../tests
mkdir build
cd build
cmake .. -DSTATIC_LIBS=ON
make -j3
./p2pool_tests
- name: Archive binary
uses: actions/upload-artifact@v3
with:
name: p2pool-${{ matrix.os.name }}-${{ matrix.os.version }}
path: build/p2pool
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
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 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 - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: true submodules: true
+2 -2
View File
@@ -9,7 +9,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: true submodules: true
@@ -35,7 +35,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: true submodules: true
+3 -3
View File
@@ -26,7 +26,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: true submodules: true
@@ -38,7 +38,7 @@ jobs:
# run: cmake --build ${{ env.build }} # run: cmake --build ${{ env.build }}
- name: Initialize MSVC Code Analysis - name: Initialize MSVC Code Analysis
uses: microsoft/msvc-code-analysis-action@1e7491d1428ffa2e8aa70916d0d0309c7ca32baf uses: microsoft/msvc-code-analysis-action@24c285ab36952c9e9182f4b78dfafbac38a7e5ee
# Provide a unique ID to access the sarif output path # Provide a unique ID to access the sarif output path
id: run-analysis id: run-analysis
with: with:
@@ -55,7 +55,7 @@ jobs:
# Upload SARIF file as an Artifact to download and view # Upload SARIF file as an Artifact to download and view
- name: Upload SARIF as an Artifact - name: Upload SARIF as an Artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: sarif-file name: sarif-file
path: ${{ steps.run-analysis.outputs.sarif }} path: ${{ steps.run-analysis.outputs.sarif }}
+26 -17
View File
@@ -15,7 +15,7 @@ jobs:
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 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 - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: true submodules: true
@@ -30,13 +30,16 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
run: | run: |
cd build cd build
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 4 mkdir data
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --data-api data --local-api --loglevel 6
- name: Archive p2pool.log - name: Archive p2pool.log
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: p2pool_ubuntu.log name: p2pool_ubuntu_data
path: build/p2pool.log path: |
build/p2pool.log
build/data/
sync-test-macos: sync-test-macos:
@@ -44,7 +47,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
@@ -62,13 +65,16 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
run: | run: |
cd build cd build
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 4 mkdir data
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --data-api data --local-api --loglevel 6
- name: Archive p2pool.log - name: Archive p2pool.log
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: p2pool_macos.log name: p2pool_macos_data
path: build/p2pool.log path: |
build/p2pool.log
build/data/
sync-test-windows: sync-test-windows:
@@ -76,7 +82,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
@@ -88,16 +94,19 @@ jobs:
mkdir build mkdir build
cd build cd build
cmake .. -G "Visual Studio 17 2022" -DDEV_TEST_SYNC=ON cmake .. -G "Visual Studio 17 2022" -DDEV_TEST_SYNC=ON
& "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\msbuild" /m /p:Configuration=Release p2pool.vcxproj & "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\msbuild" /m /p:Configuration=Debug p2pool.vcxproj
- name: Run p2pool - name: Run p2pool
timeout-minutes: 15 timeout-minutes: 15
run: | run: |
cd build/Release cd build/Debug
./p2pool.exe --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 4 mkdir data
./p2pool.exe --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --data-api data --local-api --loglevel 6
- name: Archive p2pool.log - name: Archive p2pool.log
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: p2pool_windows.log name: p2pool_windows_data
path: build/Release/p2pool.log path: |
build/Debug/p2pool.log
build/Debug/data/
+4 -3
View File
@@ -1,9 +1,6 @@
[submodule "tests/googletest"] [submodule "tests/googletest"]
path = tests/googletest path = tests/googletest
url = https://github.com/google/googletest url = https://github.com/google/googletest
[submodule "external/src/RandomX"]
path = external/src/RandomX
url = https://github.com/SChernykh/RandomX
[submodule "external/src/rapidjson"] [submodule "external/src/rapidjson"]
path = external/src/rapidjson path = external/src/rapidjson
url = https://github.com/SChernykh/rapidjson url = https://github.com/SChernykh/rapidjson
@@ -22,3 +19,7 @@
[submodule "external/src/curl"] [submodule "external/src/curl"]
path = external/src/curl path = external/src/curl
url = https://github.com/SChernykh/curl url = https://github.com/SChernykh/curl
[submodule "external/src/RandomWOW"]
path = external/src/RandomWOW
url = https://git.wownero.com/wownero/RandomWOW
branch = 1.1.10-p2pool
+41 -10
View File
@@ -1,19 +1,27 @@
cmake_minimum_required(VERSION 3.6) cmake_minimum_required(VERSION 2.8.12)
project(p2pool) project(p2pool)
option(STATIC_BINARY "Build static binary" OFF) option(STATIC_BINARY "Build static binary" OFF)
option(STATIC_LIBS "Link libuv and libzmq statically" OFF) option(STATIC_LIBS "Link libuv and libzmq statically" OFF)
option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned off, p2pool will rely on monerod for verifying RandomX hashes" ON) option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned off, p2pool will rely on wownerod for verifying RandomX hashes" ON)
option(DEV_TEST_SYNC "[Developer only] Sync test, stop p2pool after sync is complete" OFF) option(DEV_TEST_SYNC "[Developer only] Sync test, stop p2pool after sync is complete" OFF)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool) if (${CMAKE_VERSION} VERSION_GREATER "3.5.2")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
endif()
find_package(Git)
if(GIT_FOUND)
message(STATUS "Initializing submodules")
execute_process(COMMAND git "submodule" "update" "--init" "--recursive")
endif()
if (WITH_RANDOMX) if (WITH_RANDOMX)
add_definitions(-DWITH_RANDOMX) add_definitions(-DWITH_RANDOMX)
add_subdirectory(external/src/RandomX) add_subdirectory(external/src/RandomWOW)
set(LIBS randomx) set(LIBS randomx)
endif() endif()
@@ -25,6 +33,7 @@ include(cmake/flags.cmake)
set(HEADERS set(HEADERS
external/src/cryptonote/crypto-ops.h external/src/cryptonote/crypto-ops.h
external/src/hardforks/hardforks.h
src/block_cache.h src/block_cache.h
src/block_template.h src/block_template.h
src/common.h src/common.h
@@ -55,6 +64,7 @@ set(HEADERS
set(SOURCES set(SOURCES
external/src/cryptonote/crypto-ops-data.c external/src/cryptonote/crypto-ops-data.c
external/src/cryptonote/crypto-ops.c external/src/cryptonote/crypto-ops.c
external/src/hardforks/hardforks.cpp
src/block_cache.cpp src/block_cache.cpp
src/block_template.cpp src/block_template.cpp
src/console_commands.cpp src/console_commands.cpp
@@ -88,30 +98,41 @@ if (NOT STATIC_BINARY AND NOT STATIC_LIBS)
endif() endif()
if (CURL_INCLUDE_DIRS) if (CURL_INCLUDE_DIRS)
include_directories(CURL_INCLUDE_DIRS) include_directories(${CURL_INCLUDE_DIRS})
else() else()
include_directories(external/src/curl/include) include_directories(external/src/curl/include)
endif() 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(src)
include_directories(external/src) include_directories(external/src)
include_directories(external/src/cryptonote) 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/cppzmq)
include_directories(external/src/libzmq/include) include_directories(${ZMQ_INCLUDE_DIR})
if (WITH_RANDOMX) if (WITH_RANDOMX)
include_directories(external/src/RandomX/src) include_directories(external/src/RandomWOW/src)
endif() endif()
include_directories(external/src/rapidjson/include) include_directories(external/src/rapidjson/include)
include_directories(external/src/robin-hood-hashing/src/include) include_directories(external/src/robin-hood-hashing/src/include)
if (WIN32) if (WIN32)
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi) set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(LIBS ${LIBS} bcrypt)
endif()
add_definitions(-DCURL_STATICLIB) add_definitions(-DCURL_STATICLIB)
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(LIBS ${LIBS} pthread) set(LIBS ${LIBS} pthread)
elseif (NOT APPLE) elseif (NOT APPLE)
set(LIBS ${LIBS} pthread dl) set(LIBS ${LIBS} pthread)
endif() endif()
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC) if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
@@ -121,6 +142,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
find_library(UV_LIBRARY NAMES uv_a PATHS "external/lib/libuv/Release") 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_DEBUG NAMES libcurld PATHS "external/lib/libcurl/Debug")
find_library(CURL_LIBRARY NAMES libcurl PATHS "external/lib/libcurl/Release") 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) 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_DEBUG NAMES zmq libzmq.a)
find_library(ZMQ_LIBRARY NAMES zmq libzmq.a) find_library(ZMQ_LIBRARY NAMES zmq libzmq.a)
@@ -171,6 +194,7 @@ include(CheckCXXSourceCompiles)
check_cxx_source_compiles("int main(){ return __builtin_clzll(1);}" HAVE_BUILTIN_CLZLL) 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 <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) if (HAVE_BUILTIN_CLZLL)
add_definitions(/DHAVE_BUILTIN_CLZLL) add_definitions(/DHAVE_BUILTIN_CLZLL)
@@ -180,6 +204,10 @@ if (HAVE_BITSCANREVERSE64)
add_definitions(/DHAVE_BITSCANREVERSE64) add_definitions(/DHAVE_BITSCANREVERSE64)
endif() endif()
if (HAVE_SCHED)
add_definitions(/DHAVE_SCHED)
endif()
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES}) add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
if (STATIC_BINARY OR STATIC_LIBS) if (STATIC_BINARY OR STATIC_LIBS)
@@ -195,6 +223,9 @@ if (STATIC_BINARY OR STATIC_LIBS)
if (WIN32) if (WIN32)
set(STATIC_LIBS ${STATIC_LIBS} ws2_32 iphlpapi userenv psapi wldap32) 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) elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(STATIC_LIBS ${STATIC_LIBS} pthread) set(STATIC_LIBS ${STATIC_LIBS} pthread)
elseif (APPLE) elseif (APPLE)
@@ -203,7 +234,7 @@ if (STATIC_BINARY OR STATIC_LIBS)
find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration) find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration)
set(STATIC_LIBS ${STATIC_LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB}) set(STATIC_LIBS ${STATIC_LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB})
else() else()
set(STATIC_LIBS ${STATIC_LIBS} pthread dl) set(STATIC_LIBS ${STATIC_LIBS} pthread)
endif() endif()
target_link_libraries(${CMAKE_PROJECT_NAME} target_link_libraries(${CMAKE_PROJECT_NAME}
+58 -26
View File
@@ -2,11 +2,19 @@
Decentralized pool for Monero mining. Decentralized pool for Monero mining.
Pool status and monitoring pages can be found at https://p2pool.io/, https://p2pool.io/mini/ and https://p2pool.observer/ Pool status and monitoring pages can be found at https://p2pool.io/, https://p2pool.io/mini/ and https://p2pool.observer/, https://mini.p2pool.observer/
# P2Pool hardfork on March 18, 2023
P2Pool (not Monero!) will hardfork to new consensus rules on March 18th at 21:00 UTC (use [this link](https://dateful.com/convert/utc?t=9pm&d=2023-03-18) to convert to your local time). The hardfork is necessary to improve P2Pool scalability and reduce the impact on Monero blockchain. You'll need to update to **P2Pool v3.0** or newer version before this time.
Reddit discussion: https://www.reddit.com/r/MoneroMining/comments/1095730/psa_p2pool_network_upgrade_aka_hardfork_on_march/
### Build Status ### Build Status
![C/C++ CI](https://github.com/SChernykh/p2pool/actions/workflows/c-cpp.yml/badge.svg) ![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) ![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) ![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) ![cppcheck](https://github.com/SChernykh/p2pool/actions/workflows/cppcheck.yml/badge.svg)
@@ -66,34 +74,37 @@ First you need to find a pool share. This share will stay in PPLNS window for 21
## Monero version support ## Monero version support
Monero will undergo a network upgrade on August 13th, 2022 (block 2,688,888). In order to continue mining after that date, you must update both Monero and P2Pool software to the latest available versions as soon as they are released. Monero 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 |Monero protocol version|Required Monero software version|Required P2Pool version
|-|-|-| |-|-|-|
|v14 (active until August 13th, 2022)|v0.17.3.0 or newer|v1.0 or newer
|v15, v16 (active after August 13th, 2022)|v0.18.0.0 or newer|v2.2 or newer |v15, v16 (active after August 13th, 2022)|v0.18.0.0 or newer|v2.2 or newer
## How to mine on P2Pool ## How to mine on P2Pool
### General Considerations ### 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/). - 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 for mining. While P2Pool has been battle-tested for a long time now, any software may have unknown bugs/vulnerabilities. - 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. - 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. - 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! - 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**. - 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** **Wallet software compatible with P2Pool payouts**
- [Official Monero CLI and GUI v0.17.2.3 and newer](https://www.getmonero.org/downloads/) - [Official Monero CLI and GUI v0.18.0.0 and newer](https://www.getmonero.org/downloads/)
- [Monerujo v2.1.0 "Vertant" and newer](https://www.monerujo.io/) - [Monerujo v3.0.2 "Fluorine Fermi" and newer](https://www.monerujo.io/)
- [Cake Wallet v4.2.7 and newer](https://cakewallet.com/) - [Cake Wallet v4.4.5 and newer](https://cakewallet.com/)
- [Monero.com by Cake Wallet](https://monero.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/) - [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 ### GNU/Linux
1. Download the latest P2Pool binaries [here](https://github.com/SChernykh/p2pool/releases/latest). 1. Download the latest P2Pool binaries [here](https://github.com/SChernykh/p2pool/releases/latest).
@@ -106,9 +117,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. 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: 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: 6. Start P2Pool with the following command/options:
``` ```
@@ -157,21 +178,32 @@ nocreate
5. Prepare huge pages to work properly (each instance of monerod/P2Pool/XMRig needs them): 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 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. - 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.* **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. **Note:**
8. Start P2Pool with the following command/options: 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. Wait until the initial P2Pool sync is finished (shouldn't take more than 5-10 minutes).
10. Start XMRig with the following command/options: 11. Start XMRig with the following command/options:
``` ```
.\xmrig.exe -o 127.0.0.1:3333 .\xmrig.exe -o 127.0.0.1:3333
``` ```
@@ -180,17 +212,17 @@ nocreate
``` ```
xmrig.exe -u x+10000 -o 127.0.0.1:3333 xmrig.exe -u x+10000 -o 127.0.0.1:3333
``` ```
11. XMRig should connect and start mining! 12. 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`. 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 @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. 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 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. ECHO Wait until the daemon shows fully synced before continuing. This can take some time.
PAUSE PAUSE
%~dp0\xmrig.exe -u x+30000 -o 127.0.0.1 %~dp0\xmrig.exe -o 127.0.0.1
``` ```
## Build instructions ## Build instructions
+1 -1
View File
@@ -26,7 +26,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
endif() endif()
endif() endif()
elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC) elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
set(GENERAL_FLAGS "") set(GENERAL_FLAGS "/MP")
set(WARNING_FLAGS "/Wall /WX /sdl") set(WARNING_FLAGS "/Wall /WX /sdl")
set(SECURITY_FLAGS "/GS /guard:cf") set(SECURITY_FLAGS "/GS /guard:cf")
set(OPTIMIZATION_FLAGS "/O2 /Oi /Ob2 /Ot /DNDEBUG /GL") set(OPTIMIZATION_FLAGS "/O2 /Oi /Ob2 /Ot /DNDEBUG /GL")
+1 -1
View File
@@ -5,6 +5,6 @@
../external/src/cppzmq/ ../external/src/cppzmq/
../external/src/libzmq/include/ ../external/src/libzmq/include/
../external/src/curl/include ../external/src/curl/include
../external/src/RandomX/src/ ../external/src/RandomWOW/src/
../external/src/rapidjson/include ../external/src/rapidjson/include
../external/src/robin-hood-hashing/src/include ../external/src/robin-hood-hashing/src/include
+1
View File
@@ -1,3 +1,4 @@
unmatchedSuppression
missingIncludeSystem missingIncludeSystem
unusedFunction unusedFunction
useStlAlgorithm useStlAlgorithm
+4
View File
@@ -119,6 +119,10 @@ services:
--zmq-pub tcp://0.0.0.0:18083 --zmq-pub tcp://0.0.0.0:18083
--disable-dns-checkpoints --disable-dns-checkpoints
--enable-dns-blocklist --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 --non-interactive
--p2p-bind-ip=0.0.0.0 --p2p-bind-ip=0.0.0.0
--p2p-bind-port=18080 --p2p-bind-port=18080
+4
View File
@@ -96,6 +96,10 @@ services:
--zmq-pub tcp://0.0.0.0:18083 --zmq-pub tcp://0.0.0.0:18083
--disable-dns-checkpoints --disable-dns-checkpoints
--enable-dns-blocklist --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 --non-interactive
--p2p-bind-ip=0.0.0.0 --p2p-bind-ip=0.0.0.0
--p2p-bind-port=18080 --p2p-bind-port=18080
+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.
Vendored Submodule
+1
Submodule external/src/RandomWOW added at 1ce107336d
Submodule external/src/RandomX deleted from b39068f7c3
+91
View File
@@ -0,0 +1,91 @@
// 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[] = {
{ 7, 1, 0, 1519605000 },
{ 8, 6969, 0, 1524214739 },
{ 9, 53666, 0, 1538689773 },
{ 10, 63469, 0, 1541700352 },
{ 11, 81769, 0, 1549238400 },
{ 12, 82069, 0, 1549318761 },
{ 13, 114969, 0, 1559292691 },
{ 14, 115257, 0, 1559292774 },
{ 15, 160777, 0, 1573280497 },
{ 16, 253999, 0, 1600576508 },
{ 17, 254287, 0, 1600576524 },
{ 18, 331170, 0, 1623245591 },
{ 19, 331458, 0, 1624793373 },
};
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 = 0;
const hardfork_t testnet_hard_forks[] = {
{ 7, 1, 0, 1519605000 },
{ 8, 10, 0, 1524214739 },
{ 9, 20, 0, 1538689773 },
{ 10, 30, 0, 1541700352 },
{ 11, 40, 0, 1549238400 },
{ 12, 50, 0, 1549318761 },
{ 13, 60, 0, 1559292691 },
{ 14, 70, 0, 1559292774 },
{ 15, 80, 0, 1573280497 },
{ 16, 90, 0, 1600576508 },
{ 17, 100, 0, 1600576524 },
{ 18, 110, 0, 1623245591 },
{ 19, 120, 0, 1624793373 },
{ 20, 130, 0, 1674457368 },
};
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 = 0;
const hardfork_t stagenet_hard_forks[] = {
{ 7, 1, 0, 1519605000 },
{ 8, 10, 0, 1524214739 },
{ 9, 20, 0, 1538689773 },
{ 10, 30, 0, 1541700352 },
{ 11, 40, 0, 1549238400 },
{ 12, 50, 0, 1549318761 },
{ 13, 60, 0, 1559292691 },
{ 14, 70, 0, 1559292774 },
{ 15, 80, 0, 1573280497 },
{ 16, 90, 0, 1600576508 },
{ 17, 100, 0, 1600576524 },
{ 18, 110, 0, 1623245591 },
{ 19, 120, 0, 1624793373 },
{ 20, 130, 0, 1674457368 },
};
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;
+17 -8
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -145,7 +145,7 @@ struct BlockCache::Impl : public nocopy_nomove
#else #else
// Not implemented on other platforms // Not implemented on other platforms
void flush() {} void flush() { m_data = nullptr; }
#endif #endif
uint8_t* m_data = nullptr; uint8_t* m_data = nullptr;
@@ -155,6 +155,7 @@ BlockCache::BlockCache()
: m_impl(new Impl()) : m_impl(new Impl())
, m_flushRunning(0) , m_flushRunning(0)
, m_storeIndex(0) , m_storeIndex(0)
, m_loadingStarted(0)
{ {
} }
@@ -165,8 +166,11 @@ BlockCache::~BlockCache()
void BlockCache::store(const PoolBlock& block) void BlockCache::store(const PoolBlock& block)
{ {
const size_t n1 = block.m_mainChainData.size(); const std::vector<uint8_t> mainchain_data = block.serialize_mainchain_data();
const size_t n2 = block.m_sideChainData.size(); 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)) { if (!m_impl->m_data || (sizeof(uint32_t) + n1 + n2 > BLOCK_SIZE)) {
return; 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); 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); *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), mainchain_data.data(), n1);
memcpy(data + sizeof(uint32_t) + n1, block.m_sideChainData.data(), n2); memcpy(data + sizeof(uint32_t) + n1, sidechain_data.data(), n2);
} }
void BlockCache::load_all(SideChain& side_chain, P2PServer& server) void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
@@ -185,6 +189,11 @@ void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
return; return;
} }
// Can be only called once
if (m_loadingStarted.exchange(1)) {
return;
}
LOGINFO(1, "loading cached blocks"); LOGINFO(1, "loading cached blocks");
PoolBlock block; PoolBlock block;
@@ -198,7 +207,7 @@ void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
continue; continue;
} }
if (block.deserialize(data + sizeof(uint32_t), n, side_chain, uv_default_loop_checked()) == 0) { if (block.deserialize(data + sizeof(uint32_t), n, side_chain, uv_default_loop_checked(), false) == 0) {
server.add_cached_block(block); server.add_cached_block(block);
++blocks_loaded; ++blocks_loaded;
} }
@@ -209,7 +218,7 @@ void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
void BlockCache::flush() void BlockCache::flush()
{ {
if (m_flushRunning.exchange(1) == 0) { if (m_flushRunning.exchange(1)) {
m_impl->flush(); m_impl->flush();
m_flushRunning.store(0); m_flushRunning.store(0);
} }
+2 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -38,6 +38,7 @@ private:
Impl* m_impl; Impl* m_impl;
std::atomic<uint32_t> m_flushRunning; std::atomic<uint32_t> m_flushRunning;
std::atomic<uint32_t> m_storeIndex; std::atomic<uint32_t> m_storeIndex;
std::atomic<uint32_t> m_loadingStarted;
}; };
} // namespace p2pool } // namespace p2pool
+427 -188
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* Portions Copyright (c) 2012-2013 The Cryptonote developers * Portions Copyright (c) 2012-2013 The Cryptonote developers
* Portions Copyright (c) 2014-2021 The Monero Project * Portions Copyright (c) 2014-2021 The Monero Project
* Portions Copyright (c) 2021 XMRig <https://github.com/xmrig> * Portions Copyright (c) 2021 XMRig <https://github.com/xmrig>
@@ -34,11 +34,16 @@
static constexpr char log_category_prefix[] = "BlockTemplate "; 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 { namespace p2pool {
BlockTemplate::BlockTemplate(p2pool* pool) BlockTemplate::BlockTemplate(SideChain* sidechain, RandomX_Hasher_Base* hasher)
: m_pool(pool) : m_sidechain(sidechain)
, m_hasher(hasher)
, m_templateId(0) , m_templateId(0)
, m_lastUpdated(seconds_since_epoch())
, m_blockHeaderSize(0) , m_blockHeaderSize(0)
, m_minerTxOffsetInTemplate(0) , m_minerTxOffsetInTemplate(0)
, m_minerTxSize(0) , m_minerTxSize(0)
@@ -50,11 +55,17 @@ BlockTemplate::BlockTemplate(p2pool* pool)
, m_difficulty{} , m_difficulty{}
, m_seedHash{} , m_seedHash{}
, m_timestamp(0) , m_timestamp(0)
, m_txkeyPub{}
, m_txkeySec{}
, m_poolBlockTemplate(new PoolBlock()) , m_poolBlockTemplate(new PoolBlock())
, m_finalReward(0) , m_finalReward(0)
, m_minerTxKeccakState{}
, m_minerTxKeccakStateInputLength(0)
, m_sidechainHashKeccakState{}
, m_sidechainHashInputLength(0)
, m_rng(RandomDeviceSeed::instance)
{ {
// Diffuse the initial state in case it has low quality
m_rng.discard(10000);
uv_rwlock_init_checked(&m_lock); uv_rwlock_init_checked(&m_lock);
m_blockHeader.reserve(64); m_blockHeader.reserve(64);
@@ -63,10 +74,13 @@ BlockTemplate::BlockTemplate(p2pool* pool)
m_transactionHashes.reserve(8192); m_transactionHashes.reserve(8192);
m_rewards.reserve(100); m_rewards.reserve(100);
m_blockTemplateBlob.reserve(65536); m_blockTemplateBlob.reserve(65536);
m_fullDataBlob.reserve(65536);
m_sidechainHashBlob.reserve(65536);
m_merkleTreeMainBranch.reserve(HASH_SIZE * 10); m_merkleTreeMainBranch.reserve(HASH_SIZE * 10);
m_mempoolTxs.reserve(1024); m_mempoolTxs.reserve(1024);
m_mempoolTxsOrder.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) { for (size_t i = 0; i < array_size(&BlockTemplate::m_oldTemplates); ++i) {
m_oldTemplates[i] = new BlockTemplate(*this); m_oldTemplates[i] = new BlockTemplate(*this);
@@ -104,9 +118,13 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
WriteLock lock(m_lock); 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_templateId = b.m_templateId;
m_lastUpdated = b.m_lastUpdated.load();
m_blockTemplateBlob = b.m_blockTemplateBlob; m_blockTemplateBlob = b.m_blockTemplateBlob;
m_fullDataBlob = b.m_fullDataBlob;
m_sidechainHashBlob = b.m_sidechainHashBlob;
m_merkleTreeMainBranch = b.m_merkleTreeMainBranch; m_merkleTreeMainBranch = b.m_merkleTreeMainBranch;
m_blockHeaderSize = b.m_blockHeaderSize; m_blockHeaderSize = b.m_blockHeaderSize;
m_minerTxOffsetInTemplate = b.m_minerTxOffsetInTemplate; m_minerTxOffsetInTemplate = b.m_minerTxOffsetInTemplate;
@@ -119,10 +137,14 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
m_difficulty = b.m_difficulty; m_difficulty = b.m_difficulty;
m_seedHash = b.m_seedHash; m_seedHash = b.m_seedHash;
m_timestamp = b.m_timestamp; m_timestamp = b.m_timestamp;
m_txkeyPub = b.m_txkeyPub;
m_txkeySec = b.m_txkeySec;
*m_poolBlockTemplate = *b.m_poolBlockTemplate; *m_poolBlockTemplate = *b.m_poolBlockTemplate;
m_finalReward = b.m_finalReward; m_finalReward = b.m_finalReward.load();
memcpy(m_minerTxKeccakState, b.m_minerTxKeccakState, sizeof(m_minerTxKeccakState));
m_minerTxKeccakStateInputLength = b.m_minerTxKeccakStateInputLength;
memcpy(m_sidechainHashKeccakState, b.m_sidechainHashKeccakState, sizeof(m_sidechainHashKeccakState));
m_sidechainHashInputLength = b.m_sidechainHashInputLength;
m_minerTx.clear(); m_minerTx.clear();
m_blockHeader.clear(); m_blockHeader.clear();
@@ -131,8 +153,11 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
m_rewards.clear(); m_rewards.clear();
m_mempoolTxs.clear(); m_mempoolTxs.clear();
m_mempoolTxsOrder.clear(); m_mempoolTxsOrder.clear();
m_mempoolTxsOrder2.clear();
m_shares.clear(); m_shares.clear();
m_rng = b.m_rng;
#if TEST_MEMPOOL_PICKING_ALGORITHM #if TEST_MEMPOOL_PICKING_ALGORITHM
m_knapsack.clear(); m_knapsack.clear();
#endif #endif
@@ -142,7 +167,7 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
static FORCEINLINE uint64_t get_base_reward(uint64_t already_generated_coins) static FORCEINLINE uint64_t get_base_reward(uint64_t already_generated_coins)
{ {
const uint64_t result = ~already_generated_coins >> 19; const uint64_t result = ~already_generated_coins >> 20;
constexpr uint64_t min_reward = 600000000000ULL; constexpr uint64_t min_reward = 600000000000ULL;
return (result < min_reward) ? min_reward : result; return (result < min_reward) ? min_reward : result;
@@ -171,6 +196,17 @@ static FORCEINLINE uint64_t get_block_reward(uint64_t base_reward, uint64_t medi
return reward + fees; return reward + fees;
} }
void BlockTemplate::shuffle_tx_order()
{
const uint64_t n = m_mempoolTxsOrder.size();
if (n > 1) {
for (uint64_t i = 0, k; i < n - 1; ++i) {
umul128(m_rng(), n - i, &k);
std::swap(m_mempoolTxsOrder[i], m_mempoolTxsOrder[i + k]);
}
}
}
void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet) void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet)
{ {
if (data.major_version > HARDFORK_SUPPORTED_VERSION) { if (data.major_version > HARDFORK_SUPPORTED_VERSION) {
@@ -189,6 +225,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
} }
++m_templateId; ++m_templateId;
m_lastUpdated = seconds_since_epoch();
// When block template generation fails for any reason // When block template generation fails for any reason
auto use_old_template = [this]() { auto use_old_template = [this]() {
@@ -197,60 +234,10 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
*this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)]; *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_height = data.height;
m_difficulty = data.difficulty; m_difficulty = data.difficulty;
m_seedHash = data.seed_hash; 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_blockHeader.clear();
m_poolBlockTemplate->m_verified = false; m_poolBlockTemplate->m_verified = false;
@@ -280,9 +267,131 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_blockHeader.insert(m_blockHeader.end(), NONCE_SIZE, 0); m_blockHeader.insert(m_blockHeader.end(), NONCE_SIZE, 0);
m_poolBlockTemplate->m_nonce = 0; m_poolBlockTemplate->m_nonce = 0;
// Fill in m_txinGenHeight here so get_shares() can use it to calculate the correct PPLNS window
m_poolBlockTemplate->m_txinGenHeight = data.height;
m_blockHeaderSize = m_blockHeader.size(); m_blockHeaderSize = m_blockHeader.size();
m_pool->side_chain().fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares); const int sidechain_version = m_poolBlockTemplate->get_sidechain_version();
if (sidechain_version <= 1) {
get_tx_keys(m_poolBlockTemplate->m_txkeyPub, m_poolBlockTemplate->m_txkeySec, miner_wallet->spend_public_key(), data.prev_id);
// Both values are the same before v2
m_poolBlockTemplate->m_txkeySecSeed = m_poolBlockTemplate->m_txkeySec;
}
m_poolBlockTemplate->m_minerWallet = *miner_wallet;
m_sidechain->fill_sidechain_data(*m_poolBlockTemplate, m_shares);
// Pre-calculate outputs to speed up miner tx generation
if (!m_shares.empty()) {
struct Precalc
{
FORCEINLINE Precalc(const std::vector<MinerShare>& s, const hash& k) : txKeySec(k)
{
const size_t N = s.size();
counter = static_cast<int>(N) - 1;
shares = reinterpret_cast<std::pair<hash, hash>*>(malloc_hook(sizeof(std::pair<hash, hash>) * N));
if (shares) {
const MinerShare* src = &s[0];
std::pair<hash, hash>* dst = shares;
std::pair<hash, hash>* e = shares + N;
for (; dst < e; ++src, ++dst) {
const Wallet* w = src->m_wallet;
dst->first = w->view_public_key();
dst->second = w->spend_public_key();
}
}
}
FORCEINLINE Precalc(Precalc&& rhs) noexcept : txKeySec(rhs.txKeySec), counter(rhs.counter.load()), shares(rhs.shares) { rhs.shares = nullptr; }
FORCEINLINE ~Precalc() { free_hook(shares); }
FORCEINLINE void operator()()
{
if (shares) {
hash derivation, eph_public_key;
int i;
while ((i = counter.fetch_sub(1)) >= 0) {
uint8_t view_tag;
generate_key_derivation(shares[i].first, txKeySec, i, derivation, view_tag);
derive_public_key(derivation, i, shares[i].second, eph_public_key);
}
}
}
hash txKeySec;
std::atomic<int> counter;
std::pair<hash, hash>* shares;
};
parallel_run(uv_default_loop_checked(), Precalc(m_shares, m_poolBlockTemplate->m_txkeySec));
}
// Only choose transactions that were received 5 or more seconds ago, or high fee (>= 0.006 XMR) transactions
size_t total_mempool_transactions;
{
m_mempoolTxs.clear();
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 + 5) || (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)) { if (!SideChain::split_reward(max_reward, m_shares, m_rewards)) {
use_old_template(); use_old_template();
return; return;
@@ -315,16 +424,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 a block doesn't get into the penalty zone, just pick all transactions
if (total_tx_weight + miner_tx_weight <= data.median_weight) { if (total_tx_weight + miner_tx_weight <= data.median_weight) {
m_numTransactionHashes = 0;
final_fees = 0; final_fees = 0;
final_weight = miner_tx_weight; final_weight = miner_tx_weight;
m_transactionHashes.assign(HASH_SIZE, 0); shuffle_tx_order();
for (const TxMempoolData& tx : m_mempoolTxs) {
m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
++m_numTransactionHashes;
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_fees += tx.fee;
final_weight += tx.weight; final_weight += tx.weight;
} }
@@ -339,19 +454,14 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
// Sometimes it even finds the optimal solution // Sometimes it even finds the optimal solution
// Sort all transactions by fee per byte (highest to lowest) // Sort all transactions by fee per byte (highest to lowest)
std::sort(m_mempoolTxsOrder.begin(), m_mempoolTxsOrder.end(), std::sort(m_mempoolTxsOrder.begin(), m_mempoolTxsOrder.end(), [this](int a, int b) { return m_mempoolTxs[a] < m_mempoolTxs[b]; });
[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;
});
final_reward = base_reward; final_reward = base_reward;
final_fees = 0; final_fees = 0;
final_weight = miner_tx_weight; 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]]; const TxMempoolData& tx = m_mempoolTxs[m_mempoolTxsOrder[i]];
int k = -1; int k = -1;
@@ -365,8 +475,10 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
// Try replacing other transactions when we are above the limit // Try replacing other transactions when we are above the limit
if (final_weight + tx.weight > data.median_weight) { if (final_weight + tx.weight > data.median_weight) {
for (int j = 0; j < i; ++j) { // Don't check more than 100 transactions deep because they have higher and higher fee/byte
const TxMempoolData& prev_tx = m_mempoolTxs[m_mempoolTxsOrder[j]]; 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); 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 (reward2 > final_reward) {
// If replacing some other transaction increases the reward even more, remember it // If replacing some other transaction increases the reward even more, remember it
@@ -379,29 +491,35 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
if (k == i) { if (k == i) {
// Simply adding this tx improves the reward // Simply adding this tx improves the reward
m_mempoolTxsOrder2.push_back(m_mempoolTxsOrder[i]);
final_fees += tx.fee; final_fees += tx.fee;
final_weight += tx.weight; final_weight += tx.weight;
++i;
continue;
} }
else if (k >= 0) {
if (k >= 0) {
// Replacing another tx with this tx improves the reward // 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_fees += tx.fee - prev_tx.fee;
final_weight += tx.weight - prev_tx.weight; 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_fees = 0;
final_weight = miner_tx_weight; final_weight = miner_tx_weight;
shuffle_tx_order();
m_numTransactionHashes = m_mempoolTxsOrder.size(); m_numTransactionHashes = m_mempoolTxsOrder.size();
m_transactionHashes.assign(HASH_SIZE, 0); 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) { for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
const TxMempoolData& tx = m_mempoolTxs[m_mempoolTxsOrder[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); m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
final_fees += tx.fee; final_fees += tx.fee;
final_weight += tx.weight; final_weight += tx.weight;
@@ -521,42 +639,91 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_poolBlockTemplate->m_minerWallet = *miner_wallet; m_poolBlockTemplate->m_minerWallet = *miner_wallet;
m_poolBlockTemplate->serialize_sidechain_data(); // Layout: [software id, version, random number, sidechain extra_nonce]
m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(); uint32_t* sidechain_extra = m_poolBlockTemplate->m_sidechainExtraBuf;
const int sidechain_hash_offset = static_cast<int>(m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize) + 2; sidechain_extra[0] = 0;
sidechain_extra[1] = (P2POOL_VERSION_MAJOR << 16) | P2POOL_VERSION_MINOR;
sidechain_extra[2] = static_cast<uint32_t>(m_rng() >> 32);
sidechain_extra[3] = 0;
memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE); m_poolBlockTemplate->m_nonce = 0;
memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE); m_poolBlockTemplate->m_extraNonce = 0;
m_poolBlockTemplate->m_sidechainId = {};
m_poolBlockTemplate->serialize_mainchain_data(0, 0, m_poolBlockTemplate->m_sidechainId); const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
const std::vector<uint8_t>& consensus_id = m_sidechain->consensus_id();
#if POOL_BLOCK_DEBUG m_sidechainHashBlob = m_poolBlockTemplate->serialize_mainchain_data();
if (m_poolBlockTemplate->m_mainChainData != m_blockTemplateBlob) { m_sidechainHashBlob.insert(m_sidechainHashBlob.end(), sidechain_data.begin(), sidechain_data.end());
LOGERR(1, "serialize_mainchain_data() has a bug, fix it! "); m_sidechainHashBlob.insert(m_sidechainHashBlob.end(), consensus_id.begin(), consensus_id.end());
LOGERR(1, "m_poolBlockTemplate->m_mainChainData.size() = " << m_poolBlockTemplate->m_mainChainData.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) { memset(m_sidechainHashKeccakState, 0, sizeof(m_sidechainHashKeccakState));
if (m_poolBlockTemplate->m_mainChainData[i] != m_blockTemplateBlob[i]) {
LOGERR(1, "m_poolBlockTemplate->m_mainChainData is different at offset " << i); const size_t extra_nonce_offset = m_sidechainHashBlob.size() - HASH_SIZE - ((sidechain_version > 1) ? EXTRA_NONCE_SIZE : 0);
break; if (extra_nonce_offset >= KeccakParams::HASH_DATA_AREA) {
} // Sidechain data is big enough to cache keccak state up to extra_nonce
m_sidechainHashInputLength = (extra_nonce_offset / KeccakParams::HASH_DATA_AREA) * KeccakParams::HASH_DATA_AREA;
const uint8_t* in = m_sidechainHashBlob.data();
int inlen = static_cast<int>(m_sidechainHashInputLength);
keccak_step(in, inlen, m_sidechainHashKeccakState);
}
else {
m_sidechainHashInputLength = 0;
} }
} }
{ m_fullDataBlob = m_blockTemplateBlob;
std::vector<uint8_t> buf = m_blockTemplateBlob; m_fullDataBlob.insert(m_fullDataBlob.end(), sidechain_data.begin(), sidechain_data.end());
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end());
m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(0);
if (pool_block_debug()) {
const size_t sidechain_hash_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 2;
memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
memcpy(m_fullDataBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
const std::vector<uint8_t> mainchain_data = m_poolBlockTemplate->serialize_mainchain_data();
if (mainchain_data != m_blockTemplateBlob) {
LOGERR(1, "serialize_mainchain_data() has a bug, fix it! ");
LOGERR(1, "mainchain_data.size() = " << mainchain_data.size());
LOGERR(1, "m_blockTemplateBlob.size() = " << m_blockTemplateBlob.size());
for (size_t i = 0, n = std::min(mainchain_data.size(), m_blockTemplateBlob.size()); i < n; ++i) {
if (mainchain_data[i] != m_blockTemplateBlob[i]) {
LOGERR(1, "mainchain_data is different at offset " << i);
break;
}
}
}
PoolBlock check; PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain(), nullptr); const int result = check.deserialize(m_fullDataBlob.data(), m_fullDataBlob.size(), *m_sidechain, nullptr, false);
if (result != 0) { if (result != 0) {
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result); LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
} }
else { else {
LOGINFO(6, "blob size = " << buf.size()); LOGINFO(6, "blob size = " << m_fullDataBlob.size());
} }
} }
#endif
memset(m_minerTxKeccakState, 0, sizeof(m_minerTxKeccakState));
const size_t extra_nonce_offset = m_extraNonceOffsetInTemplate - m_minerTxOffsetInTemplate;
if (extra_nonce_offset >= KeccakParams::HASH_DATA_AREA) {
// Miner transaction is big enough to cache keccak state up to extra_nonce
m_minerTxKeccakStateInputLength = (extra_nonce_offset / KeccakParams::HASH_DATA_AREA) * KeccakParams::HASH_DATA_AREA;
const uint8_t* in = m_blockTemplateBlob.data() + m_minerTxOffsetInTemplate;
int inlen = static_cast<int>(m_minerTxKeccakStateInputLength);
keccak_step(in, inlen, m_minerTxKeccakState);
}
else {
m_minerTxKeccakStateInputLength = 0;
}
const hash minerTx_hash = calc_miner_tx_hash(0); const hash minerTx_hash = calc_miner_tx_hash(0);
@@ -574,9 +741,11 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_blockHeader.clear(); m_blockHeader.clear();
m_minerTxExtra.clear(); m_minerTxExtra.clear();
m_transactionHashes.clear(); m_transactionHashes.clear();
m_transactionHashesSet.clear();
m_rewards.clear(); m_rewards.clear();
m_mempoolTxs.clear(); m_mempoolTxs.clear();
m_mempoolTxsOrder.clear(); m_mempoolTxsOrder.clear();
m_mempoolTxsOrder2.clear();
} }
#if TEST_MEMPOOL_PICKING_ALGORITHM #if TEST_MEMPOOL_PICKING_ALGORITHM
@@ -708,11 +877,11 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
} }
else { else {
hash eph_public_key; hash eph_public_key;
if (!shares[i].m_wallet->get_eph_public_key(m_txkeySec, i, eph_public_key, view_tag)) { if (!shares[i].m_wallet->get_eph_public_key(m_poolBlockTemplate->m_txkeySec, i, eph_public_key, view_tag)) {
LOGERR(1, "get_eph_public_key failed at index " << i); 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_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) { if (tx_type == TXOUT_TO_TAGGED_KEY) {
@@ -731,21 +900,18 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
return -2; return -2;
} }
m_poolBlockTemplate->m_txkeyPub = m_txkeyPub;
m_poolBlockTemplate->m_txkeySec = m_txkeySec;
// TX_EXTRA begin // TX_EXTRA begin
m_minerTxExtra.clear(); m_minerTxExtra.clear();
m_minerTxExtra.push_back(TX_EXTRA_TAG_PUBKEY); m_minerTxExtra.push_back(TX_EXTRA_TAG_PUBKEY);
m_minerTxExtra.insert(m_minerTxExtra.end(), m_txkeyPub.h, m_txkeyPub.h + HASH_SIZE); m_minerTxExtra.insert(m_minerTxExtra.end(), m_poolBlockTemplate->m_txkeyPub.h, m_poolBlockTemplate->m_txkeyPub.h + HASH_SIZE);
m_minerTxExtra.push_back(TX_EXTRA_NONCE); m_minerTxExtra.push_back(TX_EXTRA_NONCE);
const uint64_t corrected_extra_nonce_size = EXTRA_NONCE_SIZE + max_reward_amounts_weight - reward_amounts_weight; 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_SIZE) {
if (corrected_extra_nonce_size > EXTRA_NONCE_MAX_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; return -3;
} }
LOGINFO(4, "increased EXTRA_NONCE from " << EXTRA_NONCE_SIZE << " to " << corrected_extra_nonce_size << " bytes to maintain miner tx weight"); LOGINFO(4, "increased EXTRA_NONCE from " << EXTRA_NONCE_SIZE << " to " << corrected_extra_nonce_size << " bytes to maintain miner tx weight");
@@ -776,47 +942,60 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
return 1; return 1;
} }
hash BlockTemplate::calc_sidechain_hash() const hash BlockTemplate::calc_sidechain_hash(uint32_t sidechain_extra_nonce) const
{ {
// Calculate side-chain hash (all block template bytes + all side-chain bytes + consensus ID, replacing NONCE, EXTRA_NONCE and HASH itself with 0's) // Calculate side-chain hash (all block template bytes + all side-chain bytes + consensus ID, replacing NONCE, EXTRA_NONCE and HASH itself with 0's)
hash sidechain_hash; const int v = m_poolBlockTemplate->get_sidechain_version();
const int sidechain_hash_offset = static_cast<int>(m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize) + 2; const size_t size = m_sidechainHashBlob.size();
const int blob_size = static_cast<int>(m_blockTemplateBlob.size()); const size_t N = m_sidechainHashInputLength;
const std::vector<uint8_t>& consensus_id = m_pool->side_chain().consensus_id(); const size_t sidechain_extra_nonce_offset = size - HASH_SIZE - ((v > 1) ? EXTRA_NONCE_SIZE : 0);
const uint8_t sidechain_extra_nonce_buf[EXTRA_NONCE_SIZE] = {
static_cast<uint8_t>(sidechain_extra_nonce >> 0),
static_cast<uint8_t>(sidechain_extra_nonce >> 8),
static_cast<uint8_t>(sidechain_extra_nonce >> 16),
static_cast<uint8_t>(sidechain_extra_nonce >> 24)
};
keccak_custom([this, sidechain_hash_offset, blob_size, consensus_id](int offset) -> uint8_t { hash result;
uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(m_nonceOffset)); uint8_t buf[288];
if (k < NONCE_SIZE) {
return 0; const bool b = N && (N <= sidechain_extra_nonce_offset) && (N < size) && (size - N <= sizeof(buf));
// Slow path: O(N)
if (!b || pool_block_debug()) {
keccak_custom([this, v, sidechain_extra_nonce_offset, &sidechain_extra_nonce_buf](int offset) -> uint8_t {
if (v > 1) {
const uint32_t k = static_cast<uint32_t>(offset - sidechain_extra_nonce_offset);
if (k < EXTRA_NONCE_SIZE) {
return sidechain_extra_nonce_buf[k];
}
} }
return m_sidechainHashBlob[offset];
}, static_cast<int>(size), result.h, HASH_SIZE);
}
k = static_cast<uint32_t>(offset - static_cast<int>(m_extraNonceOffsetInTemplate)); // Fast path: O(1)
if (k < EXTRA_NONCE_SIZE) { if (b) {
return 0; const int inlen = static_cast<int>(size - N);
}
k = static_cast<uint32_t>(offset - sidechain_hash_offset); memcpy(buf, m_sidechainHashBlob.data() + N, size - N);
if (k < HASH_SIZE) { if (v > 1) {
return 0; memcpy(buf + sidechain_extra_nonce_offset - N, sidechain_extra_nonce_buf, EXTRA_NONCE_SIZE);
} }
if (offset < blob_size) { uint64_t st[25];
return m_blockTemplateBlob[offset]; memcpy(st, m_sidechainHashKeccakState, sizeof(st));
} keccak_finish(buf, inlen, st);
const int side_chain_data_offsset = offset - blob_size; if (pool_block_debug() && (memcmp(st, result.h, HASH_SIZE) != 0)) {
const int side_chain_data_size = static_cast<int>(m_poolBlockTemplate->m_sideChainData.size()); LOGERR(1, "calc_sidechain_hash fast path is broken. Fix the code!");
if (side_chain_data_offsset < side_chain_data_size) { }
return m_poolBlockTemplate->m_sideChainData[side_chain_data_offsset];
}
const int consensus_id_offset = side_chain_data_offsset - side_chain_data_size; memcpy(result.h, st, HASH_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);
return sidechain_hash; return result;
} }
hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
@@ -826,7 +1005,7 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
const uint8_t* data = m_blockTemplateBlob.data() + m_minerTxOffsetInTemplate; const uint8_t* data = m_blockTemplateBlob.data() + m_minerTxOffsetInTemplate;
const int extra_nonce_offset = static_cast<int>(m_extraNonceOffsetInTemplate - m_minerTxOffsetInTemplate); const size_t extra_nonce_offset = m_extraNonceOffsetInTemplate - m_minerTxOffsetInTemplate;
const uint8_t extra_nonce_buf[EXTRA_NONCE_SIZE] = { const uint8_t extra_nonce_buf[EXTRA_NONCE_SIZE] = {
static_cast<uint8_t>(extra_nonce >> 0), static_cast<uint8_t>(extra_nonce >> 0),
static_cast<uint8_t>(extra_nonce >> 8), static_cast<uint8_t>(extra_nonce >> 8),
@@ -834,17 +1013,57 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
static_cast<uint8_t>(extra_nonce >> 24) static_cast<uint8_t>(extra_nonce >> 24)
}; };
// Calculate sidechain id with this extra_nonce
const hash sidechain_id = calc_sidechain_hash(extra_nonce);
const size_t sidechain_hash_offset = extra_nonce_offset + m_poolBlockTemplate->m_extraNonceSize + 2;
// 1. Prefix (everything except vin_rct_type byte in the end) // 1. Prefix (everything except vin_rct_type byte in the end)
// Apply extra_nonce in-place because we can't write to the block template here // Apply extra_nonce in-place because we can't write to the block template here
keccak_custom([data, extra_nonce_offset, &extra_nonce_buf](int offset) const size_t tx_size = m_minerTxSize - 1;
{
const uint32_t k = static_cast<uint32_t>(offset - extra_nonce_offset); hash full_hash;
uint8_t tx_buf[288];
const size_t N = m_minerTxKeccakStateInputLength;
const bool b = N && (N <= extra_nonce_offset) && (N < tx_size) && (tx_size - N <= sizeof(tx_buf));
// Slow path: O(N)
if (!b || pool_block_debug())
{
keccak_custom([data, extra_nonce_offset, &extra_nonce_buf, sidechain_hash_offset, &sidechain_id](int offset) {
uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(extra_nonce_offset));
if (k < EXTRA_NONCE_SIZE) { if (k < EXTRA_NONCE_SIZE) {
return extra_nonce_buf[k]; return extra_nonce_buf[k];
} }
k = static_cast<uint32_t>(offset - static_cast<int>(sidechain_hash_offset));
if (k < HASH_SIZE) {
return sidechain_id.h[k];
}
return data[offset]; return data[offset];
}, }, static_cast<int>(tx_size), full_hash.h, HASH_SIZE);
static_cast<int>(m_minerTxSize) - 1, hashes, HASH_SIZE); memcpy(hashes, full_hash.h, HASH_SIZE);
}
// Fast path: O(1)
if (b) {
const int inlen = static_cast<int>(tx_size - N);
memcpy(tx_buf, data + N, inlen);
memcpy(tx_buf + extra_nonce_offset - N, extra_nonce_buf, EXTRA_NONCE_SIZE);
memcpy(tx_buf + sidechain_hash_offset - N, sidechain_id.h, HASH_SIZE);
uint64_t st[25];
memcpy(st, m_minerTxKeccakState, sizeof(st));
keccak_finish(tx_buf, inlen, st);
if (pool_block_debug() && (memcmp(st, full_hash.h, HASH_SIZE) != 0)) {
LOGERR(1, "calc_miner_tx_hash fast path is broken. Fix the code!");
}
memcpy(hashes, st, HASH_SIZE);
}
// 2. Base RCT, single 0 byte in miner tx // 2. Base RCT, single 0 byte in miner tx
static constexpr uint8_t known_second_hash[HASH_SIZE] = { static constexpr uint8_t known_second_hash[HASH_SIZE] = {
@@ -857,7 +1076,7 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
// Calculate miner transaction hash // Calculate miner transaction hash
hash result; hash result;
keccak(hashes, sizeof(hashes), result.h, HASH_SIZE); keccak(hashes, sizeof(hashes), result.h);
return result; return result;
} }
@@ -867,16 +1086,14 @@ void BlockTemplate::calc_merkle_tree_main_branch()
m_merkleTreeMainBranch.clear(); m_merkleTreeMainBranch.clear();
const uint64_t count = m_numTransactionHashes + 1; const uint64_t count = m_numTransactionHashes + 1;
if (count == 1) {
return;
}
const uint8_t* h = m_transactionHashes.data(); const uint8_t* h = m_transactionHashes.data();
hash root_hash; if (count == 2) {
if (count == 1) {
memcpy(root_hash.h, h, HASH_SIZE);
}
else if (count == 2) {
m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), h + HASH_SIZE, h + HASH_SIZE * 2); m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), h + HASH_SIZE, h + HASH_SIZE * 2);
keccak(h, HASH_SIZE * 2, root_hash.h, HASH_SIZE);
} }
else { else {
size_t i, j, cnt; size_t i, j, cnt;
@@ -888,11 +1105,14 @@ void BlockTemplate::calc_merkle_tree_main_branch()
std::vector<uint8_t> ints(cnt * HASH_SIZE); std::vector<uint8_t> ints(cnt * HASH_SIZE);
memcpy(ints.data(), h, (cnt * 2 - count) * HASH_SIZE); memcpy(ints.data(), h, (cnt * 2 - count) * HASH_SIZE);
hash tmp;
for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) { for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
if (i == 0) { if (i == 0) {
m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), h + HASH_SIZE, h + HASH_SIZE * 2); m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), h + HASH_SIZE, h + HASH_SIZE * 2);
} }
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, ints.data() + j * HASH_SIZE, HASH_SIZE); keccak(h + i * HASH_SIZE, HASH_SIZE * 2, tmp.h);
memcpy(ints.data() + j * HASH_SIZE, tmp.h, HASH_SIZE);
} }
while (cnt > 2) { while (cnt > 2) {
@@ -901,20 +1121,22 @@ void BlockTemplate::calc_merkle_tree_main_branch()
if (i == 0) { if (i == 0) {
m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2); m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2);
} }
keccak(ints.data() + i * HASH_SIZE, HASH_SIZE * 2, ints.data() + j * HASH_SIZE, HASH_SIZE); keccak(ints.data() + i * HASH_SIZE, HASH_SIZE * 2, tmp.h);
memcpy(ints.data() + j * HASH_SIZE, tmp.h, HASH_SIZE);
} }
} }
m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2); m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2);
keccak(ints.data(), HASH_SIZE * 2, root_hash.h, HASH_SIZE);
} }
} }
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); ReadLock lock(m_lock);
if (template_id == m_templateId) { if (template_id == m_templateId) {
height = m_height;
sidechain_height = m_poolBlockTemplate->m_sidechainHeight;
mainchain_difficulty = m_difficulty; mainchain_difficulty = m_difficulty;
sidechain_difficulty = m_poolBlockTemplate->m_difficulty; sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
return true; return true;
@@ -923,7 +1145,7 @@ bool BlockTemplate::get_difficulties(const uint32_t template_id, difficulty_type
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)]; const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) { 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; return false;
@@ -952,11 +1174,12 @@ uint32_t BlockTemplate::get_hashing_blob(const uint32_t template_id, uint32_t ex
return 0; 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); ReadLock lock(m_lock);
height = m_height; height = m_height;
sidechain_height = m_poolBlockTemplate->m_sidechainHeight;
difficulty = m_difficulty; difficulty = m_difficulty;
sidechain_difficulty = m_poolBlockTemplate->m_difficulty; sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
seed_hash = m_seedHash; seed_hash = m_seedHash;
@@ -983,7 +1206,7 @@ uint32_t BlockTemplate::get_hashing_blob_nolock(uint32_t extra_nonce, uint8_t* b
memcpy(h, root_hash.h, HASH_SIZE); memcpy(h, root_hash.h, HASH_SIZE);
memcpy(h + HASH_SIZE, m_merkleTreeMainBranch.data() + i, HASH_SIZE); memcpy(h + HASH_SIZE, m_merkleTreeMainBranch.data() + i, HASH_SIZE);
keccak(h, HASH_SIZE * 2, root_hash.h, HASH_SIZE); keccak(h, HASH_SIZE * 2, root_hash.h);
} }
memcpy(p, root_hash.h, HASH_SIZE); memcpy(p, root_hash.h, HASH_SIZE);
@@ -1039,73 +1262,89 @@ uint32_t BlockTemplate::get_hashing_blobs(uint32_t extra_nonce_start, uint32_t c
return blob_size; return blob_size;
} }
std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id, uint32_t sidechain_extra_nonce, size_t& nonce_offset, size_t& extra_nonce_offset, size_t& sidechain_id_offset, hash& sidechain_id) const
{ {
ReadLock lock(m_lock); ReadLock lock(m_lock);
if (template_id != m_templateId) { if (template_id != m_templateId) {
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)]; const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) { if (old && (template_id == old->m_templateId)) {
return old->get_block_template_blob(template_id, nonce_offset, extra_nonce_offset); return old->get_block_template_blob(template_id, sidechain_extra_nonce, nonce_offset, extra_nonce_offset, sidechain_id_offset, sidechain_id);
} }
nonce_offset = 0; nonce_offset = 0;
extra_nonce_offset = 0; extra_nonce_offset = 0;
sidechain_id_offset = 0;
sidechain_id = {};
return std::vector<uint8_t>(); return std::vector<uint8_t>();
} }
nonce_offset = m_nonceOffset; nonce_offset = m_nonceOffset;
extra_nonce_offset = m_extraNonceOffsetInTemplate; extra_nonce_offset = m_extraNonceOffsetInTemplate;
sidechain_id_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 2;
sidechain_id = calc_sidechain_hash(sidechain_extra_nonce);
return m_blockTemplateBlob; return m_blockTemplateBlob;
} }
void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce) bool BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
{ {
WriteLock lock(m_lock); WriteLock lock(m_lock);
if (template_id == m_templateId) { if (template_id == m_templateId) {
m_poolBlockTemplate->m_nonce = nonce; m_poolBlockTemplate->m_nonce = nonce;
m_poolBlockTemplate->m_extraNonce = extra_nonce; m_poolBlockTemplate->m_extraNonce = extra_nonce;
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_nonceOffset, &nonce, NONCE_SIZE); m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(extra_nonce);
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_extraNonceOffsetInTemplate, &extra_nonce, EXTRA_NONCE_SIZE); m_poolBlockTemplate->m_sidechainExtraBuf[3] = extra_nonce;
SideChain& side_chain = m_pool->side_chain(); if (pool_block_debug()) {
std::vector<uint8_t> buf = m_poolBlockTemplate->serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
#if POOL_BLOCK_DEBUG memcpy(buf.data() + m_nonceOffset, &nonce, NONCE_SIZE);
{ memcpy(buf.data() + m_extraNonceOffsetInTemplate, &extra_nonce, EXTRA_NONCE_SIZE);
std::vector<uint8_t> buf = m_poolBlockTemplate->m_mainChainData;
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; PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), side_chain, nullptr); const int result = check.deserialize(buf.data(), buf.size(), *m_sidechain, nullptr, false);
if (result != 0) { if (result != 0) {
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result); LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
} }
hash pow_hash; if (m_hasher) {
if (!check.get_pow_hash(m_pool->hasher(), check.m_txinGenHeight, m_seedHash, pow_hash)) { hash pow_hash;
LOGERR(1, "PoW check failed for the sidechain block. Fix it! "); 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! "); else if (!check.m_difficulty.check_pow(pow_hash)) {
LOGERR(1, "Sidechain block has wrong PoW. Fix it! ");
}
} }
} }
#endif
m_poolBlockTemplate->m_verified = true; m_poolBlockTemplate->m_verified = true;
if (!side_chain.block_seen(*m_poolBlockTemplate)) { if (!m_sidechain->block_seen(*m_poolBlockTemplate)) {
m_poolBlockTemplate->m_wantBroadcast = true; m_poolBlockTemplate->m_wantBroadcast = true;
side_chain.add_block(*m_poolBlockTemplate); const bool result = m_sidechain->add_block(*m_poolBlockTemplate);
if (!result) {
LOGWARN(3, "failed to submit a share: add_block failed for template id " << template_id);
}
return result;
} }
return;
const PoolBlock* b = m_poolBlockTemplate;
LOGWARN(3, "failed to submit a share: template id " << template_id << ", block " << b->m_sidechainId << ", nonce = " << b->m_nonce << ", extra_nonce = " << b->m_extraNonce << " was already added before");
return false;
} }
BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)]; BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) { if (old && (template_id == old->m_templateId)) {
old->submit_sidechain_block(template_id, nonce, extra_nonce); return old->submit_sidechain_block(template_id, nonce, extra_nonce);
return;
} }
LOGWARN(3, "failed to submit a share: template id " << template_id << " is too old/out of range, current template id is " << m_templateId);
return false;
} }
} // namespace p2pool } // namespace p2pool
+34 -13
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -23,7 +23,8 @@
namespace p2pool { namespace p2pool {
class p2pool; class SideChain;
class RandomX_Hasher_Base;
class Mempool; class Mempool;
class Wallet; class Wallet;
struct PoolBlock; struct PoolBlock;
@@ -32,35 +33,43 @@ struct MinerShare;
class BlockTemplate class BlockTemplate
{ {
public: public:
explicit BlockTemplate(p2pool* pool); explicit BlockTemplate(SideChain* sidechain, RandomX_Hasher_Base* hasher);
~BlockTemplate(); ~BlockTemplate();
BlockTemplate(const BlockTemplate& b); BlockTemplate(const BlockTemplate& b);
BlockTemplate& operator=(const BlockTemplate& b); BlockTemplate& operator=(const BlockTemplate& b);
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet); void update(const MinerData& data, const Mempool& mempool, 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(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; uint32_t get_hashing_blobs(uint32_t extra_nonce_start, uint32_t count, std::vector<uint8_t>& blobs, uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
std::vector<uint8_t> get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const; std::vector<uint8_t> get_block_template_blob(uint32_t template_id, uint32_t sidechain_extra_nonce, size_t& nonce_offset, size_t& extra_nonce_offset, size_t& sidechain_id_offset, hash& sidechain_id) const;
FORCEINLINE uint64_t height() const { return m_height; } FORCEINLINE uint64_t height() const { return m_height; }
FORCEINLINE difficulty_type difficulty() const { return m_difficulty; } FORCEINLINE difficulty_type difficulty() const { return m_difficulty; }
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce); bool submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
FORCEINLINE const std::vector<MinerShare>& shares() const { return m_shares; } FORCEINLINE const std::vector<MinerShare>& shares() const { return m_shares; }
FORCEINLINE uint64_t get_reward() const { return m_finalReward; }
#ifdef P2POOL_UNIT_TESTS
FORCEINLINE const PoolBlock* pool_block_template() const { return m_poolBlockTemplate; }
FORCEINLINE std::mt19937_64& rng() { return m_rng; }
#endif
private: private:
p2pool* m_pool; SideChain* m_sidechain;
RandomX_Hasher_Base* m_hasher;
private: private:
int create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run); int create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run);
hash calc_sidechain_hash() const; hash calc_sidechain_hash(uint32_t sidechain_extra_nonce) const;
hash calc_miner_tx_hash(uint32_t extra_nonce) const; hash calc_miner_tx_hash(uint32_t extra_nonce) const;
void calc_merkle_tree_main_branch(); void calc_merkle_tree_main_branch();
@@ -69,8 +78,10 @@ private:
mutable uv_rwlock_t m_lock; mutable uv_rwlock_t m_lock;
uint32_t m_templateId; uint32_t m_templateId;
std::atomic<uint64_t> m_lastUpdated;
std::vector<uint8_t> m_blockTemplateBlob; std::vector<uint8_t> m_blockTemplateBlob;
std::vector<uint8_t> m_fullDataBlob;
std::vector<uint8_t> m_merkleTreeMainBranch; std::vector<uint8_t> m_merkleTreeMainBranch;
size_t m_blockHeaderSize; size_t m_blockHeaderSize;
@@ -87,25 +98,35 @@ private:
uint64_t m_timestamp; uint64_t m_timestamp;
hash m_txkeyPub;
hash m_txkeySec;
PoolBlock* m_poolBlockTemplate; PoolBlock* m_poolBlockTemplate;
BlockTemplate* m_oldTemplates[4] = {}; BlockTemplate* m_oldTemplates[4] = {};
uint64_t m_finalReward; std::atomic<uint64_t> m_finalReward;
// Temp vectors, will be cleaned up after use and skipped in copy constructor/assignment operators // Temp vectors, will be cleaned up after use and skipped in copy constructor/assignment operators
std::vector<uint8_t> m_minerTx; std::vector<uint8_t> m_minerTx;
uint64_t m_minerTxKeccakState[25];
size_t m_minerTxKeccakStateInputLength;
std::vector<uint8_t> m_sidechainHashBlob;
uint64_t m_sidechainHashKeccakState[25];
size_t m_sidechainHashInputLength;
std::vector<uint8_t> m_blockHeader; std::vector<uint8_t> m_blockHeader;
std::vector<uint8_t> m_minerTxExtra; std::vector<uint8_t> m_minerTxExtra;
std::vector<uint8_t> m_transactionHashes; std::vector<uint8_t> m_transactionHashes;
unordered_set<hash> m_transactionHashesSet;
std::vector<uint64_t> m_rewards; std::vector<uint64_t> m_rewards;
std::vector<TxMempoolData> m_mempoolTxs; std::vector<TxMempoolData> m_mempoolTxs;
std::vector<int> m_mempoolTxsOrder; std::vector<int> m_mempoolTxsOrder;
std::vector<int> m_mempoolTxsOrder2;
std::vector<MinerShare> m_shares; std::vector<MinerShare> m_shares;
std::mt19937_64 m_rng;
void shuffle_tx_order();
#if TEST_MEMPOOL_PICKING_ALGORITHM #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); 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);
+127 -12
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
#ifdef _MSC_VER #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 FORCEINLINE __forceinline
#define NOINLINE __declspec(noinline) #define NOINLINE __declspec(noinline)
#define LIKELY(expression) expression #define LIKELY(expression) expression
@@ -81,12 +81,20 @@
#endif #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 { namespace p2pool {
constexpr size_t HASH_SIZE = 32; constexpr size_t HASH_SIZE = 32;
constexpr uint8_t HARDFORK_VIEW_TAGS_VERSION = 15; constexpr uint8_t HARDFORK_VIEW_TAGS_VERSION = 20;
constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 16; constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 20;
constexpr uint8_t MINER_REWARD_UNLOCK_TIME = 60; constexpr uint16_t MINER_REWARD_UNLOCK_TIME = 288;
constexpr uint8_t NONCE_SIZE = 4; constexpr uint8_t NONCE_SIZE = 4;
constexpr uint8_t EXTRA_NONCE_SIZE = 4; constexpr uint8_t EXTRA_NONCE_SIZE = 4;
constexpr uint8_t EXTRA_NONCE_MAX_SIZE = EXTRA_NONCE_SIZE + 10; constexpr uint8_t EXTRA_NONCE_MAX_SIZE = EXTRA_NONCE_SIZE + 10;
@@ -101,6 +109,8 @@ constexpr uint8_t TX_EXTRA_MERGE_MINING_TAG = 3;
#ifdef _MSC_VER #ifdef _MSC_VER
#define umul128 _umul128 #define umul128 _umul128
#define udiv128 _udiv128 #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 #else
FORCEINLINE uint64_t umul128(uint64_t a, uint64_t b, uint64_t* hi) 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; 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 #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{} {} FORCEINLINE hash() : h{} {}
@@ -166,10 +179,14 @@ struct hash
static_assert(sizeof(hash) == HASH_SIZE, "struct hash has invalid size, check your compiler options"); 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"); 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 constexpr difficulty_type() : lo(0), hi(0) {}
FORCEINLINE difficulty_type(uint64_t a, uint64_t b) : lo(a), hi(b) {} FORCEINLINE constexpr difficulty_type(uint64_t a, uint64_t b) : lo(a), hi(b) {}
uint64_t lo; uint64_t lo;
uint64_t hi; uint64_t hi;
@@ -189,6 +206,47 @@ struct difficulty_type
return *this; 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 FORCEINLINE bool operator<(const difficulty_type& other) const
{ {
if (hi < other.hi) return true; if (hi < other.hi) return true;
@@ -196,7 +254,10 @@ struct difficulty_type
return (lo < other.lo); return (lo < other.lo);
} }
FORCEINLINE bool operator>(const difficulty_type& other) const { return other.operator<(*this); }
FORCEINLINE bool operator>=(const difficulty_type& other) const { return !operator<(other); } FORCEINLINE bool operator>=(const difficulty_type& other) const { return !operator<(other); }
FORCEINLINE bool operator<=(const difficulty_type& other) const { return !operator>(other); }
FORCEINLINE bool operator==(const difficulty_type& other) const { return (lo == other.lo) && (hi == other.hi); } FORCEINLINE bool operator==(const difficulty_type& other) const { return (lo == other.lo) && (hi == other.hi); }
FORCEINLINE bool operator!=(const difficulty_type& other) const { return (lo != other.lo) || (hi != other.hi); } FORCEINLINE bool operator!=(const difficulty_type& other) const { return (lo != other.lo) || (hi != other.hi); }
@@ -236,12 +297,60 @@ struct difficulty_type
static_assert(sizeof(difficulty_type) == sizeof(uint64_t) * 2, "struct difficulty_type has invalid size, check your compiler options"); static_assert(sizeof(difficulty_type) == sizeof(uint64_t) * 2, "struct difficulty_type has invalid size, check your compiler options");
static_assert(std::is_standard_layout<difficulty_type>::value, "struct difficulty_type is not a POD, check your compiler options"); static_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); static constexpr difficulty_type diff_max = { std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max() };
template<typename T>
FORCEINLINE difficulty_type operator+(const difficulty_type& a, const T& b)
{
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 struct TxMempoolData
{ {
FORCEINLINE TxMempoolData() : id(), blob_size(0), weight(0), fee(0), time_received(0) {} 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; hash id;
uint64_t blob_size; uint64_t blob_size;
uint64_t weight; uint64_t weight;
@@ -325,6 +434,12 @@ struct raw_ip
return (*this == localhost_ipv4) || (*this == localhost_ipv6); 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"); static_assert(sizeof(raw_ip) == 16, "struct raw_ip has invalid size");
+33 -9
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021 hyc <https://github.com/hyc> * Copyright (c) 2021 hyc <https://github.com/hyc>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@@ -117,8 +117,8 @@ static cmd cmds[] = {
{ STRCONST("peers"), "", "show all peers", do_showpeers }, { STRCONST("peers"), "", "show all peers", do_showpeers },
{ STRCONST("workers"), "", "show all connected workers", do_showworkers }, { STRCONST("workers"), "", "show all connected workers", do_showworkers },
{ STRCONST("bans"), "", "show all banned IPs", do_showbans }, { STRCONST("bans"), "", "show all banned IPs", do_showbans },
{ STRCONST("outpeers"), "", "set maximum number of outgoing connections", do_outpeers }, { STRCONST("outpeers"), "<N>", "set maximum number of outgoing connections", do_outpeers },
{ STRCONST("inpeers"), "", "set maximum number of incoming connections", do_inpeers }, { STRCONST("inpeers"), "<N>", "set maximum number of incoming connections", do_inpeers },
#ifdef WITH_RANDOMX #ifdef WITH_RANDOMX
{ STRCONST("start_mining"), "<threads>", "start mining", do_start_mining }, { STRCONST("start_mining"), "<threads>", "start mining", do_start_mining },
{ STRCONST("stop_mining"), "", "stop mining", do_stop_mining }, { STRCONST("stop_mining"), "", "stop mining", do_stop_mining },
@@ -131,7 +131,7 @@ static void do_help(p2pool * /* m_pool */, const char * /* args */)
{ {
LOGINFO(0, "List of commands"); LOGINFO(0, "List of commands");
for (int i = 0; cmds[i].name.len; ++i) { for (int i = 0; cmds[i].name.len; ++i) {
LOGINFO(0, cmds[i].name.str << " " << cmds[i].arg << "\t" << cmds[i].descr); LOGINFO(0, log::pad_right(cmds[i].name.str, 20) << log::pad_right(cmds[i].arg, 12) << cmds[i].descr);
} }
} }
@@ -161,21 +161,21 @@ static void do_loglevel(p2pool * /* m_pool */, const char *args)
static void do_addpeers(p2pool *m_pool, const char *args) static void do_addpeers(p2pool *m_pool, const char *args)
{ {
if (m_pool->p2p_server()) { if (m_pool->p2p_server()) {
m_pool->p2p_server()->connect_to_peers(args); m_pool->p2p_server()->connect_to_peers_async(args);
} }
} }
static void do_droppeers(p2pool *m_pool, const char * /* args */) static void do_droppeers(p2pool *m_pool, const char * /* args */)
{ {
if (m_pool->p2p_server()) { if (m_pool->p2p_server()) {
m_pool->p2p_server()->drop_connections(); m_pool->p2p_server()->drop_connections_async();
} }
} }
static void do_showpeers(p2pool* m_pool, const char* /* args */) static void do_showpeers(p2pool* m_pool, const char* /* args */)
{ {
if (m_pool->p2p_server()) { if (m_pool->p2p_server()) {
m_pool->p2p_server()->show_peers(); m_pool->p2p_server()->show_peers_async();
} }
} }
@@ -266,6 +266,19 @@ void ConsoleCommands::stdinReadCallback(uv_stream_t* stream, ssize_t nread, cons
for (; c->name.len; ++c) { for (; c->name.len; ++c) {
if (!strncmp(command.c_str(), c->name.str, c->name.len)) { 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) : ""; 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); c->func(pThis->m_pool, args);
break; break;
} }
@@ -273,6 +286,7 @@ void ConsoleCommands::stdinReadCallback(uv_stream_t* stream, ssize_t nread, cons
if (!c->name.len) { if (!c->name.len) {
LOGWARN(0, "Unknown command " << command.c_str()); LOGWARN(0, "Unknown command " << command.c_str());
do_help(nullptr, nullptr);
} }
k = command.find_first_not_of("\r\n", k + 1); k = command.find_first_not_of("\r\n", k + 1);
@@ -289,9 +303,19 @@ void ConsoleCommands::stdinReadCallback(uv_stream_t* stream, ssize_t nread, cons
void ConsoleCommands::loop(void* data) void ConsoleCommands::loop(void* data)
{ {
LOGINFO(1, "event loop started"); LOGINFO(1, "event loop started");
ConsoleCommands* pThis = static_cast<ConsoleCommands*>(data); ConsoleCommands* pThis = static_cast<ConsoleCommands*>(data);
uv_run(&pThis->m_loop, UV_RUN_DEFAULT);
uv_loop_close(&pThis->m_loop); 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"); LOGINFO(1, "event loop stopped");
} }
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+93 -33
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -134,7 +134,7 @@ bool check_keys(const hash& pub, const hash& sec)
static FORCEINLINE void hash_to_scalar(const uint8_t* data, int length, uint8_t (&res)[HASH_SIZE]) static FORCEINLINE void hash_to_scalar(const uint8_t* data, int length, uint8_t (&res)[HASH_SIZE])
{ {
keccak(data, length, res, HASH_SIZE); keccak(data, length, res);
sc_reduce32(res); sc_reduce32(res);
} }
@@ -154,10 +154,13 @@ static FORCEINLINE void derivation_to_scalar(const hash& derivation, size_t outp
hash_to_scalar(data, static_cast<int>(p - data), res); hash_to_scalar(data, static_cast<int>(p - data), res);
} }
class Cache class Cache : public nocopy_nomove
{ {
public: public:
Cache() Cache()
: derivations(new DerivationsMap())
, public_keys(new PublicKeysMap())
, tx_keys(new TxKeysMap())
{ {
uv_rwlock_init_checked(&derivations_lock); uv_rwlock_init_checked(&derivations_lock);
uv_rwlock_init_checked(&public_keys_lock); uv_rwlock_init_checked(&public_keys_lock);
@@ -166,6 +169,10 @@ public:
~Cache() ~Cache()
{ {
delete derivations;
delete public_keys;
delete tx_keys;
uv_rwlock_destroy(&derivations_lock); uv_rwlock_destroy(&derivations_lock);
uv_rwlock_destroy(&public_keys_lock); uv_rwlock_destroy(&public_keys_lock);
uv_rwlock_destroy(&tx_keys_lock); uv_rwlock_destroy(&tx_keys_lock);
@@ -180,8 +187,8 @@ public:
derivation = {}; derivation = {};
{ {
ReadLock lock(derivations_lock); ReadLock lock(derivations_lock);
auto it = derivations.find(index); auto it = derivations->find(index);
if (it != derivations.end()) { if (it != derivations->end()) {
const DerivationEntry& entry = it->second; const DerivationEntry& entry = it->second;
derivation = entry.m_derivation; derivation = entry.m_derivation;
if (entry.find_view_tag(output_index, view_tag)) { if (entry.find_view_tag(output_index, view_tag)) {
@@ -210,12 +217,8 @@ public:
{ {
WriteLock lock(derivations_lock); WriteLock lock(derivations_lock);
DerivationEntry& entry = derivations.emplace(index, DerivationEntry{ derivation, {} }).first->second; DerivationEntry& entry = derivations->emplace(index, DerivationEntry{ derivation, { 0xFFFFFFFFUL, 0xFFFFFFFFUL }, {} }).first->second;
entry.add_view_tag(static_cast<uint32_t>(output_index << 8) | view_tag);
const uint32_t k = static_cast<uint32_t>(output_index << 8) | view_tag;
if (std::find(entry.m_viewTags.begin(), entry.m_viewTags.end(), k) == entry.m_viewTags.end()) {
entry.m_viewTags.emplace_back(k);
}
} }
return true; return true;
@@ -230,8 +233,8 @@ public:
{ {
ReadLock lock(public_keys_lock); ReadLock lock(public_keys_lock);
auto it = public_keys.find(index); auto it = public_keys->find(index);
if (it != public_keys.end()) { if (it != public_keys->end()) {
derived_key = it->second; derived_key = it->second;
return true; return true;
} }
@@ -257,22 +260,22 @@ public:
{ {
WriteLock lock(public_keys_lock); WriteLock lock(public_keys_lock);
public_keys.emplace(index, derived_key); public_keys->emplace(index, derived_key);
} }
return true; return true;
} }
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id) void get_tx_keys(hash& pub, hash& sec, const hash& seed, const hash& monero_block_id)
{ {
std::array<uint8_t, HASH_SIZE * 2> index; std::array<uint8_t, HASH_SIZE * 2> index;
memcpy(index.data(), wallet_spend_key.h, HASH_SIZE); memcpy(index.data(), seed.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE); memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE);
{ {
ReadLock lock(tx_keys_lock); ReadLock lock(tx_keys_lock);
auto it = tx_keys.find(index); auto it = tx_keys->find(index);
if (it != tx_keys.end()) { if (it != tx_keys->end()) {
pub = it->second.first; pub = it->second.first;
sec = it->second.second; sec = it->second.second;
return; return;
@@ -284,32 +287,61 @@ public:
uint8_t entropy[N + HASH_SIZE * 2]; uint8_t entropy[N + HASH_SIZE * 2];
memcpy(entropy, domain, N); memcpy(entropy, domain, N);
memcpy(entropy + N, wallet_spend_key.h, HASH_SIZE); memcpy(entropy + N, seed.h, HASH_SIZE);
memcpy(entropy + N + HASH_SIZE, monero_block_id.h, HASH_SIZE); memcpy(entropy + N + HASH_SIZE, monero_block_id.h, HASH_SIZE);
generate_keys_deterministic(pub, sec, entropy, sizeof(entropy)); generate_keys_deterministic(pub, sec, entropy, sizeof(entropy));
{ {
WriteLock lock(tx_keys_lock); WriteLock lock(tx_keys_lock);
tx_keys.emplace(index, std::pair<hash, hash>(pub, sec)); tx_keys->emplace(index, std::pair<hash, hash>(pub, sec));
} }
} }
void clear() void clear()
{ {
{ WriteLock lock(derivations_lock); derivations.clear(); } {
{ WriteLock lock(public_keys_lock); public_keys.clear(); } WriteLock lock(derivations_lock);
{ WriteLock lock(tx_keys_lock); tx_keys.clear(); } 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: private:
struct DerivationEntry struct DerivationEntry
{ {
hash m_derivation; hash m_derivation;
std::vector<uint32_t> m_viewTags; uint32_t m_viewTags1[2] = { 0xFFFFFFFFUL, 0xFFFFFFFFUL };
std::vector<uint32_t> m_viewTags2;
bool find_view_tag(size_t output_index, uint8_t& view_tag) const { FORCEINLINE bool find_view_tag(size_t output_index, uint8_t& view_tag) const
for (uint32_t k : m_viewTags) { {
#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) { if ((k >> 8) == output_index) {
view_tag = static_cast<uint8_t>(k); view_tag = static_cast<uint8_t>(k);
return true; return true;
@@ -317,16 +349,42 @@ private:
} }
return false; 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);
}
}
}; };
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; uv_rwlock_t derivations_lock;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, DerivationEntry> derivations; DerivationsMap* derivations;
uv_rwlock_t public_keys_lock; uv_rwlock_t public_keys_lock;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys; PublicKeysMap* public_keys;
uv_rwlock_t tx_keys_lock; uv_rwlock_t tx_keys_lock;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> tx_keys; TxKeysMap* tx_keys;
}; };
static Cache* cache = nullptr; static Cache* cache = nullptr;
@@ -341,9 +399,9 @@ bool derive_public_key(const hash& derivation, size_t output_index, const hash&
return cache->get_public_key(derivation, output_index, base, derived_key); return cache->get_public_key(derivation, output_index, base, derived_key);
} }
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id) void get_tx_keys(hash& pub, hash& sec, const hash& seed, const hash& monero_block_id)
{ {
cache->get_tx_keys(pub, sec, wallet_spend_key, monero_block_id); cache->get_tx_keys(pub, sec, seed, monero_block_id);
} }
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag) void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag)
@@ -358,7 +416,7 @@ void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_
writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; }); writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; });
hash view_tag_full; hash view_tag_full;
keccak(buf, static_cast<int>(p - buf), view_tag_full.h, HASH_SIZE); keccak(buf, static_cast<int>(p - buf), view_tag_full.h);
view_tag = view_tag_full.h[0]; view_tag = view_tag_full.h[0];
} }
@@ -379,7 +437,9 @@ void destroy_crypto_cache()
void clear_crypto_cache() void clear_crypto_cache()
{ {
cache->clear(); if (cache) {
cache->clear();
}
} }
} // namespace p2pool } // namespace p2pool
+2 -2
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 @@ namespace p2pool {
void generate_keys(hash& pub, hash& sec); void generate_keys(hash& pub, hash& sec);
void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len); void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len);
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id); void get_tx_keys(hash& pub, hash& sec, const hash& seed, const hash& monero_block_id);
bool check_keys(const hash& pub, const hash& sec); bool 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 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); bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key);
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+22 -12
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -27,7 +27,7 @@ namespace JSONRPCRequest {
struct CurlContext struct CurlContext
{ {
CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop); 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(); ~CurlContext();
static int socket_func(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp) static int socket_func(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp)
@@ -73,7 +73,6 @@ struct CurlContext
std::string m_url; std::string m_url;
std::string m_req; std::string m_req;
std::string m_auth;
std::vector<char> m_response; std::vector<char> m_response;
std::string m_error; std::string m_error;
@@ -81,7 +80,7 @@ struct CurlContext
curl_slist* m_headers; curl_slist* m_headers;
}; };
CurlContext::CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop) 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_callback(cb)
, m_closeCallback(close_cb) , m_closeCallback(close_cb)
, m_loop(loop) , m_loop(loop)
@@ -90,7 +89,6 @@ CurlContext::CurlContext(const std::string& address, int port, const std::string
, m_multiHandle(nullptr) , m_multiHandle(nullptr)
, m_handle(nullptr) , m_handle(nullptr)
, m_req(req) , m_req(req)
, m_auth(auth)
, m_headers(nullptr) , m_headers(nullptr)
{ {
m_pollHandles.reserve(2); m_pollHandles.reserve(2);
@@ -176,19 +174,31 @@ CurlContext::CurlContext(const std::string& address, int port, const std::string
curl_easy_setopt_checked(m_handle, CURLOPT_WRITEFUNCTION, write_func); curl_easy_setopt_checked(m_handle, CURLOPT_WRITEFUNCTION, write_func);
curl_easy_setopt_checked(m_handle, CURLOPT_WRITEDATA, this); curl_easy_setopt_checked(m_handle, CURLOPT_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_URL, m_url.c_str());
curl_easy_setopt_checked(m_handle, CURLOPT_POSTFIELDS, m_req.c_str()); curl_easy_setopt_checked(m_handle, CURLOPT_POSTFIELDS, m_req.c_str());
curl_easy_setopt_checked(m_handle, CURLOPT_CONNECTTIMEOUT, 1); curl_easy_setopt_checked(m_handle, CURLOPT_CONNECTTIMEOUT, timeout);
curl_easy_setopt_checked(m_handle, CURLOPT_TIMEOUT, 10); curl_easy_setopt_checked(m_handle, CURLOPT_TIMEOUT, timeout * 10);
m_headers = curl_slist_append(m_headers, "Content-Type: application/json"); m_headers = curl_slist_append(m_headers, "Content-Type: application/json");
if (m_headers) { if (m_headers) {
curl_easy_setopt_checked(m_handle, CURLOPT_HTTPHEADER, m_headers); curl_easy_setopt_checked(m_handle, CURLOPT_HTTPHEADER, m_headers);
} }
if (!m_auth.empty()) { if (!auth.empty()) {
curl_easy_setopt_checked(m_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_ONLY); curl_easy_setopt_checked(m_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_ONLY);
curl_easy_setopt_checked(m_handle, CURLOPT_USERPWD, m_auth.c_str()); 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); CURLMcode curl_err = curl_multi_add_handle(m_multiHandle, m_handle);
@@ -305,7 +315,7 @@ int CurlContext::on_timer(CURLM* /*multi*/, long timeout_ms)
return 0; return 0;
} }
if (timeout_ms == 0) { if ((timeout_ms == 0) && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_async))) {
// 0 ms timeout, but we can't just call on_timeout() here - we have to kick the UV loop // 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); const int result = uv_async_send(&m_async);
if (result < 0) { if (result < 0) {
@@ -443,7 +453,7 @@ void CurlContext::shutdown()
} }
} }
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop) 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)
{ {
if (!loop) { if (!loop) {
loop = uv_default_loop(); loop = uv_default_loop();
@@ -453,7 +463,7 @@ void Call(const std::string& address, int port, const std::string& req, const st
[=]() [=]()
{ {
try { try {
new CurlContext(address, port, req, auth, cb, close_cb, loop); new CurlContext(address, port, req, auth, proxy, cb, close_cb, loop);
} }
catch (const std::exception& e) { catch (const std::exception& e) {
const char* msg = e.what(); const char* msg = e.what();
+4 -4
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -37,12 +37,12 @@ private:
T m_cb; T m_cb;
}; };
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop); 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> template<typename T, typename U>
FORCEINLINE void call(const std::string& address, int port, const std::string& req, const std::string& auth, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr) 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, new Callback<T>(std::move(cb)), new Callback<U>(std::move(close_cb)), loop); Call(address, port, req, auth, proxy, new Callback<T>(std::move(cb)), new Callback<U>(std::move(close_cb)), loop);
} }
} // namespace JSONRPCRequest } // namespace JSONRPCRequest
+14 -17
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@ namespace p2pool {
#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y)))) #define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y))))
#endif #endif
const uint64_t keccakf_rndc[24] = static const uint64_t keccakf_rndc[24] =
{ {
0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
0x8000000080008000, 0x000000000000808b, 0x0000000080000001, 0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
@@ -36,7 +36,7 @@ const uint64_t keccakf_rndc[24] =
0x8000000000008080, 0x0000000080000001, 0x8000000080008008 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
}; };
NOINLINE void keccakf(uint64_t* st) NOINLINE void keccakf(uint64_t (&st)[25])
{ {
for (int round = 0; round < KeccakParams::ROUNDS; ++round) { for (int round = 0; round < KeccakParams::ROUNDS; ++round) {
uint64_t bc[5]; uint64_t bc[5];
@@ -115,14 +115,10 @@ NOINLINE void keccakf(uint64_t* st)
} }
} }
NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen) NOINLINE void keccak_step(const uint8_t* &in, int &inlen, uint64_t (&st)[25])
{ {
uint64_t st[25]; constexpr int rsiz = KeccakParams::HASH_DATA_AREA;
constexpr int rsizw = rsiz / 8;
const int rsiz = sizeof(st) == mdlen ? KeccakParams::HASH_DATA_AREA : 200 - 2 * mdlen;
const int rsizw = rsiz / 8;
memset(st, 0, sizeof(st));
for (; inlen >= rsiz; inlen -= rsiz, in += rsiz) { for (; inlen >= rsiz; inlen -= rsiz, in += rsiz) {
for (int i = 0; i < rsizw; i++) { for (int i = 0; i < rsizw; i++) {
@@ -130,6 +126,14 @@ NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen)
} }
keccakf(st); keccakf(st);
} }
}
NOINLINE void keccak_finish(const uint8_t* in, int inlen, uint64_t (&st)[25])
{
constexpr int rsiz = KeccakParams::HASH_DATA_AREA;
constexpr int rsizw = rsiz / 8;
keccak_step(in, inlen, st);
// last block and padding // last block and padding
alignas(8) uint8_t temp[144]; alignas(8) uint8_t temp[144];
@@ -144,13 +148,6 @@ NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen)
} }
keccakf(st); keccakf(st);
memcpy(md, st, mdlen);
}
void keccak(const uint8_t *in, int inlen, uint8_t (&md)[200])
{
keccak(in, inlen, md, 200);
} }
} // namespace p2pool } // namespace p2pool
+16 -7
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -24,20 +24,29 @@ enum KeccakParams {
ROUNDS = 24, ROUNDS = 24,
}; };
void keccakf(uint64_t* st); void keccakf(uint64_t (&st)[25]);
void keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen); void keccak_step(const uint8_t* &in, int &inlen, uint64_t (&st)[25]);
void keccak(const uint8_t* in, int inlen, uint8_t (&md)[200]); void keccak_finish(const uint8_t* in, int inlen, uint64_t (&st)[25]);
template<size_t N>
FORCEINLINE void keccak(const uint8_t* in, int inlen, uint8_t (&md)[N])
{
static_assert((N == 32) || (N == 200), "invalid size");
uint64_t st[25] = {};
keccak_step(in, inlen, st);
keccak_finish(in, inlen, st);
memcpy(md, st, N);
}
template<typename T> template<typename T>
FORCEINLINE void keccak_custom(T&& in, int inlen, uint8_t* md, int mdlen) FORCEINLINE void keccak_custom(T&& in, int inlen, uint8_t* md, int mdlen)
{ {
uint64_t st[25]; uint64_t st[25] = {};
const int rsiz = sizeof(st) == mdlen ? KeccakParams::HASH_DATA_AREA : 200 - 2 * mdlen; const int rsiz = sizeof(st) == mdlen ? KeccakParams::HASH_DATA_AREA : 200 - 2 * mdlen;
const int rsizw = rsiz / 8; const int rsizw = rsiz / 8;
memset(st, 0, sizeof(st));
int offset = 0; int offset = 0;
for (; inlen >= rsiz; inlen -= rsiz, offset += rsiz) { for (; inlen >= rsiz; inlen -= rsiz, offset += rsiz) {
+77 -16
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -17,9 +17,15 @@
#include "common.h" #include "common.h"
#include "uv_util.h" #include "uv_util.h"
#include "wallet.h"
#include <ctime> #include <ctime>
#include <fstream> #include <fstream>
#include <thread> #include <thread>
#include <stdlib.h>
#ifdef _MSC_VER
#pragma warning(disable : 4996)
#endif
static constexpr char log_category_prefix[] = "Log "; static constexpr char log_category_prefix[] = "Log ";
static constexpr char log_file_name[] = "p2pool.log"; static constexpr char log_file_name[] = "p2pool.log";
@@ -33,9 +39,6 @@ bool CONSOLE_COLORS = true;
#ifndef P2POOL_LOG_DISABLE #ifndef P2POOL_LOG_DISABLE
static std::atomic<bool> stopped{ false };
static volatile bool worker_started = false;
#ifdef _WIN32 #ifdef _WIN32
static const HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); static const HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
static const HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); static const HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -54,6 +57,8 @@ public:
FORCEINLINE Worker() FORCEINLINE Worker()
: m_writePos(0) : m_writePos(0)
, m_readPos(0) , m_readPos(0)
, m_started{ false }
, m_stopped(false)
{ {
set_main_thread(); set_main_thread();
@@ -73,25 +78,31 @@ public:
abort(); abort();
} }
while (!worker_started) { while (m_started.load() == false) {
std::this_thread::yield(); std::this_thread::yield();
} }
#ifdef _WIN32 #ifdef _WIN32
SetConsoleMode(hStdIn, ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_EXTENDED_FLAGS);
DWORD dwConsoleMode; DWORD dwConsoleMode;
if (GetConsoleMode(hStdIn, &dwConsoleMode)) {
SetConsoleMode(hStdIn, dwConsoleMode & ~ENABLE_QUICK_EDIT_MODE);
}
if (GetConsoleMode(hStdOut, &dwConsoleMode)) { if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
SetConsoleMode(hStdOut, dwConsoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); SetConsoleMode(hStdOut, dwConsoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
} }
#endif #endif
const char* no_color = getenv("NO_COLOR");
if (no_color && *no_color) {
CONSOLE_COLORS = false;
}
LOGINFO(0, "started"); LOGINFO(0, "started");
if (!m_logFile.is_open()) { if (!m_logFile.is_open()) {
LOGERR(0, "failed to open " << log_file_name); LOGERR(0, "failed to open " << log_file_name);
} }
init_uv_threadpool();
} }
~Worker() ~Worker()
@@ -105,16 +116,25 @@ public:
FORCEINLINE void stop() 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_thread_join(&m_worker);
uv_cond_destroy(&m_cond); uv_cond_destroy(&m_cond);
uv_mutex_destroy(&m_mutex); uv_mutex_destroy(&m_mutex);
uv_loop_close(uv_default_loop()); 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(); m_logFile.close();
} }
@@ -143,17 +163,47 @@ public:
uv_cond_signal(&m_cond); 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: private:
static void run_wrapper(void* arg) { reinterpret_cast<Worker*>(arg)->run(); } static void run_wrapper(void* arg) { reinterpret_cast<Worker*>(arg)->run(); }
NOINLINE void run() NOINLINE void run()
{ {
worker_started = true; m_started.exchange(true);
do { do {
uv_mutex_lock(&m_mutex); uv_mutex_lock(&m_mutex);
if (m_readPos == m_writePos.load()) { 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_cond_wait(&m_cond, &m_mutex);
} }
uv_mutex_unlock(&m_mutex); uv_mutex_unlock(&m_mutex);
@@ -217,7 +267,7 @@ private:
severity = '\0'; severity = '\0';
m_readPos += SLOT_SIZE; 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 // 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); 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) static FORCEINLINE void strip_colors(char* buf, uint32_t& size)
@@ -269,6 +319,9 @@ private:
uv_mutex_t m_mutex; uv_mutex_t m_mutex;
uv_thread_t m_worker; uv_thread_t m_worker;
std::atomic<bool> m_started;
bool m_stopped;
std::ofstream m_logFile; std::ofstream m_logFile;
}; };
@@ -278,6 +331,7 @@ static Worker worker;
NOINLINE Writer::Writer(Severity severity) : Stream(m_stackBuf) NOINLINE Writer::Writer(Severity severity) : Stream(m_stackBuf)
{ {
m_stackBuf[BUF_SIZE] = '\0';
m_buf[0] = static_cast<char>(severity); m_buf[0] = static_cast<char>(severity);
m_pos = 3; m_pos = 3;
@@ -336,7 +390,7 @@ NOINLINE void Stream::writeCurrentTime()
m_numberWidth = 1; 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; const char* addr_str;
char addr_str_buf[64]; 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 log
} // namespace p2pool } // namespace p2pool
+10 -12
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -19,6 +19,8 @@
namespace p2pool { namespace p2pool {
class Wallet;
namespace log { namespace log {
extern int GLOBAL_LOG_LEVEL; extern int GLOBAL_LOG_LEVEL;
@@ -362,15 +364,15 @@ template<> struct log::Stream::Entry<XMRAmount>
{ {
static NOINLINE void put(XMRAmount value, Stream* wrapper) static NOINLINE void put(XMRAmount value, Stream* wrapper)
{ {
constexpr uint64_t denomination = 1000000000000ULL; constexpr uint64_t denomination = 100000000000ULL;
const int w = wrapper->getNumberWidth(); const int w = wrapper->getNumberWidth();
wrapper->setNumberWidth(1); wrapper->setNumberWidth(1);
*wrapper << value.m_data / denomination << '.'; *wrapper << value.m_data / denomination << '.';
wrapper->setNumberWidth(12); wrapper->setNumberWidth(11);
*wrapper << value.m_data % denomination << " XMR"; *wrapper << value.m_data % denomination << " WOW";
wrapper->setNumberWidth(w); wrapper->setNumberWidth(w);
} }
@@ -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 NOINLINE void put(const raw_ip& value, Stream* wrapper); };
template<> struct log::Stream::Entry<Wallet> { static NOINLINE void put(const Wallet& w, Stream* wrapper); };
template<> struct log::Stream::Entry<raw_ip>
{
static FORCEINLINE void put(const raw_ip& value, Stream* wrapper) { put_rawip(value, wrapper); }
};
namespace { namespace {
template<log::Severity severity> void apply_severity(log::Stream&); template<log::Severity severity> void apply_severity(log::Stream&);
@@ -494,7 +492,7 @@ struct DummyStream
MSVC_PRAGMA(warning(suppress:26444)) \ MSVC_PRAGMA(warning(suppress:26444)) \
[=]() { \ [=]() { \
log::DummyStream x; \ log::DummyStream x; \
x << level << __VA_ARGS__; \ x << (level) << __VA_ARGS__; \
}; \ }; \
} \ } \
} while (0) } while (0)
@@ -510,7 +508,7 @@ struct DummyStream
#define LOG(level, severity, ...) \ #define LOG(level, severity, ...) \
do { \ do { \
SIDE_EFFECT_CHECK(level, __VA_ARGS__); \ SIDE_EFFECT_CHECK(level, __VA_ARGS__); \
if (level <= log::GLOBAL_LOG_LEVEL) { \ if ((level) <= log::GLOBAL_LOG_LEVEL) { \
log::Writer CONCAT(log_wrapper_, __LINE__)(severity); \ log::Writer CONCAT(log_wrapper_, __LINE__)(severity); \
CONCAT(log_wrapper_, __LINE__) << log::Gray() << log_category_prefix; \ CONCAT(log_wrapper_, __LINE__) << log::Gray() << log_category_prefix; \
log::apply_severity<severity>(CONCAT(log_wrapper_, __LINE__)); \ log::apply_severity<severity>(CONCAT(log_wrapper_, __LINE__)); \
+12 -9
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -27,9 +27,9 @@ void p2pool_usage()
printf("P2Pool %s\n" printf("P2Pool %s\n"
"\nUsage:\n\n" \ "\nUsage:\n\n" \
"--wallet Wallet address to mine to. Subaddresses and integrated addresses are not supported!\n" "--wallet Wallet address to mine to. Subaddresses and integrated addresses are not supported!\n"
"--host IP address of your Monero node, default is 127.0.0.1\n" "--host IP address of your Wownero node, default is 127.0.0.1\n"
"--rpc-port monerod RPC API port number, default is 18081\n" "--rpc-port wownerod RPC API port number, default is 34568\n"
"--zmq-port monerod ZMQ pub port number, default is 18083 (same port as in monerod's \"--zmq-pub\" command line parameter)\n" "--zmq-port wownerod ZMQ pub port number, default is 34569 (same port as in wownerod's \"--zmq-pub\" command line parameter)\n"
"--stratum Comma-separated list of IP:port for stratum server to listen on\n" "--stratum Comma-separated list of IP:port for stratum server to listen on\n"
"--p2p Comma-separated list of IP:port for p2p server to listen on\n" "--p2p Comma-separated list of IP:port for p2p server to listen on\n"
"--addpeers Comma-separated list of IP:port of other p2pool nodes to connect to\n" "--addpeers Comma-separated list of IP:port of other p2pool nodes to connect to\n"
@@ -41,16 +41,19 @@ void p2pool_usage()
"--stratum-api An alias for --local-api\n" "--stratum-api An alias for --local-api\n"
"--no-cache Disable p2pool.cache\n" "--no-cache Disable p2pool.cache\n"
"--no-color Disable colors in console output\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" "--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to wownerod to check PoW hashes\n"
"--out-peers N Maximum number of outgoing 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 1000)\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" "--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" "--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d\n"
"--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum\n" "--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum\n"
"--rpc-login Specify username[:password] required for Monero RPC server\n" "--rpc-login Specify username[:password] required for Wownero 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" "--help Show this help message\n\n"
"Example command line:\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", "%s --host 127.0.0.1 --rpc-port 34568 --zmq-port 34569 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",
p2pool::VERSION, p2pool::VERSION,
p2pool::log::MAX_GLOBAL_LOG_LEVEL, p2pool::log::MAX_GLOBAL_LOG_LEVEL,
p2pool::DEFAULT_P2P_PORT, p2pool::DEFAULT_P2P_PORT,
+96 -35
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -32,15 +32,48 @@ namespace p2pool {
static bool track_memory = false; static bool track_memory = false;
constexpr size_t N = 2097152; constexpr size_t N = 1 << 22;
constexpr size_t MAX_FRAMES = 30; constexpr size_t MAX_FRAMES = 29;
struct TrackedAllocation struct TrackedAllocation
{ {
void* p; void* p;
void* stack_trace[MAX_FRAMES]; void* stack_trace[MAX_FRAMES];
uint64_t allocated_size;
uint32_t thread_id; 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, ""); static_assert(sizeof(TrackedAllocation) == 256, "");
@@ -51,15 +84,62 @@ uint32_t first[N];
uint32_t next[N]; uint32_t next[N];
TrackedAllocation allocations[N]; TrackedAllocation allocations[N];
uint32_t num_allocations = 0; uint32_t num_allocations = 0;
uint64_t total_allocated = 0;
uint32_t cur_allocation_index = 1; 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) FORCEINLINE static void add_alocation(void* p, size_t size)
{ {
if (!track_memory) { if (!track_memory) {
return; return;
} }
void* stack_trace[MAX_FRAMES]; void* stack_trace[MAX_FRAMES] = {};
DWORD hash; DWORD hash;
CaptureStackBackTrace(1, MAX_FRAMES, stack_trace, &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 // Make N two times bigger if this triggers
__debugbreak(); __debugbreak();
} }
total_allocated += size;
for (uint64_t i = cur_allocation_index;; i = (i + 1) & (N - 1)) { for (uint64_t i = cur_allocation_index;; i = (i + 1) & (N - 1)) {
if (i && !allocations[i].allocated_size) { 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]) { for (uint32_t prev = 0, k = first[index]; k != 0; prev = k, k = next[k]) {
if (allocations[k].p == p) { if (allocations[k].p == p) {
total_allocated -= allocations[k].allocated_size;
allocations[k].allocated_size = 0; allocations[k].allocated_size = 0;
if (prev) { if (prev) {
next[prev] = next[k]; next[prev] = next[k];
@@ -181,6 +263,8 @@ void* calloc_hook(size_t count, size_t size) noexcept
void memory_tracking_start() void memory_tracking_start()
{ {
SymInitialize(GetCurrentProcess(), NULL, TRUE);
using namespace p2pool; using namespace p2pool;
uv_replace_allocator(malloc_hook, realloc_hook, calloc_hook, free_hook); uv_replace_allocator(malloc_hook, realloc_hook, calloc_hook, free_hook);
@@ -196,47 +280,24 @@ void memory_tracking_stop()
uv_mutex_destroy(&allocation_lock); uv_mutex_destroy(&allocation_lock);
const HANDLE h = GetCurrentProcess(); const HANDLE h = GetCurrentProcess();
SymInitialize(h, NULL, TRUE);
uint64_t total_leaks = 0; uint64_t total_leaks = 0;
for (uint32_t i = 0; i < N; ++i) { for (uint32_t i = 0; i < N; ++i) {
if (allocations[i].allocated_size) { const TrackedAllocation& t = allocations[i];
total_leaks += allocations[i].allocated_size; if (t.allocated_size) {
total_leaks += t.allocated_size;
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = {}; printf("Memory leak detected, %I64u bytes allocated at %p by thread %u:\n", t.allocated_size, t.p, t.thread_id);
PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer); t.print(h);
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");
} }
} }
if (total_leaks > 0) { if (total_leaks > 0) {
printf("%I64u bytes leaked\n\n", total_leaks); printf("%I64u bytes leaked\n\n", total_leaks);
} }
SymCleanup(h);
} }
NOINLINE void* operator new(size_t n) { return p2pool::allocate(n); } NOINLINE void* operator new(size_t n) { return p2pool::allocate(n); }
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+3 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@
namespace p2pool { namespace p2pool {
constexpr uint64_t HIGH_FEE_VALUE = 6000000000ULL;
class p2pool; class p2pool;
class Mempool : public nocopy_nomove class Mempool : public nocopy_nomove
+43 -14
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -25,6 +25,7 @@
#include "params.h" #include "params.h"
#include "p2pool_api.h" #include "p2pool_api.h"
#include "side_chain.h" #include "side_chain.h"
#include "p2p_server.h"
#include <thread> #include <thread>
static constexpr char log_category_prefix[] = "Miner "; static constexpr char log_category_prefix[] = "Miner ";
@@ -38,14 +39,18 @@ Miner::Miner(p2pool* pool, uint32_t threads)
, m_threads(threads) , m_threads(threads)
, m_stopped{ false } , m_stopped{ false }
, m_startTimestamp(high_resolution_clock::now()) , m_startTimestamp(high_resolution_clock::now())
, m_nonce(0) , m_rng(RandomDeviceSeed::instance)
, m_fullNonce(std::numeric_limits<uint64_t>::max())
, m_nonceTimestamp(m_startTimestamp) , m_nonceTimestamp(m_startTimestamp)
, m_extraNonce(0xF19E3779U)
, m_totalHashes(0) , m_totalHashes(0)
, m_sharesFound(0) , m_sharesFound(0)
, m_sharesFailed(0)
, m_job{} , m_job{}
, m_jobIndex{ 0 } , m_jobIndex{ 0 }
{ {
// Diffuse the initial state in case it has low quality
m_rng.discard(10000);
on_block(m_pool->block_template()); on_block(m_pool->block_template());
m_minerThreads.reserve(threads); m_minerThreads.reserve(threads);
@@ -74,15 +79,24 @@ Miner::~Miner()
void Miner::print_status() void Miner::print_status()
{ {
const uint32_t hash_count = 0 - m_nonce.load(); const uint32_t hash_count = std::numeric_limits<uint32_t>::max() - static_cast<uint32_t>(m_fullNonce.load());
const double dt = static_cast<double>(duration_cast<nanoseconds>(high_resolution_clock::now() - m_nonceTimestamp).count()) / 1e9; const double dt = static_cast<double>(duration_cast<nanoseconds>(high_resolution_clock::now() - m_nonceTimestamp).count()) / 1e9;
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0; const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
char shares_failed_buf[64] = {};
log::Stream s(shares_failed_buf);
const uint32_t shares_found = m_sharesFound;
const uint32_t shares_failed = m_sharesFailed;
if (shares_failed) {
s << log::Yellow() << "\nShares failed = " << shares_failed << log::NoColor();
}
LOGINFO(0, "status" << LOGINFO(0, "status" <<
"\nThreads = " << m_threads << "\nThreads = " << m_threads <<
"\nHashrate = " << log::Hashrate(hr) << "\nHashrate = " << log::Hashrate(hr) <<
"\nShares found = " << m_sharesFound.load() "\nShares found = " << shares_found << static_cast<const char*>(shares_failed_buf)
); );
} }
@@ -91,9 +105,12 @@ void Miner::on_block(const BlockTemplate& block)
const uint32_t next_index = m_jobIndex ^ 1; const uint32_t next_index = m_jobIndex ^ 1;
Job& j = m_job[next_index]; Job& j = m_job[next_index];
hash seed; hash seed;
j.m_blobSize = block.get_hashing_blob(m_extraNonce, j.m_blob, j.m_height, j.m_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
const uint32_t hash_count = 0 - m_nonce.exchange(0); const uint32_t extra_nonce = PoolBlock::signal_v2_readiness(static_cast<uint32_t>(m_rng() >> 32));
j.m_blobSize = block.get_hashing_blob(extra_nonce, j.m_blob, j.m_height, j.m_sidechainHeight, j.m_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
const uint64_t next_full_nonce = (static_cast<uint64_t>(extra_nonce) << 32) | std::numeric_limits<uint32_t>::max();
const uint32_t hash_count = std::numeric_limits<uint32_t>::max() - static_cast<uint32_t>(m_fullNonce.exchange(next_full_nonce));
m_jobIndex = next_index; m_jobIndex = next_index;
const auto cur_ts = high_resolution_clock::now(); const auto cur_ts = high_resolution_clock::now();
@@ -115,6 +132,7 @@ void Miner::on_block(const BlockTemplate& block)
<< ",\"total_hashes\":" << m_totalHashes.load() << ",\"total_hashes\":" << m_totalHashes.load()
<< ",\"time_running\":" << time_running << ",\"time_running\":" << time_running
<< ",\"shares_found\":" << m_sharesFound.load() << ",\"shares_found\":" << m_sharesFound.load()
<< ",\"shares_failed\":" << m_sharesFailed.load()
<< ",\"block_reward_share_percent\":" << block_reward_share_percent << ",\"block_reward_share_percent\":" << block_reward_share_percent
<< ",\"threads\":" << m_threads << ",\"threads\":" << m_threads
<< "}"; << "}";
@@ -126,6 +144,7 @@ void Miner::reset_share_counters()
{ {
m_totalHashes = 0; m_totalHashes = 0;
m_sharesFound = 0; m_sharesFound = 0;
m_sharesFailed = 0;
} }
void Miner::run(void* data) void Miner::run(void* data)
@@ -190,27 +209,37 @@ void Miner::run(WorkerData* data)
if (first) { if (first) {
first = false; first = false;
memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job)); memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job));
job[index].set_nonce(miner->m_nonce.fetch_sub(1), miner->m_extraNonce);
const uint64_t full_nonce = miner->m_fullNonce.fetch_sub(1);
job[index].set_nonce(static_cast<uint32_t>(full_nonce), static_cast<uint32_t>(full_nonce >> 32));
randomx_calculate_hash_first(vm, job[index].m_blob, job[index].m_blobSize); randomx_calculate_hash_first(vm, job[index].m_blob, job[index].m_blobSize);
} }
const Job& j = job[index]; const Job& j = job[index];
index ^= 1; index ^= 1;
memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job)); memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job));
job[index].set_nonce(miner->m_nonce.fetch_sub(1), miner->m_extraNonce);
const uint64_t full_nonce = miner->m_fullNonce.fetch_sub(1);
job[index].set_nonce(static_cast<uint32_t>(full_nonce), static_cast<uint32_t>(full_nonce >> 32));
hash h; hash h;
randomx_calculate_hash_next(vm, job[index].m_blob, job[index].m_blobSize, &h); randomx_calculate_hash_next(vm, job[index].m_blob, job[index].m_blobSize, &h);
if (j.m_diff.check_pow(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->submit_block_async(j.m_templateId, j.m_nonce, j.m_extraNonce);
} }
if (j.m_sidechainDiff.check_pow(h)) { 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); 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_pool->submit_sidechain_block(j.m_templateId, j.m_nonce, j.m_extraNonce);
++m_sharesFound; ++m_sharesFound;
if (!m_pool->submit_sidechain_block(j.m_templateId, j.m_nonce, j.m_extraNonce)) {
if (m_sharesFound > 0) {
--m_sharesFound;
}
++m_sharesFailed;
}
} }
std::this_thread::yield(); std::this_thread::yield();
+6 -3
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -54,12 +54,14 @@ private:
std::chrono::high_resolution_clock::time_point m_startTimestamp; std::chrono::high_resolution_clock::time_point m_startTimestamp;
std::atomic<uint32_t> m_nonce; std::mt19937_64 m_rng;
std::atomic<uint64_t> m_fullNonce;
std::chrono::high_resolution_clock::time_point m_nonceTimestamp; std::chrono::high_resolution_clock::time_point m_nonceTimestamp;
const uint32_t m_extraNonce;
std::atomic<uint64_t> m_totalHashes; std::atomic<uint64_t> m_totalHashes;
std::atomic<uint32_t> m_sharesFound; std::atomic<uint32_t> m_sharesFound;
std::atomic<uint32_t> m_sharesFailed;
struct Job struct Job
{ {
@@ -69,6 +71,7 @@ private:
difficulty_type m_diff = {}; difficulty_type m_diff = {};
difficulty_type m_sidechainDiff = {}; difficulty_type m_sidechainDiff = {};
uint64_t m_height = 0; uint64_t m_height = 0;
uint64_t m_sidechainHeight = 0;
size_t m_nonceOffset = 0; size_t m_nonceOffset = 0;
uint32_t m_nonce = 0; uint32_t m_nonce = 0;
uint32_t m_extraNonce = 0; uint32_t m_extraNonce = 0;
+472 -206
View File
File diff suppressed because it is too large Load Diff
+51 -17
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
#pragma once #pragma once
#include "tcp_server.h" #include "tcp_server.h"
#include <list>
namespace p2pool { namespace p2pool {
@@ -27,8 +28,13 @@ class BlockCache;
static constexpr size_t P2P_BUF_SIZE = 128 * 1024; static constexpr size_t P2P_BUF_SIZE = 128 * 1024;
static constexpr size_t PEER_LIST_RESPONSE_MAX_PEERS = 16; static constexpr size_t PEER_LIST_RESPONSE_MAX_PEERS = 16;
static constexpr int DEFAULT_P2P_PORT = 37889; static constexpr int DEFAULT_P2P_PORT = 47889;
static constexpr int DEFAULT_P2P_PORT_MINI = 37888; static constexpr int DEFAULT_P2P_PORT_MINI = 47888;
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> class P2PServer : public TCPServer<P2P_BUF_SIZE, P2P_BUF_SIZE>
{ {
@@ -42,6 +48,7 @@ public:
BLOCK_BROADCAST = 5, BLOCK_BROADCAST = 5,
PEER_LIST_REQUEST = 6, PEER_LIST_REQUEST = 6,
PEER_LIST_RESPONSE = 7, PEER_LIST_RESPONSE = 7,
BLOCK_BROADCAST_COMPACT = 8,
}; };
explicit P2PServer(p2pool *pool); explicit P2PServer(p2pool *pool);
@@ -51,6 +58,7 @@ public:
void clear_cached_blocks(); void clear_cached_blocks();
void store_in_cache(const PoolBlock& block); 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 connect_to_peers(const std::string& peer_list);
void on_connect_failed(bool is_v6, const raw_ip& ip, int port) override; void on_connect_failed(bool is_v6, const raw_ip& ip, int port) override;
@@ -92,18 +100,23 @@ public:
void on_after_handshake(uint8_t* &p); void on_after_handshake(uint8_t* &p);
bool on_listen_port(const uint8_t* buf); bool on_listen_port(const uint8_t* buf);
bool on_block_request(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_response(const uint8_t* buf, uint32_t size, const hash& expected_id);
bool on_block_broadcast(const uint8_t* buf, uint32_t size); 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_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(const 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 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); 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); } bool is_good() const { return m_handshakeComplete && !m_handshakeInvalid && (m_listenPort >= 0); }
const char* software_name() const;
uint64_t m_peerId; uint64_t m_peerId;
uint64_t m_connectedTime;
uint64_t m_broadcastMaxHeight;
MessageId m_expectedMessage; MessageId m_expectedMessage;
uint64_t m_handshakeChallenge; uint64_t m_handshakeChallenge;
bool m_handshakeSolutionSent; bool m_handshakeSolutionSent;
@@ -116,10 +129,14 @@ public:
uint64_t m_nextOutgoingPeerListRequest; uint64_t m_nextOutgoingPeerListRequest;
std::chrono::high_resolution_clock::time_point m_lastPeerListRequestTime; std::chrono::high_resolution_clock::time_point m_lastPeerListRequestTime;
int m_peerListPendingRequests; int m_peerListPendingRequests;
uint32_t m_protocolVersion;
uint32_t m_SoftwareVersion;
uint32_t m_SoftwareID;
int64_t m_pingTime; int64_t m_pingTime;
int m_blockPendingRequests; std::list<hash> m_blockPendingRequests;
bool m_chainTipBlockRequest;
uint64_t m_lastAlive; uint64_t m_lastAlive;
uint64_t m_lastBroadcastTimestamp; uint64_t m_lastBroadcastTimestamp;
@@ -129,21 +146,23 @@ public:
std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 }; 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_random64();
uint64_t get_peerId() const { return m_peerId; } uint64_t get_peerId() const { return m_peerId; }
void print_status() override; 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(); } 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_outgoing_peers() const { return m_maxOutgoingPeers; }
uint32_t max_incoming_peers() const { return m_maxIncomingPeers; } 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_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), 1000U); } 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); int deserialize_block(const uint8_t* buf, uint32_t size, bool compact);
const PoolBlock* get_block() const { return m_block; } const PoolBlock* get_block() const { return m_block; }
private: private:
@@ -164,8 +183,10 @@ private:
void flush_cache(); void flush_cache();
void download_missing_blocks(); void download_missing_blocks();
void check_zmq(); void check_zmq();
void check_block_template();
void update_peer_connections(); void update_peer_connections();
void update_peer_list(); void update_peer_list();
void send_peer_list_request(P2PClient* client, uint64_t cur_time);
void save_peer_list_async(); void save_peer_list_async();
void save_peer_list(); void save_peer_list();
void load_peer_list(); void load_peer_list();
@@ -174,7 +195,6 @@ private:
void remove_peer_from_list(P2PClient* client); void remove_peer_from_list(P2PClient* client);
void remove_peer_from_list(const raw_ip& ip); void remove_peer_from_list(const raw_ip& ip);
uv_mutex_t m_rngLock;
std::mt19937_64 m_rng; std::mt19937_64 m_rng;
uv_mutex_t m_blockLock; uv_mutex_t m_blockLock;
@@ -207,6 +227,7 @@ private:
{ {
std::vector<uint8_t> blob; std::vector<uint8_t> blob;
std::vector<uint8_t> pruned_blob; std::vector<uint8_t> pruned_blob;
std::vector<uint8_t> compact_blob;
std::vector<hash> ancestor_hashes; std::vector<hash> ancestor_hashes;
}; };
@@ -215,12 +236,25 @@ private:
std::vector<Broadcast*> m_broadcastQueue; std::vector<Broadcast*> m_broadcastQueue;
bool m_lookForMissingBlocks; bool m_lookForMissingBlocks;
uv_mutex_t m_missingBlockRequestsLock;
unordered_set<std::pair<uint64_t, uint64_t>> m_missingBlockRequests; 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(); } static void on_broadcast(uv_async_t* handle) { reinterpret_cast<P2PServer*>(handle->data)->on_broadcast(); }
void 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 } // namespace p2pool
+137 -88
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -38,6 +38,7 @@
#include "keccak.h" #include "keccak.h"
#include <thread> #include <thread>
#include <fstream> #include <fstream>
#include <numeric>
constexpr char log_category_prefix[] = "P2Pool "; constexpr char log_category_prefix[] = "P2Pool ";
constexpr int BLOCK_HEADERS_REQUIRED = 720; constexpr int BLOCK_HEADERS_REQUIRED = 720;
@@ -64,10 +65,18 @@ p2pool::p2pool(int argc, char* argv[])
throw std::exception(); throw std::exception();
} }
bool is_v6; if (m_params->m_socks5Proxy.empty()) {
if (!resolve_host(m_params->m_host, is_v6)) { if (m_params->m_dns) {
LOGERR(1, "resolve_host failed for " << m_params->m_host); bool is_v6;
throw std::exception(); 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; hash pub, sec, eph_public_key;
@@ -154,7 +163,7 @@ p2pool::p2pool(int argc, char* argv[])
m_hasher = new RandomX_Hasher_RPC(this); m_hasher = new RandomX_Hasher_RPC(this);
#endif #endif
m_blockTemplate = new BlockTemplate(this); m_blockTemplate = new BlockTemplate(m_sideChain, m_hasher);
m_mempool = new Mempool(); m_mempool = new Mempool();
try { try {
@@ -182,7 +191,6 @@ p2pool::~p2pool()
delete m_blockTemplate; delete m_blockTemplate;
delete m_mempool; delete m_mempool;
delete m_params; delete m_params;
delete m_consoleCommands;
} }
bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result)
@@ -237,6 +245,11 @@ void p2pool::handle_tx(TxMempoolData& tx)
", weight = " << log::Gray() << tx.weight << log::NoColor() << ", weight = " << log::Gray() << tx.weight << log::NoColor() <<
", fee = " << log::Gray() << static_cast<double>(tx.fee) / 1e6 << " um"); ", 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 #if TEST_MEMPOOL_PICKING_ALGORITHM
m_blockTemplate->update(miner_data(), *m_mempool, &m_params->m_wallet); m_blockTemplate->update(miner_data(), *m_mempool, &m_params->m_wallet);
#endif #endif
@@ -294,6 +307,11 @@ void p2pool::handle_miner_data(MinerData& data)
"\n---------------------------------------------------------------------------------------------------------------" "\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()) { if (!is_main_thread()) {
update_block_template_async(); update_block_template_async();
} }
@@ -311,7 +329,7 @@ void p2pool::handle_miner_data(MinerData& data)
for (uint64_t h = data.height; h && (h + BLOCK_HEADERS_REQUIRED > data.height); --h) { for (uint64_t h = data.height; h && (h + BLOCK_HEADERS_REQUIRED > data.height); --h) {
auto it = m_mainchainByHeight.find(h); auto it = m_mainchainByHeight.find(h);
if ((it == m_mainchainByHeight.end()) || it->second.difficulty.empty()) { if ((it == m_mainchainByHeight.end()) || it->second.difficulty.empty()) {
LOGWARN(3, "Mainchain data for height " << h << " is missing, requesting it from monerod again"); LOGWARN(3, "Mainchain data for height " << h << " is missing, requesting it from wownerod again");
missing_heights.push_back(h); missing_heights.push_back(h);
} }
} }
@@ -322,7 +340,7 @@ void p2pool::handle_miner_data(MinerData& data)
log::Stream s(buf); log::Stream s(buf);
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << h << "}}\0"; s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << h << "}}\0";
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin, 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) [this, h](const char* data, size_t size)
{ {
ChainMain block; ChainMain block;
@@ -393,11 +411,17 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
PoolBlock* block = side_chain().find_block(sidechain_id); PoolBlock* block = side_chain().find_block(sidechain_id);
if (block) { if (block) {
LOGINFO(0, log::LightGreen() << "BLOCK FOUND: main chain block at height " << data.height << " was mined by this p2pool" << BLOCK_FOUND); 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) { 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 { else {
side_chain().watch_mainchain_block(data, sidechain_id); side_chain().watch_mainchain_block(data, sidechain_id);
@@ -433,7 +457,7 @@ void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t e
} }
} }
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); MutexLock lock(m_submitBlockDataLock);
@@ -441,7 +465,7 @@ void p2pool::submit_block_async(const std::vector<uint8_t>& blob)
m_submitBlockData.template_id = 0; m_submitBlockData.template_id = 0;
m_submitBlockData.nonce = 0; m_submitBlockData.nonce = 0;
m_submitBlockData.extra_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 p2pool is stopped, m_submitBlockAsync is most likely already closed
@@ -457,10 +481,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) void p2pool::on_stop(uv_async_t* async)
{ {
p2pool* pool = reinterpret_cast<p2pool*>(async->data); p2pool* pool = reinterpret_cast<p2pool*>(async->data);
delete pool->m_consoleCommands;
if (pool->m_api) { if (pool->m_api) {
pool->m_api->on_stop(); pool->m_api->on_stop();
} }
@@ -470,9 +498,11 @@ void p2pool::on_stop(uv_async_t* async)
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_stopAsync), nullptr); uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_stopAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_restartZMQAsync), nullptr); uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_restartZMQAsync), nullptr);
init_signals(pool, false);
uv_loop_t* loop = uv_default_loop_checked(); uv_loop_t* loop = uv_default_loop_checked();
delete GetLoopUserData(loop, false); delete GetLoopUserData(loop, false);
uv_stop(loop); loop->data = nullptr;
} }
void p2pool::submit_block() const void p2pool::submit_block() const
@@ -488,19 +518,26 @@ void p2pool::submit_block() const
size_t nonce_offset = 0; size_t nonce_offset = 0;
size_t extra_nonce_offset = 0; size_t extra_nonce_offset = 0;
size_t sidechain_id_offset = 0;
hash sidechain_id;
bool is_external = false; bool is_external = false;
if (submit_data.blob.empty()) { if (submit_data.blob.empty()) {
LOGINFO(0, "submit_block: height = " << height << ", template id = " << submit_data.template_id << ", nonce = " << submit_data.nonce << ", extra_nonce = " << submit_data.extra_nonce); submit_data.blob = m_blockTemplate->get_block_template_blob(submit_data.template_id, submit_data.extra_nonce, nonce_offset, extra_nonce_offset, sidechain_id_offset, sidechain_id);
LOGINFO(0, log::LightGreen() << "submit_block: height = " << height
<< ", template id = " << submit_data.template_id
<< ", nonce = " << submit_data.nonce
<< ", extra_nonce = " << submit_data.extra_nonce
<< ", id = " << sidechain_id);
submit_data.blob = m_blockTemplate->get_block_template_blob(submit_data.template_id, nonce_offset, extra_nonce_offset);
if (submit_data.blob.empty()) { if (submit_data.blob.empty()) {
LOGERR(0, "submit_block: couldn't find block template with id " << submit_data.template_id); LOGERR(0, "submit_block: couldn't find block template with id " << submit_data.template_id);
return; return;
} }
} }
else { else {
LOGINFO(0, "submit_block: height = " << height << ", external blob (" << submit_data.blob.size() << " bytes)"); LOGINFO(0, log::LightGreen() << "submit_block: height = " << height << ", external blob (" << submit_data.blob.size() << " bytes)");
is_external = true; is_external = true;
} }
@@ -514,26 +551,28 @@ void p2pool::submit_block() const
const uint32_t extra_nonce = submit_data.extra_nonce; const uint32_t extra_nonce = submit_data.extra_nonce;
for (size_t i = 0; i < submit_data.blob.size(); ++i) { for (size_t i = 0; i < submit_data.blob.size(); ++i) {
char buf[16]; uint8_t b;
if (nonce_offset && nonce_offset <= i && i < nonce_offset + sizeof(submit_data.nonce)) { if (nonce_offset && nonce_offset <= i && i < nonce_offset + sizeof(submit_data.nonce)) {
snprintf(buf, sizeof(buf), "%02x", submit_data.nonce & 255); b = submit_data.nonce & 255;
submit_data.nonce >>= 8; submit_data.nonce >>= 8;
} }
else if (extra_nonce_offset && extra_nonce_offset <= i && i < extra_nonce_offset + sizeof(submit_data.extra_nonce)) { else if (extra_nonce_offset && extra_nonce_offset <= i && i < extra_nonce_offset + sizeof(submit_data.extra_nonce)) {
snprintf(buf, sizeof(buf), "%02x", submit_data.extra_nonce & 255); b = submit_data.extra_nonce & 255;
submit_data.extra_nonce >>= 8; submit_data.extra_nonce >>= 8;
} }
else { else if (sidechain_id_offset && sidechain_id_offset <= i && i < sidechain_id_offset + HASH_SIZE) {
snprintf(buf, sizeof(buf), "%02x", submit_data.blob[i]); b = sidechain_id.h[i - sidechain_id_offset];
} }
else {
request.append(buf); b = submit_data.blob[i];
}
request.append(1, "0123456789abcdef"[b >> 4]);
request.append(1, "0123456789abcdef"[b & 15]);
} }
request.append("\"]}"); request.append("\"]}");
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, request, m_params->m_rpcLogin, JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, request, m_params->m_rpcLogin, m_params->m_socks5Proxy,
[height, diff, template_id, nonce, extra_nonce, is_external](const char* data, size_t size) [height, diff, template_id, nonce, extra_nonce, sidechain_id, is_external](const char* data, size_t size)
{ {
rapidjson::Document doc; rapidjson::Document doc;
if (doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size).HasParseError() || !doc.IsObject()) { if (doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size).HasParseError() || !doc.IsObject()) {
@@ -551,8 +590,8 @@ void p2pool::submit_block() const
const char* error_msg = nullptr; const char* error_msg = nullptr;
auto it = doc.FindMember("message"); auto it = err.FindMember("message");
if (it != doc.MemberEnd() && it->value.IsString()) { if (it != err.MemberEnd() && it->value.IsString()) {
error_msg = it->value.GetString(); error_msg = it->value.GetString();
} }
@@ -560,7 +599,7 @@ void p2pool::submit_block() const
LOGWARN(3, "submit_block (external blob): daemon returned error: " << (error_msg ? error_msg : "unknown error")); LOGWARN(3, "submit_block (external blob): daemon returned error: " << (error_msg ? error_msg : "unknown error"));
} }
else { else {
LOGERR(0, "submit_block: daemon returned error: '" << (error_msg ? error_msg : "unknown error") << "', template id = " << template_id << ", nonce = " << nonce << ", extra_nonce = " << extra_nonce); LOGERR(0, "submit_block: daemon returned error: '" << (error_msg ? error_msg : "unknown error") << "', template id = " << template_id << ", nonce = " << nonce << ", extra_nonce = " << extra_nonce << ", id = " << sidechain_id);
} }
return; return;
} }
@@ -590,10 +629,10 @@ void p2pool::submit_block() const
}); });
} }
void p2pool::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce) bool p2pool::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
{ {
LOGINFO(3, "submit_sidechain_block: template id = " << template_id << ", nonce = " << nonce << ", extra_nonce = " << extra_nonce); LOGINFO(3, "submit_sidechain_block: template id = " << template_id << ", nonce = " << nonce << ", extra_nonce = " << extra_nonce);
m_blockTemplate->submit_sidechain_block(template_id, nonce, extra_nonce); return m_blockTemplate->submit_sidechain_block(template_id, nonce, extra_nonce);
} }
void p2pool::update_block_template_async(bool is_alternative_block) void p2pool::update_block_template_async(bool is_alternative_block)
@@ -603,7 +642,9 @@ void p2pool::update_block_template_async(bool is_alternative_block)
return; return;
} }
m_isAlternativeBlock = is_alternative_block; if (is_alternative_block) {
m_isAlternativeBlock = true;
}
const int err = uv_async_send(&m_blockTemplateAsync); const int err = uv_async_send(&m_blockTemplateAsync);
if (err) { if (err) {
@@ -648,7 +689,7 @@ void p2pool::download_block_headers(uint64_t current_height)
s.m_pos = 0; s.m_pos = 0;
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << height << "}}\0"; s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << height << "}}\0";
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin, 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) [this, prev_seed_height, height](const char* data, size_t size)
{ {
ChainMain block; ChainMain block;
@@ -659,15 +700,15 @@ void p2pool::download_block_headers(uint64_t current_height)
} }
} }
else { 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(); PANIC_STOP();
} }
}, },
[height](const char* data, size_t size) [height](const char* data, size_t size)
{ {
if (size > 0) { 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(); PANIC_STOP();
} }
}); });
} }
@@ -677,18 +718,18 @@ void p2pool::download_block_headers(uint64_t current_height)
s.m_pos = 0; 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"; s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_headers_range\",\"params\":{\"start_height\":" << start_height << ",\"end_height\":" << current_height - 1 << "}}\0";
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin, 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) [this, start_height, current_height](const char* data, size_t size)
{ {
if (parse_block_headers_range(data, size) == current_height - start_height) { if (parse_block_headers_range(data, size) == current_height - start_height) {
update_median_timestamp(); update_median_timestamp();
if (m_serversStarted.exchange(1) == 0) { if (m_serversStarted.exchange(1) == 0) {
try { 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) { catch (const std::exception& e) {
LOGERR(1, "Couldn't start ZMQ reader: exception " << e.what()); LOGERR(1, "Couldn't start ZMQ reader: exception " << e.what());
panic(); PANIC_STOP();
} }
m_stratumServer = new StratumServer(this); m_stratumServer = new StratumServer(this);
@@ -699,18 +740,19 @@ void p2pool::download_block_headers(uint64_t current_height)
} }
#endif #endif
api_update_network_stats(); api_update_network_stats();
get_miner_data();
} }
} }
else { else {
LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1); LOGERR(1, "Couldn't download block headers for heights " << start_height << " - " << current_height - 1);
panic(); 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) { 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)); LOGERR(1, "Couldn't download block headers for heights " << start_height << " - " << current_height - 1 << ", error " << log::const_buf(data, size));
panic(); download_block_headers(current_height);
} }
}); });
} }
@@ -785,7 +827,7 @@ void p2pool::stratum_on_block()
void p2pool::get_info() void p2pool::get_info()
{ {
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_info\"}", m_params->m_rpcLogin, 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) [this](const char* data, size_t size)
{ {
parse_get_info_rpc(data, size); parse_get_info_rpc(data, size);
@@ -813,30 +855,31 @@ void p2pool::load_found_blocks()
return; return;
} }
while (!f.eof()) { while (f.good()) {
time_t timestamp; time_t timestamp;
f >> timestamp; f >> timestamp;
if (f.eof()) break; if (!f.good()) break;
uint64_t height; uint64_t height;
f >> height; f >> height;
if (f.eof()) break; if (!f.good()) break;
hash id; hash id;
f >> id; f >> id;
if (f.eof()) break; if (!f.good()) break;
difficulty_type block_difficulty; difficulty_type block_difficulty;
f >> block_difficulty; f >> block_difficulty;
if (f.eof()) break; if (!f.good()) break;
difficulty_type cumulative_difficulty; difficulty_type cumulative_difficulty;
f >> cumulative_difficulty; f >> cumulative_difficulty;
if (!f.good() && !f.eof()) break;
m_foundBlocks.emplace_back(timestamp, height, id, block_difficulty, cumulative_difficulty); 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) void p2pool::parse_get_info_rpc(const char* data, size_t size)
@@ -873,7 +916,7 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
} }
if (info.busy_syncing || !info.synchronized) { if (info.busy_syncing || !info.synchronized) {
LOGINFO(1, "monerod is " << (info.busy_syncing ? "busy syncing" : "not synchronized") << ", trying again in 1 second"); LOGINFO(1, "wownerod is " << (info.busy_syncing ? "busy syncing" : "not synchronized") << ", trying again in 1 second");
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_info(); get_info();
return; return;
@@ -888,8 +931,8 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
const NetworkType sidechain_network = m_sideChain->network_type(); const NetworkType sidechain_network = m_sideChain->network_type();
if (monero_network != sidechain_network) { if (monero_network != sidechain_network) {
LOGERR(1, "monerod is on " << monero_network << ", but you're mining to a " << sidechain_network << " sidechain"); LOGERR(1, "wownerod is on " << monero_network << ", but you're mining to a " << sidechain_network << " sidechain");
panic(); PANIC_STOP();
} }
get_version(); get_version();
@@ -897,7 +940,7 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
void p2pool::get_version() void p2pool::get_version()
{ {
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_version\"}", m_params->m_rpcLogin, 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) [this](const char* data, size_t size)
{ {
parse_get_version_rpc(data, size); parse_get_version_rpc(data, size);
@@ -949,15 +992,17 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
return; return;
} }
const uint64_t required = m_params->m_disableRandomX ? 0x30009 : 0x30008; constexpr uint64_t major = 3;
constexpr uint64_t minor = 11;
constexpr uint64_t required = (major << 16) | minor;
if (version < required) { if (version < required) {
const uint64_t version_hi = version >> 16; const uint64_t version_hi = version >> 16;
const uint64_t version_lo = version & 65535; const uint64_t version_lo = version & 65535;
const uint64_t required_version_hi = required >> 16; const uint64_t required_version_hi = required >> 16;
const uint64_t required_version_lo = required & 65535; 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, "wownerod RPC v" << version_hi << '.' << version_lo << " is incompatible, update to RPC >= v" << required_version_hi << '.' << required_version_lo << " (Wownero v0.11.0.0 or newer)");
panic(); PANIC_STOP();
} }
get_miner_data(); get_miner_data();
@@ -965,9 +1010,12 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
void p2pool::get_miner_data() void p2pool::get_miner_data()
{ {
if (m_getMinerDataPending) {
return;
}
m_getMinerDataPending = true; m_getMinerDataPending = true;
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_miner_data\"}", m_params->m_rpcLogin, 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) [this](const char* data, size_t size)
{ {
parse_get_miner_data_rpc(data, size); parse_get_miner_data_rpc(data, size);
@@ -994,7 +1042,7 @@ void p2pool::parse_get_miner_data_rpc(const char* data, size_t size)
} }
hash h; hash h;
keccak(reinterpret_cast<const uint8_t*>(data), static_cast<int>(size), h.h, HASH_SIZE); keccak(reinterpret_cast<const uint8_t*>(data), static_cast<int>(size), h.h);
if (h == m_getMinerDataHash) { if (h == m_getMinerDataHash) {
LOGWARN(4, "Received a duplicate get_miner_data RPC response, ignoring it"); LOGWARN(4, "Received a duplicate get_miner_data RPC response, ignoring it");
return; return;
@@ -1044,7 +1092,9 @@ void p2pool::parse_get_miner_data_rpc(const char* data, size_t size)
} }
handle_miner_data(minerData); 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) bool p2pool::parse_block_header(const char* data, size_t size, ChainMain& c)
@@ -1183,6 +1233,9 @@ void p2pool::api_update_pool_stats()
const uint64_t miners = std::max<uint64_t>(m_sideChain->miner_count(), m_p2pServer ? m_p2pServer->peer_list_size() : 0U); const uint64_t miners = std::max<uint64_t>(m_sideChain->miner_count(), m_p2pServer ? m_p2pServer->peer_list_size() : 0U);
const difficulty_type total_hashes = m_sideChain->total_hashes(); const difficulty_type total_hashes = m_sideChain->total_hashes();
const auto& s = m_blockTemplate->shares();
const difficulty_type pplns_weight = std::accumulate(s.begin(), s.end(), difficulty_type(), [](const auto& a, const auto& b) { return a + b.m_weight; });
time_t last_block_found_time = 0; time_t last_block_found_time = 0;
uint64_t last_block_found_height = 0; uint64_t last_block_found_height = 0;
uint64_t total_blocks_found = 0; uint64_t total_blocks_found = 0;
@@ -1197,7 +1250,7 @@ void p2pool::api_update_pool_stats()
} }
m_api->set(p2pool_api::Category::POOL, "stats", m_api->set(p2pool_api::Category::POOL, "stats",
[hashrate, miners, &total_hashes, last_block_found_time, last_block_found_height, total_blocks_found](log::Stream& s) [hashrate, miners, &total_hashes, last_block_found_time, last_block_found_height, total_blocks_found, &pplns_weight](log::Stream& s)
{ {
s << "{\"pool_list\":[\"pplns\"],\"pool_statistics\":{\"hashRate\":" << hashrate s << "{\"pool_list\":[\"pplns\"],\"pool_statistics\":{\"hashRate\":" << hashrate
<< ",\"miners\":" << miners << ",\"miners\":" << miners
@@ -1205,6 +1258,7 @@ void p2pool::api_update_pool_stats()
<< ",\"lastBlockFoundTime\":" << last_block_found_time << ",\"lastBlockFoundTime\":" << last_block_found_time
<< ",\"lastBlockFound\":" << last_block_found_height << ",\"lastBlockFound\":" << last_block_found_height
<< ",\"totalBlocksFound\":" << total_blocks_found << ",\"totalBlocksFound\":" << total_blocks_found
<< ",\"pplnsWeight\":" << pplns_weight
<< "}}"; << "}}";
}); });
@@ -1304,22 +1358,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) { if (!m_api) {
return; return;
} }
const time_t cur_time = time(nullptr); 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; difficulty_type diff;
if (data && get_difficulty_at_height(data->height, diff)) { if (data && get_difficulty_at_height(data->height, diff)) {
std::ofstream f(FOUND_BLOCKS_FILE, std::ios::app); std::ofstream f(FOUND_BLOCKS_FILE, std::ios::app);
if (f.is_open()) { if (f.is_open()) {
f << cur_time << ' ' << data->height << ' ' << data->id << ' ' << diff << ' ' << total_hashes << '\n'; f << cur_time << ' ' << data->height << ' ' << data->id << ' ' << diff << ' ' << total_hashes << '\n';
f.flush();
f.close();
} }
} }
@@ -1421,13 +1475,7 @@ static void on_signal(uv_signal_t* handle, int signum)
pool->stop(); pool->stop();
} }
static bool init_uv_threadpool() bool init_signals(p2pool* pool, bool init)
{
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)
{ {
#ifdef SIGPIPE #ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
@@ -1447,6 +1495,14 @@ static bool init_signals(p2pool* pool)
static uv_signal_t signals[array_size(signal_names)]; 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) { for (size_t i = 0; i < array_size(signal_names); ++i) {
uv_signal_init(uv_default_loop_checked(), &signals[i]); uv_signal_init(uv_default_loop_checked(), &signals[i]);
signals[i].data = pool; signals[i].data = pool;
@@ -1480,15 +1536,13 @@ void p2pool::restart_zmq()
return; return;
} }
if (!m_getMinerDataPending) { get_miner_data();
get_miner_data();
}
delete m_ZMQReader; delete m_ZMQReader;
m_ZMQReader = nullptr; m_ZMQReader = nullptr;
try { 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(); m_zmqLastActive = seconds_since_epoch();
} }
catch (const std::exception& e) { catch (const std::exception& e) {
@@ -1498,17 +1552,12 @@ void p2pool::restart_zmq()
int p2pool::run() int p2pool::run()
{ {
if (!m_params->ok()) { if (!m_params->valid()) {
LOGERR(1, "Invalid or missing command line. Try \"p2pool --help\"."); LOGERR(1, "Invalid or missing command line. Try \"p2pool --help\".");
return 1; return 1;
} }
if (!init_uv_threadpool()) { if (!init_signals(this, true)) {
LOGERR(1, "failed to start UV thread pool");
return 1;
}
if (!init_signals(this)) {
LOGERR(1, "failed to initialize signal handlers"); LOGERR(1, "failed to initialize signal handlers");
return 1; return 1;
} }
@@ -1528,7 +1577,7 @@ int p2pool::run()
catch (const std::exception& e) { catch (const std::exception& e) {
const char* s = e.what(); const char* s = e.what();
LOGERR(1, "exception " << s); LOGERR(1, "exception " << s);
panic(); PANIC_STOP();
} }
m_stopped = true; m_stopped = true;
+7 -6
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -33,8 +33,9 @@ class Miner;
class ConsoleCommands; class ConsoleCommands;
class p2pool_api; class p2pool_api;
class ZMQReader; class ZMQReader;
struct PoolBlock;
class p2pool : public MinerCallbackHandler class p2pool : public MinerCallbackHandler, public nocopy_nomove
{ {
public: public:
p2pool(int argc, char* argv[]); p2pool(int argc, char* argv[]);
@@ -74,8 +75,8 @@ public:
virtual void handle_chain_main(ChainMain& data, const char* extra) override; 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(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); bool submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
void update_block_template_async(bool is_alternative_block = false); void update_block_template_async(bool is_alternative_block = false);
void update_block_template(); void update_block_template();
@@ -84,7 +85,7 @@ public:
bool chainmain_get_by_hash(const hash& id, ChainMain& data) const; 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); bool get_difficulty_at_height(uint64_t height, difficulty_type& diff);
@@ -142,7 +143,7 @@ private:
void get_miner_data(); void get_miner_data();
void parse_get_miner_data_rpc(const char* data, size_t size); 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); uint32_t parse_block_headers_range(const char* data, size_t size);
void api_update_network_stats(); void api_update_network_stats();
+77 -26
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@ p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats)
{ {
if (m_apiPath.empty()) { if (m_apiPath.empty()) {
LOGERR(1, "api path is empty"); LOGERR(1, "api path is empty");
panic(); PANIC_STOP();
} }
if ((m_apiPath.back() != '/') if ((m_apiPath.back() != '/')
@@ -48,13 +48,13 @@ p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats)
struct stat buf; struct stat buf;
if (stat(m_apiPath.c_str(), &buf) != 0) { if (stat(m_apiPath.c_str(), &buf) != 0) {
LOGERR(1, "path " << m_apiPath << " doesn't exist"); LOGERR(1, "path " << m_apiPath << " doesn't exist");
panic(); PANIC_STOP();
} }
int result = uv_async_init(uv_default_loop_checked(), &m_dumpToFileAsync, on_dump_to_file); int result = uv_async_init(uv_default_loop_checked(), &m_dumpToFileAsync, on_dump_to_file);
if (result) { if (result) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(result)); LOGERR(1, "uv_async_init failed, error " << uv_err_name(result));
panic(); PANIC_STOP();
} }
m_dumpToFileAsync.data = this; m_dumpToFileAsync.data = this;
@@ -93,7 +93,7 @@ void p2pool_api::create_dir(const std::string& path)
result = errno; result = errno;
if (result != EEXIST) { if (result != EEXIST) {
LOGERR(1, "mkdir(" << path << ") failed, error " << result); LOGERR(1, "mkdir(" << path << ") failed, error " << result);
panic(); PANIC_STOP();
} }
} }
} }
@@ -124,7 +124,9 @@ void p2pool_api::dump_to_file_async_internal(Category category, const char* file
m_dumpData[path] = std::move(buf); m_dumpData[path] = std::move(buf);
} }
uv_async_send(&m_dumpToFileAsync); if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_dumpToFileAsync))) {
uv_async_send(&m_dumpToFileAsync);
}
} }
void p2pool_api::dump_to_file() void p2pool_api::dump_to_file()
@@ -170,59 +172,108 @@ void p2pool_api::on_fs_open(uv_fs_t* req)
buf[0].base = work->buf.data(); buf[0].base = work->buf.data();
buf[0].len = static_cast<uint32_t>(work->buf.size()); buf[0].len = static_cast<uint32_t>(work->buf.size());
const int result = uv_fs_write(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), buf, 1, 0, on_fs_write); int result = uv_fs_write(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), buf, 1, -1, on_fs_write);
if (result < 0) { if (result < 0) {
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result)); LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
return; result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_error_cleanup);
if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
}
} }
} }
void p2pool_api::on_fs_write(uv_fs_t* req) void p2pool_api::on_fs_write(uv_fs_t* req)
{ {
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data); DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
int result = static_cast<int>(req->result);
if (req->result < 0) {
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(static_cast<int>(req->result)));
}
uv_fs_req_cleanup(req); uv_fs_req_cleanup(req);
const int result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_close); if (result < 0) {
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
}
else if (result && (static_cast<size_t>(result) < work->buf.size())) {
work->buf.erase(work->buf.begin(), work->buf.begin() + result);
uv_buf_t buf[1];
buf[0].base = work->buf.data();
buf[0].len = static_cast<uint32_t>(work->buf.size());
result = uv_fs_write(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), buf, 1, -1, on_fs_write);
if (result < 0) {
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_error_cleanup);
if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
}
}
return;
}
result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_close);
if (result < 0) { if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result)); LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
delete work; delete work;
return;
} }
} }
void p2pool_api::on_fs_close(uv_fs_t* req) void p2pool_api::on_fs_close(uv_fs_t* req)
{ {
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data); DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
int result = static_cast<int>(req->result);
if (req->result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(static_cast<int>(req->result)));
}
uv_fs_req_cleanup(req); uv_fs_req_cleanup(req);
const int result = uv_fs_rename(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), work->name.c_str(), on_fs_rename); if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
}
result = uv_fs_rename(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), work->name.c_str(), on_fs_rename);
if (result < 0) { if (result < 0) {
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(result)); LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(result));
delete work;
return; result = uv_fs_unlink(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), on_fs_error_cleanup);
if (result < 0) {
LOGWARN(4, "failed to delete " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
}
} }
} }
void p2pool_api::on_fs_rename(uv_fs_t* req) void p2pool_api::on_fs_rename(uv_fs_t* req)
{ {
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data); DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
int result = static_cast<int>(req->result);
uv_fs_req_cleanup(req);
if (req->result < 0) { if (result < 0) {
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(static_cast<int>(req->result))); LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(result));
result = uv_fs_unlink(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), on_fs_error_cleanup);
if (result < 0) {
LOGWARN(4, "failed to delete " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
}
return;
} }
delete work;
}
void p2pool_api::on_fs_error_cleanup(uv_fs_t* req)
{
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
int result = static_cast<int>(req->result);
uv_fs_req_cleanup(req); uv_fs_req_cleanup(req);
if (result < 0) {
LOGWARN(4, "failed to cleanup after previous errors " << work->tmp_name << ", error " << uv_err_name(result));
}
delete work; delete work;
} }
+2 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -78,6 +78,7 @@ private:
static void on_fs_write(uv_fs_t* req); static void on_fs_write(uv_fs_t* req);
static void on_fs_close(uv_fs_t* req); static void on_fs_close(uv_fs_t* req);
static void on_fs_rename(uv_fs_t* req); static void on_fs_rename(uv_fs_t* req);
static void on_fs_error_cleanup(uv_fs_t* req);
std::string m_apiPath; std::string m_apiPath;
std::string m_networkPath; std::string m_networkPath;
+24 -8
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -35,12 +35,12 @@ Params::Params(int argc, char* argv[])
} }
if ((strcmp(argv[i], "--rpc-port") == 0) && (i + 1 < argc)) { 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; ok = true;
} }
if ((strcmp(argv[i], "--zmq-port") == 0) && (i + 1 < argc)) { 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; ok = true;
} }
@@ -105,13 +105,13 @@ Params::Params(int argc, char* argv[])
ok = true; ok = true;
} }
if ((strcmp(argv[i], "--out-peers") == 0) && (i + 1 < argc)) { 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), 1000UL); m_maxOutgoingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 450UL);
ok = true; ok = true;
} }
if ((strcmp(argv[i], "--in-peers") == 0) && (i + 1 < argc)) { 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), 1000UL); m_maxIncomingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 450UL);
ok = true; ok = true;
} }
@@ -135,6 +135,22 @@ Params::Params(int argc, char* argv[])
ok = true; 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) { if (!ok) {
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]); fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
p2pool_usage(); p2pool_usage();
@@ -153,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(); return !m_host.empty() && m_rpcPort && m_zmqPort && m_wallet.valid();
} }
+8 -5
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -25,11 +25,11 @@ struct Params
{ {
Params(int argc, char* argv[]); Params(int argc, char* argv[]);
bool ok() const; bool valid() const;
std::string m_host = "127.0.0.1"; std::string m_host = "127.0.0.1";
uint32_t m_rpcPort = 18081; uint32_t m_rpcPort = 34568;
uint32_t m_zmqPort = 18083; uint32_t m_zmqPort = 34569;
bool m_lightMode = false; bool m_lightMode = false;
Wallet m_wallet{ nullptr }; Wallet m_wallet{ nullptr };
std::string m_stratumAddresses; std::string m_stratumAddresses;
@@ -45,11 +45,14 @@ struct Params
bool m_disableRandomX = true; bool m_disableRandomX = true;
#endif #endif
uint32_t m_maxOutgoingPeers = 10; uint32_t m_maxOutgoingPeers = 10;
uint32_t m_maxIncomingPeers = 1000; uint32_t m_maxIncomingPeers = 450;
uint32_t m_minerThreads = 0; uint32_t m_minerThreads = 0;
bool m_mini = false; bool m_mini = false;
bool m_autoDiff = true; bool m_autoDiff = true;
std::string m_rpcLogin; std::string m_rpcLogin;
std::string m_socks5Proxy;
bool m_dns = true;
uint32_t m_p2pExternalPort = 0;
}; };
} // namespace p2pool } // namespace p2pool
+198 -110
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -29,11 +29,7 @@ static constexpr char log_category_prefix[] = "PoolBlock ";
namespace p2pool { namespace p2pool {
PoolBlock::PoolBlock() PoolBlock::PoolBlock()
: m_mainChainHeaderSize(0) : m_majorVersion(0)
, m_mainChainMinerTxSize(0)
, m_mainChainOutputsOffset(0)
, m_mainChainOutputsBlobSize(0)
, m_majorVersion(0)
, m_minorVersion(0) , m_minorVersion(0)
, m_timestamp(0) , m_timestamp(0)
, m_prevId{} , m_prevId{}
@@ -42,11 +38,13 @@ PoolBlock::PoolBlock()
, m_txkeyPub{} , m_txkeyPub{}
, m_extraNonceSize(0) , m_extraNonceSize(0)
, m_extraNonce(0) , m_extraNonce(0)
, m_txkeySecSeed{}
, m_txkeySec{} , m_txkeySec{}
, m_parent{} , m_parent{}
, m_sidechainHeight(0) , m_sidechainHeight(0)
, m_difficulty{} , m_difficulty{}
, m_cumulativeDifficulty{} , m_cumulativeDifficulty{}
, m_sidechainExtraBuf{}
, m_sidechainId{} , m_sidechainId{}
, m_depth(0) , m_depth(0)
, m_verified(false) , m_verified(false)
@@ -56,19 +54,10 @@ PoolBlock::PoolBlock()
, m_precalculated(false) , m_precalculated(false)
, m_localTimestamp(seconds_since_epoch()) , 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) PoolBlock::PoolBlock(const PoolBlock& b)
{ {
uv_mutex_init_checked(&m_lock);
operator=(b); operator=(b);
} }
@@ -79,16 +68,11 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
return *this; return *this;
} }
const int lock_result = uv_mutex_trylock(&b.m_lock); #if POOL_BLOCK_DEBUG
if (lock_result) { m_mainChainDataDebug = b.m_mainChainDataDebug;
LOGERR(1, "operator= uv_mutex_trylock failed. Fix the code!"); m_sideChainDataDebug = b.m_sideChainDataDebug;
} #endif
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;
m_majorVersion = b.m_majorVersion; m_majorVersion = b.m_majorVersion;
m_minorVersion = b.m_minorVersion; m_minorVersion = b.m_minorVersion;
m_timestamp = b.m_timestamp; m_timestamp = b.m_timestamp;
@@ -100,16 +84,16 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_extraNonceSize = b.m_extraNonceSize; m_extraNonceSize = b.m_extraNonceSize;
m_extraNonce = b.m_extraNonce; m_extraNonce = b.m_extraNonce;
m_transactions = b.m_transactions; m_transactions = b.m_transactions;
m_sideChainData = b.m_sideChainData;
m_minerWallet = b.m_minerWallet; m_minerWallet = b.m_minerWallet;
m_txkeySecSeed = b.m_txkeySecSeed;
m_txkeySec = b.m_txkeySec; m_txkeySec = b.m_txkeySec;
m_parent = b.m_parent; m_parent = b.m_parent;
m_uncles = b.m_uncles; m_uncles = b.m_uncles;
m_sidechainHeight = b.m_sidechainHeight; m_sidechainHeight = b.m_sidechainHeight;
m_difficulty = b.m_difficulty; m_difficulty = b.m_difficulty;
m_cumulativeDifficulty = b.m_cumulativeDifficulty; m_cumulativeDifficulty = b.m_cumulativeDifficulty;
memcpy(m_sidechainExtraBuf, b.m_sidechainExtraBuf, sizeof(m_sidechainExtraBuf));
m_sidechainId = b.m_sidechainId; m_sidechainId = b.m_sidechainId;
m_tmpTxExtra = b.m_tmpTxExtra;
m_depth = b.m_depth; m_depth = b.m_depth;
m_verified = b.m_verified; m_verified = b.m_verified;
m_invalid = b.m_invalid; m_invalid = b.m_invalid;
@@ -119,123 +103,173 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_localTimestamp = seconds_since_epoch(); m_localTimestamp = seconds_since_epoch();
if (lock_result == 0) {
uv_mutex_unlock(&b.m_lock);
}
return *this; return *this;
} }
PoolBlock::~PoolBlock() 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 uint32_t* nonce, const uint32_t* extra_nonce) const
{ {
MutexLock lock(m_lock); std::vector<uint8_t> data;
data.reserve(128 + m_outputs.size() * 39 + m_transactions.size() * HASH_SIZE);
m_mainChainData.clear();
// Header // Header
m_mainChainData.push_back(m_majorVersion); data.push_back(m_majorVersion);
m_mainChainData.push_back(m_minorVersion); data.push_back(m_minorVersion);
writeVarint(m_timestamp, m_mainChainData); writeVarint(m_timestamp, data);
m_mainChainData.insert(m_mainChainData.end(), m_prevId.h, m_prevId.h + HASH_SIZE); data.insert(data.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);
m_mainChainHeaderSize = m_mainChainData.size(); if (!nonce) {
nonce = &m_nonce;
}
data.insert(data.end(), reinterpret_cast<const uint8_t*>(nonce), reinterpret_cast<const uint8_t*>(nonce) + NONCE_SIZE);
const size_t header_size0 = data.size();
if (header_size) {
*header_size = header_size0;
}
// Miner tx // Miner tx
m_mainChainData.push_back(TX_VERSION); data.push_back(TX_VERSION);
writeVarint(m_txinGenHeight + MINER_REWARD_UNLOCK_TIME, m_mainChainData); writeVarint(m_txinGenHeight + MINER_REWARD_UNLOCK_TIME, data);
m_mainChainData.push_back(1); data.push_back(1);
m_mainChainData.push_back(TXIN_GEN); data.push_back(TXIN_GEN);
writeVarint(m_txinGenHeight, m_mainChainData); 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) { const uint8_t tx_type = get_tx_type();
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);
if (output.m_txType == TXOUT_TO_TAGGED_KEY) { for (const TxOutput& output : m_outputs) {
m_mainChainData.push_back(output.m_viewTag); 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; if (outputs_blob_size) {
*outputs_blob_size = static_cast<int>(data.size()) - outputs_offset0;
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);
} }
m_tmpTxExtra.push_back(TX_EXTRA_MERGE_MINING_TAG); uint8_t tx_extra[128];
writeVarint(HASH_SIZE, m_tmpTxExtra); uint8_t* p = tx_extra;
m_tmpTxExtra.insert(m_tmpTxExtra.end(), sidechain_hash.h, sidechain_hash.h + HASH_SIZE);
writeVarint(m_tmpTxExtra.size(), m_mainChainData); *(p++) = TX_EXTRA_TAG_PUBKEY;
m_mainChainData.insert(m_mainChainData.end(), m_tmpTxExtra.begin(), m_tmpTxExtra.end()); 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; if (!extra_nonce) {
extra_nonce = &m_extraNonce;
}
memcpy(p, extra_nonce, EXTRA_NONCE_SIZE);
p += EXTRA_NONCE_SIZE;
if (extra_nonce_size > EXTRA_NONCE_SIZE) {
memset(p, 0, extra_nonce_size - EXTRA_NONCE_SIZE);
p += extra_nonce_size - EXTRA_NONCE_SIZE;
}
writeVarint(m_transactions.size() - 1, m_mainChainData); *(p++) = TX_EXTRA_MERGE_MINING_TAG;
const uint8_t* data = reinterpret_cast<const uint8_t*>(m_transactions.data()); *(p++) = HASH_SIZE;
m_mainChainData.insert(m_mainChainData.end(), data + HASH_SIZE, data + m_transactions.size() * 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 (!nonce && !extra_nonce && !m_mainChainDataDebug.empty() && (data != m_mainChainDataDebug)) {
LOGERR(1, "serialize_mainchain_data() has a bug, fix it!");
PANIC_STOP();
}
#endif
return data;
} }
void PoolBlock::serialize_sidechain_data() std::vector<uint8_t> PoolBlock::serialize_sidechain_data() const
{ {
MutexLock lock(m_lock); std::vector<uint8_t> data;
m_sideChainData.clear(); data.reserve((m_uncles.size() + 4) * HASH_SIZE + 36);
m_sideChainData.reserve((m_uncles.size() + 4) * HASH_SIZE + 11);
const hash& spend = m_minerWallet.spend_public_key(); const hash& spend = m_minerWallet.spend_public_key();
const hash& view = m_minerWallet.view_public_key(); const hash& view = m_minerWallet.view_public_key();
m_sideChainData.insert(m_sideChainData.end(), spend.h, spend.h + HASH_SIZE); const int sidechain_version = get_sidechain_version();
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);
writeVarint(m_uncles.size(), m_sideChainData); data.insert(data.end(), spend.h, spend.h + HASH_SIZE);
data.insert(data.end(), view.h, view.h + HASH_SIZE);
for (const hash& id : m_uncles) { if (sidechain_version > 1) {
m_sideChainData.insert(m_sideChainData.end(), id.h, id.h + HASH_SIZE); data.insert(data.end(), m_txkeySecSeed.h, m_txkeySecSeed.h + HASH_SIZE);
}
else {
data.insert(data.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE);
} }
writeVarint(m_sidechainHeight, m_sideChainData); data.insert(data.end(), m_parent.h, m_parent.h + HASH_SIZE);
writeVarint(m_difficulty.lo, m_sideChainData); writeVarint(m_uncles.size(), data);
writeVarint(m_difficulty.hi, m_sideChainData);
writeVarint(m_cumulativeDifficulty.lo, m_sideChainData); for (const hash& id : m_uncles) {
writeVarint(m_cumulativeDifficulty.hi, m_sideChainData); data.insert(data.end(), id.h, id.h + HASH_SIZE);
}
writeVarint(m_sidechainHeight, data);
writeVarint(m_difficulty.lo, data);
writeVarint(m_difficulty.hi, data);
writeVarint(m_cumulativeDifficulty.lo, data);
writeVarint(m_cumulativeDifficulty.hi, data);
if (sidechain_version > 1) {
const uint8_t* p = reinterpret_cast<const uint8_t*>(m_sidechainExtraBuf);
data.insert(data.end(), p, p + sizeof(m_sidechainExtraBuf));
}
#if POOL_BLOCK_DEBUG
if (!m_sideChainDataDebug.empty() && (data != m_sideChainDataDebug)) {
LOGERR(1, "serialize_sidechain_data() has a bug, fix it!");
PANIC_STOP();
}
#endif
return data;
} }
void PoolBlock::reset_offchain_data() void PoolBlock::reset_offchain_data()
{ {
// Defaults for off-chain variables // Defaults for off-chain variables
m_tmpTxExtra.clear();
m_depth = 0; m_depth = 0;
m_verified = false; m_verified = false;
@@ -267,29 +301,34 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
size_t blob_size = 0; size_t blob_size = 0;
{ {
MutexLock lock(m_lock); size_t header_size, miner_tx_size;
const std::vector<uint8_t> mainchain_data = serialize_mainchain_data(&header_size, &miner_tx_size, nullptr, nullptr, nullptr, nullptr);
if (!m_mainChainHeaderSize || !m_mainChainMinerTxSize || (m_mainChainData.size() < m_mainChainHeaderSize + m_mainChainMinerTxSize)) { if (!header_size || !miner_tx_size || (mainchain_data.size() < header_size + miner_tx_size)) {
LOGERR(1, "tried to calculate PoW of uninitialized block"); LOGERR(1, "tried to calculate PoW of uninitialized block");
return false; return false;
} }
blob_size = m_mainChainHeaderSize; blob_size = header_size;
memcpy(blob, m_mainChainData.data(), blob_size); memcpy(blob, mainchain_data.data(), blob_size);
uint8_t* miner_tx = m_mainChainData.data() + m_mainChainHeaderSize; const uint8_t* miner_tx = mainchain_data.data() + header_size;
keccak(miner_tx, static_cast<int>(m_mainChainMinerTxSize) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE); hash tmp;
keccak(miner_tx, static_cast<int>(miner_tx_size) - 1, tmp.h);
memcpy(hashes, tmp.h, HASH_SIZE);
count = m_transactions.size(); count = m_transactions.size();
uint8_t* h = reinterpret_cast<uint8_t*>(m_transactions.data()); uint8_t* h = reinterpret_cast<uint8_t*>(m_transactions.data());
keccak(reinterpret_cast<uint8_t*>(hashes), HASH_SIZE * 3, h, HASH_SIZE); keccak(reinterpret_cast<uint8_t*>(hashes), HASH_SIZE * 3, tmp.h);
memcpy(h, tmp.h, HASH_SIZE);
if (count == 1) { if (count == 1) {
memcpy(blob + blob_size, h, HASH_SIZE); memcpy(blob + blob_size, h, HASH_SIZE);
} }
else if (count == 2) { else if (count == 2) {
keccak(h, HASH_SIZE * 2, blob + blob_size, HASH_SIZE); keccak(h, HASH_SIZE * 2, tmp.h);
memcpy(blob + blob_size, tmp.h, HASH_SIZE);
} }
else { else {
size_t i, j, cnt; size_t i, j, cnt;
@@ -302,17 +341,20 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
memcpy(tmp_ints.data(), h, (cnt * 2 - count) * HASH_SIZE); memcpy(tmp_ints.data(), h, (cnt * 2 - count) * HASH_SIZE);
for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) { for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, tmp_ints.data() + j * HASH_SIZE, HASH_SIZE); keccak(h + i * HASH_SIZE, HASH_SIZE * 2, tmp.h);
memcpy(tmp_ints.data() + j * HASH_SIZE, tmp.h, HASH_SIZE);
} }
while (cnt > 2) { while (cnt > 2) {
cnt >>= 1; cnt >>= 1;
for (i = 0, j = 0; j < cnt; i += 2, ++j) { for (i = 0, j = 0; j < cnt; i += 2, ++j) {
keccak(tmp_ints.data() + i * HASH_SIZE, HASH_SIZE * 2, tmp_ints.data() + j * HASH_SIZE, HASH_SIZE); keccak(tmp_ints.data() + i * HASH_SIZE, HASH_SIZE * 2, tmp.h);
memcpy(tmp_ints.data() + j * HASH_SIZE, tmp.h, HASH_SIZE);
} }
} }
keccak(tmp_ints.data(), HASH_SIZE * 2, blob + blob_size, HASH_SIZE); keccak(tmp_ints.data(), HASH_SIZE * 2, tmp.h);
memcpy(blob + blob_size, tmp.h, HASH_SIZE);
} }
} }
blob_size += HASH_SIZE; blob_size += HASH_SIZE;
@@ -324,12 +366,16 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
uint64_t PoolBlock::get_payout(const Wallet& w) 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) { for (size_t i = 0, n = m_outputs.size(); i < n; ++i) {
const TxOutput& out = m_outputs[i]; const TxOutput& out = m_outputs[i];
hash eph_public_key; hash eph_public_key;
if (out.m_txType == TXOUT_TO_TAGGED_KEY) { if (tx_type == 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)) { 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; return out.m_reward;
} }
} }
@@ -344,4 +390,46 @@ uint64_t PoolBlock::get_payout(const Wallet& w) const
return 0; return 0;
} }
static constexpr uint64_t VERSION2_MAINNET_TIMESTAMP = 100U; //
static constexpr uint64_t VERSION2_TESTNET_TIMESTAMP = 100U; //
uint32_t PoolBlock::signal_v2_readiness(uint32_t extra_nonce)
{
const uint64_t ts = (SideChain::network_type() == NetworkType::Mainnet) ? VERSION2_MAINNET_TIMESTAMP : VERSION2_TESTNET_TIMESTAMP;
if (time(nullptr) < static_cast<int64_t>(ts)) {
return (extra_nonce & 0x007FFFFFUL) | 0xFF000000UL;
}
return extra_nonce;
}
int PoolBlock::get_sidechain_version() const
{
const uint64_t ts = (SideChain::network_type() == NetworkType::Mainnet) ? VERSION2_MAINNET_TIMESTAMP : VERSION2_TESTNET_TIMESTAMP;
return (m_timestamp >= ts) ? 2 : 1;
}
hash PoolBlock::calculate_tx_key_seed() const
{
const char domain[] = "tx_key_seed";
const uint32_t zero = 0;
const std::vector<uint8_t> mainchain_data = serialize_mainchain_data(nullptr, nullptr, nullptr, nullptr, &zero, &zero);
const std::vector<uint8_t> sidechain_data = serialize_sidechain_data();
hash result;
keccak_custom([&domain, &mainchain_data, &sidechain_data](int offset) -> uint8_t {
size_t k = offset;
if (k < sizeof(domain)) return domain[k];
k -= sizeof(domain);
if (k < mainchain_data.size()) return mainchain_data[k];
k -= mainchain_data.size();
return sidechain_data[k];
}, static_cast<int>(sizeof(domain) + mainchain_data.size() + sidechain_data.size()), result.h, HASH_SIZE);
return result;
}
} // namespace p2pool } // namespace p2pool
+40 -21
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -28,6 +28,8 @@
namespace p2pool { namespace p2pool {
static FORCEINLINE constexpr int pool_block_debug() { return POOL_BLOCK_DEBUG; }
class RandomX_Hasher_Base; class RandomX_Hasher_Base;
class SideChain; class SideChain;
@@ -61,15 +63,12 @@ struct PoolBlock
PoolBlock(const PoolBlock& b); PoolBlock(const PoolBlock& b);
PoolBlock& operator=(const PoolBlock& b); PoolBlock& operator=(const PoolBlock& b);
mutable uv_mutex_t m_lock; #if POOL_BLOCK_DEBUG
std::vector<uint8_t> m_mainChainDataDebug;
std::vector<uint8_t> m_sideChainDataDebug;
#endif
// Monero block template // Monero block template
std::vector<uint8_t> m_mainChainData;
size_t m_mainChainHeaderSize;
size_t m_mainChainMinerTxSize;
int m_mainChainOutputsOffset;
int m_mainChainOutputsBlobSize;
uint8_t m_majorVersion; uint8_t m_majorVersion;
uint8_t m_minorVersion; uint8_t m_minorVersion;
uint64_t m_timestamp; uint64_t m_timestamp;
@@ -81,15 +80,16 @@ struct PoolBlock
struct TxOutput struct TxOutput
{ {
FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey(), m_txType(0), m_viewTag(0) {} FORCEINLINE TxOutput() : m_ephPublicKey(), m_reward(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(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; hash m_ephPublicKey;
uint8_t m_txType; uint64_t m_reward : 56;
uint8_t m_viewTag; 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; std::vector<TxOutput> m_outputs;
hash m_txkeyPub; hash m_txkeyPub;
@@ -99,14 +99,12 @@ struct PoolBlock
// All block transaction hashes including the miner transaction hash at index 0 // All block transaction hashes including the miner transaction hash at index 0
std::vector<hash> m_transactions; std::vector<hash> m_transactions;
// Side-chain data
std::vector<uint8_t> m_sideChainData;
// Miner's wallet // Miner's wallet
Wallet m_minerWallet{ nullptr }; Wallet m_minerWallet{ nullptr };
// Transaction secret key // Transaction secret key
// Required to check that pub keys in the miner transaction pay out to correct miner wallet addresses // Required to check that pub keys in the miner transaction pay out to correct miner wallet addresses
hash m_txkeySecSeed;
hash m_txkeySec; hash m_txkeySec;
// Side-chain parent and uncle blocks // Side-chain parent and uncle blocks
@@ -118,12 +116,13 @@ struct PoolBlock
difficulty_type m_difficulty; difficulty_type m_difficulty;
difficulty_type m_cumulativeDifficulty; difficulty_type m_cumulativeDifficulty;
// Arbitrary extra data
uint32_t m_sidechainExtraBuf[4];
// HASH (see diagram in the comment above) // HASH (see diagram in the comment above)
hash m_sidechainId; hash m_sidechainId;
// Just temporary stuff, not a part of the block // Just temporary stuff, not a part of the block
std::vector<uint8_t> m_tmpTxExtra;
uint64_t m_depth; uint64_t m_depth;
bool m_verified; bool m_verified;
@@ -136,10 +135,10 @@ struct PoolBlock
uint64_t m_localTimestamp; uint64_t m_localTimestamp;
void serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash); std::vector<uint8_t> serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr, const uint32_t* nonce = nullptr, const uint32_t* extra_nonce = nullptr) const;
void serialize_sidechain_data(); std::vector<uint8_t> serialize_sidechain_data() const;
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop); int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact);
void reset_offchain_data(); void reset_offchain_data();
bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash); bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash);
@@ -149,6 +148,26 @@ struct PoolBlock
// Both tx types are allowed by Monero consensus during v15 because it needs to process pre-fork mempool transactions, // 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 // 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; } FORCEINLINE uint8_t get_tx_type() const { return (m_majorVersion < HARDFORK_VIEW_TAGS_VERSION) ? TXOUT_TO_KEY : TXOUT_TO_TAGGED_KEY; }
// Signal hardfork readiness (only before the v2 hardfork)
// TODO: remove this code after hardfork
static uint32_t signal_v2_readiness(uint32_t extra_nonce);
int get_sidechain_version() const;
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;
}
hash calculate_tx_key_seed() const;
}; };
} // namespace p2pool } // namespace p2pool
+139 -60
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@ namespace p2pool {
// Since data here can come from external and possibly malicious sources, check everything // 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 // 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) // 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, const SideChain& sidechain, uv_loop_t* loop) int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact)
{ {
try { try {
// Sanity check // Sanity check
@@ -61,8 +61,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
#define READ_BUF(buf, size) do { if (!read_buf((buf), (size))) return __LINE__; } while(0) #define READ_BUF(buf, size) do { if (!read_buf((buf), (size))) return __LINE__; } while(0)
MutexLock lock(m_lock);
READ_BYTE(m_majorVersion); READ_BYTE(m_majorVersion);
if (m_majorVersion > HARDFORK_SUPPORTED_VERSION) return __LINE__; if (m_majorVersion > HARDFORK_SUPPORTED_VERSION) return __LINE__;
@@ -75,8 +73,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
const int nonce_offset = static_cast<int>(data - data_begin); const int nonce_offset = static_cast<int>(data - data_begin);
READ_BUF(&m_nonce, NONCE_SIZE); READ_BUF(&m_nonce, NONCE_SIZE);
m_mainChainHeaderSize = data - data_begin;
EXPECT_BYTE(TX_VERSION); EXPECT_BYTE(TX_VERSION);
uint64_t unlock_height; uint64_t unlock_height;
@@ -86,15 +82,17 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
EXPECT_BYTE(TXIN_GEN); EXPECT_BYTE(TXIN_GEN);
READ_VARINT(m_txinGenHeight); 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__; if (unlock_height != m_txinGenHeight + MINER_REWARD_UNLOCK_TIME) return __LINE__;
std::vector<uint8_t> outputs_blob; 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; uint64_t num_outputs;
READ_VARINT(num_outputs); READ_VARINT(num_outputs);
uint64_t total_reward = 0; uint64_t total_reward = 0;
int outputs_blob_size;
if (num_outputs > 0) { if (num_outputs > 0) {
// Outputs are in the buffer, just read them // Outputs are in the buffer, just read them
@@ -105,31 +103,32 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
if (num_outputs > std::numeric_limits<uint64_t>::max() / MIN_OUTPUT_SIZE) return __LINE__; 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__; if (static_cast<uint64_t>(data_end - data) < num_outputs * MIN_OUTPUT_SIZE) return __LINE__;
m_outputs.clear(); m_outputs.resize(num_outputs);
m_outputs.reserve(num_outputs); m_outputs.shrink_to_fit();
const uint8_t expected_tx_type = get_tx_type(); const uint8_t expected_tx_type = get_tx_type();
for (uint64_t i = 0; i < num_outputs; ++i) { for (uint64_t i = 0; i < num_outputs; ++i) {
TxOutput t; TxOutput& t = m_outputs[i];
READ_VARINT(t.m_reward); uint64_t reward;
total_reward += t.m_reward; READ_VARINT(reward);
t.m_reward = reward;
total_reward += reward;
EXPECT_BYTE(expected_tx_type); EXPECT_BYTE(expected_tx_type);
t.m_txType = expected_tx_type;
READ_BUF(t.m_ephPublicKey.h, HASH_SIZE); READ_BUF(t.m_ephPublicKey.h, HASH_SIZE);
if (expected_tx_type == TXOUT_TO_TAGGED_KEY) { 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_size = static_cast<int>(data - data_begin) - outputs_offset;
outputs_blob.assign(data_begin + m_mainChainOutputsOffset, data); outputs_blob.assign(data_begin + outputs_offset, data);
} }
else { else {
// Outputs are not in the buffer and must be calculated from sidechain data // Outputs are not in the buffer and must be calculated from sidechain data
@@ -144,7 +143,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__; 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 // Technically some p2pool node could keep stuffing block with transactions until reward is less than 0.6 XMR
@@ -153,13 +152,12 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__; return __LINE__;
} }
const int outputs_actual_blob_size = static_cast<int>(data - data_begin) - m_mainChainOutputsOffset; const int outputs_actual_blob_size = static_cast<int>(data - data_begin) - outputs_offset;
if (outputs_blob_size < outputs_actual_blob_size) {
if (m_mainChainOutputsBlobSize < outputs_actual_blob_size) {
return __LINE__; 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; uint64_t tx_extra_size;
READ_VARINT(tx_extra_size); READ_VARINT(tx_extra_size);
@@ -191,29 +189,66 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
EXPECT_BYTE(0); EXPECT_BYTE(0);
m_mainChainMinerTxSize = (data - data_begin) + outputs_blob_size_diff - m_mainChainHeaderSize;
uint64_t num_transactions; uint64_t num_transactions;
READ_VARINT(num_transactions); READ_VARINT(num_transactions);
if (num_transactions > std::numeric_limits<uint64_t>::max() / HASH_SIZE) return __LINE__; const int transactions_offset = static_cast<int>(data - data_begin);
if (static_cast<uint64_t>(data_end - data) < num_transactions * HASH_SIZE) return __LINE__;
m_transactions.resize(1); std::vector<uint64_t> parent_indices;
m_transactions.reserve(num_transactions + 1); if (compact) {
if (static_cast<uint64_t>(data_end - data) < num_transactions) return __LINE__;
for (uint64_t i = 0; i < num_transactions; ++i) { m_transactions.resize(1);
hash id; parent_indices.resize(1);
READ_BUF(id.h, HASH_SIZE);
m_transactions.emplace_back(std::move(id)); // 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); const int transactions_actual_blob_size = static_cast<int>(data - data_begin) - transactions_offset;
m_mainChainData.assign(data_begin, data_begin + m_mainChainOutputsOffset); const int transactions_blob_size = static_cast<int>(num_transactions) * HASH_SIZE;
m_mainChainData.insert(m_mainChainData.end(), m_mainChainOutputsBlobSize, 0); const int transactions_blob_size_diff = transactions_blob_size - transactions_actual_blob_size;
m_mainChainData.insert(m_mainChainData.end(), data_begin + m_mainChainOutputsOffset + outputs_actual_blob_size, data);
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; const uint8_t* sidechain_data_begin = data;
#endif
hash spend_pub_key; hash spend_pub_key;
hash view_pub_key; hash view_pub_key;
@@ -223,22 +258,53 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__; return __LINE__;
} }
READ_BUF(m_txkeySec.h, HASH_SIZE); READ_BUF(m_txkeySecSeed.h, HASH_SIZE);
const int sidechain_version = get_sidechain_version();
if (sidechain_version > 1) {
hash pub;
get_tx_keys(pub, m_txkeySec, m_txkeySecSeed, m_prevId);
if (pub != m_txkeyPub) {
return __LINE__;
}
}
else {
// Both values are the same before v2
m_txkeySec = m_txkeySecSeed;
// Enforce deterministic tx keys starting from v15
if (m_majorVersion >= HARDFORK_VIEW_TAGS_VERSION) {
hash pub, sec;
get_tx_keys(pub, sec, spend_pub_key, m_prevId);
if ((pub != m_txkeyPub) || (sec != m_txkeySec)) {
return __LINE__;
}
}
}
if (!check_keys(m_txkeyPub, m_txkeySec)) { if (!check_keys(m_txkeyPub, m_txkeySec)) {
return __LINE__; return __LINE__;
} }
// Enforce deterministic tx keys starting from v15 READ_BUF(m_parent.h, HASH_SIZE);
if (m_majorVersion >= HARDFORK_VIEW_TAGS_VERSION) {
hash pub, sec; if (compact) {
get_tx_keys(pub, sec, spend_pub_key, m_prevId); const PoolBlock* parent = sidechain.find_block(m_parent);
if ((pub != m_txkeyPub) || (sec != m_txkeySec)) { if (!parent) {
return __LINE__; return __LINE__;
} }
}
READ_BUF(m_parent.h, HASH_SIZE); 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; uint64_t num_uncles;
READ_VARINT(num_uncles); READ_VARINT(num_uncles);
@@ -252,7 +318,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
for (uint64_t i = 0; i < num_uncles; ++i) { for (uint64_t i = 0; i < num_uncles; ++i) {
hash id; hash id;
READ_BUF(id.h, HASH_SIZE); READ_BUF(id.h, HASH_SIZE);
m_uncles.emplace_back(std::move(id)); m_uncles.emplace_back(id);
} }
READ_VARINT(m_sidechainHeight); READ_VARINT(m_sidechainHeight);
@@ -263,6 +329,10 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
READ_VARINT(m_cumulativeDifficulty.lo); READ_VARINT(m_cumulativeDifficulty.lo);
READ_VARINT(m_cumulativeDifficulty.hi); READ_VARINT(m_cumulativeDifficulty.hi);
if (sidechain_version > 1) {
READ_BUF(m_sidechainExtraBuf, sizeof(m_sidechainExtraBuf));
}
#undef READ_BYTE #undef READ_BYTE
#undef EXPECT_BYTE #undef EXPECT_BYTE
#undef READ_VARINT #undef READ_VARINT
@@ -276,16 +346,22 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__; return __LINE__;
} }
if (static_cast<int>(outputs_blob.size()) != m_mainChainOutputsBlobSize) { if (static_cast<int>(outputs_blob.size()) != outputs_blob_size) {
return __LINE__; 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; hash check;
const std::vector<uint8_t>& consensus_id = sidechain.consensus_id(); 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( 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); uint32_t k = static_cast<uint32_t>(offset - nonce_offset);
if (k < NONCE_SIZE) { if (k < NONCE_SIZE) {
@@ -302,30 +378,33 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return 0; return 0;
} }
const int data_size = static_cast<int>((data_end - data_begin) + outputs_blob_size_diff);
if (offset < data_size) { if (offset < data_size) {
if (offset < m_mainChainOutputsOffset) { if (offset < outputs_offset) {
return data_begin[offset]; return data_begin[offset];
} }
else if (offset < m_mainChainOutputsOffset + m_mainChainOutputsBlobSize) { else if (offset < outputs_offset + outputs_blob_size) {
const int tmp = offset - m_mainChainOutputsOffset; return outputs_blob[offset - outputs_offset];
return outputs_blob[tmp];
} }
else { else if (offset < transactions_offset + outputs_blob_size_diff) {
return data_begin[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 POOL_BLOCK_DEBUG
m_sideChainDataDebug.assign(sidechain_data_begin, data_end);
#endif
if (check != m_sidechainId) { if (check != m_sidechainId) {
return __LINE__; return __LINE__;
} }
m_sideChainData.assign(sidechain_data_begin, data_end);
} }
catch (std::exception& e) { catch (std::exception& e) {
const char* msg = e.what(); const char* msg = e.what();
+23 -13
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -68,7 +68,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
m_cache[i] = randomx_alloc_cache(flags); m_cache[i] = randomx_alloc_cache(flags);
if (!m_cache[i]) { if (!m_cache[i]) {
LOGERR(1, "couldn't allocate RandomX cache, aborting"); LOGERR(1, "couldn't allocate RandomX cache, aborting");
panic(); PANIC_STOP();
} }
} }
memory_allocated += RANDOMX_ARGON_MEMORY * 1024; memory_allocated += RANDOMX_ARGON_MEMORY * 1024;
@@ -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, const int err = uv_queue_work(uv_default_loop_checked(), &work->req,
[](uv_work_t* 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); Work* work = reinterpret_cast<Work*>(req->data);
if (!work->pool->stopped()) { if (!work->pool->stopped()) {
work->hasher->set_seed(work->seed); work->hasher->set_seed(work->seed);
@@ -150,7 +150,7 @@ void RandomX_Hasher::set_seed_async(const hash& seed)
[](uv_work_t* req, int) [](uv_work_t* req, int)
{ {
delete reinterpret_cast<Work*>(req->data); delete reinterpret_cast<Work*>(req->data);
bkg_jobs_tracker.stop("RandomX_Hasher::set_seed_async"); BACKGROUND_JOB_STOP(RandomX_Hasher::set_seed_async);
} }
); );
@@ -206,7 +206,7 @@ void RandomX_Hasher::set_seed(const hash& seed)
m_vm[m_index].vm = randomx_create_vm(flags, m_cache[m_index], nullptr); m_vm[m_index].vm = randomx_create_vm(flags, m_cache[m_index], nullptr);
if (!m_vm[m_index].vm) { if (!m_vm[m_index].vm) {
LOGERR(1, "couldn't allocate RandomX light VM, aborting"); LOGERR(1, "couldn't allocate RandomX light VM, aborting");
panic(); PANIC_STOP();
} }
} }
} }
@@ -308,7 +308,7 @@ void RandomX_Hasher::set_old_seed(const hash& seed)
m_vm[old_index].vm = randomx_create_vm(flags, m_cache[old_index], nullptr); m_vm[old_index].vm = randomx_create_vm(flags, m_cache[old_index], nullptr);
if (!m_vm[old_index].vm) { if (!m_vm[old_index].vm) {
LOGERR(1, "couldn't allocate RandomX light VM, aborting"); LOGERR(1, "couldn't allocate RandomX light VM, aborting");
panic(); PANIC_STOP();
} }
} }
} }
@@ -376,14 +376,14 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
int err = uv_loop_init(&m_loop); int err = uv_loop_init(&m_loop);
if (err) { if (err) {
LOGERR(1, "failed to create event loop, error " << uv_err_name(err)); LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
panic(); PANIC_STOP();
} }
// Init loop user data before running it // Init loop user data before running it
GetLoopUserData(&m_loop); GetLoopUserData(&m_loop);
uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown); uv_async_init_checked(&m_loop, &m_shutdownAsync, on_shutdown);
uv_async_init(&m_loop, &m_kickTheLoopAsync, nullptr); uv_async_init_checked(&m_loop, &m_kickTheLoopAsync, nullptr);
m_shutdownAsync.data = this; m_shutdownAsync.data = this;
uv_mutex_init_checked(&m_requestMutex); uv_mutex_init_checked(&m_requestMutex);
@@ -394,7 +394,7 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
err = uv_thread_create(&m_loopThread, loop, this); err = uv_thread_create(&m_loopThread, loop, this);
if (err) { if (err) {
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err)); LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
panic(); PANIC_STOP();
} }
} }
@@ -413,9 +413,19 @@ RandomX_Hasher_RPC::~RandomX_Hasher_RPC()
void RandomX_Hasher_RPC::loop(void* data) void RandomX_Hasher_RPC::loop(void* data)
{ {
LOGINFO(1, "event loop started"); LOGINFO(1, "event loop started");
RandomX_Hasher_RPC* hasher = static_cast<RandomX_Hasher_RPC*>(data); 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"); LOGINFO(1, "event loop stopped");
} }
@@ -438,7 +448,7 @@ bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t h
const Params& params = m_pool->params(); const Params& params = m_pool->params();
JSONRPCRequest::call(params.m_host, params.m_rpcPort, buf, params.m_rpcLogin, JSONRPCRequest::call(params.m_host, params.m_rpcPort, buf, params.m_rpcLogin, params.m_socks5Proxy,
[&result, &h](const char* data, size_t size) [&result, &h](const char* data, size_t size)
{ {
rapidjson::Document doc; rapidjson::Document doc;
+2 -2
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -100,7 +100,7 @@ public:
explicit RandomX_Hasher_RPC(p2pool* pool); explicit RandomX_Hasher_RPC(p2pool* pool);
~RandomX_Hasher_RPC(); ~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: private:
static void loop(void* data); static void loop(void* data);
+380 -306
View File
File diff suppressed because it is too large Load Diff
+36 -16
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
#pragma once #pragma once
#include "uv_util.h" #include "uv_util.h"
#include "pool_block.h"
#include <map> #include <map>
#include <thread> #include <thread>
@@ -25,16 +26,15 @@ namespace p2pool {
class p2pool; class p2pool;
class P2PServer; class P2PServer;
struct DifficultyData;
struct PoolBlock;
class Wallet;
struct MinerShare struct MinerShare
{ {
FORCEINLINE MinerShare() : m_weight(0), m_wallet(nullptr) {} FORCEINLINE MinerShare() : m_weight(), m_wallet(nullptr) {}
FORCEINLINE MinerShare(uint64_t w, const Wallet* x) : m_weight(w), m_wallet(x) {} FORCEINLINE MinerShare(const difficulty_type& w, const Wallet* x) : m_weight(w), m_wallet(x) {}
uint64_t m_weight; FORCEINLINE bool operator==(const MinerShare& s) const { return *m_wallet == *s.m_wallet; }
difficulty_type m_weight;
const Wallet* m_wallet; const Wallet* m_wallet;
}; };
@@ -44,12 +44,12 @@ public:
SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr); SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr);
~SideChain(); ~SideChain();
void fill_sidechain_data(PoolBlock& block, const Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares) const; void fill_sidechain_data(PoolBlock& block, std::vector<MinerShare>& shares) const;
bool block_seen(const PoolBlock& block); bool block_seen(const PoolBlock& block);
void unsee_block(const PoolBlock& block); void unsee_block(const PoolBlock& block);
bool add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks); bool add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks);
void add_block(const PoolBlock& block); bool add_block(const PoolBlock& block);
void get_missing_blocks(std::vector<hash>& missing_blocks) const; void get_missing_blocks(std::vector<hash>& missing_blocks) const;
PoolBlock* find_block(const hash& id) const; PoolBlock* find_block(const hash& id) const;
@@ -66,7 +66,8 @@ public:
// Consensus ID can therefore be used as a password to create private P2Pools // Consensus ID can therefore be used as a password to create private P2Pools
const std::vector<uint8_t>& consensus_id() const { return m_consensusId; } const std::vector<uint8_t>& consensus_id() const { return m_consensusId; }
uint64_t chain_window_size() const { return m_chainWindowSize; } uint64_t chain_window_size() const { return m_chainWindowSize; }
NetworkType network_type() const { return m_networkType; } static NetworkType network_type() { return s_networkType; }
static uint64_t network_major_version(uint64_t height);
FORCEINLINE difficulty_type difficulty() const { ReadLock lock(m_curDifficultyLock); return m_curDifficulty; } FORCEINLINE difficulty_type difficulty() const { ReadLock lock(m_curDifficultyLock); return m_curDifficulty; }
difficulty_type total_hashes() const; difficulty_type total_hashes() const;
uint64_t block_time() const { return m_targetBlockTime; } uint64_t block_time() const { return m_targetBlockTime; }
@@ -76,18 +77,22 @@ public:
bool is_mini() const; bool is_mini() const;
const PoolBlock* chainTip() const { return m_chainTip; } const PoolBlock* chainTip() const { return m_chainTip; }
bool precalcFinished() const { return m_precalcFinished.load(); }
#ifdef P2POOL_UNIT_TESTS
difficulty_type m_testMainChainDiff;
#endif
static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards); static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards);
private: private:
p2pool* m_pool; p2pool* m_pool;
P2PServer* p2pServer() const; P2PServer* p2pServer() const;
NetworkType m_networkType; static NetworkType s_networkType;
private: private:
bool get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares) const; bool get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares, uint64_t* bottom_height = nullptr, bool quiet = false) const;
bool get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const; bool get_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_loop(PoolBlock* block);
void verify(PoolBlock* block); void verify(PoolBlock* block);
void update_chain_tip(const PoolBlock* block); void update_chain_tip(const PoolBlock* block);
@@ -99,7 +104,7 @@ private:
void prune_old_blocks(); void prune_old_blocks();
bool load_config(const std::string& filename); bool load_config(const std::string& filename);
bool check_config(); bool check_config() const;
mutable uv_rwlock_t m_sidechainLock; mutable uv_rwlock_t m_sidechainLock;
std::atomic<PoolBlock*> m_chainTip; std::atomic<PoolBlock*> m_chainTip;
@@ -111,7 +116,7 @@ private:
uint64_t m_seenWalletsLastPruneTime; uint64_t m_seenWalletsLastPruneTime;
uv_mutex_t m_seenBlocksLock; uv_mutex_t m_seenBlocksLock;
unordered_set<hash> m_seenBlocks; unordered_set<PoolBlock::full_id> m_seenBlocks;
std::vector<DifficultyData> m_difficultyData; std::vector<DifficultyData> m_difficultyData;
@@ -134,7 +139,7 @@ private:
struct PrecalcJob struct PrecalcJob
{ {
const PoolBlock* b; const PoolBlock* b;
std::vector<const Wallet*> wallets; std::vector<MinerShare> shares;
}; };
uv_cond_t m_precalcJobsCond; uv_cond_t m_precalcJobsCond;
@@ -146,9 +151,24 @@ private:
std::atomic<bool> m_precalcFinished; std::atomic<bool> m_precalcFinished;
hash m_consensusHash;
void launch_precalc(const PoolBlock* block); void launch_precalc(const PoolBlock* block);
void precalc_worker(); void precalc_worker();
void finish_precalc(); void finish_precalc();
}; };
} // namespace p2pool } // namespace p2pool
namespace robin_hood {
template<>
struct hash<p2pool::MinerShare>
{
FORCEINLINE size_t operator()(const p2pool::MinerShare& value) const noexcept
{
return hash_bytes(value.m_wallet->spend_public_key().h, p2pool::HASH_SIZE);
}
};
} // namespace robin_hood
+110 -50
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -45,7 +45,6 @@ StratumServer::StratumServer(p2pool* pool)
: TCPServer(StratumClient::allocate) : TCPServer(StratumClient::allocate)
, m_pool(pool) , m_pool(pool)
, m_autoDiff(pool->params().m_autoDiff) , m_autoDiff(pool->params().m_autoDiff)
, m_extraNonce(0)
, m_rng(RandomDeviceSeed::instance) , m_rng(RandomDeviceSeed::instance)
, m_cumulativeHashes(0) , m_cumulativeHashes(0)
, m_cumulativeHashesAtLastShare(0) , m_cumulativeHashesAtLastShare(0)
@@ -55,6 +54,7 @@ StratumServer::StratumServer(p2pool* pool)
, m_hashrateDataTail_24h(0) , m_hashrateDataTail_24h(0)
, m_cumulativeFoundSharesDiff(0.0) , m_cumulativeFoundSharesDiff(0.0)
, m_totalFoundShares(0) , m_totalFoundShares(0)
, m_totalFailedShares(0)
, m_apiLastUpdateTime(0) , m_apiLastUpdateTime(0)
{ {
// Diffuse the initial state in case it has low quality // Diffuse the initial state in case it has low quality
@@ -66,6 +66,8 @@ StratumServer::StratumServer(p2pool* pool)
uv_mutex_init_checked(&m_rngLock); uv_mutex_init_checked(&m_rngLock);
uv_rwlock_init_checked(&m_hashrateDataLock); uv_rwlock_init_checked(&m_hashrateDataLock);
m_extraNonce = PoolBlock::signal_v2_readiness(get_random32());
m_submittedSharesPool.resize(10); m_submittedSharesPool.resize(10);
for (size_t i = 0; i < m_submittedSharesPool.size(); ++i) { for (size_t i = 0; i < m_submittedSharesPool.size(); ++i) {
m_submittedSharesPool[i] = new SubmittedShare{}; m_submittedSharesPool[i] = new SubmittedShare{};
@@ -74,7 +76,7 @@ StratumServer::StratumServer(p2pool* pool)
const int err = uv_async_init(&m_loop, &m_blobsAsync, on_blobs_ready); const int err = uv_async_init(&m_loop, &m_blobsAsync, on_blobs_ready);
if (err) { if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err)); LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
panic(); PANIC_STOP();
} }
m_blobsAsync.data = this; m_blobsAsync.data = this;
m_blobsQueue.reserve(2); m_blobsQueue.reserve(2);
@@ -84,8 +86,6 @@ StratumServer::StratumServer(p2pool* pool)
StratumServer::~StratumServer() StratumServer::~StratumServer()
{ {
uv_close(reinterpret_cast<uv_handle_t*>(&m_blobsAsync), nullptr);
shutdown_tcp(); shutdown_tcp();
uv_mutex_destroy(&m_blobsQueueLock); uv_mutex_destroy(&m_blobsQueueLock);
@@ -106,9 +106,12 @@ void StratumServer::on_block(const BlockTemplate& block)
LOGINFO(4, "no clients connected"); LOGINFO(4, "no clients connected");
return; return;
} }
m_extraNonce.exchange(num_connections);
const uint32_t extra_nonce_start = PoolBlock::signal_v2_readiness(get_random32());
m_extraNonce.exchange(extra_nonce_start + num_connections);
BlobsData* blobs_data = new BlobsData{}; BlobsData* blobs_data = new BlobsData{};
blobs_data->m_extraNonceStart = extra_nonce_start;
difficulty_type difficulty; difficulty_type difficulty;
difficulty_type sidechain_difficulty; difficulty_type sidechain_difficulty;
@@ -118,7 +121,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() // 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 // 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_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 // Integrity checks
if (blobs_data->m_blobSize < 76) { if (blobs_data->m_blobSize < 76) {
@@ -240,14 +243,14 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
const uint32_t extra_nonce = m_extraNonce.fetch_add(1); const uint32_t extra_nonce = m_extraNonce.fetch_add(1);
uint8_t hashing_blob[128]; uint8_t hashing_blob[128];
uint64_t height; uint64_t height, sidechain_height;
difficulty_type difficulty; difficulty_type difficulty;
difficulty_type sidechain_difficulty; difficulty_type sidechain_difficulty;
hash seed_hash; hash seed_hash;
size_t nonce_offset; size_t nonce_offset;
uint32_t template_id; 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()); uint64_t target = std::max(difficulty.target(), sidechain_difficulty.target());
@@ -280,7 +283,7 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
[client, id, &hashing_blob, job_id, blob_size, target, height, &seed_hash](void* buf, size_t buf_size) [client, id, &hashing_blob, job_id, blob_size, target, height, &seed_hash](void* buf, size_t buf_size)
{ {
do { do {
client->m_rpcId = static_cast<uint32_t>(static_cast<StratumServer*>(client->m_owner)->get_random64()); client->m_rpcId = static_cast<StratumServer*>(client->m_owner)->get_random32();
} while (!client->m_rpcId); } while (!client->m_rpcId);
log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t)); log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t));
@@ -295,7 +298,7 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
s << log::Hex(client->m_rpcId) << "\",\"job\":{\"blob\":\""; s << log::Hex(client->m_rpcId) << "\",\"job\":{\"blob\":\"";
s << log::hex_buf(hashing_blob, blob_size) << "\",\"job_id\":\""; s << log::hex_buf(hashing_blob, blob_size) << "\",\"job_id\":\"";
s << log::Hex(job_id) << "\",\"target\":\""; s << log::Hex(job_id) << "\",\"target\":\"";
s << target_hex << "\",\"algo\":\"rx/0\",\"height\":"; s << target_hex << "\",\"algo\":\"rx/wow\",\"height\":";
s << height << ",\"seed_hash\":\""; s << height << ",\"seed_hash\":\"";
s << seed_hash << "\"},\"extensions\":[\"algo\"],\"status\":\"OK\"}}\n"; s << seed_hash << "\"},\"extensions\":[\"algo\"],\"status\":\"OK\"}}\n";
return s.m_pos; return s.m_pos;
@@ -361,9 +364,10 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
if (found) { if (found) {
BlockTemplate& block = m_pool->block_template(); BlockTemplate& block = m_pool->block_template();
uint64_t height, sidechain_height;
difficulty_type mainchain_diff, sidechain_diff; 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"); LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
return send(client, return send(client,
[id](void* buf, size_t buf_size) [id](void* buf, size_t buf_size)
@@ -376,7 +380,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
if (mainchain_diff.check_pow(resultHash)) { if (mainchain_diff.check_pow(resultHash)) {
const char* s = client->m_customUser; 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); m_pool->submit_block_async(template_id, nonce, extra_nonce);
} }
@@ -408,12 +412,22 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
share->m_target = target; share->m_target = target;
share->m_resultHash = resultHash; share->m_resultHash = resultHash;
share->m_sidechainDifficulty = sidechain_diff; 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(); share->m_timestamp = seconds_since_epoch();
uint64_t rem; uint64_t rem;
share->m_hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 1; share->m_hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 1;
share->m_highEnoughDifficulty = sidechain_diff.check_pow(resultHash); 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); 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 // If this share is below sidechain difficulty, process it in this thread because it'll be quick
@@ -436,7 +450,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
return true; 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, const bool result = send(client,
[id](void* buf, size_t buf_size) [id](void* buf, size_t buf_size)
@@ -449,10 +463,10 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
return result; return result;
} }
uint64_t StratumServer::get_random64() uint32_t StratumServer::get_random32()
{ {
MutexLock lock(m_rngLock); MutexLock lock(m_rngLock);
return m_rng(); return static_cast<uint32_t>(m_rng() >> 32);
} }
void StratumServer::print_status() void StratumServer::print_status()
@@ -507,10 +521,13 @@ void StratumServer::show_workers()
void StratumServer::reset_share_counters() void StratumServer::reset_share_counters()
{ {
WriteLock lock(m_hashrateDataLock);
m_cumulativeHashes = 0; m_cumulativeHashes = 0;
m_cumulativeHashesAtLastShare = 0; m_cumulativeHashesAtLastShare = 0;
m_cumulativeFoundSharesDiff = 0.0; m_cumulativeFoundSharesDiff = 0.0;
m_totalFoundShares = 0; m_totalFoundShares = 0;
m_totalFailedShares = 0;
} }
void StratumServer::print_stratum_status() const void StratumServer::print_stratum_status() const
@@ -519,6 +536,8 @@ void StratumServer::print_stratum_status() const
int64_t dt_15m, dt_1h, dt_24h; int64_t dt_15m, dt_1h, dt_24h;
uint64_t hashes_since_last_share; uint64_t hashes_since_last_share;
double average_effort;
uint32_t shares_found, shares_failed;
{ {
ReadLock lock(m_hashrateDataLock); ReadLock lock(m_hashrateDataLock);
@@ -540,16 +559,25 @@ void StratumServer::print_stratum_status() const
hashes_24h = head.m_cumulativeHashes - tail_24h.m_cumulativeHashes; hashes_24h = head.m_cumulativeHashes - tail_24h.m_cumulativeHashes;
dt_24h = static_cast<int64_t>(head.m_timestamp - tail_24h.m_timestamp); 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;
shares_failed = m_totalFailedShares;
} }
const uint64_t hashrate_15m = (dt_15m > 0) ? (hashes_15m / dt_15m) : 0; 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_1h = (dt_1h > 0) ? (hashes_1h / dt_1h ) : 0;
const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0; const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0;
double average_effort = 0.0; char shares_failed_buf[64] = {};
const double diff = m_cumulativeFoundSharesDiff; log::Stream s(shares_failed_buf);
if (diff > 0.0) { if (shares_failed) {
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / diff; s << log::Yellow() << "\nShares failed = " << shares_failed << log::NoColor();
} }
LOGINFO(0, "status" << LOGINFO(0, "status" <<
@@ -557,7 +585,7 @@ void StratumServer::print_stratum_status() const
"\nHashrate (1h est) = " << log::Hashrate(hashrate_1h) << "\nHashrate (1h est) = " << log::Hashrate(hashrate_1h) <<
"\nHashrate (24h est) = " << log::Hashrate(hashrate_24h) << "\nHashrate (24h est) = " << log::Hashrate(hashrate_24h) <<
"\nTotal hashes = " << total_hashes << "\nTotal hashes = " << total_hashes <<
"\nShares found = " << m_totalFoundShares << "\nShares found = " << shares_found << static_cast<const char*>(shares_failed_buf) <<
"\nAverage effort = " << average_effort << '%' << "\nAverage effort = " << average_effort << '%' <<
"\nCurrent effort = " << static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double() << '%' << "\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)" "\nConnections = " << m_numConnections.load() << " (" << m_numIncomingConnections.load() << " incoming)"
@@ -661,9 +689,10 @@ void StratumServer::on_blobs_ready()
// Only send the latest blob // Only send the latest blob
BlobsData* data = blobs_queue.back(); BlobsData* data = blobs_queue.back();
const uint32_t extra_nonce_start = data->m_extraNonceStart;
size_t numClientsProcessed = 0; size_t numClientsProcessed = 0;
uint32_t extra_nonce = 0; uint32_t num_sent = 0;
const uint64_t cur_time = seconds_since_epoch(); const uint64_t cur_time = seconds_since_epoch();
{ {
@@ -682,12 +711,12 @@ void StratumServer::on_blobs_ready()
continue; continue;
} }
if (extra_nonce >= data->m_numClientsExpected) { if (num_sent >= data->m_numClientsExpected) {
// We don't have any more extra_nonce values available // We don't have any more extra_nonce values available
continue; 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; uint64_t target = data->m_target;
if (client->m_customDiff.lo) { if (client->m_customDiff.lo) {
@@ -723,13 +752,13 @@ void StratumServer::on_blobs_ready()
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE]; StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE];
saved_job.job_id = job_id; 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.template_id = data->m_templateId;
saved_job.target = target; saved_job.target = target;
} }
const bool result = send(client, const bool result = send(client,
[data, target, hashing_blob, &job_id](void* buf, size_t buf_size) [data, target, hashing_blob, job_id](void* buf, size_t buf_size)
{ {
log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t)); log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t));
@@ -742,14 +771,14 @@ void StratumServer::on_blobs_ready()
s << "{\"jsonrpc\":\"2.0\",\"method\":\"job\",\"params\":{\"blob\":\""; s << "{\"jsonrpc\":\"2.0\",\"method\":\"job\",\"params\":{\"blob\":\"";
s << log::hex_buf(hashing_blob, data->m_blobSize) << "\",\"job_id\":\""; s << log::hex_buf(hashing_blob, data->m_blobSize) << "\",\"job_id\":\"";
s << log::Hex(job_id) << "\",\"target\":\""; s << log::Hex(job_id) << "\",\"target\":\"";
s << target_hex << "\",\"algo\":\"rx/0\",\"height\":"; s << target_hex << "\",\"algo\":\"rx/wow\",\"height\":";
s << data->m_height << ",\"seed_hash\":\""; s << data->m_height << ",\"seed_hash\":\"";
s << data->m_seedHash << "\"}}\n"; s << data->m_seedHash << "\"}}\n";
return s.m_pos; return s.m_pos;
}); });
if (result) { if (result) {
++extra_nonce; ++num_sent;
} }
else { else {
client->close(); client->close();
@@ -762,7 +791,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) void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp)
@@ -799,12 +828,19 @@ void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp)
void StratumServer::on_share_found(uv_work_t* req) void StratumServer::on_share_found(uv_work_t* req)
{ {
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data); SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
StratumServer* server = share->m_server;
if (server->is_banned(share->m_clientAddr)) {
share->m_highEnoughDifficulty = false;
share->m_result = SubmittedShare::Result::BANNED;
return;
}
if (share->m_highEnoughDifficulty) { if (share->m_highEnoughDifficulty) {
bkg_jobs_tracker.start("StratumServer::on_share_found"); BACKGROUND_JOB_START(StratumServer::on_share_found);
} }
StratumClient* client = share->m_client; StratumClient* client = share->m_client;
StratumServer* server = share->m_server;
p2pool* pool = server->m_pool; p2pool* pool = server->m_pool;
const uint64_t target = share->m_target; const uint64_t target = share->m_target;
@@ -850,17 +886,26 @@ void StratumServer::on_share_found(uv_work_t* req)
client->m_score += GOOD_SHARE_POINTS; client->m_score += GOOD_SHARE_POINTS;
const uint64_t n = server->m_cumulativeHashes + hashes;
const double diff = sidechain_difficulty.to_double(); 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; const uint64_t n = server->m_cumulativeHashes + hashes;
++server->m_totalFoundShares; share->m_effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
server->m_cumulativeHashesAtLastShare = n;
const char* s = client->m_customUser; server->m_cumulativeFoundSharesDiff += diff;
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << ", effort " << effort << '%'); ++server->m_totalFoundShares;
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce); }
if (!pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce)) {
WriteLock lock(server->m_hashrateDataLock);
if (server->m_totalFoundShares > 0) {
--server->m_totalFoundShares;
++server->m_totalFailedShares;
}
}
} }
// Send the response to miner // Send the response to miner
@@ -882,13 +927,16 @@ void StratumServer::on_share_found(uv_work_t* req)
void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/) void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
{ {
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data); SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
StratumClient* client = share->m_client;
if (share->m_highEnoughDifficulty) { 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); }); ON_SCOPE_LEAVE([share]() { share->m_server->m_submittedSharesPool.push_back(share); });
StratumClient* client = share->m_client;
StratumServer* server = share->m_server; StratumServer* server = share->m_server;
const bool bad_share = (share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW); const bool bad_share = (share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW);
@@ -911,6 +959,9 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
case SubmittedShare::Result::INVALID_POW: case SubmittedShare::Result::INVALID_POW:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Invalid PoW\"}}\n"; s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Invalid PoW\"}}\n";
break; break;
case SubmittedShare::Result::BANNED:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Banned\"}}\n";
break;
case SubmittedShare::Result::OK: case SubmittedShare::Result::OK:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":null,\"result\":{\"status\":\"OK\"}}\n"; s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":null,\"result\":{\"status\":\"OK\"}}\n";
break; break;
@@ -931,6 +982,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() StratumServer::StratumClient::StratumClient()
: m_rpcId(0) : m_rpcId(0)
, m_perConnectionJobId(0) , m_perConnectionJobId(0)
@@ -1172,6 +1228,8 @@ void StratumServer::api_update_local_stats(uint64_t timestamp)
int64_t dt_15m, dt_1h, dt_24h; int64_t dt_15m, dt_1h, dt_24h;
uint64_t hashes_since_last_share; uint64_t hashes_since_last_share;
double average_effort;
uint32_t shares_found, shares_failed;
{ {
ReadLock lock(m_hashrateDataLock); ReadLock lock(m_hashrateDataLock);
@@ -1193,33 +1251,35 @@ void StratumServer::api_update_local_stats(uint64_t timestamp)
hashes_24h = head.m_cumulativeHashes - tail_24h.m_cumulativeHashes; hashes_24h = head.m_cumulativeHashes - tail_24h.m_cumulativeHashes;
dt_24h = static_cast<int64_t>(head.m_timestamp - tail_24h.m_timestamp); 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;
shares_failed = m_totalFailedShares;
} }
const uint64_t hashrate_15m = (dt_15m > 0) ? (hashes_15m / dt_15m) : 0; 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_1h = (dt_1h > 0) ? (hashes_1h / dt_1h ) : 0;
const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 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(); double current_effort = static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double();
int connections = m_numConnections; int connections = m_numConnections;
int incoming_connections = m_numIncomingConnections; int incoming_connections = m_numIncomingConnections;
m_pool->api()->set(p2pool_api::Category::LOCAL, "stats", m_pool->api()->set(p2pool_api::Category::LOCAL, "stats",
[hashrate_15m, hashrate_1h, hashrate_24h, total_hashes, shares_found, average_effort, current_effort, connections, incoming_connections](log::Stream& s) [hashrate_15m, hashrate_1h, hashrate_24h, total_hashes, shares_found, shares_failed, average_effort, current_effort, connections, incoming_connections](log::Stream& s)
{ {
s << "{\"hashrate_15m\":" << hashrate_15m s << "{\"hashrate_15m\":" << hashrate_15m
<< ",\"hashrate_1h\":" << hashrate_1h << ",\"hashrate_1h\":" << hashrate_1h
<< ",\"hashrate_24h\":" << hashrate_24h << ",\"hashrate_24h\":" << hashrate_24h
<< ",\"total_hashes\":" << total_hashes << ",\"total_hashes\":" << total_hashes
<< ",\"shares_found\":" << shares_found << ",\"shares_found\":" << shares_found
<< ",\"shares_failed\":" << shares_failed
<< ",\"average_effort\":" << average_effort << ",\"average_effort\":" << average_effort
<< ",\"current_effort\":" << current_effort << ",\"current_effort\":" << current_effort
<< ",\"connections\":" << connections << ",\"connections\":" << connections
+10 -2
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -84,7 +84,7 @@ public:
bool on_login(StratumClient* client, uint32_t id, const char* login); bool on_login(StratumClient* client, uint32_t id, const char* login);
bool on_submit(StratumClient* client, uint32_t id, const char* job_id_str, const char* nonce_str, const char* result_str); bool on_submit(StratumClient* client, uint32_t id, const char* job_id_str, const char* nonce_str, const char* result_str);
uint64_t get_random64(); uint32_t get_random32();
void print_status() override; void print_status() override;
void show_workers(); void show_workers();
@@ -103,6 +103,7 @@ private:
struct BlobsData struct BlobsData
{ {
uint32_t m_extraNonceStart;
std::vector<uint8_t> m_blobs; std::vector<uint8_t> m_blobs;
size_t m_blobSize; size_t m_blobSize;
uint64_t m_target; uint64_t m_target;
@@ -139,6 +140,9 @@ private:
uint64_t m_target; uint64_t m_target;
hash m_resultHash; hash m_resultHash;
difficulty_type m_sidechainDifficulty; difficulty_type m_sidechainDifficulty;
uint64_t m_mainchainHeight;
uint64_t m_sidechainHeight;
double m_effort;
uint64_t m_timestamp; uint64_t m_timestamp;
uint64_t m_hashes; uint64_t m_hashes;
bool m_highEnoughDifficulty; bool m_highEnoughDifficulty;
@@ -148,6 +152,7 @@ private:
COULDNT_CHECK_POW, COULDNT_CHECK_POW,
LOW_DIFF, LOW_DIFF,
INVALID_POW, INVALID_POW,
BANNED,
OK OK
} m_result; } m_result;
}; };
@@ -172,11 +177,14 @@ private:
double m_cumulativeFoundSharesDiff; double m_cumulativeFoundSharesDiff;
uint32_t m_totalFoundShares; uint32_t m_totalFoundShares;
uint32_t m_totalFailedShares;
uint64_t m_apiLastUpdateTime; uint64_t m_apiLastUpdateTime;
void update_hashrate_data(uint64_t hashes, uint64_t timestamp); void update_hashrate_data(uint64_t hashes, uint64_t timestamp);
void api_update_local_stats(uint64_t timestamp); void api_update_local_stats(uint64_t timestamp);
void on_shutdown() override;
}; };
} // namespace p2pool } // namespace p2pool
+30 -22
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -36,16 +36,16 @@ public:
bool connect_to_peer(bool is_v6, const char* ip, int port); bool connect_to_peer(bool is_v6, const char* ip, int port);
void drop_connections() { uv_async_send(&m_dropConnectionsAsync); } void drop_connections_async() { if (m_finished.load() == 0) { uv_async_send(&m_dropConnectionsAsync); } }
void shutdown_tcp(); void shutdown_tcp();
virtual void print_status(); virtual void print_status();
uv_loop_t* get_loop() { return &m_loop; } uv_loop_t* get_loop() { return &m_loop; }
int listen_port() const { return m_listenPort; } virtual int listen_port() const { return m_listenPort; }
bool connect_to_peer(bool is_v6, const raw_ip& ip, int port); bool connect_to_peer(bool is_v6, const raw_ip& ip, int port);
virtual void on_connect_failed(bool is_v6, const raw_ip& ip, int port); virtual void on_connect_failed(bool /*is_v6*/, const raw_ip& /*ip*/, int /*port*/) {}
void ban(const raw_ip& ip, uint64_t seconds); void ban(const raw_ip& ip, uint64_t seconds);
virtual void print_bans(); virtual void print_bans();
@@ -58,6 +58,7 @@ public:
virtual void reset(); virtual void reset();
virtual bool on_connect() = 0; virtual bool on_connect() = 0;
virtual bool on_read(char* data, uint32_t size) = 0; virtual bool on_read(char* data, uint32_t size) = 0;
bool on_proxy_handshake(char* data, uint32_t size);
virtual void on_read_failed(int /*err*/) {} virtual void on_read_failed(int /*err*/) {}
virtual void on_disconnected() {} virtual void on_disconnected() {}
@@ -68,7 +69,7 @@ public:
void close(); void close();
void ban(uint64_t seconds); void ban(uint64_t seconds);
void init_addr_string(bool is_v6, const sockaddr_storage* peer_addr); void init_addr_string();
alignas(8) char m_readBuf[READ_BUF_SIZE]; alignas(8) char m_readBuf[READ_BUF_SIZE];
@@ -88,19 +89,25 @@ public:
raw_ip m_addr; raw_ip m_addr;
int m_port; int m_port;
char m_addrString[64]; char m_addrString[72];
enum class Socks5ProxyState {
Default,
MethodSelectionSent,
ConnectRequestSent,
} m_socks5ProxyState;
std::atomic<uint32_t> m_resetCounter; std::atomic<uint32_t> m_resetCounter;
}; };
struct WriteBuf struct WriteBuf
{ {
Client* m_client = nullptr;
uv_write_t m_write = {}; uv_write_t m_write = {};
std::vector<uint8_t> m_data; Client* m_client = nullptr;
void* m_data = nullptr;
size_t m_dataCapacity = 0;
}; };
uv_mutex_t m_writeBuffersLock;
std::vector<WriteBuf*> m_writeBuffers; std::vector<WriteBuf*> m_writeBuffers;
struct SendCallbackBase struct SendCallbackBase
@@ -131,9 +138,9 @@ private:
static void on_connection_error(uv_handle_t* handle); static void on_connection_error(uv_handle_t* handle);
static void on_connect(uv_connect_t* req, int status); static void on_connect(uv_connect_t* req, int status);
void on_new_client(uv_stream_t* server); void on_new_client(uv_stream_t* server);
void on_new_client_nolock(uv_stream_t* server, Client* client); void on_new_client(uv_stream_t* server, Client* client);
bool connect_to_peer_nolock(Client* client, bool is_v6, const sockaddr* addr); bool connect_to_peer(Client* client);
bool send_internal(Client* client, SendCallbackBase&& callback); bool send_internal(Client* client, SendCallbackBase&& callback);
@@ -148,11 +155,15 @@ private:
protected: protected:
void start_listening(const std::string& listen_addresses); void start_listening(const std::string& listen_addresses);
std::string m_socks5Proxy;
bool m_socks5ProxyV6;
raw_ip m_socks5ProxyIP;
int m_socks5ProxyPort;
std::atomic<int> m_finished; std::atomic<int> m_finished;
int m_listenPort; int m_listenPort;
uv_loop_t m_loop; uv_loop_t m_loop;
std::atomic<bool> m_loopStopped;
uv_mutex_t m_clientsListLock; uv_mutex_t m_clientsListLock;
std::vector<Client*> m_preallocatedClients; std::vector<Client*> m_preallocatedClients;
@@ -165,23 +176,20 @@ protected:
bool is_banned(const raw_ip& ip); bool is_banned(const raw_ip& ip);
uv_mutex_t m_pendingConnectionsLock;
unordered_set<raw_ip> m_pendingConnections; unordered_set<raw_ip> m_pendingConnections;
uv_async_t m_dropConnectionsAsync; uv_async_t m_dropConnectionsAsync;
static void on_drop_connections(uv_async_t* async) { reinterpret_cast<TCPServer*>(async->data)->close_sockets(false); } static void on_drop_connections(uv_async_t* async) { reinterpret_cast<TCPServer*>(async->data)->close_sockets(false); }
virtual void on_shutdown() = 0;
uv_async_t m_shutdownAsync; uv_async_t m_shutdownAsync;
static void on_shutdown(uv_async_t* async) uv_prepare_t m_shutdownPrepare;
{ uv_timer_t m_shutdownTimer;
TCPServer* server = reinterpret_cast<TCPServer*>(async->data); uint32_t m_shutdownCountdown;
server->close_sockets(true); uint32_t m_numHandles;
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_dropConnectionsAsync), nullptr); static void on_shutdown(uv_async_t* async);
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_shutdownAsync), nullptr);
delete GetLoopUserData(&server->m_loop, false);
}
}; };
} // namespace p2pool } // namespace p2pool
+412 -255
View File
File diff suppressed because it is too large Load Diff
+144 -34
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 @@
#include <map> #include <map>
#include <thread> #include <thread>
#ifndef _WIN32 #if !defined(_WIN32) && defined(HAVE_SCHED)
#include <sched.h> #include <sched.h>
#endif #endif
@@ -29,10 +29,7 @@ static constexpr char log_category_prefix[] = "Util ";
namespace p2pool { namespace p2pool {
#define STR2(X) STR(X) const char* VERSION = "v" STR2(P2POOL_VERSION_MAJOR) "." STR2(P2POOL_VERSION_MINOR) " (built"
#define STR(X) #X
const char* VERSION = "v2.3 (built"
#if defined(__clang__) #if defined(__clang__)
" with clang/" __clang_version__ " with clang/" __clang_version__
#elif defined(__GNUC__) #elif defined(__GNUC__)
@@ -42,13 +39,12 @@ const char* VERSION = "v2.3 (built"
#endif #endif
" on " __DATE__ ")"; " on " __DATE__ ")";
#undef STR2
#undef STR
MinerCallbackHandler::~MinerCallbackHandler() {} MinerCallbackHandler::~MinerCallbackHandler() {}
void panic() void panic_stop(const char* message)
{ {
fprintf(stderr, "P2Pool can't continue execution: panic at %s\n", message);
p2pool::log::stop(); p2pool::log::stop();
do { do {
#ifdef _WIN32 #ifdef _WIN32
@@ -66,7 +62,7 @@ void make_thread_background()
SetThreadPriorityBoost(GetCurrentThread(), true); SetThreadPriorityBoost(GetCurrentThread(), true);
SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN); SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
#elif !defined(__APPLE__) && !defined(__FreeBSD__) #elif defined(HAVE_SCHED)
sched_param param; sched_param param;
param.sched_priority = 0; param.sched_priority = 0;
if (sched_setscheduler(0, SCHED_IDLE, &param) != 0) { if (sched_setscheduler(0, SCHED_IDLE, &param) != 0) {
@@ -75,6 +71,54 @@ void make_thread_background()
#endif #endif
} }
NOINLINE difficulty_type& difficulty_type::operator/=(difficulty_type b)
{
if (*this < b) {
lo = 0;
hi = 0;
return *this;
}
if (*this - b < b) {
lo = 1;
hi = 0;
return *this;
}
if (b.hi == 0) {
return operator/=(b.lo);
}
const uint64_t shift = bsr(b.hi) + 1;
const uint64_t divisor = shiftleft128(b.lo, b.hi, 64 - shift);
uint64_t t;
if (hi < divisor) {
uint64_t r;
t = udiv128(hi, lo, divisor, &r) >> shift;
}
else {
uint64_t r;
t = shiftright128(udiv128(hi - divisor, lo, divisor, &r), 1, shift);
}
difficulty_type product;
product.lo = umul128(b.lo, t, &product.hi);
uint64_t t1, t2;
t1 = umul128(b.hi, t, &t2);
product.hi += t1;
if (t2 || (product.hi < t1) || (*this < product)) {
--t;
}
lo = t;
hi = 0;
return *this;
}
NOINLINE bool difficulty_type::check_pow(const hash& pow_hash) const NOINLINE bool difficulty_type::check_pow(const hash& pow_hash) const
{ {
const uint64_t* a = reinterpret_cast<const uint64_t*>(pow_hash.h); const uint64_t* a = reinterpret_cast<const uint64_t*>(pow_hash.h);
@@ -126,13 +170,6 @@ NOINLINE bool difficulty_type::check_pow(const hash& pow_hash) const
return true; return true;
} }
difficulty_type operator+(const difficulty_type& a, const difficulty_type& b)
{
difficulty_type result = a;
result += b;
return result;
}
std::ostream& operator<<(std::ostream& s, const difficulty_type& d) std::ostream& operator<<(std::ostream& s, const difficulty_type& d)
{ {
char buf[log::Stream::BUF_SIZE + 1]; char buf[log::Stream::BUF_SIZE + 1];
@@ -197,6 +234,9 @@ std::istream& operator>>(std::istream& s, hash& h)
found_number = true; found_number = true;
h.h[index >> 1] = (h.h[index >> 1] << 4) | digit; h.h[index >> 1] = (h.h[index >> 1] << 4) | digit;
++index; ++index;
if (index >= HASH_SIZE * 2) {
return s;
}
} }
else if (found_number) { else if (found_number) {
return s; return s;
@@ -210,7 +250,7 @@ void uv_cond_init_checked(uv_cond_t* cond)
const int result = uv_cond_init(cond); const int result = uv_cond_init(cond);
if (result) { if (result) {
LOGERR(1, "failed to create conditional variable, error " << uv_err_name(result)); LOGERR(1, "failed to create conditional variable, error " << uv_err_name(result));
panic(); PANIC_STOP();
} }
} }
@@ -219,7 +259,7 @@ void uv_mutex_init_checked(uv_mutex_t* mutex)
const int result = uv_mutex_init(mutex); const int result = uv_mutex_init(mutex);
if (result) { if (result) {
LOGERR(1, "failed to create mutex, error " << uv_err_name(result)); LOGERR(1, "failed to create mutex, error " << uv_err_name(result));
panic(); PANIC_STOP();
} }
} }
@@ -228,7 +268,16 @@ void uv_rwlock_init_checked(uv_rwlock_t* lock)
const int result = uv_rwlock_init(lock); const int result = uv_rwlock_init(lock);
if (result) { if (result) {
LOGERR(1, "failed to create rwlock, error " << uv_err_name(result)); LOGERR(1, "failed to create rwlock, error " << uv_err_name(result));
panic(); PANIC_STOP();
}
}
void uv_async_init_checked(uv_loop_t* loop, uv_async_t* async, uv_async_cb async_cb)
{
const int err = uv_async_init(loop, async, async_cb);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
PANIC_STOP();
} }
} }
@@ -278,21 +327,22 @@ struct BackgroundJobTracker::Impl
void wait() void wait()
{ {
uint64_t last_msg_time = 0;
do { do {
bool is_empty = true;
{ {
MutexLock lock(m_lock); MutexLock lock(m_lock);
is_empty = m_jobs.empty(); if (m_jobs.empty()) {
for (const auto& job : m_jobs) { return;
LOGINFO(1, "waiting for " << job.second << " \"" << job.first << "\" jobs to finish"); }
const uint64_t t = seconds_since_epoch();
if (t != last_msg_time) {
last_msg_time = t;
for (const auto& job : m_jobs) {
LOGINFO(1, "waiting for " << job.second << " \"" << job.first << "\" jobs to finish");
}
} }
} }
std::this_thread::sleep_for(std::chrono::milliseconds(1));
if (is_empty) {
return;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
} while (1); } while (1);
} }
@@ -314,8 +364,10 @@ struct BackgroundJobTracker::Impl
LOGINFO(0, "background jobs running:" << log::const_buf(buf, s.m_pos)); LOGINFO(0, "background jobs running:" << log::const_buf(buf, s.m_pos));
} }
struct Compare { FORCEINLINE bool operator()(const char* a, const char* b) const { return strcmp(a, b) < 0; } };
uv_mutex_t m_lock; uv_mutex_t m_lock;
std::map<std::string, int32_t> m_jobs; std::map<const char*, int32_t, Compare> m_jobs;
}; };
BackgroundJobTracker::BackgroundJobTracker() : m_impl(new Impl()) BackgroundJobTracker::BackgroundJobTracker() : m_impl(new Impl())
@@ -327,12 +379,12 @@ BackgroundJobTracker::~BackgroundJobTracker()
delete m_impl; delete m_impl;
} }
void BackgroundJobTracker::start(const char* name) void BackgroundJobTracker::start_internal(const char* name)
{ {
m_impl->start(name); m_impl->start(name);
} }
void BackgroundJobTracker::stop(const char* name) void BackgroundJobTracker::stop_internal(const char* name)
{ {
m_impl->stop(name); m_impl->stop(name);
} }
@@ -353,8 +405,15 @@ static thread_local bool main_thread = false;
void set_main_thread() { main_thread = true; } void set_main_thread() { main_thread = true; }
bool is_main_thread() { return main_thread; } bool is_main_thread() { return main_thread; }
bool disable_resolve_host = false;
bool resolve_host(std::string& host, bool& is_v6) bool resolve_host(std::string& host, bool& is_v6)
{ {
if (disable_resolve_host) {
LOGERR(1, "resolve_host was called with DNS disabled for host " << host);
return false;
}
addrinfo hints{}; addrinfo hints{};
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
@@ -432,6 +491,57 @@ NOINLINE uint64_t bsr_reference(uint64_t x)
return bsr8_table.data[y >> 24] - n0 - n1 - n2; return bsr8_table.data[y >> 24] - n0 - n1 - n2;
} }
bool str_to_ip(bool is_v6, const char* ip, raw_ip& result)
{
sockaddr_storage addr;
if (is_v6) {
sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(&addr);
const int err = uv_ip6_addr(ip, 0, addr6);
if (err) {
LOGERR(1, "failed to parse IPv6 address " << ip << ", error " << uv_err_name(err));
return false;
}
memcpy(result.data, &addr6->sin6_addr, sizeof(in6_addr));
}
else {
sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(&addr);
const int err = uv_ip4_addr(ip, 0, addr4);
if (err) {
LOGERR(1, "failed to parse IPv4 address " << ip << ", error " << uv_err_name(err));
return false;
}
result = {};
result.data[10] = 0xFF;
result.data[11] = 0xFF;
memcpy(result.data + 12, &addr4->sin_addr, sizeof(in_addr));
}
return true;
}
bool is_localhost(const std::string& host)
{
if (host.empty()) {
return false;
}
if (host.compare("localhost") == 0) {
return true;
}
if (host.find_first_not_of("0123456789.:") != std::string::npos) {
return false;
}
raw_ip addr;
if (!str_to_ip(host.find(':') != std::string::npos, host.c_str(), addr)) {
return false;
}
return addr.is_localhost();
}
UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create) UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create)
{ {
UV_LoopUserData* data = reinterpret_cast<UV_LoopUserData*>(loop->data); UV_LoopUserData* data = reinterpret_cast<UV_LoopUserData*>(loop->data);
+31 -4
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -34,6 +34,9 @@
namespace p2pool { namespace p2pool {
#define P2POOL_VERSION_MAJOR 2
#define P2POOL_VERSION_MINOR 7
extern const char* VERSION; extern const char* VERSION;
template<typename T> struct not_implemented { enum { value = 0 }; }; template<typename T> struct not_implemented { enum { value = 0 }; };
@@ -146,7 +149,12 @@ FORCEINLINE T read_unaligned(const T* p)
template<typename T, size_t N> FORCEINLINE constexpr size_t array_size(T(&)[N]) { return N; } template<typename T, size_t N> FORCEINLINE constexpr size_t array_size(T(&)[N]) { return N; }
template<typename T, typename U, size_t N> FORCEINLINE constexpr size_t array_size(T(U::*)[N]) { return N; } template<typename T, typename U, size_t N> FORCEINLINE constexpr size_t array_size(T(U::*)[N]) { return N; }
[[noreturn]] void panic(); [[noreturn]] void panic_stop(const char* message);
#define STR(X) #X
#define STR2(X) STR(X)
#define PANIC_STOP(...) panic_stop(__FILE__ ":" STR2(__LINE__))
void make_thread_background(); void make_thread_background();
@@ -156,21 +164,29 @@ public:
BackgroundJobTracker(); BackgroundJobTracker();
~BackgroundJobTracker(); ~BackgroundJobTracker();
void start(const char* name); template<size_t N> FORCEINLINE void start(const char (&name)[N]) { start_internal(name); }
void stop(const char* name); template<size_t N> FORCEINLINE void stop (const char (&name)[N]) { stop_internal (name); }
void wait(); void wait();
void print_status(); void print_status();
private: private:
void start_internal(const char* name);
void stop_internal(const char* name);
struct Impl; struct Impl;
Impl* m_impl; Impl* m_impl;
}; };
extern BackgroundJobTracker bkg_jobs_tracker; extern BackgroundJobTracker bkg_jobs_tracker;
#define BACKGROUND_JOB_START(x) do { bkg_jobs_tracker.start(#x); } while (0)
#define BACKGROUND_JOB_STOP(x) do { bkg_jobs_tracker.stop(#x); } while (0)
void set_main_thread(); void set_main_thread();
bool is_main_thread(); bool is_main_thread();
extern bool disable_resolve_host;
bool resolve_host(std::string& host, bool& is_v6); bool resolve_host(std::string& host, bool& is_v6);
template <typename Key, typename T> template <typename Key, typename T>
@@ -197,6 +213,14 @@ struct RandomDeviceSeed
static RandomDeviceSeed instance; static RandomDeviceSeed instance;
}; };
FORCEINLINE uint64_t xorshift64star(uint64_t x)
{
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
return x * 0x2545F4914F6CDD1DULL;
}
FORCEINLINE uint64_t seconds_since_epoch() FORCEINLINE uint64_t seconds_since_epoch()
{ {
using namespace std::chrono; using namespace std::chrono;
@@ -219,6 +243,9 @@ FORCEINLINE uint64_t bsr(uint64_t x)
#define bsr bsr_reference #define bsr bsr_reference
#endif #endif
bool str_to_ip(bool is_v6, const char* ip, raw_ip& result);
bool is_localhost(const std::string& host);
} // namespace p2pool } // namespace p2pool
void memory_tracking_start(); void memory_tracking_start();
+64 -2
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
#pragma once #pragma once
#include <uv.h> #include <uv.h>
#include <thread>
static_assert(sizeof(in6_addr) == 16, "struct in6_addr has invalid size"); static_assert(sizeof(in6_addr) == 16, "struct in6_addr has invalid size");
static_assert(sizeof(in_addr) == 4, "struct in_addr has invalid size"); static_assert(sizeof(in_addr) == 4, "struct in_addr has invalid size");
@@ -60,6 +61,7 @@ typedef RWLock<true> WriteLock;
void uv_cond_init_checked(uv_cond_t* cond); void uv_cond_init_checked(uv_cond_t* cond);
void uv_mutex_init_checked(uv_mutex_t* mutex); void uv_mutex_init_checked(uv_mutex_t* mutex);
void uv_rwlock_init_checked(uv_rwlock_t* lock); void uv_rwlock_init_checked(uv_rwlock_t* lock);
void uv_async_init_checked(uv_loop_t* loop, uv_async_t* async, uv_async_cb async_cb);
uv_loop_t* uv_default_loop_checked(); uv_loop_t* uv_default_loop_checked();
struct UV_LoopCallbackBase struct UV_LoopCallbackBase
@@ -96,7 +98,7 @@ struct UV_LoopUserData
, m_callbacks{} , m_callbacks{}
, m_callbacksToRun{} , m_callbacksToRun{}
{ {
uv_async_init(m_loop, m_async, async_cb); uv_async_init_checked(m_loop, m_async, async_cb);
m_async->data = this; m_async->data = this;
uv_mutex_init_checked(&m_callbacksLock); uv_mutex_init_checked(&m_callbacksLock);
@@ -174,4 +176,64 @@ bool CallOnLoop(uv_loop_t* loop, T&& callback)
return false; return false;
} }
template<typename T>
void parallel_run(uv_loop_t* loop, T&& callback, bool wait = false)
{
uint32_t THREAD_COUNT = std::thread::hardware_concurrency();
if (THREAD_COUNT > 0) {
--THREAD_COUNT;
}
// No more than 8 threads because our UV worker thread pool has 8 threads
if (THREAD_COUNT > 8) {
THREAD_COUNT = 8;
}
struct Callback
{
explicit FORCEINLINE Callback(T&& f) : m_func(std::move(f)) {}
Callback& operator=(Callback&&) = delete;
T m_func;
};
std::shared_ptr<Callback> cb = std::make_shared<Callback>(std::move(callback));
struct Work
{
uv_work_t req;
std::shared_ptr<Callback> cb;
};
for (size_t i = 0; i < THREAD_COUNT; ++i) {
Work* w = new Work{ {}, cb };
w->req.data = w;
const int err = uv_queue_work(loop, &w->req,
[](uv_work_t* req)
{
std::shared_ptr<Callback>& cb = reinterpret_cast<Work*>(req->data)->cb;
cb->m_func();
cb.reset();
},
[](uv_work_t* req, int)
{
delete reinterpret_cast<Work*>(req->data);
});
if (err) {
delete w;
}
}
if (wait) {
cb->m_func();
while (cb.use_count() > 1) {
std::this_thread::yield();
}
}
}
} // namespace p2pool } // namespace p2pool
+52 -27
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* Portions Copyright (c) 2012-2013 The Cryptonote developers * Portions Copyright (c) 2012-2013 The Cryptonote developers
* Portions Copyright (c) 2014-2021 The Monero Project * Portions Copyright (c) 2014-2021 The Monero Project
* Portions Copyright (c) 2021 XMRig <https://github.com/xmrig> * Portions Copyright (c) 2021 XMRig <https://github.com/xmrig>
@@ -29,16 +29,14 @@ extern "C" {
namespace { namespace {
// public keys: 64 bytes -> 88 characters in base58
// prefix (1 byte) + checksum (4 bytes) -> 7 characters in base58
// 95 characters in total
constexpr int ADDRESS_LENGTH = 95;
// Allow only regular addresses (no integrated addresses, no subaddresses) // Allow only regular addresses (no integrated addresses, no subaddresses)
// Values taken from cryptonote_config.h (CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX) // Values taken from cryptonote_config.h (CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX)
constexpr uint64_t valid_prefixes[] = { 18, 53, 24 }; constexpr uint64_t valid_prefixes[] = { 18, 53, 24 };
constexpr std::array<int, 9> block_sizes{ 0, 2, 3, 5, 6, 7, 9, 10, 11 }; constexpr std::array<int, 9> block_sizes{ 0, 2, 3, 5, 6, 7, 9, 10, 11 };
constexpr int num_full_blocks = p2pool::Wallet::ADDRESS_LENGTH / block_sizes.back();
constexpr int last_block_size = p2pool::Wallet::ADDRESS_LENGTH % block_sizes.back();
constexpr int block_sizes_lookup[11] = { 0, -1, 1, 2, -1, 3, 4, 5, -1, 6, 7 }; constexpr int block_sizes_lookup[11] = { 0, -1, 1, 2, -1, 3, 4, 5, -1, 6, 7 };
constexpr char alphabet[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; constexpr char alphabet[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
@@ -112,8 +110,6 @@ bool Wallet::decode(const char* address)
return false; return false;
} }
constexpr int num_full_blocks = ADDRESS_LENGTH / block_sizes.back();
constexpr int last_block_size = ADDRESS_LENGTH % block_sizes.back();
constexpr int last_block_size_index = block_sizes_lookup[last_block_size]; constexpr int last_block_size_index = block_sizes_lookup[last_block_size];
static_assert(last_block_size_index >= 0, "Check ADDRESS_LENGTH"); static_assert(last_block_size_index >= 0, "Check ADDRESS_LENGTH");
@@ -150,11 +146,13 @@ bool Wallet::decode(const char* address)
m_prefix = data[0]; m_prefix = data[0];
if (m_prefix == valid_prefixes[0]) m_type = NetworkType::Mainnet; switch (m_prefix)
if (m_prefix == valid_prefixes[1]) m_type = NetworkType::Testnet; {
if (m_prefix == valid_prefixes[2]) m_type = NetworkType::Stagenet; case valid_prefixes[0]: m_type = NetworkType::Mainnet; break;
case valid_prefixes[1]: m_type = NetworkType::Testnet; break;
case valid_prefixes[2]: m_type = NetworkType::Stagenet; break;
if (m_type == NetworkType::Invalid) { default:
return false; return false;
} }
@@ -184,35 +182,62 @@ bool Wallet::assign(const hash& spend_pub_key, const hash& view_pub_key, Network
return false; return false;
} }
m_prefix = 0; switch (type)
{
case NetworkType::Mainnet: m_prefix = valid_prefixes[0]; break;
case NetworkType::Testnet: m_prefix = valid_prefixes[1]; break;
case NetworkType::Stagenet: m_prefix = valid_prefixes[2]; break;
default: m_prefix = 0; break;
}
m_spendPublicKey = spend_pub_key; m_spendPublicKey = spend_pub_key;
m_viewPublicKey = view_pub_key; m_viewPublicKey = view_pub_key;
m_checksum = 0;
uint8_t data[1 + HASH_SIZE * 2];
data[0] = static_cast<uint8_t>(m_prefix);
memcpy(data + 1, spend_pub_key.h, HASH_SIZE);
memcpy(data + 1 + HASH_SIZE, view_pub_key.h, HASH_SIZE);
uint8_t md[200];
keccak(data, sizeof(data), md);
memcpy(&m_checksum, md, sizeof(m_checksum));
m_type = type; m_type = type;
return true; return true;
} }
bool Wallet::get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag) const void Wallet::encode(char (&buf)[ADDRESS_LENGTH]) const
{
uint8_t data[1 + HASH_SIZE * 2 + sizeof(m_checksum)];
data[0] = static_cast<uint8_t>(m_prefix);
memcpy(data + 1, m_spendPublicKey.h, HASH_SIZE);
memcpy(data + 1 + HASH_SIZE, m_viewPublicKey.h, HASH_SIZE);
memcpy(data + 1 + HASH_SIZE * 2, &m_checksum, sizeof(m_checksum));
for (int i = 0; i <= num_full_blocks; ++i) {
uint64_t n = 0;
for (int j = 0; (j < 8) && (i * sizeof(uint64_t) + j < sizeof(data)); ++j) {
n = (n << 8) | data[i * sizeof(uint64_t) + j];
}
for (int j = ((i < num_full_blocks) ? block_sizes.back() : last_block_size) - 1; j >= 0; --j) {
const int digit = n % alphabet_size;
n /= alphabet_size;
buf[i * block_sizes.back() + j] = alphabet[digit];
}
}
}
bool Wallet::get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag, const uint8_t* expected_view_tag) const
{ {
hash derivation; hash derivation;
if (!generate_key_derivation(m_viewPublicKey, txkey_sec, output_index, derivation, view_tag)) { if (!generate_key_derivation(m_viewPublicKey, txkey_sec, output_index, derivation, view_tag)) {
return false; return false;
} }
if (!derive_public_key(derivation, output_index, m_spendPublicKey, eph_public_key)) { if (expected_view_tag && (view_tag != *expected_view_tag)) {
return false;
}
return true;
}
bool Wallet::get_eph_public_key_with_view_tag(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t expected_view_tag) const
{
hash derivation;
uint8_t view_tag;
if (!generate_key_derivation(m_viewPublicKey, txkey_sec, output_index, derivation, view_tag) || (view_tag != expected_view_tag)) {
return false; return false;
} }
+23 -8
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -24,25 +24,40 @@ namespace p2pool {
class Wallet class Wallet
{ {
public: public:
// public keys: 64 bytes -> 88 characters in base58
// prefix (1 byte) + checksum (4 bytes) -> 7 characters in base58
// 95 characters in total
static constexpr int ADDRESS_LENGTH = 95;
explicit Wallet(const char* address); explicit Wallet(const char* address);
Wallet(const Wallet& w); Wallet(const Wallet& w);
Wallet& operator=(const Wallet& w); Wallet& operator=(const Wallet& w);
FORCEINLINE bool valid() const { return m_type != NetworkType::Invalid; } FORCEINLINE bool valid() const { return m_type != NetworkType::Invalid; }
FORCEINLINE NetworkType type() const { return m_type; }
bool decode(const char* address); bool decode(const char* address);
bool assign(const hash& spend_pub_key, const hash& view_pub_key, NetworkType type); bool assign(const hash& spend_pub_key, const hash& view_pub_key, NetworkType type);
void encode(char (&buf)[ADDRESS_LENGTH]) const;
FORCEINLINE std::string encode() const
{
char buf[ADDRESS_LENGTH];
encode(buf);
return std::string(buf, buf + ADDRESS_LENGTH);
}
bool get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag, const uint8_t* expected_view_tag = nullptr) const;
FORCEINLINE bool operator<(const Wallet& w) const { return (m_spendPublicKey < w.m_spendPublicKey) || ((m_spendPublicKey == w.m_spendPublicKey) && (m_viewPublicKey < w.m_viewPublicKey)); }
FORCEINLINE bool operator==(const Wallet& w) const { return (m_spendPublicKey == w.m_spendPublicKey) && (m_viewPublicKey == w.m_viewPublicKey); }
FORCEINLINE uint64_t prefix() const { return m_prefix; }
FORCEINLINE const hash& spend_public_key() const { return m_spendPublicKey; } FORCEINLINE const hash& spend_public_key() const { return m_spendPublicKey; }
FORCEINLINE const hash& view_public_key() const { return m_viewPublicKey; } FORCEINLINE const hash& view_public_key() const { return m_viewPublicKey; }
FORCEINLINE uint32_t checksum() const { return m_checksum; }
bool get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag) const; FORCEINLINE NetworkType type() const { return m_type; }
bool get_eph_public_key_with_view_tag(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t expected_view_tag) const;
FORCEINLINE bool operator<(const Wallet& w) const { return m_spendPublicKey < w.m_spendPublicKey; }
FORCEINLINE bool operator==(const Wallet& w) const { return m_spendPublicKey == w.m_spendPublicKey; }
private: private:
uint64_t m_prefix; uint64_t m_prefix;
+16 -6
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -24,14 +24,20 @@ static constexpr char log_category_prefix[] = "ZMQReader ";
namespace p2pool { namespace p2pool {
ZMQReader::ZMQReader(const char* address, uint32_t zmq_port, MinerCallbackHandler* handler) ZMQReader::ZMQReader(const std::string& address, uint32_t zmq_port, const std::string& proxy, MinerCallbackHandler* handler)
: m_address(address) : m_address(address)
, m_zmqPort(zmq_port) , m_zmqPort(zmq_port)
, m_proxy(proxy)
, m_handler(handler) , m_handler(handler)
, m_tx() , m_tx()
, m_minerData() , m_minerData()
, m_chainmainData() , m_chainmainData()
{ {
if (!m_proxy.empty() && is_localhost(address)) {
LOGINFO(5, "not using proxy to connect to localhost address " << log::Gray() << address);
m_proxy.clear();
}
for (uint32_t i = m_publisherPort; i < std::numeric_limits<uint16_t>::max(); ++i) { for (uint32_t i = m_publisherPort; i < std::numeric_limits<uint16_t>::max(); ++i) {
try { try {
m_publisherPort = 0; m_publisherPort = 0;
@@ -84,14 +90,18 @@ void ZMQReader::run_wrapper(void* arg)
void ZMQReader::run() void ZMQReader::run()
{ {
try { try {
char addr[32]; if (!m_proxy.empty()) {
m_subscriber.set(zmq::sockopt::socks_proxy, zmq::const_buffer(m_proxy.c_str(), m_proxy.length()));
}
snprintf(addr, sizeof(addr), "tcp://%s:%u", m_address, m_zmqPort); std::string addr = "tcp://" + m_address + ':' + std::to_string(m_zmqPort);
if (!connect(addr)) { if (!connect(addr)) {
return; return;
} }
snprintf(addr, sizeof(addr), "tcp://127.0.0.1:%u", m_publisherPort); m_subscriber.set(zmq::sockopt::socks_proxy, zmq::const_buffer());
addr = "tcp://127.0.0.1:" + std::to_string(m_publisherPort);
if (!connect(addr)) { if (!connect(addr)) {
return; return;
} }
@@ -128,7 +138,7 @@ void ZMQReader::run()
} }
} }
bool ZMQReader::connect(const char* address) bool ZMQReader::connect(const std::string& address)
{ {
struct ConnectMonitor : public zmq::monitor_t struct ConnectMonitor : public zmq::monitor_t
{ {
+5 -4
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -24,18 +24,19 @@ namespace p2pool {
class ZMQReader { class ZMQReader {
public: public:
ZMQReader(const char* address, uint32_t zmq_port, MinerCallbackHandler* handler); ZMQReader(const std::string& address, uint32_t zmq_port, const std::string& proxy, MinerCallbackHandler* handler);
~ZMQReader(); ~ZMQReader();
private: private:
static void run_wrapper(void* arg); static void run_wrapper(void* arg);
void run(); void run();
bool connect(const char* address); bool connect(const std::string& address);
void parse(char* data, size_t size); void parse(char* data, size_t size);
const char* m_address; std::string m_address;
uint32_t m_zmqPort; uint32_t m_zmqPort;
std::string m_proxy;
MinerCallbackHandler* m_handler; MinerCallbackHandler* m_handler;
uv_thread_t m_worker{}; uv_thread_t m_worker{};
+33 -7
View File
@@ -5,23 +5,26 @@ option(STATIC_LIBS "Use locally built libuv and libzmq static libs" OFF)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.6.0") if (${CMAKE_VERSION} VERSION_GREATER "3.5.2")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool_tests) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool_tests)
endif() endif()
add_subdirectory(googletest) add_subdirectory(googletest)
set(LIBS gtest) set(LIBS gtest)
add_subdirectory(../external/src/RandomX RandomX) add_subdirectory(../external/src/RandomWOW RandomWOW)
set(LIBS ${LIBS} randomx) set(LIBS ${LIBS} randomx)
add_definitions(-DWITH_RANDOMX) add_definitions(-DWITH_RANDOMX)
add_definitions(-DP2POOL_UNIT_TESTS)
include(cmake/flags.cmake) include(cmake/flags.cmake)
set(HEADERS set(HEADERS
) )
set(SOURCES set(SOURCES
src/block_template_tests.cpp
src/crypto_tests.cpp src/crypto_tests.cpp
src/difficulty_type_tests.cpp src/difficulty_type_tests.cpp
src/hash_tests.cpp src/hash_tests.cpp
@@ -32,6 +35,7 @@ set(SOURCES
src/wallet_tests.cpp src/wallet_tests.cpp
../external/src/cryptonote/crypto-ops-data.c ../external/src/cryptonote/crypto-ops-data.c
../external/src/cryptonote/crypto-ops.c ../external/src/cryptonote/crypto-ops.c
../external/src/hardforks/hardforks.cpp
../src/block_cache.cpp ../src/block_cache.cpp
../src/block_template.cpp ../src/block_template.cpp
../src/console_commands.cpp ../src/console_commands.cpp
@@ -55,14 +59,31 @@ set(SOURCES
../src/zmq_reader.cpp ../src/zmq_reader.cpp
) )
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(../src)
include_directories(../external/src) include_directories(../external/src)
include_directories(../external/src/cryptonote) include_directories(../external/src/cryptonote)
include_directories(../external/src/curl/include) include_directories(${UV_INCLUDE_DIR})
include_directories(../external/src/libuv/include)
include_directories(../external/src/cppzmq) include_directories(../external/src/cppzmq)
include_directories(../external/src/libzmq/include) include_directories(${ZMQ_INCLUDE_DIR})
include_directories(../external/src/RandomX/src) include_directories(../external/src/RandomWOW/src)
include_directories(../external/src/rapidjson/include) include_directories(../external/src/rapidjson/include)
include_directories(../external/src/robin-hood-hashing/src/include) include_directories(../external/src/robin-hood-hashing/src/include)
include_directories(src) include_directories(src)
@@ -70,9 +91,12 @@ include_directories(googletest/googletest/include)
if (WIN32) if (WIN32)
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi wldap32) set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi wldap32)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(LIBS ${LIBS} bcrypt)
endif()
add_definitions(-DCURL_STATICLIB) add_definitions(-DCURL_STATICLIB)
elseif (NOT APPLE) elseif (NOT APPLE)
set(LIBS ${LIBS} pthread gss dl) set(LIBS ${LIBS} pthread)
endif() endif()
if (STATIC_LIBS) if (STATIC_LIBS)
@@ -90,6 +114,8 @@ else()
find_library(UV_LIBRARY NAMES uv_a PATHS "../external/lib/libuv/Release") 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_DEBUG NAMES libcurld PATHS "../external/lib/libcurl/Debug")
find_library(CURL_LIBRARY NAMES libcurl PATHS "../external/lib/libcurl/Release") 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) 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_DEBUG NAMES zmq libzmq.a)
find_library(ZMQ_LIBRARY NAMES zmq libzmq.a) find_library(ZMQ_LIBRARY NAMES zmq libzmq.a)
+102
View File
@@ -0,0 +1,102 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include "crypto.h"
#include "block_template.h"
#include "mempool.h"
#include "side_chain.h"
#include "wallet.h"
#include "keccak.h"
#include "gtest/gtest.h"
namespace p2pool {
TEST(block_template, update)
{
init_crypto_cache();
SideChain sidechain(nullptr, NetworkType::Mainnet);
BlockTemplate tpl(&sidechain, nullptr);
tpl.rng().seed(123);
auto H = [](const char* s)
{
std::stringstream ss;
ss << s;
hash result;
ss >> result;
return result;
};
MinerData data;
data.major_version = 16;
data.height = 2762973;
data.prev_id = H("81a0260b29d5224e88d04b11faff321fbdc11c4570779386b2a1817a86dc622c");
data.seed_hash = H("33d0fb381466f04d6a1919ced3b698f54a28add3da5a6479b096c67df7a4974c");
data.difficulty = { 300346053753ULL, 0 };
data.median_weight = 300000;
data.already_generated_coins = 18204981557254756780ULL;
data.median_timestamp = (1ULL << 35) - 2;
Mempool mempool;
Wallet wallet("44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg");
// Test 1: empty template
tpl.update(data, mempool, &wallet);
const PoolBlock* b = tpl.pool_block_template();
ASSERT_EQ(b->m_sidechainId, H("16d6a5c45d452288fcc439e5a258e8230798dfb6bbfd32220303efe932061aa9"));
std::vector<uint8_t> blobs;
uint64_t height;
difficulty_type diff, sidechain_diff;
hash seed_hash;
size_t nonce_offset;
uint32_t template_id;
tpl.get_hashing_blobs(0, 10000, blobs, height, diff, sidechain_diff, seed_hash, nonce_offset, template_id);
ASSERT_EQ(height, 2762973);
ASSERT_EQ(diff, 300346053753ULL);
ASSERT_EQ(sidechain_diff, sidechain.difficulty());
ASSERT_EQ(seed_hash, data.seed_hash);
ASSERT_EQ(nonce_offset, 39);
ASSERT_EQ(template_id, 1);
hash blobs_hash;
keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h);
ASSERT_EQ(blobs_hash, H("27bd8678420c8a0948f71c71356252be790899d61f14e35b2d0440a30d730f4c"));
// Test 2: mempool with high fee and low fee transactions, it must choose high fee transactions
for (uint64_t i = 0; i < 512; ++i) {
TxMempoolData tx;
*reinterpret_cast<uint64_t*>(tx.id.h) = i;
tx.fee = (i < 256) ? 30000000 : 60000000;
tx.weight = 1500;
mempool.add(tx);
}
tpl.update(data, mempool, &wallet);
for (size_t i = 1; i < b->m_transactions.size(); ++i) {
ASSERT_GE(*reinterpret_cast<const uint64_t*>(b->m_transactions[i].h), 256);
}
destroy_crypto_cache();
}
}
+319 -14
View File
@@ -24,6 +24,8 @@
namespace p2pool { namespace p2pool {
static const difficulty_type max_diff{ std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max() };
TEST(difficulty_type, constructors) TEST(difficulty_type, constructors)
{ {
difficulty_type diff; difficulty_type diff;
@@ -56,10 +58,7 @@ TEST(difficulty_type, target)
} }
// diff = max // diff = max
{ ASSERT_EQ(max_diff.target(), 1);
difficulty_type d(std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max());
ASSERT_EQ(d.target(), 1);
}
// diff = 2^32 // diff = 2^32
{ {
@@ -74,18 +73,35 @@ TEST(difficulty_type, target)
} }
} }
TEST(difficulty_type, sum) TEST(difficulty_type, add_sub)
{ {
auto check = [](const difficulty_type& a, const difficulty_type& b, const difficulty_type& sum)
{
difficulty_type result1 = a + b;
difficulty_type result2 = a;
result2 += b;
ASSERT_EQ(result1, sum);
ASSERT_EQ(result2, sum);
ASSERT_EQ(sum - a, b);
ASSERT_EQ(sum - b, a);
result1 -= a;
ASSERT_EQ(result1, b);
result2 -= b;
ASSERT_EQ(result2, a);
};
// No carry // No carry
{ {
difficulty_type diff[4] = { { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } }; difficulty_type diff[4] = { { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
for (int i = 0; i <= 3; ++i) { for (int i = 0; i <= 3; ++i) {
for (int j = 0; j <= 3; ++j) { for (int j = 0; j <= 3; ++j) {
difficulty_type a = diff[i]; difficulty_type sum(diff[i].lo + diff[j].lo, diff[i].hi + diff[j].hi);
a += diff[j]; check(diff[i], diff[j], sum);
ASSERT_EQ(a.lo, diff[i].lo + diff[j].lo);
ASSERT_EQ(a.hi, diff[i].hi + diff[j].hi);
} }
} }
} }
@@ -94,18 +110,307 @@ TEST(difficulty_type, sum)
{ {
difficulty_type a(11400714819323198485ull, 0); difficulty_type a(11400714819323198485ull, 0);
difficulty_type b(15975348984942515101ull, 0); difficulty_type b(15975348984942515101ull, 0);
a += b; difficulty_type sum(8929319730556161970ull, 1);
ASSERT_EQ(a.lo, 8929319730556161970ull); check(a, b, sum);
ASSERT_EQ(a.hi, 1);
} }
// Carry (edge case) // Carry (edge case)
{ {
difficulty_type a(std::numeric_limits<uint64_t>::max(), 0); difficulty_type a(std::numeric_limits<uint64_t>::max(), 0);
difficulty_type b(1, 0); difficulty_type b(1, 0);
a += b; difficulty_type sum(0, 1);
check(a, b, sum);
}
}
TEST(difficulty_type, mul_div)
{
auto check = [](const difficulty_type& a, uint64_t b, const difficulty_type& product)
{
ASSERT_EQ(a * b, product);
difficulty_type result = a;
result *= b;
ASSERT_EQ(result, product);
if (b) {
ASSERT_EQ(result / b, a);
difficulty_type tmp = result;
tmp /= difficulty_type(b, 0);
ASSERT_EQ(tmp, a);
difficulty_type tmp2 = result;
tmp2 /= b;
ASSERT_EQ(tmp2, a);
}
};
// (2^128 - 1) * 0 = 0
check(max_diff, 0, { 0, 0 });
// (2^128 - 1) * 1 = 2^128 - 1
check(max_diff, 1, max_diff);
// 5057672949897463733145855 * 67280421310721 = 2^128 - 1
check({ 18446744073709277439ull, 274176ull }, 67280421310721ull, max_diff);
// 10^19 * 10 = 10^20
check({ 10000000000000000000ull, 0 }, 10, { 7766279631452241920ull, 5 });
// 10^20 * 10 = 10^21
check({ 7766279631452241920ull, 5 }, 10, { 3875820019684212736ull, 54 });
// 0 * (2^64 - 1) = 0
check({ 0, 0 }, std::numeric_limits<uint64_t>::max(), { 0, 0 });
// 1 * (2^64 - 1) = 2^64 - 1
check({ 1, 0 }, std::numeric_limits<uint64_t>::max(), { std::numeric_limits<uint64_t>::max(), 0 });
// 2^64 * (2^64 - 1) = 2^128 - 2^64
check({ 0, 1 }, std::numeric_limits<uint64_t>::max(), { 0, std::numeric_limits<uint64_t>::max() });
// (2^64 + 1) * (2^64 - 1) = 2^128 - 1
check({ 1, 1 }, std::numeric_limits<uint64_t>::max(), max_diff);
// 2753074036095 * 6700417 = 2^64 - 1
check({ 2753074036095ull, 0 }, 6700417, { std::numeric_limits<uint64_t>::max(), 0 });
// 2^32 * 2^32 = 2^64
check({ 4294967296ull, 0 }, 4294967296ull, { 0, 1 });
// 274177 * 67280421310721 = 2^64 + 1
check({ 274177, 0 }, 67280421310721ull, { 1, 1 });
// Powers of 2
{
difficulty_type a(1, 0);
for (int i = 0; i < 64; ++i) {
ASSERT_EQ(a.lo, 1ull << i);
ASSERT_EQ(a.hi, 0);
a *= 2;
difficulty_type b = a;
b /= 2;
ASSERT_EQ(b.lo, 1ull << i);
ASSERT_EQ(b.hi, 0);
}
for (int i = 0; i < 64; ++i) {
ASSERT_EQ(a.lo, 0);
ASSERT_EQ(a.hi, 1ull << i);
a *= 2;
if (i < 63) {
difficulty_type b = a;
b /= 2;
ASSERT_EQ(b.lo, 0);
ASSERT_EQ(b.hi, 1ull << i);
}
}
ASSERT_EQ(a.lo, 0); ASSERT_EQ(a.lo, 0);
ASSERT_EQ(a.hi, 1); ASSERT_EQ(a.hi, 0);
}
// No carry
check({ 123, 456 }, 789, { 97047, 359784 });
}
static NOINLINE difficulty_type div128_ref(difficulty_type a, difficulty_type b)
{
difficulty_type result{};
while (a >= b) {
difficulty_type t = b;
difficulty_type q{ 1, 0 };
while (a - t >= t) {
t += t;
q += q;
}
a -= t;
result += q;
}
return result;
}
TEST(difficulty_type, div128)
{
auto check = [](difficulty_type a, difficulty_type b, difficulty_type result)
{
ASSERT_EQ(div128_ref(a, b), result);
ASSERT_EQ(a / b, result);
a /= b;
ASSERT_EQ(a, result);
};
// (2^128 - 1) / (2^128 - 1) = 1
check(max_diff, max_diff, { 1, 0 });
// (2^128 - 1) / (2^64 - 1) = 2^64 + 1
check(max_diff, { std::numeric_limits<uint64_t>::max(), 0 }, { 1, 1 });
// (2^128 - 1) / 2^64 = 2^64 - 1
check(max_diff, { 0, 1 }, { std::numeric_limits<uint64_t>::max(), 0 });
// (2^128 - 1) / (2^64 + 1) = 2^64 - 1
check(max_diff, { 1, 1 }, { std::numeric_limits<uint64_t>::max(), 0 });
// (2^128 - 1) / 8100430714362380904069067128193 = 42007935
check(max_diff, { 439125228929, 439125228929 }, { 42007935, 0 });
// (2^128 - 2^64) / (2^64 + 1) = 2^64 - 2
check({ 0, std::numeric_limits<uint64_t>::max() }, { 1, 1 }, { std::numeric_limits<uint64_t>::max() - 1, 0 });
// (2^128 - 2^64) / 2^64 = 2^64 - 1
check({ 0, std::numeric_limits<uint64_t>::max() }, { 0, 1 }, { std::numeric_limits<uint64_t>::max(), 0 });
// (2^128 - 2^64) / (2^64 - 1) = 2^64
check({ 0, std::numeric_limits<uint64_t>::max() }, { std::numeric_limits<uint64_t>::max(), 0 }, { 0, 1 });
{
difficulty_type a = max_diff - 4;
// (2^128 - 5) / 2002733033099709041094789607565039 = 169909
check(a, { 7565587230673184495, 108568375269759 }, { 169909, 0 });
// (2^128 - 5) / 2002733033099709041094789607565040 = 169908
check(a, { 7565587230673184496, 108568375269759 }, { 169908, 0 });
a -= 1;
// (2^128 - 6) / 2002733033099709041094789607565039 = 169908
check(a, { 7565587230673184495, 108568375269759 }, { 169908, 0 });
// (2^128 - 6) / 2002733033099709041094789607565038 = 169909
check(a, { 7565587230673184494, 108568375269759 }, { 169909, 0 });
}
// Powers of 2
for (difficulty_type i{ 1, 0 }, j = max_diff; !i.empty(); i += i, j /= 2) {
check(max_diff, i, j);
}
// Trivial tests
check({ 0, 3 }, { 0, 1 }, { 3, 0 });
check({ 0, 3 }, { 1, 1 }, { 2, 0 });
check({ 123 * 4 - 1, 456 * 4 }, { 123, 456 }, { 3, 0 });
check({ 123 * 4, 456 * 4 }, { 123, 456 }, { 4, 0 });
// Exhaustive tests (top 8 bits of each number)
for (uint64_t i = 1; i < 256; ++i) {
for (uint64_t j = 1; j < 256; ++j) {
const difficulty_type a{ 0, i << 56 };
const difficulty_type b{ 0, j << 56 };
{
difficulty_type t = a;
t /= b;
ASSERT_EQ(t.lo, i / j);
ASSERT_EQ(t.hi, 0);
}
}
}
// Bit patterns
std::vector<difficulty_type> patterns;
// 2^N-1, 2^N, 2^N+1
for (uint64_t i = 0; i < 128; ++i) {
difficulty_type t;
reinterpret_cast<uint64_t*>(&t)[i / 64] |= 1ull << (i % 64);
patterns.emplace_back(t - 1);
patterns.emplace_back(t);
patterns.emplace_back(t + 1);
}
// 2^N+2^M, 2^N-2^M
bool check_bits[128] = {};
for (uint64_t i = 64 - 4; i < 64 + 4; ++i) {
check_bits[i] = true;
}
for (uint64_t i = 128 - 8; i < 128; ++i) {
check_bits[i] = true;
}
for (uint64_t i = 0; i < 128; ++i) {
if (!check_bits[i]) {
continue;
}
difficulty_type t1;
reinterpret_cast<uint64_t*>(&t1)[i / 64] = 1ull << (i % 64);
for (uint64_t j = i + 1; j < 128; ++j) {
if (!check_bits[j]) {
continue;
}
difficulty_type t2;
reinterpret_cast<uint64_t*>(&t2)[j / 64] = 1ull << (j % 64);
patterns.emplace_back(t2 + t1);
patterns.emplace_back(t2 - t1);
}
}
// All previous patterns, but ~X
for (size_t i = 0, n = patterns.size(); i < n; ++i) {
patterns.emplace_back(~patterns[i].lo, ~patterns[i].hi);
}
std::sort(patterns.begin(), patterns.end());
patterns.erase(std::unique(patterns.begin(), patterns.end()), patterns.end());
// remove 0
patterns.erase(patterns.begin());
for (size_t i = 0, n = patterns.size(); i < n; ++i) {
const difficulty_type& a = patterns[i];
for (size_t j = i + 1; j < n; ++j) {
const difficulty_type& b = patterns[j];
ASSERT_EQ(div128_ref(b, a), b / a);
}
}
// Random tests with fixed seed
std::mt19937_64 r(0);
for (uint64_t i = 0; i < 10000000; ++i) {
// Random number of bits [1, 63]
const uint64_t N = (r() % 63) + 1;
// Random multiplier [1, 2^N - 1]
uint64_t k;
do {
k = r() & ((1ull << N) - 1);
} while (k == 0);
uint64_t t;
const uint64_t max_a = udiv128(1, 0, k + 1, &t);
// Random number [2^64, 2^128 / (k + 1)]
difficulty_type a{ r(), 0 };
do {
a.hi = r() % max_a;
} while (a.hi == 0);
difficulty_type b1 = a * k;
difficulty_type b2 = b1 - 1;
difficulty_type b3 = b1 + a;
difficulty_type b4 = b3 - 1;
b1 /= a;
ASSERT_EQ(b1.lo, k);
ASSERT_EQ(b1.hi, 0);
b2 /= a;
ASSERT_EQ(b2.lo, k - 1);
ASSERT_EQ(b2.hi, 0);
b3 /= a;
ASSERT_EQ(b3.lo, k + 1);
ASSERT_EQ(b3.hi, 0);
b4 /= a;
ASSERT_EQ(b4.lo, k);
ASSERT_EQ(b4.hi, 0);
} }
} }
+1 -1
View File
@@ -27,7 +27,7 @@ TEST(keccak, hashing)
hash output; hash output;
const uint8_t* data = reinterpret_cast<const uint8_t*>(input); const uint8_t* data = reinterpret_cast<const uint8_t*>(input);
const int len = static_cast<int>(size); const int len = static_cast<int>(size);
keccak(data, len, output.h, HASH_SIZE); keccak(data, len, output.h);
char buf[log::Stream::BUF_SIZE + 1]; char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf); log::Stream s(buf);
+16 -9
View File
@@ -27,6 +27,8 @@ namespace p2pool {
TEST(pool_block, deserialize) TEST(pool_block, deserialize)
{ {
init_crypto_cache();
PoolBlock b; PoolBlock b;
SideChain sidechain(nullptr, NetworkType::Mainnet, "mainnet test 2"); SideChain sidechain(nullptr, NetworkType::Mainnet, "mainnet test 2");
@@ -49,13 +51,18 @@ TEST(pool_block, deserialize)
f.read(reinterpret_cast<char*>(buf.data()), buf.size()); f.read(reinterpret_cast<char*>(buf.data()), buf.size());
ASSERT_EQ(f.good(), true); ASSERT_EQ(f.good(), true);
ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain, nullptr), 0); ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain, nullptr, false), 0);
size_t header_size, miner_tx_size;
int outputs_offset, outputs_blob_size;
const std::vector<uint8_t> mainchain_data = b.serialize_mainchain_data(&header_size, &miner_tx_size, &outputs_offset, &outputs_blob_size);
ASSERT_EQ(mainchain_data.size(), 5607);
ASSERT_EQ(header_size, 43);
ASSERT_EQ(miner_tx_size, 506);
ASSERT_EQ(outputs_offset, 54);
ASSERT_EQ(outputs_blob_size, 420);
ASSERT_EQ(b.m_mainChainData.size(), 5607);
ASSERT_EQ(b.m_mainChainHeaderSize, 43);
ASSERT_EQ(b.m_mainChainMinerTxSize, 506);
ASSERT_EQ(b.m_mainChainOutputsOffset, 54);
ASSERT_EQ(b.m_mainChainOutputsBlobSize, 420);
ASSERT_EQ(b.m_majorVersion, 14); ASSERT_EQ(b.m_majorVersion, 14);
ASSERT_EQ(b.m_minorVersion, 14); ASSERT_EQ(b.m_minorVersion, 14);
ASSERT_EQ(b.m_timestamp, 1630934403); ASSERT_EQ(b.m_timestamp, 1630934403);
@@ -65,14 +72,12 @@ TEST(pool_block, deserialize)
ASSERT_EQ(b.m_extraNonceSize, 4); ASSERT_EQ(b.m_extraNonceSize, 4);
ASSERT_EQ(b.m_extraNonce, 28); ASSERT_EQ(b.m_extraNonce, 28);
ASSERT_EQ(b.m_transactions.size(), 159); ASSERT_EQ(b.m_transactions.size(), 159);
ASSERT_EQ(b.m_sideChainData.size(), 146);
ASSERT_EQ(b.m_uncles.size(), 0); ASSERT_EQ(b.m_uncles.size(), 0);
ASSERT_EQ(b.m_sidechainHeight, 53450); ASSERT_EQ(b.m_sidechainHeight, 53450);
ASSERT_EQ(b.m_difficulty.lo, 319296691); ASSERT_EQ(b.m_difficulty.lo, 319296691);
ASSERT_EQ(b.m_difficulty.hi, 0); ASSERT_EQ(b.m_difficulty.hi, 0);
ASSERT_EQ(b.m_cumulativeDifficulty.lo, 12544665764606ull); ASSERT_EQ(b.m_cumulativeDifficulty.lo, 12544665764606ull);
ASSERT_EQ(b.m_cumulativeDifficulty.hi, 0); ASSERT_EQ(b.m_cumulativeDifficulty.hi, 0);
ASSERT_EQ(b.m_tmpTxExtra.size(), 0);
ASSERT_EQ(b.m_depth, 0); ASSERT_EQ(b.m_depth, 0);
ASSERT_EQ(b.m_verified, false); ASSERT_EQ(b.m_verified, false);
ASSERT_EQ(b.m_invalid, false); ASSERT_EQ(b.m_invalid, false);
@@ -98,6 +103,8 @@ TEST(pool_block, deserialize)
ASSERT_EQ(s.str(), "f76d731c61c9c9b6c3f46be2e60c9478930b49b4455feecd41ecb9420d000000"); ASSERT_EQ(s.str(), "f76d731c61c9c9b6c3f46be2e60c9478930b49b4455feecd41ecb9420d000000");
ASSERT_EQ(b.m_difficulty.check_pow(pow_hash), true); ASSERT_EQ(b.m_difficulty.check_pow(pow_hash), true);
destroy_crypto_cache();
} }
TEST(pool_block, verify) TEST(pool_block, verify)
@@ -134,7 +141,7 @@ TEST(pool_block, verify)
p += sizeof(uint32_t); p += sizeof(uint32_t);
ASSERT_TRUE(p + n <= e); ASSERT_TRUE(p + n <= e);
ASSERT_EQ(b.deserialize(p, n, sidechain, nullptr), 0); ASSERT_EQ(b.deserialize(p, n, sidechain, nullptr, false), 0);
p += n; p += n;
sidechain.add_block(b); sidechain.add_block(b);
+40 -11
View File
@@ -21,7 +21,7 @@
namespace p2pool { namespace p2pool {
TEST(wallet, decode) TEST(wallet, input_output)
{ {
// No data // No data
{ {
@@ -59,10 +59,13 @@ TEST(wallet, decode)
ASSERT_EQ(w.valid(), false); ASSERT_EQ(w.valid(), false);
} }
auto check = [](NetworkType t, const char* address, const char* spendkey, const char* viewkey) auto check = [](NetworkType t, uint64_t prefix, uint32_t checksum, const char* address, const char* spendkey, const char* viewkey)
{ {
// Test Wallet::decode()
Wallet w(address); Wallet w(address);
ASSERT_EQ(w.type(), t); ASSERT_EQ(w.type(), t);
ASSERT_EQ(w.prefix(), prefix);
ASSERT_EQ(w.checksum(), checksum);
char buf[log::Stream::BUF_SIZE + 1]; char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf); log::Stream s(buf);
@@ -73,53 +76,79 @@ TEST(wallet, decode)
s.m_pos = 0; s.m_pos = 0;
s << w.view_public_key(); s << w.view_public_key();
ASSERT_EQ(memcmp(buf, viewkey, HASH_SIZE * 2), 0); ASSERT_EQ(memcmp(buf, viewkey, HASH_SIZE * 2), 0);
// Test Wallet::assign()
Wallet w2(nullptr);
w2.assign(w.spend_public_key(), w.view_public_key(), w.type());
ASSERT_EQ(w2.prefix(), w.prefix());
ASSERT_EQ(w2.spend_public_key(), w.spend_public_key());
ASSERT_EQ(w2.view_public_key(), w.view_public_key());
ASSERT_EQ(w2.checksum(), w.checksum());
ASSERT_EQ(w2.type(), w.type());
// Test Wallet::encode()
const std::string s0 = address;
const std::string s1 = w.encode();
const std::string s2 = w2.encode();
ASSERT_EQ(s1, s0);
ASSERT_EQ(s2, s0);
// Test Wallet::encode(buf)
char buf1[Wallet::ADDRESS_LENGTH] = {};
w.encode(buf1);
ASSERT_EQ(memcmp(buf1, address, Wallet::ADDRESS_LENGTH), 0);
char buf2[Wallet::ADDRESS_LENGTH] = {};
w2.encode(buf2);
ASSERT_EQ(memcmp(buf2, address, Wallet::ADDRESS_LENGTH), 0);
}; };
// Correct mainnet addresses // Correct mainnet addresses
check( check(
NetworkType::Mainnet, "49ccoSmrBTPJd5yf8VYCULh4J5rHQaXP1TeC8Cnqhd5H9Y2cMwkJ9w42euLmMghKtCiQcgZEiGYW1K6Ae4biZ7w1HLSexS6", NetworkType::Mainnet, 18, 0xA345C1C9UL, "49ccoSmrBTPJd5yf8VYCULh4J5rHQaXP1TeC8Cnqhd5H9Y2cMwkJ9w42euLmMghKtCiQcgZEiGYW1K6Ae4biZ7w1HLSexS6",
"d2e232e441546a695b27187692d035ef7be5c54692700c9f470dcd706753a833", "06f68970da46f709e2b4d0ffabd0d1f78ea6717786b5766c25c259111f212490" "d2e232e441546a695b27187692d035ef7be5c54692700c9f470dcd706753a833", "06f68970da46f709e2b4d0ffabd0d1f78ea6717786b5766c25c259111f212490"
); );
check( check(
NetworkType::Mainnet, "45JHuqGBSqUXUyZx95H4C2J5aEL4zFjM3jpTmMTESPXPa3jmtSQWYezHX7r4A2xPQNBGsQupJqmPhRZb2QgBcEWRDQ9ywwR", NetworkType::Mainnet, 18, 0x8C8FB6E6UL, "45JHuqGBSqUXUyZx95H4C2J5aEL4zFjM3jpTmMTESPXPa3jmtSQWYezHX7r4A2xPQNBGsQupJqmPhRZb2QgBcEWRDQ9ywwR",
"60fe176eaf3cffb63df130bc25036b661b947900941052fffe6ff4b51fc4f2c5", "9387910b0a2e4f62c32621b77ddbeb3d6c0054e5ed9bc492d87bab1a1eef366d" "60fe176eaf3cffb63df130bc25036b661b947900941052fffe6ff4b51fc4f2c5", "9387910b0a2e4f62c32621b77ddbeb3d6c0054e5ed9bc492d87bab1a1eef366d"
); );
check( check(
NetworkType::Mainnet, "43S5vhReDY4fJs99DBZtFS8JoJVNG17iaAVAARvRT8xzSYZqnJfXfTACLrZUzoBHQKhiJZCWCpqB4Kf3c64CEagdSRXd5D7", NetworkType::Mainnet, 18, 0x0E705A56UL, "43S5vhReDY4fJs99DBZtFS8JoJVNG17iaAVAARvRT8xzSYZqnJfXfTACLrZUzoBHQKhiJZCWCpqB4Kf3c64CEagdSRXd5D7",
"2fc2f902659541e50753853ddb96912baf55f26bebe7d338b5c2239c437ddb98", "b814951166253543cfb0e1b8bdea58f366de824fddb8ef6f895fcf631873f6e1" "2fc2f902659541e50753853ddb96912baf55f26bebe7d338b5c2239c437ddb98", "b814951166253543cfb0e1b8bdea58f366de824fddb8ef6f895fcf631873f6e1"
); );
// Correct testnet addresses // Correct testnet addresses
check( check(
NetworkType::Testnet, "9x6aEN1yd2WhPMPw89LV5LLK1ZFe6N8xiAm18Ay4q1U4LKMde7MpDdPRN6GiiGCJMVTHuptGGmfj2Qfp2vcKSRSG79HJrQn", NetworkType::Testnet, 53, 0x6F896672UL, "9x6aEN1yd2WhPMPw89LV5LLK1ZFe6N8xiAm18Ay4q1U4LKMde7MpDdPRN6GiiGCJMVTHuptGGmfj2Qfp2vcKSRSG79HJrQn",
"821623ac165f07f172c86980254a43737332fd89ca36d33a57dc02d8026d9173", "7c55413e672f8691a9211eac6003109d2fdf224ba72c4d8d82353427a02bc136" "821623ac165f07f172c86980254a43737332fd89ca36d33a57dc02d8026d9173", "7c55413e672f8691a9211eac6003109d2fdf224ba72c4d8d82353427a02bc136"
); );
check( check(
NetworkType::Testnet, "9zsJP6KFF6ZGern5UkR7gyRXHFRTba6jG8JKnfzDySeqEdwPZaD8MNYGkjyADdVpWs7rXgyeu712JdxhX2k7d9SNB4TdRdS", NetworkType::Testnet, 53, 0x4124092AUL, "9zsJP6KFF6ZGern5UkR7gyRXHFRTba6jG8JKnfzDySeqEdwPZaD8MNYGkjyADdVpWs7rXgyeu712JdxhX2k7d9SNB4TdRdS",
"cb366a3b44f6aa5d94e03db06325b6929b9e75dbf19dcf2ba2d14eb2efa53651", "8789afa33dca295e301baef826cec028fa22b831822c1bdcf8a847a43a3bff59" "cb366a3b44f6aa5d94e03db06325b6929b9e75dbf19dcf2ba2d14eb2efa53651", "8789afa33dca295e301baef826cec028fa22b831822c1bdcf8a847a43a3bff59"
); );
check( check(
NetworkType::Testnet, "A1SqL5oPjh8Km1At7mao7U1fNjWkzeSwvQ39GimMqvhBF3FUoJhx1zxL2i6XbHzzAXDhKetiwSmYQeVwG6sUgwJuEqPyjWq", NetworkType::Testnet, 53, 0x0AC6459FUL, "A1SqL5oPjh8Km1At7mao7U1fNjWkzeSwvQ39GimMqvhBF3FUoJhx1zxL2i6XbHzzAXDhKetiwSmYQeVwG6sUgwJuEqPyjWq",
"da78298fb6eb8f702698bec873bad703f4a51e1377a66d89ba977ca7f43b8e53", "eeb348f70afad971c50aa062f1d1544be64ef9cdc12475e030f2d295305e6e7a" "da78298fb6eb8f702698bec873bad703f4a51e1377a66d89ba977ca7f43b8e53", "eeb348f70afad971c50aa062f1d1544be64ef9cdc12475e030f2d295305e6e7a"
); );
// Correct stagenet addresses // Correct stagenet addresses
check( check(
NetworkType::Stagenet, "55AJ4jJBhV6JsoqrEsAazTLrJjg9SA1SFReLUoXDudrsA9tdL9i2VkJefEbx3zrFRt6swuibPVySPGNzsNvyshrRNZbSDnD", NetworkType::Stagenet, 24, 0x36E99D1DUL, "55AJ4jJBhV6JsoqrEsAazTLrJjg9SA1SFReLUoXDudrsA9tdL9i2VkJefEbx3zrFRt6swuibPVySPGNzsNvyshrRNZbSDnD",
"57e0c2fef80a1d6adfa3189134009076ad0ddc4c4668709355cea98524e9fc36", "b94fafe59d5037e126557665f76cd3232504ebd82500e05bf25801d853d182bf" "57e0c2fef80a1d6adfa3189134009076ad0ddc4c4668709355cea98524e9fc36", "b94fafe59d5037e126557665f76cd3232504ebd82500e05bf25801d853d182bf"
); );
check( check(
NetworkType::Stagenet, "5BQqg4HTWuN3j4NzBHTK31eTaygRXYxWRQW9dTD7qMuJSiVtskraSErXQ24FUBeifiV6NaQPmxLS559vbUT4xYUoF2fiGvH", NetworkType::Stagenet, 24, 0x16DF3958UL, "5BQqg4HTWuN3j4NzBHTK31eTaygRXYxWRQW9dTD7qMuJSiVtskraSErXQ24FUBeifiV6NaQPmxLS559vbUT4xYUoF2fiGvH",
"fcd35a53cef9a1104ae556f01cee0cdff2f18f2f2f6bde8c833d5bd980fe8999", "be2b1142a046bfb5bb21e1f2a49bd1a7f46e1c18b009b218d5962f663938707c" "fcd35a53cef9a1104ae556f01cee0cdff2f18f2f2f6bde8c833d5bd980fe8999", "be2b1142a046bfb5bb21e1f2a49bd1a7f46e1c18b009b218d5962f663938707c"
); );
check( check(
NetworkType::Stagenet, "53CFYfjzcouW95hQ7AHvqS3GZ2UAAaRLKc1ymmhHATQTZxhtakpYcfjiRVzrRdxVZ5F8p61KSpPEmFu9DVRULRDkK4v1TCU", NetworkType::Stagenet, 24, 0xF17D6524UL, "53CFYfjzcouW95hQ7AHvqS3GZ2UAAaRLKc1ymmhHATQTZxhtakpYcfjiRVzrRdxVZ5F8p61KSpPEmFu9DVRULRDkK4v1TCU",
"23fdd143264794ae367083791bb8fd0d8f719b27b7b858d15a2b67d6eddd60c5", "0ebafc1284ab1af7a5ff4ade682bcc54817a319a00eede591344855c420beba0" "23fdd143264794ae367083791bb8fd0d8f719b27b7b858d15a2b67d6eddd60c5", "0ebafc1284ab1af7a5ff4ade682bcc54817a319a00eede591344855c420beba0"
); );
} }