Compare commits

...
37 Commits
Author SHA1 Message Date
SChernykh f17c23528a Update version 2021-10-17 11:00:51 +02:00
SChernykh a06f4dcdcd P2PServer: fixed ever growing memory usage 2021-10-17 10:57:33 +02:00
SChernykh 3bc03e4801 CI: added Visual Studio 2022 build 2021-10-16 15:01:29 +02:00
SChernykh 816a29c5ab Added --no-cache command line parameter 2021-10-16 13:45:28 +02:00
SChernykh 285560e120 Fixed possible sidechain split during RandomX epoch change 2021-10-16 09:57:13 +02:00
SChernykh 39ba5c4131 P2PServer: check for messages that shouldn't be sent 2021-10-15 17:09:51 +02:00
SChernykh 9b86f8e81f Peer list refactoring
- Remove peers that weren't seen for > 1 hour
- Better peer selection algorithm for PEER_LIST_RESPONSE
2021-10-15 11:32:01 +02:00
SChernykh 9e90e988fa Refactored array_size to make it always constexpr 2021-10-14 16:46:49 +02:00
SChernykh 39b2167c98 Show correct max log level in usage 2021-10-14 13:59:11 +02:00
SChernykh f3024d3556 StratumServer: submit Monero block as soon as possible
1-3 ms saved when a block is found
2021-10-13 18:57:21 +02:00
SChernykhandGitHub 61ac90f1d5 Fixed typo 2021-10-13 14:56:27 +02:00
SChernykh 60a8538e0c P2PServer: show an error when there are no ZMQ messages 2021-10-09 11:01:26 +02:00
SChernykh 77a3a5857c P2PServer: show an error when there are no connections 2021-10-08 18:21:31 +02:00
SChernykh af2a8eeb08 Tweaked warning level for lagging nodes 2021-10-06 15:22:03 +02:00
SChernykh 54d0ab3658 Move most block broadcasts to log level 6 2021-10-06 09:47:41 +02:00
SChernykh c9faa92098 StratumServer: fixed "bad share" ban logic 2021-10-04 15:51:28 +02:00
SChernykh 52e5008bf4 Refactoring
- Skip calling time(nullptr) twice
- Use getter method for m_api instead of exposing it
- Moved api_udate_*() back to private since they're not used anywhere else
2021-10-04 10:28:56 +02:00
SChernykhandGitHub a0a31b0f3b Merge pull request #51 from Tonux599/stratumServerJson-staging
API for stratum server statistics.
2021-10-04 11:16:37 +03:00
Thomas Clarke 7742d163f7 API for stratum server statistics.
Users can pass `--stratum-api` to command line to enable a file called `local/stats` in `--data-api` directory. `local/stats` contains statistics that `StratumServer status` provides but in JSON format. It is currently setup that `local/stats` is updated each time a new job is sent to a worker.
2021-10-03 22:32:13 +01:00
SChernykh 8106cc1c1c Update rapidjson 2021-10-02 17:16:30 +02:00
SChernykh 7d66864b01 Print P2Pool version 2021-10-02 17:06:48 +02:00
SChernykh 2a3cd13b19 P2PServer: delete old cached blocks after initial sync
Saves ~20 MB of memory
2021-10-01 15:31:47 +02:00
SChernykh 00281fb006 Updated monerod command line for optimal mining 2021-09-28 17:32:30 +02:00
SChernykh 4bcaa41c8e Clear crypto derivation/key cache when a block is found 2021-09-27 13:47:27 +02:00
SChernykh 4fce76576f ZMQ reader: abort if connect to monerod failed 2021-09-27 13:47:27 +02:00
SChernykhandGitHub 19043ace90 Merge pull request #38 from QuickBASIC/fix-batch-2-electric-boogaloo
fix run as admin and add pauses
2021-09-26 19:47:22 +03:00
QuickBASIC f9d75360ba fix run as admin and add pauses 2021-09-26 12:29:04 -04:00
SChernykh f05f7c4268 Fixed wrong nonce values in submit_block error log 2021-09-26 16:50:24 +02:00
SChernykh d8d84f08c4 Update llhttp.c 2021-09-26 14:35:16 +02:00
SChernykh ae8733c187 Update RandomX 2021-09-22 17:59:58 +02:00
SChernykhandGitHub 3d1db2fa22 Merge pull request #28 from QuickBASIC/readme-updates
update how to mine on p2pool section
2021-09-20 09:38:36 +03:00
Mike JustmanandGitHub 3adc0b8ecd fixed typo 2021-09-20 01:38:53 -04:00
QuickBASIC 08dff2a63a update how to mine on p2pool section 2021-09-19 21:59:53 -04:00
SChernykh c29f211ebf Update README.md 2021-09-19 09:21:51 +02:00
SChernykhandGitHub 75b629c64c Merge pull request #27 from QuickBASIC/post-release-readme-cleanup
remove testing verbiage
2021-09-19 03:16:38 +03:00
QuickBASIC 516e7196a4 remove testing verbiage 2021-09-18 20:08:41 -04:00
SChernykh 7d1372ff05 Update README.md 2021-09-18 23:47:59 +02:00
30 changed files with 548 additions and 155 deletions
+12 -9
View File
@@ -96,7 +96,13 @@ jobs:
build-windows-msbuild: build-windows-msbuild:
runs-on: windows-latest runs-on: windows-${{ matrix.config.os }}
strategy:
matrix:
config:
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\"}
- {vs: Visual Studio 17 2022, os: 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Preview\\Msbuild\\Current\\Bin\\amd64\\"}
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -107,23 +113,20 @@ jobs:
- name: Setup cmake - name: Setup cmake
uses: lukka/get-cmake@latest uses: lukka/get-cmake@latest
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.3
- name: Build p2pool - name: Build p2pool
run: | run: |
mkdir build mkdir build
cd build cd build
cmake .. -G "Visual Studio 16 2019" cmake .. -G "${{ matrix.config.vs }}"
msbuild /m /p:Configuration=Release p2pool.vcxproj & "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool.vcxproj
- name: Build tests - name: Build tests
run: | run: |
cd tests cd tests
mkdir build mkdir build
cd build cd build
cmake .. -G "Visual Studio 16 2019" cmake .. -G "${{ matrix.config.vs }}"
msbuild /m /p:Configuration=Release p2pool_tests.vcxproj & "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool_tests.vcxproj
- name: Run tests - name: Run tests
run: | run: |
@@ -133,7 +136,7 @@ jobs:
- name: Archive binary - name: Archive binary
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: p2pool-msbuild.exe name: p2pool-msbuild-${{ matrix.config.os }}.exe
path: build/Release/p2pool.exe path: build/Release/p2pool.exe
build-macos: build-macos:
+53 -17
View File
@@ -2,9 +2,7 @@
Decentralized pool for Monero mining. Decentralized pool for Monero mining.
**NOTE** This is a highly experimental and untested software. I did some extensive testing locally, but there's zero guarantee it will work for you! It requires a custom monerod version (for now). No binaries are provided yet. Testing on mainnet has started! You can check the pool status at https://p2pool.io/ Mining on the mainnet has started! You can check the pool status at https://p2pool.io/
No ETA on the official release date, but hopefully before the end of September 2021.
### Build status ### Build status
@@ -126,6 +124,7 @@ make -j$(sysctl -n hw.logicalcpu)
### Windows ### Windows
p2pool binary (Visual Studio Community 2019 build): p2pool binary (Visual Studio Community 2019 build):
*NOTE: You need to have the "Desktop Development with C++" module installed.*
``` ```
git clone --recursive https://github.com/SChernykh/p2pool git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool cd p2pool
@@ -135,6 +134,8 @@ cmake .. -G "Visual Studio 16 2019"
``` ```
then open generated build\p2pool.sln in Visual Studio and build it there then open generated build\p2pool.sln in Visual Studio and build it there
Alternatively, you can select "Clone a repository" within the GUI, then select "Build" from the menu.
monerod binary compatible with p2pool: monerod binary compatible with p2pool:
``` ```
git clone --recursive https://github.com/SChernykh/monero git clone --recursive https://github.com/SChernykh/monero
@@ -144,28 +145,35 @@ git submodule sync && git submodule update --init --force --recursive
``` ```
then follow the instructions from https://github.com/monero-project/monero/#on-windows then follow the instructions from https://github.com/monero-project/monero/#on-windows
## How to test ## How to mine on P2Pool
Mainnet test has started! **PPLNS window = 2160 blocks, block time = 10 seconds**. This guide assumes that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup. It's highly recommended to create a new mainnet wallet for testing because **wallet addresses are public on p2pool**. This guide assumes that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup. It's highly recommended to create a new mainnet wallet for mining because **wallet addresses are public on p2pool**.
**Wallet software compatible with p2pool payouts** **Wallet software compatible with p2pool payouts**
- Official Monero CLI and GUI v0.17.2.3 and newer - Official Monero CLI and GUI v0.17.2.3 and newer
- Monerujo v2.1.0 "Vertant" and newer - Monerujo v2.1.0 "Vertant" and newer
- Cake Wallet v4.2.7 and newer - Cake Wallet v4.2.7 and newer
- MyMonero
The purpose of this test is to bring as much hashrate as possible and check if stratum server works fine! **General Considerations**
- Grab the latest source code for both p2pool and monerod and build them (see above, also notice that the branch name for monerod changed, you'll need to checkout p2pool-api-v0.17) - Create a separate restricted user account for mining. p2pool is relatively new and may still have serious bugs/vulnerabilities!
- You have to use the primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
- Check that ports 18080 (Monero p2p port) and 37889 (p2pool p2p port) are open in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine.
- You can connect multiple miners to the same p2pool node. The more the better!
Step-by-step guide:
### GNU/Linux
- Download binaries from https://github.com/SChernykh/p2pool/releases/latest
- Alternatively, grab the latest source code for both p2pool and monerod and build them (see above, also notice that the branch name for monerod changed, you'll need to checkout p2pool-api-v0.17)
- Prepare enough huge pages (each of monerod/p2pool/xmrig needs them): `sudo sysctl vm.nr_hugepages=3072` - Prepare enough huge pages (each of monerod/p2pool/xmrig needs them): `sudo sysctl vm.nr_hugepages=3072`
- Create a separate restricted user account for testing. p2pool is untested and can have serious bugs/vulnerabilities!
- Get xmrig (linux-static-x64) binary from https://github.com/xmrig/xmrig/releases/latest - Get xmrig (linux-static-x64) binary from https://github.com/xmrig/xmrig/releases/latest
- Check that ports 18080 (Monero p2p port) and 37889 (p2pool p2p port) are open in your firewall to ensure better connectivity - Check that ports 18080 (Monero p2p port) and 37889 (p2pool p2p port) are open in your firewall to ensure better connectivity
- Create a new mainnet wallet - Run `./monerod --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist` **don't forget --zmq-pub parameter in the command line**
- You have to use the primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining - Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-7dbb0d1fc)** on startup. Wait until it's synchronized.
- Run `./monerod --zmq-pub tcp://127.0.0.1:18083` **don't forget --zmq-pub parameter in the command line**
- Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-04bfd948a)** on startup. Wait until it's synchronized.
- Run `./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS` - Run `./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
- Keep both monerod and p2pool running for the whole duration of your test
- p2pool has _very_ verbose logging by default, it will spam a lot, no I mean A LOT in both console and in p2pool.log. Logs help testing immensely! - p2pool has _very_ verbose logging by default, it will spam a lot, no I mean A LOT in both console and in p2pool.log. Logs help testing immensely!
- You can use `logrotate` with a config like this to control logfile growth: - You can use `logrotate` with a config like this to control logfile growth:
``` ```
@@ -178,15 +186,43 @@ The purpose of this test is to bring as much hashrate as possible and check if s
nocreate nocreate
} }
``` ```
- Wait until initial p2pool sync is finished, it shouldn't take more than 5-10 minutes. Of course it depends on your connection speed! - Wait until initial p2pool sync is finished, it shouldn't take more than 5-10 minutes, once completed xmrig should be able to connect to the stratum server on port 3333.
- p2pool has a stratum server listening on port 3333, you can connect xmrig to it now
- Run `./xmrig -o 127.0.0.1:3333`. Note that you don't need to specify wallet address for xmrig. **Wallet address set in xmrig config will be ignored!** - Run `./xmrig -o 127.0.0.1:3333`. Note that you don't need to specify wallet address for xmrig. **Wallet address set in xmrig config will be ignored!**
- To set custom fixed difficulty for your miner (for example, 10000), run `./xmrig -u x+10000 -o 127.0.0.1:3333` - To set custom fixed difficulty for your miner (for example, 10000), run `./xmrig -u x+10000 -o 127.0.0.1:3333`
- xmrig should connect and start mining - xmrig should connect and start mining
- you can connect multiple miners to the same p2pool node. The more the better!
- From now on, watch your wallet to see if it gets anything
- Also check p2pool.log for any warnings and errors: `grep -E 'WARNING|ERROR' p2pool.log` - Also check p2pool.log for any warnings and errors: `grep -E 'WARNING|ERROR' p2pool.log`
### Windows
*NOTE: Windows SmartScreen may block incoming connections by files that are "Downloaded from the Internet". You can allow 'p2pool.exe' and 'monerod.exe' by double-clicking them, clicking "More Info", then click "Run Anyway" and then closing them immediately so you can run them from the command line. Advanced users can use the PowerShell cmdlet `Unblock-File` to remove this flag.*
- Download p2pool binaries from https://github.com/SChernykh/p2pool/releases/latest
- Download xmrig binary from https://github.com/xmrig/xmrig/releases/latest *(xmrig-6.15.0-gcc-win64.zip is the current version)*
- Expand the p2pool binaries into an appropriate location (`%USERPROFILE%/bin` or `C:/bin/` are good options)
- Expand xmrig binary into appropriate location (same folder as p2pool is fine)
- Prepare huge pages (each of monerod/p2pool/xmrig needs them):
- On Windows 10 or above, run xmrig at least once as Administrator (right-click Run As Administrator)
- On earlier versions of Windows, you'll need to run it as admin at least once per login.
- Open a command prompt and navigate to the folder where you extracted p2pool.
- *When running these commands, Windows Firewall may prompt to allow connections, click "Allow"*
- Run `.\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist` *NOTE: don't forget --zmq-pub parameter in the command line*
- Double check that it shows **Monero 'Oxygen Orion' (v0.17.2.3-7dbb0d1fc)** on startup. Wait until it's synchronized.
- Run `.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS`
- Wait until initial p2pool sync is finished, it shouldn't take more than 5-10 minutes, once completed xmrig should be able to connect to the stratum server on port 3333.
- Run `.\xmrig.exe -o 127.0.0.1:3333`. Note that you don't need to specify wallet address for xmrig. **Wallet address set in xmrig config will be ignored!**
- To set custom fixed difficulty for your miner (for example, 10000), run `xmrig.exe -u x+10000 -o 127.0.0.1:3333`
- Windows Quickstart: Create a batch (.bat) file with the following contents and place it in your p2pool directory along with xmrig.exe.
```
@ECHO OFF
start cmd /k %~dp0\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
ECHO Wait until the Monero daemon shows fully synced before continuing. This can take some time. Type 'status' in other window to check progress.
PAUSE
start cmd /k %~dp0\p2pool.exe --wallet YOUR_WALLET_ADDRESS
ECHO Wait until the daemon shows fully synced before continuing. This can take some time.
PAUSE
%~dp0\xmrig.exe -u x+30000 -o 127.0.0.1
```
## Donations ## Donations
If you'd like to support further development of Monero P2Pool, you're welcome to send any amount of XMR to the following address: If you'd like to support further development of Monero P2Pool, you're welcome to send any amount of XMR to the following address:
+1
View File
@@ -2,6 +2,7 @@
#pragma warning(disable : 4061 4065 4100 4152 4242 4244 4668 4702 4710 4711 4820 5045) #pragma warning(disable : 4061 4065 4100 4152 4242 4244 4668 4702 4710 4711 4820 5045)
#elif defined __GNUC__ #elif defined __GNUC__
#pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#endif #endif
#if LLHTTP_STRICT_MODE #if LLHTTP_STRICT_MODE
+25 -6
View File
@@ -66,7 +66,7 @@ BlockTemplate::BlockTemplate(p2pool* pool)
m_mempoolTxsOrder.reserve(1024); m_mempoolTxsOrder.reserve(1024);
m_shares.reserve(m_pool->side_chain().chain_window_size() * 2); m_shares.reserve(m_pool->side_chain().chain_window_size() * 2);
for (size_t i = 0; i < array_size(m_oldTemplates); ++i) { for (size_t i = 0; i < array_size(&BlockTemplate::m_oldTemplates); ++i) {
m_oldTemplates[i] = new BlockTemplate(*this); m_oldTemplates[i] = new BlockTemplate(*this);
} }
@@ -79,7 +79,7 @@ BlockTemplate::BlockTemplate(p2pool* pool)
BlockTemplate::~BlockTemplate() BlockTemplate::~BlockTemplate()
{ {
for (size_t i = 0; i < array_size(m_oldTemplates); ++i) { for (size_t i = 0; i < array_size(&BlockTemplate::m_oldTemplates); ++i) {
delete m_oldTemplates[i]; delete m_oldTemplates[i];
} }
@@ -185,7 +185,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
WriteLock lock(m_lock); WriteLock lock(m_lock);
if (m_templateId > 0) { if (m_templateId > 0) {
*m_oldTemplates[m_templateId % array_size(m_oldTemplates)] = *this; *m_oldTemplates[m_templateId % array_size(&BlockTemplate::m_oldTemplates)] = *this;
} }
++m_templateId; ++m_templateId;
@@ -838,6 +838,25 @@ void BlockTemplate::calc_merkle_tree_main_branch()
} }
} }
bool BlockTemplate::get_difficulties(const uint32_t template_id, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const
{
ReadLock lock(m_lock);
if (template_id == m_templateId) {
mainchain_difficulty = m_difficulty;
sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
return true;
}
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) {
return old->get_difficulties(template_id, mainchain_difficulty, sidechain_difficulty);
}
return false;
}
uint32_t BlockTemplate::get_hashing_blob(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const uint32_t BlockTemplate::get_hashing_blob(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const
{ {
ReadLock lock(m_lock); ReadLock lock(m_lock);
@@ -852,7 +871,7 @@ uint32_t BlockTemplate::get_hashing_blob(const uint32_t template_id, uint32_t ex
return get_hashing_blob_nolock(extra_nonce, blob); return get_hashing_blob_nolock(extra_nonce, blob);
} }
const BlockTemplate* old = m_oldTemplates[template_id % array_size(m_oldTemplates)]; const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) { if (old && (template_id == old->m_templateId)) {
return old->get_hashing_blob(template_id, extra_nonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset); return old->get_hashing_blob(template_id, extra_nonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset);
@@ -953,7 +972,7 @@ std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id
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(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, nonce_offset, extra_nonce_offset);
} }
@@ -1016,7 +1035,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
return; return;
} }
BlockTemplate* old = m_oldTemplates[template_id % array_size(m_oldTemplates)]; BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) { if (old && (template_id == old->m_templateId)) {
old->submit_sidechain_block(template_id, nonce, extra_nonce); old->submit_sidechain_block(template_id, nonce, extra_nonce);
+1
View File
@@ -40,6 +40,7 @@ public:
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet); void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet);
bool get_difficulties(const uint32_t template_id, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
uint32_t get_hashing_blob(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const; uint32_t get_hashing_blob(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, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
+31 -3
View File
@@ -227,22 +227,50 @@ public:
return true; return true;
} }
void clear()
{
MutexLock lock(m);
derivations.clear();
public_keys.clear();
}
private: private:
uv_mutex_t m; uv_mutex_t m;
std::unordered_map<std::array<uint8_t, HASH_SIZE * 2>, hash> derivations; std::unordered_map<std::array<uint8_t, HASH_SIZE * 2>, hash> derivations;
std::unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys; std::unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
}; };
static Cache cache; static Cache* cache = nullptr;
bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation) bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation)
{ {
return cache.get_derivation(key1, key2, derivation); return cache->get_derivation(key1, key2, derivation);
} }
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key) bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key)
{ {
return cache.get_public_key(derivation, output_index, base, derived_key); return cache->get_public_key(derivation, output_index, base, derived_key);
}
void init_crypto_cache()
{
if (!cache) {
cache = new Cache();
}
}
void destroy_crypto_cache()
{
if (cache) {
delete cache;
cache = nullptr;
}
}
void clear_crypto_cache()
{
cache->clear();
} }
} // namespace p2pool } // namespace p2pool
+4
View File
@@ -24,4 +24,8 @@ bool check_keys(const hash& pub, const hash& sec);
bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation); bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation);
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key); bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key);
void init_crypto_cache();
void destroy_crypto_cache();
void clear_crypto_cache();
} // namespace p2pool } // namespace p2pool
+1
View File
@@ -371,6 +371,7 @@ template<> struct log::Stream::Entry<XMRAmount>
template<> struct log::Stream::Entry<NetworkType> template<> struct log::Stream::Entry<NetworkType>
{ {
// cppcheck-suppress constParameter
static NOINLINE void put(const NetworkType& value, Stream* wrapper) static NOINLINE void put(const NetworkType& value, Stream* wrapper)
{ {
switch (value) { switch (value) {
+11 -2
View File
@@ -16,13 +16,14 @@
*/ */
#include "common.h" #include "common.h"
#include "crypto.h"
#include "p2pool.h" #include "p2pool.h"
#include "stratum_server.h" #include "stratum_server.h"
#include "p2p_server.h" #include "p2p_server.h"
static void usage() static void usage()
{ {
printf( 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 Monero node, default is 127.0.0.1\n"
@@ -32,12 +33,16 @@ static void usage()
"--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"
"--light-mode Don't allocate RandomX dataset, saves 2GB of RAM\n" "--light-mode Don't allocate RandomX dataset, saves 2GB of RAM\n"
"--loglevel Verbosity of the log, integer number between 0 and 5\n" "--loglevel Verbosity of the log, integer number between 0 and %d\n"
"--config Name of the p2pool config file\n" "--config Name of the p2pool config file\n"
"--data-api Path to the p2pool JSON data (use it in tandem with an external web-server)\n" "--data-api Path to the p2pool JSON data (use it in tandem with an external web-server)\n"
"--stratum-api Enable /local/ path in api path for Stratum Server statistics\n"
"--no-cache Disable p2pool.cache\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 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",
p2pool::VERSION,
p2pool::log::MAX_GLOBAL_LOG_LEVEL,
#ifdef _WIN32 #ifdef _WIN32
"p2pool.exe" "p2pool.exe"
#else #else
@@ -68,10 +73,14 @@ int main(int argc, char* argv[])
int result; int result;
memory_tracking_start(); memory_tracking_start();
p2pool::init_crypto_cache();
{ {
p2pool::p2pool pool(argc, argv); p2pool::p2pool pool(argc, argv);
result = pool.run(); result = pool.run();
} }
p2pool::destroy_crypto_cache();
memory_tracking_stop(); memory_tracking_stop();
return result; return result;
+136 -50
View File
@@ -42,13 +42,14 @@ namespace p2pool {
P2PServer::P2PServer(p2pool* pool) P2PServer::P2PServer(p2pool* pool)
: TCPServer(P2PClient::allocate) : TCPServer(P2PClient::allocate)
, m_pool(pool) , m_pool(pool)
, m_cache(new BlockCache()) , m_cache(pool->params().m_blockCache ? new BlockCache() : nullptr)
, m_cacheLoaded(false) , m_cacheLoaded(false)
, m_initialPeerList(pool->params().m_p2pPeerList) , m_initialPeerList(pool->params().m_p2pPeerList)
, m_rd{} , m_rd{}
, m_rng(m_rd()) , m_rng(m_rd())
, m_block(new PoolBlock()) , m_block(new PoolBlock())
, m_timer{} , m_timer{}
, m_timerCounter(0)
, m_peerId(m_rng()) , m_peerId(m_rng())
, m_peerListLastSaved(0) , m_peerListLastSaved(0)
{ {
@@ -102,14 +103,11 @@ P2PServer::~P2PServer()
uv_mutex_destroy(&m_peerListLock); uv_mutex_destroy(&m_peerListLock);
uv_mutex_destroy(&m_broadcastLock); uv_mutex_destroy(&m_broadcastLock);
uv_mutex_destroy(&m_missingBlockRequestsLock); uv_mutex_destroy(&m_missingBlockRequestsLock);
clear_cached_blocks();
uv_rwlock_destroy(&m_cachedBlocksLock); uv_rwlock_destroy(&m_cachedBlocksLock);
delete m_block; delete m_block;
for (auto it : m_cachedBlocks) {
delete it.second;
}
delete m_cache; delete m_cache;
} }
@@ -124,6 +122,16 @@ void P2PServer::add_cached_block(const PoolBlock& block)
m_cachedBlocks.insert({ new_block->m_sidechainId, new_block }); m_cachedBlocks.insert({ new_block->m_sidechainId, new_block });
} }
void P2PServer::clear_cached_blocks()
{
WriteLock lock(m_cachedBlocksLock);
for (auto it : m_cachedBlocks) {
delete it.second;
}
m_cachedBlocks.clear();
}
void P2PServer::store_in_cache(const PoolBlock& block) void P2PServer::store_in_cache(const PoolBlock& block)
{ {
if (m_cache && block.m_verified && !block.m_invalid) { if (m_cache && block.m_verified && !block.m_invalid) {
@@ -159,12 +167,6 @@ void P2PServer::on_connect_failed(bool is_v6, const raw_ip& ip, int port)
void P2PServer::update_peer_connections() void P2PServer::update_peer_connections()
{ {
std::vector<Peer> peer_list;
{
MutexLock lock(m_peerListLock);
peer_list = m_peerList;
}
const time_t cur_time = time(nullptr); const time_t cur_time = time(nullptr);
const time_t last_updated = m_pool->side_chain().last_updated(); const time_t last_updated = m_pool->side_chain().last_updated();
@@ -173,13 +175,14 @@ void P2PServer::update_peer_connections()
MutexLock lock(m_clientsListLock); MutexLock lock(m_clientsListLock);
connected_clients.reserve(m_numConnections); connected_clients.reserve(m_numConnections);
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) { for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
connected_clients.emplace_back(client->m_addr); bool disconnected = false;
const int timeout = client->m_handshakeComplete ? 300 : 10; const int timeout = client->m_handshakeComplete ? 300 : 10;
if (cur_time >= client->m_lastAlive + timeout) { if (cur_time >= client->m_lastAlive + timeout) {
const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive); const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive);
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting"); LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting");
client->close(); client->close();
disconnected = true;
} }
if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) { if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) {
@@ -193,11 +196,35 @@ void P2PServer::update_peer_connections()
client->ban(DEFAULT_BAN_TIME); client->ban(DEFAULT_BAN_TIME);
remove_peer_from_list(client); remove_peer_from_list(client);
client->close(); client->close();
disconnected = true;
} }
} }
if (!disconnected) {
connected_clients.emplace_back(client->m_addr);
}
} }
} }
std::vector<Peer> peer_list;
{
MutexLock lock(m_peerListLock);
if ((m_timerCounter % 30) == 1) {
// Update last seen time for currently connected peers
for (Peer& p : m_peerList) {
if (std::find_if(connected_clients.begin(), connected_clients.end(), [&p](const raw_ip& addr) { return p.m_addr == addr; }) != connected_clients.end()) {
p.m_lastSeen = cur_time;
}
}
// Remove all peers that weren't seen for more than 1 hour
m_peerList.erase(std::remove_if(m_peerList.begin(), m_peerList.end(), [cur_time](const Peer& p) { return p.m_lastSeen + 3600 < cur_time; }), m_peerList.end());
}
peer_list = m_peerList;
}
// Try to have at least 8 outgoing connections // Try to have at least 8 outgoing connections
for (uint32_t i = m_numConnections - m_numIncomingConnections; (i < 8) && !peer_list.empty();) { for (uint32_t i = m_numConnections - m_numIncomingConnections; (i < 8) && !peer_list.empty();) {
const uint64_t k = get_random64() % peer_list.size(); const uint64_t k = get_random64() % peer_list.size();
@@ -220,6 +247,10 @@ void P2PServer::update_peer_connections()
} }
peer_list.pop_back(); peer_list.pop_back();
} }
if ((m_numConnections == 0) && ((m_timerCounter % 30) == 0)) {
LOGERR(1, "no connections to other p2pool nodes, check your monerod/p2pool/network/firewall setup!!!");
}
} }
void P2PServer::update_peer_list() void P2PServer::update_peer_list()
@@ -237,13 +268,17 @@ void P2PServer::update_peer_list()
// Send peer list requests at random intervals (60-120 seconds) // Send peer list requests at random intervals (60-120 seconds)
client->m_nextPeerListRequest = cur_time + 60 + (get_random64() % 61); client->m_nextPeerListRequest = cur_time + 60 + (get_random64() % 61);
send(client, const bool result = send(client,
[](void* buf) [](void* buf)
{ {
LOGINFO(5, "sending PEER_LIST_REQUEST"); LOGINFO(5, "sending PEER_LIST_REQUEST");
*reinterpret_cast<uint8_t*>(buf) = static_cast<uint8_t>(MessageId::PEER_LIST_REQUEST); *reinterpret_cast<uint8_t*>(buf) = static_cast<uint8_t>(MessageId::PEER_LIST_REQUEST);
return 1; return 1;
}); });
if (result) {
++client->m_peerListPendingRequests;
}
} }
} }
} }
@@ -417,8 +452,6 @@ void P2PServer::load_peer_list()
} }
p.m_isV6 = true; p.m_isV6 = true;
memcpy(p.m_addr.data, &addr6.sin6_addr, sizeof(in6_addr)); memcpy(p.m_addr.data, &addr6.sin6_addr, sizeof(in6_addr));
p.m_port = port;
p.m_numFailedConnections = 0;
} }
else { else {
sockaddr_in addr4; sockaddr_in addr4;
@@ -432,8 +465,6 @@ void P2PServer::load_peer_list()
p.m_addr.data[10] = 0xFF; p.m_addr.data[10] = 0xFF;
p.m_addr.data[11] = 0xFF; p.m_addr.data[11] = 0xFF;
memcpy(p.m_addr.data + 12, &addr4.sin_addr, sizeof(in_addr)); memcpy(p.m_addr.data + 12, &addr4.sin_addr, sizeof(in_addr));
p.m_port = port;
p.m_numFailedConnections = 0;
} }
bool already_added = false; bool already_added = false;
@@ -444,6 +475,10 @@ void P2PServer::load_peer_list()
} }
} }
p.m_port = port;
p.m_numFailedConnections = 0;
p.m_lastSeen = time(nullptr);
if (!already_added && !is_banned(p.m_addr)) { if (!already_added && !is_banned(p.m_addr)) {
m_peerList.push_back(p); m_peerList.push_back(p);
} }
@@ -454,18 +489,21 @@ void P2PServer::load_peer_list()
void P2PServer::update_peer_in_list(bool is_v6, const raw_ip& ip, int port) void P2PServer::update_peer_in_list(bool is_v6, const raw_ip& ip, int port)
{ {
const time_t cur_time = time(nullptr);
MutexLock lock(m_peerListLock); MutexLock lock(m_peerListLock);
for (Peer& p : m_peerList) { for (Peer& p : m_peerList) {
if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) { if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) {
p.m_port = port; p.m_port = port;
p.m_numFailedConnections = 0; p.m_numFailedConnections = 0;
p.m_lastSeen = cur_time;
return; return;
} }
} }
if (!is_banned(ip)) { if (!is_banned(ip)) {
m_peerList.emplace_back(Peer{ is_v6, ip, port, 0 }); m_peerList.emplace_back(Peer{ is_v6, ip, port, 0, cur_time });
} }
} }
@@ -602,18 +640,19 @@ void P2PServer::on_broadcast()
uint8_t* p = p0; uint8_t* p = p0;
bool send_pruned = true; bool send_pruned = true;
{
ReadLock lock(client->m_broadcastedHashesLock); const hash* a = client->m_broadcastedHashes;
for (const hash& id : data->ancestor_hashes) { const hash* b = client->m_broadcastedHashes + array_size(&P2PClient::m_broadcastedHashes);
if (client->m_broadcastedHashes.find(id) == client->m_broadcastedHashes.end()) {
send_pruned = false; for (const hash& id : data->ancestor_hashes) {
break; if (std::find(a, b, id) == b) {
} send_pruned = false;
break;
} }
} }
if (send_pruned) { if (send_pruned) {
LOGINFO(5, "sending BLOCK_BROADCAST (pruned) to " << log::Gray() << static_cast<char*>(client->m_addrString)); LOGINFO(6, "sending BLOCK_BROADCAST (pruned) to " << log::Gray() << static_cast<char*>(client->m_addrString));
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST); *(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
*reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(data->pruned_blob.size()); *reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(data->pruned_blob.size());
@@ -655,6 +694,8 @@ void P2PServer::print_status()
void P2PServer::on_timer() void P2PServer::on_timer()
{ {
++m_timerCounter;
if (!m_initialPeerList.empty()) { if (!m_initialPeerList.empty()) {
connect_to_peers(m_initialPeerList); connect_to_peers(m_initialPeerList);
m_initialPeerList.clear(); m_initialPeerList.clear();
@@ -665,6 +706,7 @@ void P2PServer::on_timer()
update_peer_list(); update_peer_list();
save_peer_list_async(); save_peer_list_async();
update_peer_connections(); update_peer_connections();
check_zmq();
} }
void P2PServer::flush_cache() void P2PServer::flush_cache()
@@ -765,6 +807,21 @@ void P2PServer::download_missing_blocks()
} }
} }
void P2PServer::check_zmq()
{
if ((m_timerCounter % 30) != 0) {
return;
}
const time_t cur_time = time(nullptr);
const time_t last_active = m_pool->zmq_last_active();
if (cur_time >= last_active + 300) {
const uint64_t dt = static_cast<uint64_t>(cur_time - last_active);
LOGERR(1, "no ZMQ messages received from monerod in the last " << dt << " seconds, check your monerod/p2pool/network/firewall setup!!!");
}
}
P2PServer::P2PClient::P2PClient() P2PServer::P2PClient::P2PClient()
: m_peerId(0) : m_peerId(0)
, m_expectedMessage(MessageId::HANDSHAKE_CHALLENGE) , m_expectedMessage(MessageId::HANDSHAKE_CHALLENGE)
@@ -774,16 +831,16 @@ P2PServer::P2PClient::P2PClient()
, m_handshakeInvalid(false) , m_handshakeInvalid(false)
, m_listenPort(-1) , m_listenPort(-1)
, m_nextPeerListRequest(0) , m_nextPeerListRequest(0)
, m_peerListPendingRequests(0)
, m_lastAlive(0) , m_lastAlive(0)
, m_lastBroadcastTimestamp(0) , m_lastBroadcastTimestamp(0)
, m_lastBlockrequestTimestamp(0) , m_lastBlockrequestTimestamp(0)
, m_broadcastedHashes{}
{ {
uv_rwlock_init_checked(&m_broadcastedHashesLock);
} }
P2PServer::P2PClient::~P2PClient() P2PServer::P2PClient::~P2PClient()
{ {
uv_rwlock_destroy(&m_broadcastedHashesLock);
} }
void P2PServer::P2PClient::reset() void P2PServer::P2PClient::reset()
@@ -798,12 +855,15 @@ void P2PServer::P2PClient::reset()
m_handshakeInvalid = false; m_handshakeInvalid = false;
m_listenPort = -1; m_listenPort = -1;
m_nextPeerListRequest = 0; m_nextPeerListRequest = 0;
m_peerListPendingRequests = 0;
m_lastAlive = 0; m_lastAlive = 0;
m_lastBroadcastTimestamp = 0; m_lastBroadcastTimestamp = 0;
m_lastBlockrequestTimestamp = 0; m_lastBlockrequestTimestamp = 0;
WriteLock lock(m_broadcastedHashesLock); for (hash& h : m_broadcastedHashes) {
m_broadcastedHashes.clear(); h = {};
}
m_broadcastedHashesIndex = 0;
} }
bool P2PServer::P2PClient::on_connect() bool P2PServer::P2PClient::on_connect()
@@ -859,6 +919,13 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
switch (id) switch (id)
{ {
case MessageId::HANDSHAKE_CHALLENGE: case MessageId::HANDSHAKE_CHALLENGE:
if (m_handshakeComplete) {
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected HANDSHAKE_CHALLENGE");
ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this);
return false;
}
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent HANDSHAKE_CHALLENGE"); LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent HANDSHAKE_CHALLENGE");
if (bytes_left >= 1 + CHALLENGE_SIZE + sizeof(uint64_t)) { if (bytes_left >= 1 + CHALLENGE_SIZE + sizeof(uint64_t)) {
@@ -873,6 +940,13 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
break; break;
case MessageId::HANDSHAKE_SOLUTION: case MessageId::HANDSHAKE_SOLUTION:
if (m_handshakeComplete) {
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected HANDSHAKE_SOLUTION");
ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this);
return false;
}
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent HANDSHAKE_SOLUTION"); LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent HANDSHAKE_SOLUTION");
if (bytes_left >= 1 + HASH_SIZE + CHALLENGE_SIZE) { if (bytes_left >= 1 + HASH_SIZE + CHALLENGE_SIZE) {
@@ -928,7 +1002,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
break; break;
case MessageId::BLOCK_BROADCAST: case MessageId::BLOCK_BROADCAST:
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_BROADCAST"); LOGINFO(6, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_BROADCAST");
if (bytes_left >= 1 + sizeof(uint32_t)) { if (bytes_left >= 1 + sizeof(uint32_t)) {
const uint32_t block_size = *reinterpret_cast<uint32_t*>(buf + 1); const uint32_t block_size = *reinterpret_cast<uint32_t*>(buf + 1);
@@ -957,6 +1031,13 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
break; break;
case MessageId::PEER_LIST_RESPONSE: case MessageId::PEER_LIST_RESPONSE:
if (m_peerListPendingRequests <= 0) {
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected PEER_LIST_RESPONSE");
ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this);
return false;
}
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent PEER_LIST_RESPONSE"); LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent PEER_LIST_RESPONSE");
if (bytes_left >= 2) { if (bytes_left >= 2) {
@@ -970,6 +1051,8 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
if (bytes_left >= 2u + num_peers * 19) { if (bytes_left >= 2u + num_peers * 19) {
bytes_read = 2u + num_peers * 19; bytes_read = 2u + num_peers * 19;
--m_peerListPendingRequests;
if (!on_peer_list_response(buf + 1)) { if (!on_peer_list_response(buf + 1)) {
ban(DEFAULT_BAN_TIME); ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this); server->remove_peer_from_list(this);
@@ -1406,10 +1489,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
return false; return false;
} }
{ m_broadcastedHashes[m_broadcastedHashesIndex.fetch_add(1) % array_size(&P2PClient::m_broadcastedHashes)] = server->m_block->m_sidechainId;
WriteLock lock2(m_broadcastedHashesLock);
m_broadcastedHashes.insert(server->m_block->m_sidechainId);
}
const MinerData& miner_data = server->m_pool->miner_data(); const MinerData& miner_data = server->m_pool->miner_data();
@@ -1437,8 +1517,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
} }
else if (peer_height > our_height) { else if (peer_height > our_height) {
if (peer_height >= our_height + 2) { if (peer_height >= our_height + 2) {
const int level = (peer_height >= our_height + 3) ? 3 : 4; LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " is ahead on mainchain (height " << peer_height << ", your height " << our_height << "). Is your monerod stuck or lagging?");
LOGWARN(level , "peer " << static_cast<char*>(m_addrString) << " is ahead on mainchain (height " << peer_height << ", your height " << our_height << "). Is your monerod stuck or lagging?");
} }
} }
else { else {
@@ -1463,23 +1542,28 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
MutexLock lock(server->m_clientsListLock); MutexLock lock(server->m_clientsListLock);
// Send every 4th peer on average, selected at random // Send every 4th peer on average, selected at random
const uint32_t n = server->m_numConnections; const uint32_t peers_to_send_target = std::min<uint32_t>(PEER_LIST_RESPONSE_MAX_PEERS, std::max<uint32_t>(1, server->m_numConnections / 4));
const uint32_t peers_to_send_target = std::min<uint32_t>(PEER_LIST_RESPONSE_MAX_PEERS, std::max<uint32_t>(1, n / 4)); uint32_t n = 0;
for (P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) { for (P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if (client->m_listenPort < 0) { if ((client->m_listenPort < 0) || (client->m_addr == m_addr)) {
continue; continue;
} }
uint64_t hi; const Peer p{ client->m_isV6, client->m_addr, client->m_listenPort, 0, 0 };
umul128(server->get_random64(), n, &hi); ++n;
if ((hi < peers_to_send_target) && (client->m_addr != m_addr)) { // Use https://en.wikipedia.org/wiki/Reservoir_sampling algorithm
peers[num_selected_peers++] = { client->m_isV6, client->m_addr, client->m_listenPort, 0 }; if (num_selected_peers < peers_to_send_target) {
peers[num_selected_peers++] = p;
continue;
}
if (num_selected_peers >= PEER_LIST_RESPONSE_MAX_PEERS) { uint64_t k;
break; umul128(server->get_random64(), n, &k);
}
if (k < peers_to_send_target) {
peers[k] = p;
} }
} }
} }
@@ -1513,6 +1597,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
{ {
P2PServer* server = static_cast<P2PServer*>(m_owner); P2PServer* server = static_cast<P2PServer*>(m_owner);
const time_t cur_time = time(nullptr);
MutexLock lock(server->m_peerListLock); MutexLock lock(server->m_peerListLock);
@@ -1529,15 +1614,16 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
buf += 2; buf += 2;
bool already_added = false; bool already_added = false;
for (const Peer& p : server->m_peerList) { for (Peer& p : server->m_peerList) {
if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) { if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) {
already_added = true; already_added = true;
p.m_lastSeen = cur_time;
break; break;
} }
} }
if (!already_added && !server->is_banned(ip)) { if (!already_added && !server->is_banned(ip)) {
server->m_peerList.emplace_back(Peer{ is_v6, ip, port, 0 }); server->m_peerList.emplace_back(Peer{ is_v6, ip, port, 0, cur_time });
} }
} }
+7 -2
View File
@@ -49,6 +49,7 @@ public:
~P2PServer(); ~P2PServer();
void add_cached_block(const PoolBlock& block); void add_cached_block(const PoolBlock& block);
void clear_cached_blocks();
void store_in_cache(const PoolBlock& block); void store_in_cache(const PoolBlock& block);
void connect_to_peers(const std::string& peer_list); void connect_to_peers(const std::string& peer_list);
@@ -109,12 +110,13 @@ public:
bool m_handshakeInvalid; bool m_handshakeInvalid;
int m_listenPort; int m_listenPort;
time_t m_nextPeerListRequest; time_t m_nextPeerListRequest;
int m_peerListPendingRequests;
time_t m_lastAlive; time_t m_lastAlive;
time_t m_lastBroadcastTimestamp; time_t m_lastBroadcastTimestamp;
time_t m_lastBlockrequestTimestamp; time_t m_lastBlockrequestTimestamp;
uv_rwlock_t m_broadcastedHashesLock; hash m_broadcastedHashes[8];
std::set<hash> m_broadcastedHashes; std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 };
}; };
void broadcast(const PoolBlock& block); void broadcast(const PoolBlock& block);
@@ -138,6 +140,7 @@ private:
void flush_cache(); void flush_cache();
void download_missing_blocks(); void download_missing_blocks();
void check_zmq();
void update_peer_connections(); void update_peer_connections();
void update_peer_list(); void update_peer_list();
void save_peer_list_async(); void save_peer_list_async();
@@ -155,6 +158,7 @@ private:
PoolBlock* m_block; PoolBlock* m_block;
uv_timer_t m_timer; uv_timer_t m_timer;
uint32_t m_timerCounter;
uint64_t m_peerId; uint64_t m_peerId;
@@ -166,6 +170,7 @@ private:
raw_ip m_addr; raw_ip m_addr;
int m_port; int m_port;
uint32_t m_numFailedConnections; uint32_t m_numFailedConnections;
time_t m_lastSeen;
}; };
std::vector<Peer> m_peerList; std::vector<Peer> m_peerList;
+15 -5
View File
@@ -50,6 +50,8 @@ p2pool::p2pool(int argc, char* argv[])
, m_updateSeed(true) , m_updateSeed(true)
, m_submitBlockData{} , m_submitBlockData{}
{ {
LOGINFO(1, log::LightCyan() << VERSION);
if (!m_params->m_wallet.valid()) { if (!m_params->m_wallet.valid()) {
LOGERR(1, "Invalid wallet address. Try \"p2pool --help\"."); LOGERR(1, "Invalid wallet address. Try \"p2pool --help\".");
panic(); panic();
@@ -103,7 +105,7 @@ p2pool::p2pool(int argc, char* argv[])
uv_mutex_init_checked(&m_foundBlocksLock); uv_mutex_init_checked(&m_foundBlocksLock);
uv_mutex_init_checked(&m_submitBlockDataLock); uv_mutex_init_checked(&m_submitBlockDataLock);
m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath); m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath, m_params->m_localStats);
m_sideChain = new SideChain(this, type); m_sideChain = new SideChain(this, type);
m_hasher = new RandomX_Hasher(this); m_hasher = new RandomX_Hasher(this);
@@ -171,6 +173,8 @@ void p2pool::handle_tx(TxMempoolData& tx)
#if TEST_MEMPOOL_PICKING_ALGORITHM #if TEST_MEMPOOL_PICKING_ALGORITHM
m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet); m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet);
#endif #endif
m_zmqLastActive = time(nullptr);
} }
void p2pool::handle_miner_data(MinerData& data) void p2pool::handle_miner_data(MinerData& data)
@@ -224,6 +228,8 @@ void p2pool::handle_miner_data(MinerData& data)
else { else {
update_block_template(); update_block_template();
} }
m_zmqLastActive = time(nullptr);
} }
const char* BLOCK_FOUND = "\n\ const char* BLOCK_FOUND = "\n\
@@ -286,6 +292,8 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
} }
api_update_network_stats(); api_update_network_stats();
m_zmqLastActive = time(nullptr);
} }
void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce) void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
@@ -370,6 +378,10 @@ void p2pool::submit_block() const
request = "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"submit_block\",\"params\":[\""; request = "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"submit_block\",\"params\":[\"";
const uint32_t template_id = submit_data.template_id;
const uint32_t nonce = submit_data.nonce;
const uint32_t extra_nonce = submit_data.extra_nonce;
for (size_t i = 0; i < submit_data.blob.size(); ++i) { for (size_t i = 0; i < submit_data.blob.size(); ++i) {
char buf[16]; char buf[16];
@@ -389,10 +401,6 @@ void p2pool::submit_block() const
} }
request.append("\"]}"); request.append("\"]}");
const uint32_t template_id = submit_data.template_id;
const uint32_t nonce = submit_data.nonce;
const uint32_t extra_nonce = submit_data.extra_nonce;
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, request.c_str(), JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, request.c_str(),
[height, diff, template_id, nonce, extra_nonce, is_external](const char* data, size_t size) [height, diff, template_id, nonce, extra_nonce, is_external](const char* data, size_t size)
{ {
@@ -1055,6 +1063,8 @@ void p2pool::api_update_stats_mod()
void p2pool::api_update_block_found(const ChainMain* data) void p2pool::api_update_block_found(const ChainMain* data)
{ {
clear_crypto_cache();
if (!m_api) { if (!m_api) {
return; return;
} }
+6
View File
@@ -49,6 +49,8 @@ public:
SideChain& side_chain() { return *m_sideChain; } SideChain& side_chain() { return *m_sideChain; }
const MinerData& miner_data() const { return m_minerData; } const MinerData& miner_data() const { return m_minerData; }
p2pool_api* api() const { return m_api; }
RandomX_Hasher* hasher() const { return m_hasher; } RandomX_Hasher* hasher() const { return m_hasher; }
bool calculate_hash(const void* data, size_t size, const hash& seed, hash& result); bool calculate_hash(const void* data, size_t size, const hash& seed, hash& result);
static uint64_t get_seed_height(uint64_t height); static uint64_t get_seed_height(uint64_t height);
@@ -76,6 +78,8 @@ public:
bool get_difficulty_at_height(uint64_t height, difficulty_type& diff); bool get_difficulty_at_height(uint64_t height, difficulty_type& diff);
time_t zmq_last_active() const { return m_zmqLastActive; }
private: private:
p2pool(const p2pool&) = delete; p2pool(const p2pool&) = delete;
p2pool(p2pool&&) = delete; p2pool(p2pool&&) = delete;
@@ -165,6 +169,8 @@ private:
uv_async_t m_submitBlockAsync; uv_async_t m_submitBlockAsync;
uv_async_t m_blockTemplateAsync; uv_async_t m_blockTemplateAsync;
uv_async_t m_stopAsync; uv_async_t m_stopAsync;
time_t m_zmqLastActive = 0;
}; };
} // namespace p2pool } // namespace p2pool
+7 -1
View File
@@ -28,7 +28,7 @@ static constexpr char log_category_prefix[] = "P2Pool API ";
namespace p2pool { namespace p2pool {
p2pool_api::p2pool_api(const std::string& api_path) : m_apiPath(api_path) p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats): m_apiPath(api_path)
{ {
if (m_apiPath.empty()) { if (m_apiPath.empty()) {
LOGERR(1, "api path is empty"); LOGERR(1, "api path is empty");
@@ -60,9 +60,14 @@ p2pool_api::p2pool_api(const std::string& api_path) : m_apiPath(api_path)
m_networkPath = m_apiPath + "network/"; m_networkPath = m_apiPath + "network/";
m_poolPath = m_apiPath + "pool/"; m_poolPath = m_apiPath + "pool/";
m_localPath = m_apiPath + "local/";
create_dir(m_networkPath); create_dir(m_networkPath);
create_dir(m_poolPath); create_dir(m_poolPath);
if (local_stats) {
create_dir(m_localPath);
}
} }
p2pool_api::~p2pool_api() p2pool_api::~p2pool_api()
@@ -109,6 +114,7 @@ void p2pool_api::dump_to_file_async_internal(const Category& category, const cha
case Category::GLOBAL: path = m_apiPath + filename; break; case Category::GLOBAL: path = m_apiPath + filename; break;
case Category::NETWORK: path = m_networkPath + filename; break; case Category::NETWORK: path = m_networkPath + filename; break;
case Category::POOL: path = m_poolPath + filename; break; case Category::POOL: path = m_poolPath + filename; break;
case Category::LOCAL: path = m_localPath + filename; break;
} }
{ {
+3 -1
View File
@@ -25,13 +25,14 @@ namespace p2pool {
class p2pool_api class p2pool_api
{ {
public: public:
explicit p2pool_api(const std::string& api_path); p2pool_api(const std::string& api_path, const bool local_stats);
~p2pool_api(); ~p2pool_api();
enum class Category { enum class Category {
GLOBAL, GLOBAL,
NETWORK, NETWORK,
POOL, POOL,
LOCAL,
}; };
void on_stop(); void on_stop();
@@ -80,6 +81,7 @@ private:
std::string m_apiPath; std::string m_apiPath;
std::string m_networkPath; std::string m_networkPath;
std::string m_poolPath; std::string m_poolPath;
std::string m_localPath;
uv_mutex_t m_dumpDataLock; uv_mutex_t m_dumpDataLock;
std::unordered_map<std::string, std::vector<char>> m_dumpData; std::unordered_map<std::string, std::vector<char>> m_dumpData;
+8
View File
@@ -69,6 +69,14 @@ Params::Params(int argc, char* argv[])
if ((strcmp(argv[i], "--data-api") == 0) && (i + 1 < argc)) { if ((strcmp(argv[i], "--data-api") == 0) && (i + 1 < argc)) {
m_apiPath = argv[++i]; m_apiPath = argv[++i];
} }
if (strcmp(argv[i], "--stratum-api") == 0) {
m_localStats = true;
}
if (strcmp(argv[i], "--no-cache") == 0) {
m_blockCache = false;
}
} }
if (m_stratumAddresses.empty()) { if (m_stratumAddresses.empty()) {
+2
View File
@@ -37,6 +37,8 @@ struct Params
std::string m_p2pPeerList; std::string m_p2pPeerList;
std::string m_config; std::string m_config;
std::string m_apiPath; std::string m_apiPath;
bool m_localStats = false;
bool m_blockCache = true;
}; };
} // namespace p2pool } // namespace p2pool
+4 -4
View File
@@ -54,7 +54,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
const randomx_flags flags = randomx_get_flags(); const randomx_flags flags = randomx_get_flags();
for (size_t i = 0; i < array_size(m_cache); ++i) { for (size_t i = 0; i < array_size(&RandomX_Hasher::m_cache); ++i) {
m_cache[i] = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES); m_cache[i] = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
if (!m_cache[i]) { if (!m_cache[i]) {
LOGWARN(1, "couldn't allocate RandomX cache using large pages"); LOGWARN(1, "couldn't allocate RandomX cache using large pages");
@@ -70,7 +70,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
uv_rwlock_init_checked(&m_datasetLock); uv_rwlock_init_checked(&m_datasetLock);
uv_rwlock_init_checked(&m_cacheLock); uv_rwlock_init_checked(&m_cacheLock);
for (size_t i = 0; i < array_size(m_vm); ++i) { for (size_t i = 0; i < array_size(&RandomX_Hasher::m_vm); ++i) {
uv_mutex_init_checked(&m_vm[i].mutex); uv_mutex_init_checked(&m_vm[i].mutex);
m_vm[i].vm = nullptr; m_vm[i].vm = nullptr;
} }
@@ -91,7 +91,7 @@ RandomX_Hasher::~RandomX_Hasher()
uv_rwlock_destroy(&m_datasetLock); uv_rwlock_destroy(&m_datasetLock);
uv_rwlock_destroy(&m_cacheLock); uv_rwlock_destroy(&m_cacheLock);
for (size_t i = 0; i < array_size(m_vm); ++i) { for (size_t i = 0; i < array_size(&RandomX_Hasher::m_vm); ++i) {
{ {
MutexLock lock(m_vm[i].mutex); MutexLock lock(m_vm[i].mutex);
if (m_vm[i].vm) { if (m_vm[i].vm) {
@@ -105,7 +105,7 @@ RandomX_Hasher::~RandomX_Hasher()
randomx_release_dataset(m_dataset); randomx_release_dataset(m_dataset);
} }
for (size_t i = 0; i < array_size(m_cache); ++i) { for (size_t i = 0; i < array_size(&RandomX_Hasher::m_cache); ++i) {
if (m_cache[i]) { if (m_cache[i]) {
randomx_release_cache(m_cache[i]); randomx_release_cache(m_cache[i]);
} }
+14 -2
View File
@@ -355,6 +355,12 @@ bool SideChain::block_seen(const PoolBlock& block)
return !m_seenBlocks.insert(block.m_sidechainId).second; return !m_seenBlocks.insert(block.m_sidechainId).second;
} }
void SideChain::unsee_block(const PoolBlock& block)
{
MutexLock lock(m_sidechainLock);
m_seenBlocks.erase(block.m_sidechainId);
}
extern const char* BLOCK_FOUND; extern const char* BLOCK_FOUND;
bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks) bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks)
@@ -407,13 +413,15 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
hash seed; hash seed;
if (!m_pool->get_seed(block.m_txinGenHeight, seed)) { if (!m_pool->get_seed(block.m_txinGenHeight, seed)) {
LOGWARN(3, "add_external_block: couldn't get seed hash for mainchain height " << block.m_txinGenHeight); LOGWARN(3, "add_external_block: couldn't get seed hash for mainchain height " << block.m_txinGenHeight);
unsee_block(block);
return false; return false;
} }
hash pow_hash; hash pow_hash;
if (!block.get_pow_hash(m_pool->hasher(), seed, pow_hash)) { if (!block.get_pow_hash(m_pool->hasher(), seed, pow_hash)) {
LOGWARN(3, "add_external_block: couldn't get PoW hash for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight); LOGWARN(3, "add_external_block: couldn't get PoW hash for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight << ". Ignoring it.");
return false; unsee_block(block);
return true;
} }
// Check if it has the correct parent and difficulty to go right to monerod for checking // Check if it has the correct parent and difficulty to go right to monerod for checking
@@ -1552,6 +1560,10 @@ void SideChain::prune_old_blocks()
if (num_blocks_pruned) { if (num_blocks_pruned) {
LOGINFO(4, "pruned " << num_blocks_pruned << " old blocks at heights <= " << h); LOGINFO(4, "pruned " << num_blocks_pruned << " old blocks at heights <= " << h);
// If side-chain started pruning blocks it means the initial sync is complete
// It's now safe to delete cached blocks
m_pool->p2p_server()->clear_cached_blocks();
} }
} }
+1
View File
@@ -47,6 +47,7 @@ public:
void fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares); void fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares);
bool block_seen(const PoolBlock& block); bool block_seen(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); void add_block(const PoolBlock& block);
void get_missing_blocks(std::vector<hash>& missing_blocks); void get_missing_blocks(std::vector<hash>& missing_blocks);
+132 -44
View File
@@ -21,6 +21,7 @@
#include "p2pool.h" #include "p2pool.h"
#include "side_chain.h" #include "side_chain.h"
#include "params.h" #include "params.h"
#include "p2pool_api.h"
static constexpr char log_category_prefix[] = "StratumServer "; static constexpr char log_category_prefix[] = "StratumServer ";
@@ -48,6 +49,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_apiLastUpdateTime(0)
{ {
m_hashrateData[0] = { time(nullptr), 0 }; m_hashrateData[0] = { time(nullptr), 0 };
@@ -229,7 +231,7 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
job_id = client->m_perConnectionJobId++; job_id = client->m_perConnectionJobId++;
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(client->m_jobs)]; StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
saved_job.job_id = job_id; saved_job.job_id = job_id;
saved_job.extra_nonce = extra_nonce; saved_job.extra_nonce = extra_nonce;
saved_job.template_id = template_id; saved_job.template_id = template_id;
@@ -307,7 +309,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
{ {
MutexLock lock(client->m_jobsLock); MutexLock lock(client->m_jobsLock);
const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(client->m_jobs)]; const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
if (saved_job.job_id == job_id) { if (saved_job.job_id == job_id) {
template_id = saved_job.template_id; template_id = saved_job.template_id;
extra_nonce = saved_job.extra_nonce; extra_nonce = saved_job.extra_nonce;
@@ -317,6 +319,26 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
} }
if (found) { if (found) {
BlockTemplate& block = m_pool->block_template();
difficulty_type mainchain_diff, sidechain_diff;
if (!block.get_difficulties(template_id, mainchain_diff, sidechain_diff)) {
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
return send(client,
[id](void* buf)
{
log::Stream s(reinterpret_cast<char*>(buf));
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Stale share\"}}\n";
return s.m_pos;
});
}
if (mainchain_diff.check_pow(resultHash)) {
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << " found a mainchain block, submitting it");
m_pool->submit_block_async(template_id, nonce, extra_nonce);
block.update_tx_keys();
}
SubmittedShare* share; SubmittedShare* share;
{ {
@@ -334,6 +356,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
share->m_req.data = share; share->m_req.data = share;
share->m_server = this; share->m_server = this;
share->m_client = client; share->m_client = client;
share->m_clientAddr = client->m_addr;
share->m_clientResetCounter = client->m_resetCounter.load(); share->m_clientResetCounter = client->m_resetCounter.load();
share->m_rpcId = client->m_rpcId; share->m_rpcId = client->m_rpcId;
share->m_id = id; share->m_id = id;
@@ -342,6 +365,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
share->m_extraNonce = extra_nonce; share->m_extraNonce = extra_nonce;
share->m_target = target; share->m_target = target;
share->m_resultHash = resultHash; share->m_resultHash = resultHash;
share->m_sidechainDifficulty = sidechain_diff;
const int err = uv_queue_work(&m_loop, &share->m_req, on_share_found, on_after_share_found); const int err = uv_queue_work(&m_loop, &share->m_req, on_share_found, on_after_share_found);
if (err) { if (err) {
@@ -375,7 +399,7 @@ uint64_t StratumServer::get_random64()
void StratumServer::print_status() void StratumServer::print_status()
{ {
update_hashrate_data(0); update_hashrate_data(0, time(nullptr));
print_stratum_status(); print_stratum_status();
} }
@@ -485,7 +509,7 @@ void StratumServer::on_blobs_ready()
job_id = client->m_perConnectionJobId++; job_id = client->m_perConnectionJobId++;
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(client->m_jobs)]; StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
saved_job.job_id = job_id; saved_job.job_id = job_id;
saved_job.extra_nonce = extra_nonce; saved_job.extra_nonce = extra_nonce;
saved_job.template_id = data->m_templateId; saved_job.template_id = data->m_templateId;
@@ -528,12 +552,9 @@ void StratumServer::on_blobs_ready()
LOGINFO(3, "sent new job to " << extra_nonce << '/' << numClientsProcessed << " clients"); LOGINFO(3, "sent new job to " << extra_nonce << '/' << numClientsProcessed << " clients");
} }
void StratumServer::update_hashrate_data(uint64_t target) void StratumServer::update_hashrate_data(uint64_t hashes, time_t timestamp)
{ {
const time_t timestamp = time(nullptr); constexpr size_t N = array_size(&StratumServer::m_hashrateData);
uint64_t rem;
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
WriteLock lock(m_hashrateDataLock); WriteLock lock(m_hashrateDataLock);
@@ -545,20 +566,20 @@ void StratumServer::update_hashrate_data(uint64_t target)
head.m_cumulativeHashes = m_cumulativeHashes; head.m_cumulativeHashes = m_cumulativeHashes;
} }
else { else {
m_hashrateDataHead = (m_hashrateDataHead + 1) % array_size(m_hashrateData); m_hashrateDataHead = (m_hashrateDataHead + 1) % N;
data[m_hashrateDataHead] = { timestamp, m_cumulativeHashes }; data[m_hashrateDataHead] = { timestamp, m_cumulativeHashes };
} }
while (data[m_hashrateDataTail_15m].m_timestamp + 15 * 60 < timestamp) { while (data[m_hashrateDataTail_15m].m_timestamp + 15 * 60 < timestamp) {
m_hashrateDataTail_15m = (m_hashrateDataTail_15m + 1) % array_size(m_hashrateData); m_hashrateDataTail_15m = (m_hashrateDataTail_15m + 1) % N;
} }
while (data[m_hashrateDataTail_1h].m_timestamp + 60 * 60 < timestamp) { while (data[m_hashrateDataTail_1h].m_timestamp + 60 * 60 < timestamp) {
m_hashrateDataTail_1h = (m_hashrateDataTail_1h + 1) % array_size(m_hashrateData); m_hashrateDataTail_1h = (m_hashrateDataTail_1h + 1) % N;
} }
while (data[m_hashrateDataTail_24h].m_timestamp + 60 * 60 * 24 < timestamp) { while (data[m_hashrateDataTail_24h].m_timestamp + 60 * 60 * 24 < timestamp) {
m_hashrateDataTail_24h = (m_hashrateDataTail_24h + 1) % array_size(m_hashrateData); m_hashrateDataTail_24h = (m_hashrateDataTail_24h + 1) % N;
} }
} }
@@ -570,35 +591,34 @@ void StratumServer::on_share_found(uv_work_t* req)
StratumClient* client = share->m_client; StratumClient* client = share->m_client;
StratumServer* server = share->m_server; StratumServer* server = share->m_server;
p2pool* pool = server->m_pool; p2pool* pool = server->m_pool;
BlockTemplate& block = pool->block_template();
uint64_t target = share->m_target; uint64_t target = share->m_target;
if (target >= TARGET_4_BYTES_LIMIT) { if (target >= TARGET_4_BYTES_LIMIT) {
target = (target >> 32) << 32; target = (target >> 32) << 32;
} }
uint64_t rem;
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
if (pool->stopped()) { if (pool->stopped()) {
LOGWARN(0, "p2pool is shutting down, but a share was found. Trying to process it anyway!"); LOGWARN(0, "p2pool is shutting down, but a share was found. Trying to process it anyway!");
} }
uint8_t blob[128]; if (share->m_sidechainDifficulty.check_pow(share->m_resultHash)) {
uint64_t height; uint8_t blob[128];
difficulty_type difficulty; uint64_t height;
difficulty_type sidechain_difficulty; difficulty_type difficulty;
hash seed_hash; difficulty_type sidechain_difficulty;
size_t nonce_offset; hash seed_hash;
size_t nonce_offset;
const uint32_t blob_size = block.get_hashing_blob(share->m_templateId, share->m_extraNonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset); const uint32_t blob_size = pool->block_template().get_hashing_blob(share->m_templateId, share->m_extraNonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset);
if (!blob_size) { if (!blob_size) {
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");
share->m_result = SubmittedShare::Result::STALE; share->m_result = SubmittedShare::Result::STALE;
return; return;
} }
const bool mainchain_solution = difficulty.check_pow(share->m_resultHash);
const bool sidechain_solution = sidechain_difficulty.check_pow(share->m_resultHash);
if (mainchain_solution || sidechain_solution) {
for (uint32_t i = 0, nonce = share->m_nonce; i < sizeof(share->m_nonce); ++i) { for (uint32_t i = 0, nonce = share->m_nonce; i < sizeof(share->m_nonce); ++i) {
blob[nonce_offset + i] = nonce & 255; blob[nonce_offset + i] = nonce & 255;
nonce >>= 8; nonce >>= 8;
@@ -617,9 +637,6 @@ void StratumServer::on_share_found(uv_work_t* req)
return; return;
} }
uint64_t rem;
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
const uint64_t n = server->m_cumulativeHashes + hashes; const 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; const double effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
@@ -629,22 +646,16 @@ void StratumServer::on_share_found(uv_work_t* req)
++server->m_totalFoundShares; ++server->m_totalFoundShares;
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", effort " << effort << '%'); LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", effort " << effort << '%');
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
if (mainchain_solution) {
pool->submit_block_async(share->m_templateId, share->m_nonce, share->m_extraNonce);
block.update_tx_keys();
}
if (sidechain_solution) {
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
}
} }
// Send the response to miner // Send the response to miner
const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t)); const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t));
if (LIKELY(value < target)) { if (LIKELY(value < target)) {
server->update_hashrate_data(target); const time_t timestamp = time(nullptr);
server->update_hashrate_data(hashes, timestamp);
server->api_update_local_stats(timestamp);
share->m_result = SubmittedShare::Result::OK; share->m_result = SubmittedShare::Result::OK;
} }
else { else {
@@ -670,6 +681,8 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
StratumClient* client = share->m_client; 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);
if ((client->m_resetCounter.load() == share->m_clientResetCounter) && (client->m_rpcId == share->m_rpcId)) { if ((client->m_resetCounter.load() == share->m_clientResetCounter) && (client->m_rpcId == share->m_rpcId)) {
const bool result = server->send(client, const bool result = server->send(client,
[share](void* buf) [share](void* buf)
@@ -695,7 +708,7 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
return s.m_pos; return s.m_pos;
}); });
if ((share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW)) { if (bad_share) {
client->ban(DEFAULT_BAN_TIME); client->ban(DEFAULT_BAN_TIME);
client->close(); client->close();
} }
@@ -703,6 +716,9 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
client->close(); client->close();
} }
} }
else if (bad_share) {
server->ban(share->m_clientAddr, DEFAULT_BAN_TIME);
}
} }
StratumServer::StratumClient::StratumClient() StratumServer::StratumClient::StratumClient()
@@ -914,4 +930,76 @@ bool StratumServer::StratumClient::process_submit(rapidjson::Document& doc, uint
return static_cast<StratumServer*>(m_owner)->on_submit(this, id, job_id.GetString(), nonce.GetString(), result.GetString()); return static_cast<StratumServer*>(m_owner)->on_submit(this, id, job_id.GetString(), nonce.GetString(), result.GetString());
} }
void StratumServer::api_update_local_stats(time_t timestamp)
{
if (!m_pool->api() || !m_pool->params().m_localStats) {
return;
}
// Rate limit to no more than once in 60 seconds.
if (timestamp < m_apiLastUpdateTime + 60) {
return;
}
m_apiLastUpdateTime = timestamp;
uint64_t hashes_15m, hashes_1h, hashes_24h, total_hashes;
int64_t dt_15m, dt_1h, dt_24h;
uint64_t hashes_since_last_share;
{
ReadLock lock(m_hashrateDataLock);
total_hashes = m_cumulativeHashes;
hashes_since_last_share = m_cumulativeHashes - m_cumulativeHashesAtLastShare;
const HashrateData* data = m_hashrateData;
const HashrateData& head = data[m_hashrateDataHead];
const HashrateData& tail_15m = data[m_hashrateDataTail_15m];
const HashrateData& tail_1h = data[m_hashrateDataTail_1h];
const HashrateData& tail_24h = data[m_hashrateDataTail_24h];
hashes_15m = head.m_cumulativeHashes - tail_15m.m_cumulativeHashes;
dt_15m = static_cast<int64_t>(head.m_timestamp - tail_15m.m_timestamp);
hashes_1h = head.m_cumulativeHashes - tail_1h.m_cumulativeHashes;
dt_1h = static_cast<int64_t>(head.m_timestamp - tail_1h.m_timestamp);
hashes_24h = head.m_cumulativeHashes - tail_24h.m_cumulativeHashes;
dt_24h = static_cast<int64_t>(head.m_timestamp - tail_24h.m_timestamp);
}
const uint64_t hashrate_15m = (dt_15m > 0) ? (hashes_15m / dt_15m) : 0;
const uint64_t hashrate_1h = (dt_1h > 0) ? (hashes_1h / dt_1h ) : 0;
const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0;
double average_effort = 0.0;
if (m_cumulativeFoundSharesDiff > 0.0) {
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / m_cumulativeFoundSharesDiff;
}
int shares_found = m_totalFoundShares;
double current_effort = static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double();
int connections = m_numConnections;
int incoming_connections = m_numIncomingConnections;
m_pool->api()->set(p2pool_api::Category::LOCAL, "stats",
[hashrate_15m, hashrate_1h, hashrate_24h, total_hashes, shares_found, average_effort, current_effort, connections, incoming_connections](log::Stream& s)
{
s << "{\"hashrate_15m\":" << hashrate_15m
<< ",\"hashrate_1h\":" << hashrate_1h
<< ",\"hashrate_24h\":" << hashrate_24h
<< ",\"total_hashes\":" << total_hashes
<< ",\"shares_found\":" << shares_found
<< ",\"average_effort\":" << average_effort
<< ",\"current_effort\":" << current_effort
<< ",\"connections\":" << connections
<< ",\"incoming_connections\":" << incoming_connections
<< "}";
});
}
} // namespace p2pool } // namespace p2pool
+6 -1
View File
@@ -112,6 +112,7 @@ private:
uv_work_t m_req; uv_work_t m_req;
StratumServer* m_server; StratumServer* m_server;
StratumClient* m_client; StratumClient* m_client;
raw_ip m_clientAddr;
uint32_t m_clientResetCounter; uint32_t m_clientResetCounter;
uint32_t m_rpcId; uint32_t m_rpcId;
uint32_t m_id; uint32_t m_id;
@@ -120,6 +121,7 @@ private:
uint32_t m_extraNonce; uint32_t m_extraNonce;
uint64_t m_target; uint64_t m_target;
hash m_resultHash; hash m_resultHash;
difficulty_type m_sidechainDifficulty;
enum class Result { enum class Result {
STALE, STALE,
@@ -152,7 +154,10 @@ private:
double m_cumulativeFoundSharesDiff; double m_cumulativeFoundSharesDiff;
uint32_t m_totalFoundShares; uint32_t m_totalFoundShares;
void update_hashrate_data(uint64_t target); time_t m_apiLastUpdateTime;
void update_hashrate_data(uint64_t hashes, time_t timestamp);
void api_update_local_stats(time_t timestamp);
}; };
} // namespace p2pool } // namespace p2pool
+1 -1
View File
@@ -101,7 +101,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::parse_address_list(const std::str
} }
const int port = atoi(address.substr(k2 + 1).c_str()); const int port = atoi(address.substr(k2 + 1).c_str());
if ((port > 0) && (port < 655356)) { if ((port > 0) && (port < 65536)) {
callback(is_v6, address, ip, port); callback(is_v6, address, ip, port);
} }
else { else {
+17 -1
View File
@@ -29,6 +29,22 @@ static constexpr char log_category_prefix[] = "Util ";
namespace p2pool { namespace p2pool {
#define STR2(X) STR(X)
#define STR(X) #X
const char* VERSION = "v1.2 (built"
#if defined(__clang__)
" with clang/" __clang_version__
#elif defined(__GNUC__)
" with GCC/" STR2(__GNUC__) "." STR2(__GNUC_MINOR__) "." STR2(__GNUC_PATCHLEVEL__)
#elif defined(_MSC_VER)
" with MSVC/" STR2(_MSC_VER)
#endif
" on " __DATE__ ")";
#undef STR2
#undef STR
MinerCallbackHandler::~MinerCallbackHandler() {} MinerCallbackHandler::~MinerCallbackHandler() {}
void panic() void panic()
@@ -40,7 +56,7 @@ void panic()
__debugbreak(); __debugbreak();
} }
#endif #endif
exit(1); abort();
} while (true); } while (true);
} }
+5 -2
View File
@@ -19,6 +19,8 @@
namespace p2pool { namespace p2pool {
extern const char* VERSION;
template<typename T> struct not_implemented { enum { value = 0 }; }; template<typename T> struct not_implemented { enum { value = 0 }; };
struct nocopy_nomove struct nocopy_nomove
@@ -93,6 +95,7 @@ FORCEINLINE void writeVarint(T value, std::vector<uint8_t>& out)
} }
template<typename T, size_t N> FORCEINLINE constexpr size_t array_size(T(&)[N]) { return N; } template<typename T, size_t N> FORCEINLINE constexpr size_t array_size(T(&)[N]) { return N; }
template<typename T, typename U, size_t N> FORCEINLINE constexpr size_t array_size(T(U::*)[N]) { return N; }
[[noreturn]] void panic(); [[noreturn]] void panic();
@@ -126,7 +129,7 @@ namespace std {
template<> template<>
struct hash<p2pool::hash> struct hash<p2pool::hash>
{ {
FORCEINLINE size_t operator()(const p2pool::hash& value) const FORCEINLINE size_t operator()(const p2pool::hash& value) const noexcept
{ {
uint64_t result = 0xcbf29ce484222325ull; uint64_t result = 0xcbf29ce484222325ull;
for (size_t i = 0; i < p2pool::HASH_SIZE; ++i) { for (size_t i = 0; i < p2pool::HASH_SIZE; ++i) {
@@ -139,7 +142,7 @@ struct hash<p2pool::hash>
template<size_t N> template<size_t N>
struct hash<std::array<uint8_t, N>> struct hash<std::array<uint8_t, N>>
{ {
FORCEINLINE size_t operator()(const std::array<uint8_t, N>& value) const FORCEINLINE size_t operator()(const std::array<uint8_t, N>& value) const noexcept
{ {
uint64_t result = 0xcbf29ce484222325ull; uint64_t result = 0xcbf29ce484222325ull;
for (size_t i = 0; i < N; ++i) { for (size_t i = 0; i < N; ++i) {
+38 -2
View File
@@ -82,10 +82,14 @@ void ZMQReader::run()
char addr[32]; char addr[32];
snprintf(addr, sizeof(addr), "tcp://%s:%u", m_address, m_zmqPort); snprintf(addr, sizeof(addr), "tcp://%s:%u", m_address, m_zmqPort);
m_subscriber.connect(addr); if (!connect(addr, m_zmqPort)) {
throw zmq::error_t();
}
snprintf(addr, sizeof(addr), "tcp://127.0.0.1:%u", m_publisherPort); snprintf(addr, sizeof(addr), "tcp://127.0.0.1:%u", m_publisherPort);
m_subscriber.connect(addr); if (!connect(addr, m_publisherPort)) {
throw zmq::error_t();
}
m_subscriber.set(zmq::sockopt::subscribe, "json-full-chain_main"); m_subscriber.set(zmq::sockopt::subscribe, "json-full-chain_main");
m_subscriber.set(zmq::sockopt::subscribe, "json-full-miner_data"); m_subscriber.set(zmq::sockopt::subscribe, "json-full-miner_data");
@@ -121,6 +125,38 @@ void ZMQReader::run()
LOGINFO(1, "worker thread stopped"); LOGINFO(1, "worker thread stopped");
} }
bool ZMQReader::connect(const char* address, uint32_t id)
{
struct ConnectMonitor : public zmq::monitor_t
{
void on_event_connected(const zmq_event_t&, const char* address) ZMQ_OVERRIDE
{
LOGINFO(1, "connected to " << address);
connected = true;
}
bool connected = false;
} monitor;
char buf[32];
snprintf(buf, sizeof(buf), "inproc://connect-mon-%u", id);
monitor.init(m_subscriber, buf);
m_subscriber.connect(address);
using namespace std::chrono;
const system_clock::time_point start_time = system_clock::now();
while (!monitor.connected && monitor.check_event(-1)) {
const int64_t elapsed_time = duration_cast<milliseconds>(system_clock::now() - start_time).count();
if (elapsed_time >= 3000) {
LOGERR(1, "failed to connect to " << address);
return false;
}
}
return true;
}
void ZMQReader::parse(char* data, size_t size) void ZMQReader::parse(char* data, size_t size)
{ {
char* value = data; char* value = data;
+1
View File
@@ -30,6 +30,7 @@ public:
private: private:
static void run_wrapper(void* arg) { reinterpret_cast<ZMQReader*>(arg)->run(); } static void run_wrapper(void* arg) { reinterpret_cast<ZMQReader*>(arg)->run(); }
void run(); void run();
bool connect(const char* address, uint32_t id);
void parse(char* data, size_t size); void parse(char* data, size_t size);
+4
View File
@@ -25,6 +25,8 @@ namespace p2pool {
TEST(crypto, derivation) TEST(crypto, derivation)
{ {
init_crypto_cache();
// Run the tests twice to check how crypto cache works // Run the tests twice to check how crypto cache works
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
std::ifstream f("crypto_tests.txt"); std::ifstream f("crypto_tests.txt");
@@ -61,6 +63,8 @@ TEST(crypto, derivation)
} }
} while (!f.eof()); } while (!f.eof());
} }
destroy_crypto_cache();
} }
} }