Compare commits

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