Compare commits

...

18 Commits

Author SHA1 Message Date
SChernykh 6cf1b13500 p2pool v2.5
C/C++ CI / build-ubuntu (map[c:gcc-12 cpp:g++-12 os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-static-libs (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (map[os:ubuntu-18.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (map[os:ubuntu-20.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (map[os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-windows-msys2 (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:ON vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\amd64\ os:2022 rx:OFF vs:Visual Studio 17 2022]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\amd64\ os:2022 rx:ON vs:Visual Studio 17 2022]) (push) Has been cancelled
C/C++ CI / build-macos (macos-11) (push) Has been cancelled
C/C++ CI / build-macos (macos-12) (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-11 cpp:g++-11 os:ubuntu-20.04]) (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
cppcheck / cppcheck-ubuntu (push) Has been cancelled
cppcheck / cppcheck-windows (push) Has been cancelled
Microsoft C++ Code Analysis / Analyze (push) Has been cancelled
Sync test / sync-test-macos (push) Has been cancelled
Sync test / sync-test-windows (push) Has been cancelled
Sync test / sync-test-ubuntu (push) Has been cancelled
2022-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
SChernykh 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
34 changed files with 583 additions and 333 deletions
+12 -12
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 }}
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: true
@@ -44,7 +44,7 @@ jobs:
run: cd tests/build && ./p2pool_tests
- name: Archive binary
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool-${{ matrix.config.os }}
path: build/p2pool
@@ -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
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: true
@@ -108,7 +108,7 @@ jobs:
run: cd tests/build && ./p2pool_tests
- name: Archive binary
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool-ubuntu-static-libs
path: build/p2pool
@@ -131,7 +131,7 @@ jobs:
sudo apt install -y git build-essential cmake autoconf gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: true
@@ -166,7 +166,7 @@ jobs:
make -j$(nproc)
- name: Archive binary
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool-${{ matrix.config.os }}-aarch64
path: build/p2pool
@@ -181,7 +181,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: recursive
@@ -235,7 +235,7 @@ jobs:
./p2pool_tests.exe
- name: Archive binary
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool-msys2.exe
path: build/p2pool.exe
@@ -253,7 +253,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: recursive
@@ -281,7 +281,7 @@ jobs:
./p2pool_tests.exe
- name: Archive binary
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}.exe
path: build/Release/p2pool.exe
@@ -296,7 +296,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: recursive
@@ -347,7 +347,7 @@ jobs:
./p2pool_tests
- name: Archive binary
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool-${{ matrix.os }}
path: build/p2pool
+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
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: true
+2 -2
View File
@@ -9,7 +9,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: true
@@ -35,7 +35,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: true
+3 -3
View File
@@ -26,7 +26,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: true
@@ -38,7 +38,7 @@ jobs:
# run: cmake --build ${{ env.build }}
- 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
id: run-analysis
with:
@@ -55,7 +55,7 @@ jobs:
# Upload SARIF file as an Artifact to download and view
- name: Upload SARIF as an Artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: sarif-file
path: ${{ steps.run-analysis.outputs.sarif }}
+6 -6
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
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: true
@@ -33,7 +33,7 @@ jobs:
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 6
- name: Archive p2pool.log
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool_ubuntu.log
path: build/p2pool.log
@@ -44,7 +44,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: recursive
@@ -65,7 +65,7 @@ jobs:
./p2pool --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 6
- name: Archive p2pool.log
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool_macos.log
path: build/p2pool.log
@@ -76,7 +76,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: recursive
@@ -97,7 +97,7 @@ jobs:
./p2pool.exe --host p2pmd.xmrvsbeast.com --zmq-port 18084 --wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg --no-cache --loglevel 6
- name: Archive p2pool.log
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: p2pool_windows.log
path: build/Debug/p2pool.log
+1 -1
View File
@@ -88,7 +88,7 @@ if (NOT STATIC_BINARY AND NOT STATIC_LIBS)
endif()
if (CURL_INCLUDE_DIRS)
include_directories(CURL_INCLUDE_DIRS)
include_directories(${CURL_INCLUDE_DIRS})
else()
include_directories(external/src/curl/include)
endif()
+20 -14
View File
@@ -76,13 +76,13 @@ Monero network upgrade happened on August 13th, 2022 (block 2,688,888). In order
### General Considerations
- In order to mine on P2Pool, a synced Monero node using monerod v0.18.0.0 or newer is required. If you do not currently have one configured, you can find instructions to do so [here](https://sethforprivacy.com/guides/run-a-monero-node-advanced/).
- It is highly recommended that you create a separate restricted user account for mining. While P2Pool has been battle-tested for a long time now, any software may have unknown bugs/vulnerabilities.
- In order to mine on P2Pool, a synced Monero node using monerod v0.18.0.0 or newer is required. If you don't currently have one, you can download the official Monero binaries, start `monerod` on your PC and wait until it's fully synced. Advanced Monero node setup instructions are [here](https://sethforprivacy.com/guides/run-a-monero-node-advanced/).
- It is highly recommended that you create a separate restricted user account (in your OS) for mining. While P2Pool has been battle-tested for a long time now, any software may have unknown bugs/vulnerabilities.
- You have to use a primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
- Starting from P2Pool v1.7, you can add the `--mini` parameter to your P2Pool command to connect to the **p2pool-mini** sidechain. Note that it will also change the default p2p port from 37889 to 37888.
- You can add the `--mini` parameter to your P2Pool command to connect to the **p2pool-mini** sidechain. Note that it will also change the default p2p port from 37889 to 37888.
- Check that ports 18080 (Monero p2p port) and 37889/37888 (P2Pool/P2Pool mini p2p port) are open in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine.
- You can connect multiple miners to the same P2Pool node. The more the better!
- The below steps assume that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup.
- The steps below assume that you run everything on the same machine. If it's not the case, change `127.0.0.1` to appropriate IP addresses for your setup.
- It is highly recommended to create a new mainnet wallet for P2Pool mining because **wallet addresses are public on P2Pool**.
**Wallet software compatible with P2Pool payouts**
@@ -93,6 +93,10 @@ Monero network upgrade happened on August 13th, 2022 (block 2,688,888). In order
- [Feather Wallet v2.1.0 and newer](https://featherwallet.org/)
- [MyMonero](https://mymonero.com/)
### GUI for P2Pool
- [Gupax](https://github.com/hinto-janaiyo/gupax) project (currently in development) aims to provide an easy to use GUI to configure and run monerod/p2pool/xmrig
### GNU/Linux
1. Download the latest P2Pool binaries [here](https://github.com/SChernykh/p2pool/releases/latest).
@@ -156,21 +160,23 @@ nocreate
5. Prepare huge pages to work properly (each instance of monerod/P2Pool/XMRig needs them):
- On Windows 10 or above, run XMRig at least once as Administrator (right-click Run As Administrator)
- On earlier versions of Windows, you'll need to run XMRig as Administrator at least once per login.
6. Open a command prompt and navigate to the folder where you extracted P2Pool.
6. Create "Monero" folder inside extracted P2Pool folder and copy Monero binaries there.
7. Open a command prompt and navigate to the folder where you extracted P2Pool.
**Note:** *When running the below commands, Windows Firewall may prompt to allow connections, click "Allow" if prompted.*
7. Start `monerod` with the following command/options:
8. Start `monerod` with the following command/options:
```
.\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
```
**Note:** The `--zmq-pub` option is required for P2Pool to work properly.
8. Start P2Pool with the following command/options:
9. Start P2Pool with the following command/options:
```
.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS
.\p2pool.exe --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS --mini
```
9. Wait until the initial P2Pool sync is finished (shouldn't take more than 5-10 minutes).
10. Start XMRig with the following command/options:
10. Wait until the initial P2Pool sync is finished (shouldn't take more than 5-10 minutes).
11. Start XMRig with the following command/options:
```
.\xmrig.exe -o 127.0.0.1:3333
```
@@ -179,17 +185,17 @@ nocreate
```
xmrig.exe -u x+10000 -o 127.0.0.1:3333
```
11. XMRig should connect and start mining!
12. *(Optional but highly recommended)* You can create a Quickstart by creating a batch (.bat) file with the following contents and placing it in your P2Pool directory along with `xmrig.exe`.
12. XMRig should connect and start mining!
13. *(Optional but highly recommended)* You can create a Quickstart by creating a batch (.bat) file with the following contents and placing it in your P2Pool directory along with `xmrig.exe`.
```
@ECHO OFF
start cmd /k %~dp0\Monero\monerod.exe --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist
ECHO Wait until the Monero daemon shows fully synced before continuing. This can take some time. Type 'status' in other window to check progress.
PAUSE
start cmd /k %~dp0\p2pool.exe --wallet YOUR_WALLET_ADDRESS
start cmd /k %~dp0\p2pool.exe --wallet YOUR_WALLET_ADDRESS --mini
ECHO Wait until the daemon shows fully synced before continuing. This can take some time.
PAUSE
%~dp0\xmrig.exe -u x+30000 -o 127.0.0.1
%~dp0\xmrig.exe -o 127.0.0.1
```
## Build instructions
+1 -1
View File
@@ -26,7 +26,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
endif()
endif()
elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
set(GENERAL_FLAGS "")
set(GENERAL_FLAGS "/MP")
set(WARNING_FLAGS "/Wall /WX /sdl")
set(SECURITY_FLAGS "/GS /guard:cf")
set(OPTIMIZATION_FLAGS "/O2 /Oi /Ob2 /Ot /DNDEBUG /GL")
+1
View File
@@ -25,6 +25,7 @@
--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
+7 -4
View File
@@ -165,8 +165,11 @@ BlockCache::~BlockCache()
void BlockCache::store(const PoolBlock& block)
{
const size_t n1 = block.m_mainChainData.size();
const size_t n2 = block.m_sideChainData.size();
const std::vector<uint8_t> mainchain_data = block.serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = block.serialize_sidechain_data();
const size_t n1 = mainchain_data.size();
const size_t n2 = sidechain_data.size();
if (!m_impl->m_data || (sizeof(uint32_t) + n1 + n2 > BLOCK_SIZE)) {
return;
@@ -175,8 +178,8 @@ void BlockCache::store(const PoolBlock& block)
uint8_t* data = m_impl->m_data + (static_cast<size_t>((m_storeIndex++) % NUM_BLOCKS) * BLOCK_SIZE);
*reinterpret_cast<uint32_t*>(data) = static_cast<uint32_t>(n1 + n2);
memcpy(data + sizeof(uint32_t), block.m_mainChainData.data(), n1);
memcpy(data + sizeof(uint32_t) + n1, block.m_sideChainData.data(), n2);
memcpy(data + sizeof(uint32_t), mainchain_data.data(), n1);
memcpy(data + sizeof(uint32_t) + n1, sidechain_data.data(), n2);
}
void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
+43 -22
View File
@@ -54,7 +54,11 @@ BlockTemplate::BlockTemplate(p2pool* pool)
, m_txkeySec{}
, m_poolBlockTemplate(new PoolBlock())
, m_finalReward(0)
, m_rng(RandomDeviceSeed::instance)
{
// Diffuse the initial state in case it has low quality
m_rng.discard(10000);
uv_rwlock_init_checked(&m_lock);
m_blockHeader.reserve(64);
@@ -133,6 +137,8 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
m_mempoolTxsOrder.clear();
m_shares.clear();
m_rng = b.m_rng;
#if TEST_MEMPOOL_PICKING_ALGORITHM
m_knapsack.clear();
#endif
@@ -171,6 +177,14 @@ static FORCEINLINE uint64_t get_block_reward(uint64_t base_reward, uint64_t medi
return reward + fees;
}
void BlockTemplate::shuffle_tx_order()
{
const int64_t n = static_cast<int64_t>(m_mempoolTxsOrder.size());
for (int64_t i = n - 1; i > 0; --i) {
std::swap(m_mempoolTxsOrder[i], m_mempoolTxsOrder[m_rng() % (i + 1)]);
}
}
void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet)
{
if (data.major_version > HARDFORK_SUPPORTED_VERSION) {
@@ -315,16 +329,16 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
// if a block doesn't get into the penalty zone, just pick all transactions
if (total_tx_weight + miner_tx_weight <= data.median_weight) {
m_numTransactionHashes = 0;
final_fees = 0;
final_weight = miner_tx_weight;
m_transactionHashes.assign(HASH_SIZE, 0);
for (const TxMempoolData& tx : m_mempoolTxs) {
m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
++m_numTransactionHashes;
shuffle_tx_order();
m_numTransactionHashes = m_mempoolTxsOrder.size();
m_transactionHashes.assign(HASH_SIZE, 0);
for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
const TxMempoolData& tx = m_mempoolTxs[m_mempoolTxsOrder[i]];
m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
final_fees += tx.fee;
final_weight += tx.weight;
}
@@ -398,6 +412,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
final_fees = 0;
final_weight = miner_tx_weight;
shuffle_tx_order();
m_numTransactionHashes = m_mempoolTxsOrder.size();
m_transactionHashes.assign(HASH_SIZE, 0);
for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
@@ -528,15 +544,16 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
m_poolBlockTemplate->serialize_mainchain_data(0, 0, m_poolBlockTemplate->m_sidechainId);
#if POOL_BLOCK_DEBUG
if (m_poolBlockTemplate->m_mainChainData != m_blockTemplateBlob) {
const std::vector<uint8_t> mainchain_data = m_poolBlockTemplate->serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
if (mainchain_data != m_blockTemplateBlob) {
LOGERR(1, "serialize_mainchain_data() has a bug, fix it! ");
LOGERR(1, "m_poolBlockTemplate->m_mainChainData.size() = " << m_poolBlockTemplate->m_mainChainData.size());
LOGERR(1, "m_poolBlockTemplate->m_mainChainData.size() = " << mainchain_data.size());
LOGERR(1, "m_blockTemplateBlob.size() = " << m_blockTemplateBlob.size());
for (size_t i = 0, n = std::min(m_poolBlockTemplate->m_mainChainData.size(), m_blockTemplateBlob.size()); i < n; ++i) {
if (m_poolBlockTemplate->m_mainChainData[i] != m_blockTemplateBlob[i]) {
for (size_t i = 0, n = std::min(mainchain_data.size(), m_blockTemplateBlob.size()); i < n; ++i) {
if (mainchain_data[i] != m_blockTemplateBlob[i]) {
LOGERR(1, "m_poolBlockTemplate->m_mainChainData is different at offset " << i);
break;
}
@@ -545,7 +562,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
{
std::vector<uint8_t> buf = m_blockTemplateBlob;
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end());
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain(), nullptr);
@@ -712,7 +729,7 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
LOGERR(1, "get_eph_public_key failed at index " << i);
}
m_minerTx.insert(m_minerTx.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key, tx_type, view_tag);
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key, view_tag);
}
if (tx_type == TXOUT_TO_TAGGED_KEY) {
@@ -784,8 +801,9 @@ hash BlockTemplate::calc_sidechain_hash() const
const int blob_size = static_cast<int>(m_blockTemplateBlob.size());
const std::vector<uint8_t>& consensus_id = m_pool->side_chain().consensus_id();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
keccak_custom([this, sidechain_hash_offset, blob_size, consensus_id](int offset) -> uint8_t {
keccak_custom([this, sidechain_hash_offset, blob_size, consensus_id, &sidechain_data](int offset) -> uint8_t {
uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(m_nonceOffset));
if (k < NONCE_SIZE) {
return 0;
@@ -806,15 +824,15 @@ hash BlockTemplate::calc_sidechain_hash() const
}
const int side_chain_data_offsset = offset - blob_size;
const int side_chain_data_size = static_cast<int>(m_poolBlockTemplate->m_sideChainData.size());
const int side_chain_data_size = static_cast<int>(sidechain_data.size());
if (side_chain_data_offsset < side_chain_data_size) {
return m_poolBlockTemplate->m_sideChainData[side_chain_data_offsset];
return sidechain_data[side_chain_data_offsset];
}
const int consensus_id_offset = side_chain_data_offsset - side_chain_data_size;
return consensus_id[consensus_id_offset];
},
static_cast<int>(m_blockTemplateBlob.size() + m_poolBlockTemplate->m_sideChainData.size() + consensus_id.size()), sidechain_hash.h, HASH_SIZE);
static_cast<int>(m_blockTemplateBlob.size() + sidechain_data.size() + consensus_id.size()), sidechain_hash.h, HASH_SIZE);
return sidechain_hash;
}
@@ -1067,15 +1085,18 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
if (template_id == m_templateId) {
m_poolBlockTemplate->m_nonce = nonce;
m_poolBlockTemplate->m_extraNonce = extra_nonce;
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_nonceOffset, &nonce, NONCE_SIZE);
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_extraNonceOffsetInTemplate, &extra_nonce, EXTRA_NONCE_SIZE);
SideChain& side_chain = m_pool->side_chain();
#if POOL_BLOCK_DEBUG
{
std::vector<uint8_t> buf = m_poolBlockTemplate->m_mainChainData;
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end());
std::vector<uint8_t> buf = m_poolBlockTemplate->serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
memcpy(buf.data() + m_nonceOffset, &nonce, NONCE_SIZE);
memcpy(buf.data() + m_extraNonceOffsetInTemplate, &extra_nonce, EXTRA_NONCE_SIZE);
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), side_chain, nullptr);
+4
View File
@@ -106,6 +106,10 @@ private:
std::vector<int> m_mempoolTxsOrder;
std::vector<MinerShare> m_shares;
std::mt19937_64 m_rng;
void shuffle_tx_order();
#if TEST_MEMPOOL_PICKING_ALGORITHM
void fill_optimal_knapsack(const MinerData& data, uint64_t base_reward, uint64_t miner_tx_weight, uint64_t& best_reward, uint64_t& final_fees, uint64_t& final_weight);
+8
View File
@@ -81,6 +81,14 @@
#endif
#ifndef __has_feature
#define __has_feature(x) 0
#endif
#if defined(_DEBUG) || defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) || defined(__SANITIZE_THREAD__) || __has_feature(thread_sanitizer)
#define P2POOL_DEBUGGING 1
#endif
namespace p2pool {
constexpr size_t HASH_SIZE = 32;
+42 -17
View File
@@ -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);
}
class Cache
class Cache : public nocopy_nomove
{
public:
Cache()
: derivations(new DerivationsMap())
, public_keys(new PublicKeysMap())
, tx_keys(new TxKeysMap())
{
uv_rwlock_init_checked(&derivations_lock);
uv_rwlock_init_checked(&public_keys_lock);
@@ -166,6 +169,10 @@ public:
~Cache()
{
delete derivations;
delete public_keys;
delete tx_keys;
uv_rwlock_destroy(&derivations_lock);
uv_rwlock_destroy(&public_keys_lock);
uv_rwlock_destroy(&tx_keys_lock);
@@ -180,8 +187,8 @@ public:
derivation = {};
{
ReadLock lock(derivations_lock);
auto it = derivations.find(index);
if (it != derivations.end()) {
auto it = derivations->find(index);
if (it != derivations->end()) {
const DerivationEntry& entry = it->second;
derivation = entry.m_derivation;
if (entry.find_view_tag(output_index, view_tag)) {
@@ -210,7 +217,7 @@ public:
{
WriteLock lock(derivations_lock);
DerivationEntry& entry = derivations.emplace(index, DerivationEntry{ derivation, {} }).first->second;
DerivationEntry& entry = derivations->emplace(index, DerivationEntry{ derivation, {} }).first->second;
const uint32_t k = static_cast<uint32_t>(output_index << 8) | view_tag;
if (std::find(entry.m_viewTags.begin(), entry.m_viewTags.end(), k) == entry.m_viewTags.end()) {
@@ -230,8 +237,8 @@ public:
{
ReadLock lock(public_keys_lock);
auto it = public_keys.find(index);
if (it != public_keys.end()) {
auto it = public_keys->find(index);
if (it != public_keys->end()) {
derived_key = it->second;
return true;
}
@@ -257,7 +264,7 @@ public:
{
WriteLock lock(public_keys_lock);
public_keys.emplace(index, derived_key);
public_keys->emplace(index, derived_key);
}
return true;
@@ -271,8 +278,8 @@ public:
{
ReadLock lock(tx_keys_lock);
auto it = tx_keys.find(index);
if (it != tx_keys.end()) {
auto it = tx_keys->find(index);
if (it != tx_keys->end()) {
pub = it->second.first;
sec = it->second.second;
return;
@@ -291,15 +298,27 @@ public:
{
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()
{
{ WriteLock lock(derivations_lock); derivations.clear(); }
{ WriteLock lock(public_keys_lock); public_keys.clear(); }
{ WriteLock lock(tx_keys_lock); tx_keys.clear(); }
{
WriteLock lock(derivations_lock);
delete derivations;
derivations = new DerivationsMap();
}
{
WriteLock lock(public_keys_lock);
delete public_keys;
public_keys = new PublicKeysMap();
}
{
WriteLock lock(tx_keys_lock);
delete tx_keys;
tx_keys = new TxKeysMap();
}
}
private:
@@ -319,14 +338,18 @@ private:
}
};
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;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, DerivationEntry> derivations;
DerivationsMap* derivations;
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;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> tx_keys;
TxKeysMap* tx_keys;
};
static Cache* cache = nullptr;
@@ -379,7 +402,9 @@ void destroy_crypto_cache()
void clear_crypto_cache()
{
cache->clear();
if (cache) {
cache->clear();
}
}
} // namespace p2pool
+1
View File
@@ -50,6 +50,7 @@ void p2pool_usage()
"--rpc-login Specify username[:password] required for Monero RPC server\n"
"--socks5 Specify IP:port of a SOCKS5 proxy to use for outgoing connections\n"
"--no-dns disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too)\n"
"--p2p-external-port port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections\n"
"--help Show this help message\n\n"
"Example command line:\n\n"
"%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",
+92 -32
View File
@@ -32,15 +32,48 @@ namespace p2pool {
static bool track_memory = false;
constexpr size_t N = 2097152;
constexpr size_t MAX_FRAMES = 30;
constexpr size_t N = 1 << 22;
constexpr size_t MAX_FRAMES = 29;
struct TrackedAllocation
{
void* p;
void* stack_trace[MAX_FRAMES];
uint64_t allocated_size;
uint32_t thread_id;
uint32_t allocated_size;
FORCEINLINE bool operator<(const TrackedAllocation& rhs) { return memcmp(stack_trace, rhs.stack_trace, sizeof(stack_trace)) < 0; }
FORCEINLINE bool operator==(const TrackedAllocation& rhs) { return memcmp(stack_trace, rhs.stack_trace, sizeof(stack_trace)) == 0; }
void print(HANDLE h) const
{
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = {};
PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
IMAGEHLP_LINE64 line{};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
for (size_t j = 0; j < MAX_FRAMES; ++j) {
const DWORD64 address = reinterpret_cast<DWORD64>(stack_trace[j]);
DWORD64 t1 = 0;
DWORD t2 = 0;
if (SymFromAddr(h, address, &t1, pSymbol) && SymGetLineFromAddr64(h, address, &t2, &line)) {
const char* s = line.FileName;
const char* file_name = nullptr;
while (*s) {
if ((*s == '\\') || (*s == '/')) {
file_name = s + 1;
}
++s;
}
printf("%-25s %s (line %lu)\n", file_name ? file_name : line.FileName, pSymbol->Name, line.LineNumber);
}
}
printf("\n");
}
};
static_assert(sizeof(TrackedAllocation) == 256, "");
@@ -51,15 +84,62 @@ uint32_t first[N];
uint32_t next[N];
TrackedAllocation allocations[N];
uint32_t num_allocations = 0;
uint64_t total_allocated = 0;
uint32_t cur_allocation_index = 1;
void show_top_10()
{
TrackedAllocation* buf = reinterpret_cast<TrackedAllocation*>(VirtualAlloc(nullptr, sizeof(TrackedAllocation) * N, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
if (!buf) {
return;
}
const HANDLE h = GetCurrentProcess();
{
p2pool::MutexLock lock(allocation_lock);
TrackedAllocation* end = buf;
for (size_t i = 0; i < N; ++i) {
if (allocations[i].allocated_size) {
*(end++) = allocations[i];
}
}
std::sort(buf, end);
TrackedAllocation* prev = buf;
for (TrackedAllocation* p = buf + 1; p < end; ++p) {
if (*p == *prev) {
prev->allocated_size += p->allocated_size;
}
else {
++prev;
*prev = *p;
}
}
end = prev + 1;
std::sort(buf, end, [](const auto& a, const auto& b) { return a.allocated_size > b.allocated_size; });
printf("%I64u total bytes allocated\n", total_allocated);
for (TrackedAllocation* p = buf; (p < buf + 10) && (p < end); ++p) {
printf("%I64u bytes allocated at:\n", p->allocated_size);
p->print(h);
}
}
VirtualFree(buf, 0, MEM_RELEASE);
}
FORCEINLINE static void add_alocation(void* p, size_t size)
{
if (!track_memory) {
return;
}
void* stack_trace[MAX_FRAMES];
void* stack_trace[MAX_FRAMES] = {};
DWORD hash;
CaptureStackBackTrace(1, MAX_FRAMES, stack_trace, &hash);
@@ -74,6 +154,7 @@ FORCEINLINE static void add_alocation(void* p, size_t size)
// Make N two times bigger if this triggers
__debugbreak();
}
total_allocated += size;
for (uint64_t i = cur_allocation_index;; i = (i + 1) & (N - 1)) {
if (i && !allocations[i].allocated_size) {
@@ -105,6 +186,7 @@ FORCEINLINE static void remove_allocation(void* p)
for (uint32_t prev = 0, k = first[index]; k != 0; prev = k, k = next[k]) {
if (allocations[k].p == p) {
total_allocated -= allocations[k].allocated_size;
allocations[k].allocated_size = 0;
if (prev) {
next[prev] = next[k];
@@ -181,6 +263,8 @@ void* calloc_hook(size_t count, size_t size) noexcept
void memory_tracking_start()
{
SymInitialize(GetCurrentProcess(), NULL, TRUE);
using namespace p2pool;
uv_replace_allocator(malloc_hook, realloc_hook, calloc_hook, free_hook);
@@ -196,7 +280,6 @@ void memory_tracking_stop()
uv_mutex_destroy(&allocation_lock);
const HANDLE h = GetCurrentProcess();
SymInitialize(h, NULL, TRUE);
uint64_t total_leaks = 0;
@@ -205,39 +288,16 @@ void memory_tracking_stop()
if (t.allocated_size) {
total_leaks += t.allocated_size;
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = {};
PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
IMAGEHLP_LINE64 line{};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
printf("Memory leak detected, %u bytes allocated at %p by thread %u:\n", t.allocated_size, t.p, t.thread_id);
for (size_t j = 0; j < MAX_FRAMES; ++j) {
const DWORD64 address = reinterpret_cast<DWORD64>(t.stack_trace[j]);
DWORD64 t1 = 0;
DWORD t2 = 0;
if (SymFromAddr(h, address, &t1, pSymbol) && SymGetLineFromAddr64(h, address, &t2, &line)) {
const char* s = line.FileName;
const char* file_name = nullptr;
while (*s) {
if ((*s == '\\') || (*s == '/')) {
file_name = s + 1;
}
++s;
}
printf("%-25s %s (line %lu)\n", file_name ? file_name : line.FileName, pSymbol->Name, line.LineNumber);
}
}
printf("\n");
printf("Memory leak detected, %I64u bytes allocated at %p by thread %u:\n", t.allocated_size, t.p, t.thread_id);
t.print(h);
}
}
if (total_leaks > 0) {
printf("%I64u bytes leaked\n\n", total_leaks);
}
SymCleanup(h);
}
NOINLINE void* operator new(size_t n) { return p2pool::allocate(n); }
+2 -1
View File
@@ -25,6 +25,7 @@
#include "params.h"
#include "p2pool_api.h"
#include "side_chain.h"
#include "p2p_server.h"
#include <thread>
static constexpr char log_category_prefix[] = "Miner ";
@@ -40,7 +41,7 @@ Miner::Miner(p2pool* pool, uint32_t threads)
, m_startTimestamp(high_resolution_clock::now())
, m_nonce(0)
, m_nonceTimestamp(m_startTimestamp)
, m_extraNonce(0xF19E3779U)
, m_extraNonce(static_cast<uint32_t>(pool->p2p_server()->get_random64()))
, m_totalHashes(0)
, m_sharesFound(0)
, m_job{}
+27 -14
View File
@@ -139,12 +139,6 @@ P2PServer::P2PServer(p2pool* pool)
P2PServer::~P2PServer()
{
uv_timer_stop(&m_timer);
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_broadcastAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_connectToPeersAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_showPeersAsync), nullptr);
shutdown_tcp();
uv_mutex_destroy(&m_rngLock);
@@ -747,12 +741,16 @@ void P2PServer::broadcast(const PoolBlock& block)
Broadcast* data = new Broadcast();
data->blob.reserve(block.m_mainChainData.size() + block.m_sideChainData.size());
data->blob = block.m_mainChainData;
data->blob.insert(data->blob.end(), block.m_sideChainData.begin(), block.m_sideChainData.end());
int outputs_offset, outputs_blob_size;
const std::vector<uint8_t> mainchain_data = block.serialize_mainchain_data(nullptr, nullptr, &outputs_offset, &outputs_blob_size);
const std::vector<uint8_t> sidechain_data = block.serialize_sidechain_data();
data->pruned_blob.reserve(block.m_mainChainData.size() + block.m_sideChainData.size() + 16 - block.m_mainChainOutputsBlobSize);
data->pruned_blob.assign(block.m_mainChainData.begin(), block.m_mainChainData.begin() + block.m_mainChainOutputsOffset);
data->blob.reserve(mainchain_data.size() + sidechain_data.size());
data->blob = mainchain_data;
data->blob.insert(data->blob.end(), sidechain_data.begin(), sidechain_data.end());
data->pruned_blob.reserve(mainchain_data.size() + sidechain_data.size() + 16 - outputs_blob_size);
data->pruned_blob.assign(mainchain_data.begin(), mainchain_data.begin() + outputs_offset);
// 0 outputs in the pruned blob
data->pruned_blob.push_back(0);
@@ -764,10 +762,10 @@ void P2PServer::broadcast(const PoolBlock& block)
});
writeVarint(total_reward, data->pruned_blob);
writeVarint(block.m_mainChainOutputsBlobSize, data->pruned_blob);
writeVarint(outputs_blob_size, data->pruned_blob);
data->pruned_blob.insert(data->pruned_blob.end(), block.m_mainChainData.begin() + block.m_mainChainOutputsOffset + block.m_mainChainOutputsBlobSize, block.m_mainChainData.end());
data->pruned_blob.insert(data->pruned_blob.end(), block.m_sideChainData.begin(), block.m_sideChainData.end());
data->pruned_blob.insert(data->pruned_blob.end(), mainchain_data.begin() + outputs_offset + outputs_blob_size, mainchain_data.end());
data->pruned_blob.insert(data->pruned_blob.end(), sidechain_data.begin(), sidechain_data.end());
data->ancestor_hashes.reserve(block.m_uncles.size() + 1);
data->ancestor_hashes = block.m_uncles;
@@ -935,6 +933,12 @@ void P2PServer::show_peers()
LOGINFO(0, "Total: " << n << " peers");
}
int P2PServer::listen_port() const
{
const Params& params = m_pool->params();
return params.m_p2pExternalPort ? params.m_p2pExternalPort : m_listenPort;
}
int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size)
{
int result;
@@ -1125,6 +1129,15 @@ P2PServer::P2PClient::P2PClient()
{
}
void P2PServer::on_shutdown()
{
uv_timer_stop(&m_timer);
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_broadcastAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_connectToPeersAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_showPeersAsync), nullptr);
}
P2PServer::P2PClient::~P2PClient()
{
}
+4
View File
@@ -138,6 +138,8 @@ public:
void show_peers_async();
size_t peer_list_size() const { MutexLock lock(m_peerListLock); return m_peerList.size(); }
int listen_port() const override;
uint32_t max_outgoing_peers() const { return m_maxOutgoingPeers; }
uint32_t max_incoming_peers() const { return m_maxIncomingPeers; }
@@ -234,6 +236,8 @@ private:
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
+11 -8
View File
@@ -301,6 +301,11 @@ void p2pool::handle_miner_data(MinerData& data)
"\n---------------------------------------------------------------------------------------------------------------"
);
// Tx secret keys from all miners change every block, so cache can be cleared here
if (m_sideChain->precalcFinished()) {
clear_crypto_cache();
}
if (!is_main_thread()) {
update_block_template_async();
}
@@ -404,7 +409,7 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
if (payout) {
LOGINFO(0, log::LightCyan() << "You received a payout of " << log::LightGreen() << log::XMRAmount(payout) << log::LightCyan() << " in block " << log::LightGreen() << data.height);
}
api_update_block_found(&data);
api_update_block_found(&data, block);
}
else {
side_chain().watch_mainchain_block(data, sidechain_id);
@@ -440,7 +445,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);
@@ -448,7 +453,7 @@ void p2pool::submit_block_async(const std::vector<uint8_t>& blob)
m_submitBlockData.template_id = 0;
m_submitBlockData.nonce = 0;
m_submitBlockData.extra_nonce = 0;
m_submitBlockData.blob = blob;
m_submitBlockData.blob = std::move(blob);
}
// If p2pool is stopped, m_submitBlockAsync is most likely already closed
@@ -850,7 +855,7 @@ void p2pool::load_found_blocks()
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)
@@ -1320,16 +1325,14 @@ void p2pool::cleanup_mainchain_data(uint64_t height)
}
}
void p2pool::api_update_block_found(const ChainMain* data)
void p2pool::api_update_block_found(const ChainMain* data, const PoolBlock* block)
{
clear_crypto_cache();
if (!m_api) {
return;
}
const time_t cur_time = time(nullptr);
const difficulty_type total_hashes = m_sideChain->total_hashes();
const difficulty_type total_hashes = block ? block->m_cumulativeDifficulty : m_sideChain->total_hashes();
difficulty_type diff;
if (data && get_difficulty_at_height(data->height, diff)) {
+3 -2
View File
@@ -33,6 +33,7 @@ class Miner;
class ConsoleCommands;
class p2pool_api;
class ZMQReader;
struct PoolBlock;
class p2pool : public MinerCallbackHandler
{
@@ -74,7 +75,7 @@ public:
virtual void handle_chain_main(ChainMain& data, const char* extra) override;
void submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
void submit_block_async(const std::vector<uint8_t>& blob);
void submit_block_async(std::vector<uint8_t>&& blob);
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
void update_block_template_async(bool is_alternative_block = false);
@@ -84,7 +85,7 @@ public:
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);
+7 -2
View File
@@ -35,12 +35,12 @@ Params::Params(int argc, char* argv[])
}
if ((strcmp(argv[i], "--rpc-port") == 0) && (i + 1 < argc)) {
m_rpcPort = strtoul(argv[++i], nullptr, 10);
m_rpcPort = std::min(std::max(strtoul(argv[++i], nullptr, 10), 1UL), 65535UL);
ok = true;
}
if ((strcmp(argv[i], "--zmq-port") == 0) && (i + 1 < argc)) {
m_zmqPort = strtoul(argv[++i], nullptr, 10);
m_zmqPort = std::min(std::max(strtoul(argv[++i], nullptr, 10), 1UL), 65535UL);
ok = true;
}
@@ -146,6 +146,11 @@ Params::Params(int argc, char* argv[])
ok = true;
}
if ((strcmp(argv[i], "--p2p-external-port") == 0) && (i + 1 < argc)) {
m_p2pExternalPort = std::min(std::max(strtoul(argv[++i], nullptr, 10), 1UL), 65535UL);
ok = true;
}
if (!ok) {
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
p2pool_usage();
+1
View File
@@ -52,6 +52,7 @@ struct Params
std::string m_rpcLogin;
std::string m_socks5Proxy;
bool m_dns = true;
uint32_t m_p2pExternalPort = 0;
};
} // namespace p2pool
+97 -68
View File
@@ -29,11 +29,7 @@ static constexpr char log_category_prefix[] = "PoolBlock ";
namespace p2pool {
PoolBlock::PoolBlock()
: m_mainChainHeaderSize(0)
, m_mainChainMinerTxSize(0)
, m_mainChainOutputsOffset(0)
, m_mainChainOutputsBlobSize(0)
, m_majorVersion(0)
: m_majorVersion(0)
, m_minorVersion(0)
, m_timestamp(0)
, m_prevId{}
@@ -57,12 +53,6 @@ PoolBlock::PoolBlock()
, 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);
}
PoolBlock::PoolBlock(const PoolBlock& b)
@@ -83,11 +73,11 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
LOGERR(1, "operator= uv_mutex_trylock failed. Fix the code!");
}
m_mainChainData = b.m_mainChainData;
m_mainChainHeaderSize = b.m_mainChainHeaderSize;
m_mainChainMinerTxSize = b.m_mainChainMinerTxSize;
m_mainChainOutputsOffset = b.m_mainChainOutputsOffset;
m_mainChainOutputsBlobSize = b.m_mainChainOutputsBlobSize;
#if POOL_BLOCK_DEBUG
m_mainChainDataDebug = b.m_mainChainDataDebug;
m_sideChainDataDebug = b.m_sideChainDataDebug;
#endif
m_majorVersion = b.m_majorVersion;
m_minorVersion = b.m_minorVersion;
m_timestamp = b.m_timestamp;
@@ -99,7 +89,6 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_extraNonceSize = b.m_extraNonceSize;
m_extraNonce = b.m_extraNonce;
m_transactions = b.m_transactions;
m_sideChainData = b.m_sideChainData;
m_minerWallet = b.m_minerWallet;
m_txkeySec = b.m_txkeySec;
m_parent = b.m_parent;
@@ -129,43 +118,58 @@ PoolBlock::~PoolBlock()
uv_mutex_destroy(&m_lock);
}
void PoolBlock::serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash)
std::vector<uint8_t> PoolBlock::serialize_mainchain_data(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const
{
MutexLock lock(m_lock);
return serialize_mainchain_data_nolock(header_size, miner_tx_size, outputs_offset, outputs_blob_size);
}
m_mainChainData.clear();
std::vector<uint8_t> PoolBlock::serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const
{
std::vector<uint8_t> data;
data.reserve(128 + m_outputs.size() * 39 + m_transactions.size() * HASH_SIZE);
// Header
m_mainChainData.push_back(m_majorVersion);
m_mainChainData.push_back(m_minorVersion);
writeVarint(m_timestamp, m_mainChainData);
m_mainChainData.insert(m_mainChainData.end(), m_prevId.h, m_prevId.h + HASH_SIZE);
m_mainChainData.insert(m_mainChainData.end(), reinterpret_cast<uint8_t*>(&nonce), reinterpret_cast<uint8_t*>(&nonce) + NONCE_SIZE);
data.push_back(m_majorVersion);
data.push_back(m_minorVersion);
writeVarint(m_timestamp, data);
data.insert(data.end(), m_prevId.h, m_prevId.h + HASH_SIZE);
data.insert(data.end(), reinterpret_cast<const uint8_t*>(&m_nonce), reinterpret_cast<const uint8_t*>(&m_nonce) + NONCE_SIZE);
m_mainChainHeaderSize = m_mainChainData.size();
const size_t header_size0 = data.size();
if (header_size) {
*header_size = header_size0;
}
// Miner tx
m_mainChainData.push_back(TX_VERSION);
writeVarint(m_txinGenHeight + MINER_REWARD_UNLOCK_TIME, m_mainChainData);
m_mainChainData.push_back(1);
m_mainChainData.push_back(TXIN_GEN);
writeVarint(m_txinGenHeight, m_mainChainData);
data.push_back(TX_VERSION);
writeVarint(m_txinGenHeight + MINER_REWARD_UNLOCK_TIME, data);
data.push_back(1);
data.push_back(TXIN_GEN);
writeVarint(m_txinGenHeight, data);
m_mainChainOutputsOffset = static_cast<int>(m_mainChainData.size());
const int outputs_offset0 = static_cast<int>(data.size());
if (outputs_offset) {
*outputs_offset = outputs_offset0;
}
writeVarint(m_outputs.size(), m_mainChainData);
writeVarint(m_outputs.size(), data);
for (TxOutput& output : m_outputs) {
writeVarint(output.m_reward, m_mainChainData);
m_mainChainData.push_back(output.m_txType);
m_mainChainData.insert(m_mainChainData.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
const uint8_t tx_type = get_tx_type();
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
m_mainChainData.push_back(output.m_viewTag);
for (const TxOutput& output : m_outputs) {
writeVarint(output.m_reward, data);
data.push_back(tx_type);
data.insert(data.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
if (tx_type == TXOUT_TO_TAGGED_KEY) {
data.push_back(static_cast<uint8_t>(output.m_viewTag));
}
}
m_mainChainOutputsBlobSize = static_cast<int>(m_mainChainData.size()) - m_mainChainOutputsOffset;
if (outputs_blob_size) {
*outputs_blob_size = static_cast<int>(data.size()) - outputs_offset0;
}
uint8_t tx_extra[128];
uint8_t* p = tx_extra;
@@ -183,7 +187,6 @@ void PoolBlock::serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, c
*(p++) = TX_EXTRA_NONCE;
*(p++) = static_cast<uint8_t>(extra_nonce_size);
m_extraNonce = extra_nonce;
memcpy(p, &m_extraNonce, EXTRA_NONCE_SIZE);
p += EXTRA_NONCE_SIZE;
if (extra_nonce_size > EXTRA_NONCE_SIZE) {
@@ -193,49 +196,70 @@ void PoolBlock::serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, c
*(p++) = TX_EXTRA_MERGE_MINING_TAG;
*(p++) = HASH_SIZE;
memcpy(p, sidechain_hash.h, HASH_SIZE);
memcpy(p, m_sidechainId.h, HASH_SIZE);
p += HASH_SIZE;
writeVarint(static_cast<size_t>(p - tx_extra), m_mainChainData);
m_mainChainData.insert(m_mainChainData.end(), tx_extra, p);
writeVarint(static_cast<size_t>(p - tx_extra), data);
data.insert(data.end(), tx_extra, p);
m_mainChainData.push_back(0);
data.push_back(0);
m_mainChainMinerTxSize = m_mainChainData.size() - m_mainChainHeaderSize;
if (miner_tx_size) {
*miner_tx_size = data.size() - header_size0;
}
writeVarint(m_transactions.size() - 1, m_mainChainData);
const uint8_t* data = reinterpret_cast<const uint8_t*>(m_transactions.data());
m_mainChainData.insert(m_mainChainData.end(), data + HASH_SIZE, data + m_transactions.size() * HASH_SIZE);
writeVarint(m_transactions.size() - 1, data);
const uint8_t* t = reinterpret_cast<const uint8_t*>(m_transactions.data());
data.insert(data.end(), t + HASH_SIZE, t + m_transactions.size() * HASH_SIZE);
#if POOL_BLOCK_DEBUG
if (!m_mainChainDataDebug.empty() && (data != m_mainChainDataDebug)) {
LOGERR(1, "serialize_mainchain_data() has a bug, fix it!");
panic();
}
#endif
return data;
}
void PoolBlock::serialize_sidechain_data()
std::vector<uint8_t> PoolBlock::serialize_sidechain_data() const
{
std::vector<uint8_t> data;
MutexLock lock(m_lock);
m_sideChainData.clear();
m_sideChainData.reserve((m_uncles.size() + 4) * HASH_SIZE + 11);
data.reserve((m_uncles.size() + 4) * HASH_SIZE + 20);
const hash& spend = m_minerWallet.spend_public_key();
const hash& view = m_minerWallet.view_public_key();
m_sideChainData.insert(m_sideChainData.end(), spend.h, spend.h + HASH_SIZE);
m_sideChainData.insert(m_sideChainData.end(), view.h, view.h + HASH_SIZE);
m_sideChainData.insert(m_sideChainData.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE);
m_sideChainData.insert(m_sideChainData.end(), m_parent.h, m_parent.h + HASH_SIZE);
data.insert(data.end(), spend.h, spend.h + HASH_SIZE);
data.insert(data.end(), view.h, view.h + HASH_SIZE);
data.insert(data.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE);
data.insert(data.end(), m_parent.h, m_parent.h + HASH_SIZE);
writeVarint(m_uncles.size(), m_sideChainData);
writeVarint(m_uncles.size(), data);
for (const hash& id : m_uncles) {
m_sideChainData.insert(m_sideChainData.end(), id.h, id.h + HASH_SIZE);
data.insert(data.end(), id.h, id.h + HASH_SIZE);
}
writeVarint(m_sidechainHeight, m_sideChainData);
writeVarint(m_sidechainHeight, data);
writeVarint(m_difficulty.lo, m_sideChainData);
writeVarint(m_difficulty.hi, m_sideChainData);
writeVarint(m_difficulty.lo, data);
writeVarint(m_difficulty.hi, data);
writeVarint(m_cumulativeDifficulty.lo, m_sideChainData);
writeVarint(m_cumulativeDifficulty.hi, m_sideChainData);
writeVarint(m_cumulativeDifficulty.lo, data);
writeVarint(m_cumulativeDifficulty.hi, data);
#if POOL_BLOCK_DEBUG
if (!m_sideChainDataDebug.empty() && (data != m_sideChainDataDebug)) {
LOGERR(1, "serialize_sidechain_data() has a bug, fix it!");
panic();
}
#endif
return data;
}
void PoolBlock::reset_offchain_data()
@@ -274,16 +298,19 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
{
MutexLock lock(m_lock);
if (!m_mainChainHeaderSize || !m_mainChainMinerTxSize || (m_mainChainData.size() < m_mainChainHeaderSize + m_mainChainMinerTxSize)) {
size_t header_size, miner_tx_size;
const std::vector<uint8_t> mainchain_data = serialize_mainchain_data_nolock(&header_size, &miner_tx_size, nullptr, nullptr);
if (!header_size || !miner_tx_size || (mainchain_data.size() < header_size + miner_tx_size)) {
LOGERR(1, "tried to calculate PoW of uninitialized block");
return false;
}
blob_size = m_mainChainHeaderSize;
memcpy(blob, m_mainChainData.data(), blob_size);
blob_size = header_size;
memcpy(blob, mainchain_data.data(), blob_size);
uint8_t* miner_tx = m_mainChainData.data() + m_mainChainHeaderSize;
keccak(miner_tx, static_cast<int>(m_mainChainMinerTxSize) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE);
const uint8_t* miner_tx = mainchain_data.data() + header_size;
keccak(miner_tx, static_cast<int>(miner_tx_size) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE);
count = m_transactions.size();
uint8_t* h = reinterpret_cast<uint8_t*>(m_transactions.data());
@@ -329,11 +356,13 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
uint64_t PoolBlock::get_payout(const Wallet& w) const
{
const uint8_t tx_type = get_tx_type();
for (size_t i = 0, n = m_outputs.size(); i < n; ++i) {
const TxOutput& out = m_outputs[i];
hash eph_public_key;
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
if (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)) {
return out.m_reward;
}
+14 -16
View File
@@ -63,13 +63,12 @@ struct PoolBlock
mutable uv_mutex_t m_lock;
// Monero block template
std::vector<uint8_t> m_mainChainData;
size_t m_mainChainHeaderSize;
size_t m_mainChainMinerTxSize;
int m_mainChainOutputsOffset;
int m_mainChainOutputsBlobSize;
#if POOL_BLOCK_DEBUG
std::vector<uint8_t> m_mainChainDataDebug;
std::vector<uint8_t> m_sideChainDataDebug;
#endif
// Monero block template
uint8_t m_majorVersion;
uint8_t m_minorVersion;
uint64_t m_timestamp;
@@ -81,15 +80,16 @@ struct PoolBlock
struct TxOutput
{
FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey(), m_txType(0), m_viewTag(0) {}
FORCEINLINE TxOutput(uint64_t r, const hash& k, uint8_t tx_type, uint8_t view_tag) : m_reward(r), m_ephPublicKey(k), m_txType(tx_type), m_viewTag(view_tag) {}
FORCEINLINE TxOutput() : m_ephPublicKey(), m_reward(0), m_viewTag(0) {}
FORCEINLINE TxOutput(uint64_t r, const hash& k, uint8_t view_tag) : m_ephPublicKey(k), m_reward(r), m_viewTag(view_tag) {}
uint64_t m_reward;
hash m_ephPublicKey;
uint8_t m_txType;
uint8_t m_viewTag;
uint64_t m_reward : 56;
uint64_t m_viewTag : 8;
};
static_assert(sizeof(TxOutput) == sizeof(hash) + sizeof(uint64_t), "TxOutput bit packing didn't work with this compiler, fix the code!");
std::vector<TxOutput> m_outputs;
hash m_txkeyPub;
@@ -99,9 +99,6 @@ struct PoolBlock
// All block transaction hashes including the miner transaction hash at index 0
std::vector<hash> m_transactions;
// Side-chain data
std::vector<uint8_t> m_sideChainData;
// Miner's wallet
Wallet m_minerWallet{ nullptr };
@@ -134,8 +131,9 @@ struct PoolBlock
uint64_t m_localTimestamp;
void serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash);
void serialize_sidechain_data();
std::vector<uint8_t> serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr) const;
std::vector<uint8_t> serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const;
std::vector<uint8_t> serialize_sidechain_data() const;
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop);
void reset_offchain_data();
+35 -32
View File
@@ -75,8 +75,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
const int nonce_offset = static_cast<int>(data - data_begin);
READ_BUF(&m_nonce, NONCE_SIZE);
m_mainChainHeaderSize = data - data_begin;
EXPECT_BYTE(TX_VERSION);
uint64_t unlock_height;
@@ -89,12 +87,13 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
if (unlock_height != m_txinGenHeight + MINER_REWARD_UNLOCK_TIME) return __LINE__;
std::vector<uint8_t> outputs_blob;
m_mainChainOutputsOffset = static_cast<int>(data - data_begin);
const int outputs_offset = static_cast<int>(data - data_begin);
uint64_t num_outputs;
READ_VARINT(num_outputs);
uint64_t total_reward = 0;
int outputs_blob_size;
if (num_outputs > 0) {
// Outputs are in the buffer, just read them
@@ -105,31 +104,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 (static_cast<uint64_t>(data_end - data) < num_outputs * MIN_OUTPUT_SIZE) return __LINE__;
m_outputs.clear();
m_outputs.reserve(num_outputs);
m_outputs.resize(num_outputs);
m_outputs.shrink_to_fit();
const uint8_t expected_tx_type = get_tx_type();
for (uint64_t i = 0; i < num_outputs; ++i) {
TxOutput t;
TxOutput& t = m_outputs[i];
READ_VARINT(t.m_reward);
total_reward += t.m_reward;
uint64_t reward;
READ_VARINT(reward);
t.m_reward = reward;
total_reward += reward;
EXPECT_BYTE(expected_tx_type);
t.m_txType = expected_tx_type;
READ_BUF(t.m_ephPublicKey.h, HASH_SIZE);
if (expected_tx_type == TXOUT_TO_TAGGED_KEY) {
READ_BYTE(t.m_viewTag);
uint8_t view_tag;
READ_BYTE(view_tag);
t.m_viewTag = view_tag;
}
m_outputs.emplace_back(std::move(t));
}
m_mainChainOutputsBlobSize = static_cast<int>(data - data_begin) - m_mainChainOutputsOffset;
outputs_blob.assign(data_begin + m_mainChainOutputsOffset, data);
outputs_blob_size = static_cast<int>(data - data_begin) - outputs_offset;
outputs_blob.assign(data_begin + outputs_offset, data);
}
else {
// Outputs are not in the buffer and must be calculated from sidechain data
@@ -144,7 +144,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__;
}
m_mainChainOutputsBlobSize = static_cast<int>(tmp);
outputs_blob_size = static_cast<int>(tmp);
}
// Technically some p2pool node could keep stuffing block with transactions until reward is less than 0.6 XMR
@@ -153,13 +153,12 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__;
}
const int outputs_actual_blob_size = static_cast<int>(data - data_begin) - m_mainChainOutputsOffset;
if (m_mainChainOutputsBlobSize < outputs_actual_blob_size) {
const int outputs_actual_blob_size = static_cast<int>(data - data_begin) - outputs_offset;
if (outputs_blob_size < outputs_actual_blob_size) {
return __LINE__;
}
const int outputs_blob_size_diff = m_mainChainOutputsBlobSize - outputs_actual_blob_size;
const int outputs_blob_size_diff = outputs_blob_size - outputs_actual_blob_size;
uint64_t tx_extra_size;
READ_VARINT(tx_extra_size);
@@ -191,8 +190,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
EXPECT_BYTE(0);
m_mainChainMinerTxSize = (data - data_begin) + outputs_blob_size_diff - m_mainChainHeaderSize;
uint64_t num_transactions;
READ_VARINT(num_transactions);
@@ -208,12 +205,14 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
m_transactions.emplace_back(std::move(id));
}
m_mainChainData.reserve((data - data_begin) + outputs_blob_size_diff);
m_mainChainData.assign(data_begin, data_begin + m_mainChainOutputsOffset);
m_mainChainData.insert(m_mainChainData.end(), m_mainChainOutputsBlobSize, 0);
m_mainChainData.insert(m_mainChainData.end(), data_begin + m_mainChainOutputsOffset + outputs_actual_blob_size, data);
#if POOL_BLOCK_DEBUG
m_mainChainDataDebug.reserve((data - data_begin) + outputs_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);
const uint8_t* sidechain_data_begin = data;
#endif
hash spend_pub_key;
hash view_pub_key;
@@ -276,16 +275,18 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__;
}
if (static_cast<int>(outputs_blob.size()) != m_mainChainOutputsBlobSize) {
if (static_cast<int>(outputs_blob.size()) != outputs_blob_size) {
return __LINE__;
}
memcpy(m_mainChainData.data() + m_mainChainOutputsOffset, outputs_blob.data(), m_mainChainOutputsBlobSize);
#if POOL_BLOCK_DEBUG
memcpy(m_mainChainDataDebug.data() + outputs_offset, outputs_blob.data(), outputs_blob_size);
#endif
hash check;
const std::vector<uint8_t>& consensus_id = sidechain.consensus_id();
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_end, &consensus_id, &outputs_blob, outputs_blob_size_diff, outputs_offset, outputs_blob_size](int offset) -> uint8_t
{
uint32_t k = static_cast<uint32_t>(offset - nonce_offset);
if (k < NONCE_SIZE) {
@@ -304,11 +305,11 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
const int data_size = static_cast<int>((data_end - data_begin) + outputs_blob_size_diff);
if (offset < data_size) {
if (offset < m_mainChainOutputsOffset) {
if (offset < outputs_offset) {
return data_begin[offset];
}
else if (offset < m_mainChainOutputsOffset + m_mainChainOutputsBlobSize) {
const int tmp = offset - m_mainChainOutputsOffset;
else if (offset < outputs_offset + outputs_blob_size) {
const int tmp = offset - outputs_offset;
return outputs_blob[tmp];
}
else {
@@ -325,7 +326,9 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__;
}
m_sideChainData.assign(sidechain_data_begin, data_end);
#if POOL_BLOCK_DEBUG
m_sideChainDataDebug.assign(sidechain_data_begin, data_end);
#endif
}
catch (std::exception& e) {
const char* msg = e.what();
+31 -13
View File
@@ -32,6 +32,7 @@
#include "stratum_server.h"
#include "params.h"
#include "json_parsers.h"
#include "crypto.h"
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include <fstream>
@@ -169,7 +170,11 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
// Use between 1 and 8 threads
if (numThreads < 1) numThreads = 1;
// Don't limit thread count when debugging because debug builds are slow
#ifndef P2POOL_DEBUGGING
if (numThreads > 8) numThreads = 8;
#endif
LOGINFO(4, "running " << numThreads << " pre-calculation workers");
@@ -527,7 +532,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
MinerData miner_data = m_pool->miner_data();
if ((block.m_prevId == miner_data.prev_id) && miner_data.difficulty.check_pow(pow_hash)) {
LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero network, submitting it");
m_pool->submit_block_async(block.m_mainChainData);
m_pool->submit_block_async(block.serialize_mainchain_data());
}
else {
difficulty_type diff;
@@ -536,7 +541,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
}
else if (diff.check_pow(pow_hash)) {
LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero height " << block.m_txinGenHeight << ", submitting it");
m_pool->submit_block_async(block.m_mainChainData);
m_pool->submit_block_async(block.serialize_mainchain_data());
}
}
@@ -574,7 +579,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
}
if (block_found) {
m_pool->api_update_block_found(&data);
m_pool->api_update_block_found(&data, &block);
}
add_block(block);
@@ -675,10 +680,10 @@ bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob) const
return false;
}
blob.reserve(block->m_mainChainData.size() + block->m_sideChainData.size());
blob = block->serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = block->serialize_sidechain_data();
blob.insert(blob.end(), sidechain_data.begin(), sidechain_data.end());
blob = block->m_mainChainData;
blob.insert(blob.end(), block->m_sideChainData.begin(), block->m_sideChainData.end());
return true;
}
@@ -696,13 +701,15 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
blob.reserve(n * 39 + 64);
writeVarint(n, blob);
const uint8_t tx_type = b->get_tx_type();
for (const PoolBlock::TxOutput& output : b->m_outputs) {
writeVarint(output.m_reward, blob);
blob.emplace_back(output.m_txType);
blob.emplace_back(tx_type);
blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
blob.emplace_back(output.m_viewTag);
if (tx_type == TXOUT_TO_TAGGED_KEY) {
blob.emplace_back(static_cast<uint8_t>(output.m_viewTag));
}
}
@@ -808,9 +815,11 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
blob.emplace_back(view_tag);
}
block->m_outputs.emplace_back(tmpRewards[i], eph_public_key, tx_type, view_tag);
block->m_outputs.emplace_back(tmpRewards[i], eph_public_key, view_tag);
}
block->m_outputs.shrink_to_fit();
if (loop) {
// this will cause all helper jobs to finish immediately
counter = -1;
@@ -911,12 +920,13 @@ void SideChain::print_status(bool obtain_sidechain_lock) const
}
const Wallet& w = m_pool->params().m_wallet;
const uint8_t tx_type = tip->get_tx_type();
hash eph_public_key;
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
const PoolBlock::TxOutput& out = tip->m_outputs[i];
if (!your_reward) {
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
if (tx_type == TXOUT_TO_TAGGED_KEY) {
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
your_reward = out.m_reward;
}
@@ -983,11 +993,12 @@ double SideChain::get_reward_share(const Wallet& w) const
const PoolBlock* tip = m_chainTip;
if (tip) {
const uint8_t tx_type = tip->get_tx_type();
hash eph_public_key;
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
const PoolBlock::TxOutput& out = tip->m_outputs[i];
if (!reward) {
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
if (tx_type == TXOUT_TO_TAGGED_KEY) {
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
reward = out.m_reward;
}
@@ -1538,6 +1549,8 @@ void SideChain::verify(PoolBlock* block)
return;
}
const uint8_t tx_type = block->get_tx_type();
for (size_t i = 0, n = rewards.size(); i < n; ++i) {
const PoolBlock::TxOutput& out = block->m_outputs[i];
@@ -1561,7 +1574,7 @@ void SideChain::verify(PoolBlock* block)
return;
}
if ((out.m_txType == TXOUT_TO_TAGGED_KEY) && (out.m_viewTag != view_tag)) {
if ((tx_type == TXOUT_TO_TAGGED_KEY) && (out.m_viewTag != view_tag)) {
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
", id = " << block->m_sidechainId <<
", mainchain height = " << block->m_txinGenHeight <<
@@ -1622,6 +1635,8 @@ void SideChain::update_chain_tip(const PoolBlock* block)
if (s) {
s->reset_share_counters();
}
// Also clear cache because it has data from all old blocks now
clear_crypto_cache();
LOGINFO(0, log::LightCyan() << "SYNCHRONIZED");
}
}
@@ -2125,6 +2140,9 @@ void SideChain::finish_precalc()
LOGERR(1, "exception in finish_precalc(): " << e.what());
}
// Also clear cache because it has data from all old blocks now
clear_crypto_cache();
#ifdef DEV_TEST_SYNC
if (m_pool) {
LOGINFO(0, log::LightGreen() << "[DEV] Synchronization finished successfully, stopping P2Pool now");
+19 -11
View File
@@ -45,7 +45,6 @@ StratumServer::StratumServer(p2pool* pool)
: TCPServer(StratumClient::allocate)
, m_pool(pool)
, m_autoDiff(pool->params().m_autoDiff)
, m_extraNonce(0)
, m_rng(RandomDeviceSeed::instance)
, m_cumulativeHashes(0)
, m_cumulativeHashesAtLastShare(0)
@@ -60,6 +59,8 @@ StratumServer::StratumServer(p2pool* pool)
// Diffuse the initial state in case it has low quality
m_rng.discard(10000);
m_extraNonce = static_cast<uint32_t>(m_rng());
m_hashrateData[0] = { seconds_since_epoch(), 0 };
uv_mutex_init_checked(&m_blobsQueueLock);
@@ -84,8 +85,6 @@ StratumServer::StratumServer(p2pool* pool)
StratumServer::~StratumServer()
{
uv_close(reinterpret_cast<uv_handle_t*>(&m_blobsAsync), nullptr);
shutdown_tcp();
uv_mutex_destroy(&m_blobsQueueLock);
@@ -106,9 +105,12 @@ void StratumServer::on_block(const BlockTemplate& block)
LOGINFO(4, "no clients connected");
return;
}
m_extraNonce.exchange(num_connections);
const uint32_t extra_nonce_start = static_cast<uint32_t>(get_random64());
m_extraNonce.exchange(extra_nonce_start + num_connections);
BlobsData* blobs_data = new BlobsData{};
blobs_data->m_extraNonceStart = extra_nonce_start;
difficulty_type difficulty;
difficulty_type sidechain_difficulty;
@@ -118,7 +120,7 @@ void StratumServer::on_block(const BlockTemplate& block)
// Even if they do, they'll be added to the beginning of the list and will get their block template in on_login()
// We'll iterate through the list backwards so when we get to the beginning and run out of extra_nonce values, it'll be only new clients left
blobs_data->m_numClientsExpected = num_connections;
blobs_data->m_blobSize = block.get_hashing_blobs(0, num_connections, blobs_data->m_blobs, blobs_data->m_height, difficulty, sidechain_difficulty, blobs_data->m_seedHash, nonce_offset, blobs_data->m_templateId);
blobs_data->m_blobSize = block.get_hashing_blobs(extra_nonce_start, num_connections, blobs_data->m_blobs, blobs_data->m_height, difficulty, sidechain_difficulty, blobs_data->m_seedHash, nonce_offset, blobs_data->m_templateId);
// Integrity checks
if (blobs_data->m_blobSize < 76) {
@@ -664,9 +666,10 @@ void StratumServer::on_blobs_ready()
// Only send the latest blob
BlobsData* data = blobs_queue.back();
const uint32_t extra_nonce_start = data->m_extraNonceStart;
size_t numClientsProcessed = 0;
uint32_t extra_nonce = 0;
uint32_t num_sent = 0;
const uint64_t cur_time = seconds_since_epoch();
{
@@ -685,12 +688,12 @@ void StratumServer::on_blobs_ready()
continue;
}
if (extra_nonce >= data->m_numClientsExpected) {
if (num_sent >= data->m_numClientsExpected) {
// We don't have any more extra_nonce values available
continue;
}
uint8_t* hashing_blob = data->m_blobs.data() + extra_nonce * data->m_blobSize;
uint8_t* hashing_blob = data->m_blobs.data() + num_sent * data->m_blobSize;
uint64_t target = data->m_target;
if (client->m_customDiff.lo) {
@@ -726,7 +729,7 @@ void StratumServer::on_blobs_ready()
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE];
saved_job.job_id = job_id;
saved_job.extra_nonce = extra_nonce;
saved_job.extra_nonce = extra_nonce_start + num_sent;
saved_job.template_id = data->m_templateId;
saved_job.target = target;
}
@@ -752,7 +755,7 @@ void StratumServer::on_blobs_ready()
});
if (result) {
++extra_nonce;
++num_sent;
}
else {
client->close();
@@ -765,7 +768,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)
@@ -935,6 +938,11 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
}
}
void StratumServer::on_shutdown()
{
uv_close(reinterpret_cast<uv_handle_t*>(&m_blobsAsync), nullptr);
}
StratumServer::StratumClient::StratumClient()
: m_rpcId(0)
, m_perConnectionJobId(0)
+3
View File
@@ -103,6 +103,7 @@ private:
struct BlobsData
{
uint32_t m_extraNonceStart;
std::vector<uint8_t> m_blobs;
size_t m_blobSize;
uint64_t m_target;
@@ -179,6 +180,8 @@ private:
void update_hashrate_data(uint64_t hashes, uint64_t timestamp);
void api_update_local_stats(uint64_t timestamp);
void on_shutdown() override;
};
} // namespace p2pool
+8 -11
View File
@@ -42,7 +42,7 @@ public:
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);
virtual void on_connect_failed(bool /*is_v6*/, const raw_ip& /*ip*/, int /*port*/) {}
@@ -163,7 +163,6 @@ protected:
int m_listenPort;
uv_loop_t m_loop;
std::atomic<bool> m_loopStopped;
uv_mutex_t m_clientsListLock;
std::vector<Client*> m_preallocatedClients;
@@ -181,17 +180,15 @@ protected:
uv_async_t m_dropConnectionsAsync;
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;
static void on_shutdown(uv_async_t* async)
{
TCPServer* server = reinterpret_cast<TCPServer*>(async->data);
server->close_sockets(true);
uv_prepare_t m_shutdownPrepare;
uv_timer_t m_shutdownTimer;
uint32_t m_shutdownCountdown;
uint32_t m_numHandles;
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_dropConnectionsAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_shutdownAsync), nullptr);
delete GetLoopUserData(&server->m_loop, false);
}
static void on_shutdown(uv_async_t* async);
};
} // namespace p2pool
+65 -32
View File
@@ -31,9 +31,12 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
, m_finished(0)
, m_listenPort(-1)
, m_loop{}
, m_loopStopped{false}
, m_numConnections{ 0 }
, m_numIncomingConnections{ 0 }
, m_shutdownPrepare{}
, m_shutdownTimer{}
, m_shutdownCountdown(30)
, m_numHandles(0)
{
int err = uv_loop_init(&m_loop);
if (err) {
@@ -433,35 +436,6 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::shutdown_tcp()
}
uv_async_send(&m_shutdownAsync);
using namespace std::chrono;
const auto start_time = steady_clock::now();
int64_t counter = 0;
uv_async_t asy;
constexpr uint32_t timeout_seconds = 30;
while (!m_loopStopped) {
const int64_t elapsed_time = duration_cast<milliseconds>(steady_clock::now() - start_time).count();
if (elapsed_time >= (counter + 1) * 1000) {
++counter;
if (counter < timeout_seconds) {
LOGINFO(1, "waiting for event loop to stop for " << (timeout_seconds - counter) << " more seconds...");
}
else {
LOGWARN(1, "timed out while waiting for event loop to stop");
uv_async_init(&m_loop, &asy, [](uv_async_t* h) { uv_close(reinterpret_cast<uv_handle_t*>(h), nullptr); });
uv_stop(&m_loop);
uv_async_send(&asy);
break;
}
}
std::this_thread::sleep_for(milliseconds(1));
}
uv_thread_join(&m_loopThread);
uv_mutex_destroy(&m_clientsListLock);
@@ -598,7 +572,6 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::loop(void* data)
server->m_preallocatedClients.clear();
LOGINFO(1, "event loop stopped");
server->m_loopStopped = true;
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
@@ -729,7 +702,6 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_client(uv_stream_t* server
on_new_client(server, client);
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_client(uv_stream_t* server, Client* client)
{
@@ -825,6 +797,67 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_client(uv_stream_t* server
}
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_shutdown(uv_async_t* async)
{
TCPServer* s = reinterpret_cast<TCPServer*>(async->data);
s->on_shutdown();
s->close_sockets(true);
uv_close(reinterpret_cast<uv_handle_t*>(&s->m_dropConnectionsAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&s->m_shutdownAsync), nullptr);
delete GetLoopUserData(&s->m_loop, false);
s->m_numHandles = 0;
uv_walk(&s->m_loop, [](uv_handle_t*, void* n) { (*reinterpret_cast<size_t*>(n))++; }, &s->m_numHandles);
uv_prepare_init(&s->m_loop, &s->m_shutdownPrepare);
s->m_shutdownPrepare.data = s;
uv_timer_init(&s->m_loop, &s->m_shutdownTimer);
s->m_shutdownTimer.data = s;
s->m_shutdownCountdown = 30;
uv_timer_start(&s->m_shutdownTimer,
[](uv_timer_t* h)
{
TCPServer* s = reinterpret_cast<TCPServer*>(h->data);
const uint32_t k = --s->m_shutdownCountdown;
if (k > 0) {
LOGINFO(1, "waiting for event loop to stop for " << k << " more seconds (" << s->m_numHandles << " handles left)...");
}
else {
LOGINFO(1, "force stopping the event loop...");
uv_timer_stop(&s->m_shutdownTimer);
uv_prepare_stop(&s->m_shutdownPrepare);
uv_close(reinterpret_cast<uv_handle_t*>(&s->m_shutdownTimer), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&s->m_shutdownPrepare), nullptr);
uv_stop(&s->m_loop);
}
}, 1000, 1000);
uv_prepare_start(&s->m_shutdownPrepare,
[](uv_prepare_t* h)
{
TCPServer* s = reinterpret_cast<TCPServer*>(h->data);
s->m_numHandles = 0;
uv_walk(&s->m_loop, [](uv_handle_t*, void* n) { (*reinterpret_cast<size_t*>(n))++; }, &s->m_numHandles);
if (s->m_numHandles > 2) {
// Don't count m_shutdownTimer and m_shutdownPrepare
s->m_numHandles -= 2;
}
else {
uv_timer_stop(&s->m_shutdownTimer);
uv_prepare_stop(&s->m_shutdownPrepare);
uv_close(reinterpret_cast<uv_handle_t*>(&s->m_shutdownTimer), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&s->m_shutdownPrepare), nullptr);
}
});
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::Client()
: m_owner(nullptr)
+1 -1
View File
@@ -32,7 +32,7 @@ namespace p2pool {
#define STR2(X) STR(X)
#define STR(X) #X
const char* VERSION = "v2.4 (built"
const char* VERSION = "v2.5 (built"
#if defined(__clang__)
" with clang/" __clang_version__
#elif defined(__GNUC__)
+10 -6
View File
@@ -51,11 +51,16 @@ TEST(pool_block, deserialize)
ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain, nullptr), 0);
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);
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_majorVersion, 14);
ASSERT_EQ(b.m_minorVersion, 14);
ASSERT_EQ(b.m_timestamp, 1630934403);
@@ -65,7 +70,6 @@ TEST(pool_block, deserialize)
ASSERT_EQ(b.m_extraNonceSize, 4);
ASSERT_EQ(b.m_extraNonce, 28);
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_sidechainHeight, 53450);
ASSERT_EQ(b.m_difficulty.lo, 319296691);