Compare commits

..
2 Commits
Author SHA1 Message Date
SChernykhandGitHub c8248b2d33 Merge pull request #230 from hinto-janaiyo/master
README: Update Gupax info
2023-01-22 09:57:34 +01:00
hinto-janaiyoandGitHub d73b506d09 readme: update gupax info 2023-01-22 02:44:47 +00:00
45 changed files with 712 additions and 1246 deletions
-60
View File
@@ -350,63 +350,3 @@ jobs:
with: with:
name: p2pool-${{ matrix.os }} name: p2pool-${{ matrix.os }}
path: build/p2pool path: build/p2pool
build-freebsd:
timeout-minutes: 30
runs-on: ${{ matrix.os.host }}
strategy:
matrix:
os:
- name: freebsd
architecture: x86-64
version: '12.4'
host: macos-12
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build p2pool
uses: cross-platform-actions/action@v0.10.0
with:
operating_system: ${{ matrix.os.name }}
architecture: ${{ matrix.os.architecture }}
version: ${{ matrix.os.version }}
shell: bash
run: |
sudo pkg install -y cmake autoconf automake libtool
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j3
cd ../libuv
mkdir build
cd build
cmake .. -DBUILD_TESTING=OFF
make -j3
cd ../../libzmq
mkdir build
cd build
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j3
cd ../../../..
mkdir build
cd build
cmake .. -DSTATIC_BINARY=ON
make -j3
cd ../tests
mkdir build
cd build
cmake .. -DSTATIC_LIBS=ON
make -j3
./p2pool_tests
- name: Archive binary
uses: actions/upload-artifact@v3
with:
name: p2pool-${{ matrix.os.name }}-${{ matrix.os.version }}
path: build/p2pool
+9 -18
View File
@@ -30,16 +30,13 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
run: | run: |
cd build cd build
mkdir data ./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 --data-api data --local-api --loglevel 6
- name: Archive p2pool.log - name: Archive p2pool.log
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: p2pool_ubuntu_data name: p2pool_ubuntu.log
path: | path: build/p2pool.log
build/p2pool.log
build/data/
sync-test-macos: sync-test-macos:
@@ -65,16 +62,13 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
run: | run: |
cd build cd build
mkdir data ./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 --data-api data --local-api --loglevel 6
- name: Archive p2pool.log - name: Archive p2pool.log
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: p2pool_macos_data name: p2pool_macos.log
path: | path: build/p2pool.log
build/p2pool.log
build/data/
sync-test-windows: sync-test-windows:
@@ -100,13 +94,10 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
run: | run: |
cd build/Debug cd build/Debug
mkdir data ./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 --data-api data --local-api --loglevel 6
- name: Archive p2pool.log - name: Archive p2pool.log
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: p2pool_windows_data name: p2pool_windows.log
path: | path: build/Debug/p2pool.log
build/Debug/p2pool.log
build/Debug/data/
+3 -4
View File
@@ -1,6 +1,9 @@
[submodule "tests/googletest"] [submodule "tests/googletest"]
path = tests/googletest path = tests/googletest
url = https://github.com/google/googletest url = https://github.com/google/googletest
[submodule "external/src/RandomX"]
path = external/src/RandomX
url = https://github.com/SChernykh/RandomX
[submodule "external/src/rapidjson"] [submodule "external/src/rapidjson"]
path = external/src/rapidjson path = external/src/rapidjson
url = https://github.com/SChernykh/rapidjson url = https://github.com/SChernykh/rapidjson
@@ -19,7 +22,3 @@
[submodule "external/src/curl"] [submodule "external/src/curl"]
path = external/src/curl path = external/src/curl
url = https://github.com/SChernykh/curl url = https://github.com/SChernykh/curl
[submodule "external/src/RandomWOW"]
path = external/src/RandomWOW
url = https://git.wownero.com/wownero/RandomWOW
branch = 1.1.10-p2pool
+6 -14
View File
@@ -1,27 +1,19 @@
cmake_minimum_required(VERSION 2.8.12) cmake_minimum_required(VERSION 3.6)
project(p2pool) project(p2pool)
option(STATIC_BINARY "Build static binary" OFF) option(STATIC_BINARY "Build static binary" OFF)
option(STATIC_LIBS "Link libuv and libzmq statically" OFF) option(STATIC_LIBS "Link libuv and libzmq statically" OFF)
option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned off, p2pool will rely on wownerod for verifying RandomX hashes" ON) option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned off, p2pool will rely on monerod for verifying RandomX hashes" ON)
option(DEV_TEST_SYNC "[Developer only] Sync test, stop p2pool after sync is complete" OFF) option(DEV_TEST_SYNC "[Developer only] Sync test, stop p2pool after sync is complete" OFF)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
if (${CMAKE_VERSION} VERSION_GREATER "3.5.2")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
endif()
find_package(Git)
if(GIT_FOUND)
message(STATUS "Initializing submodules")
execute_process(COMMAND git "submodule" "update" "--init" "--recursive")
endif()
if (WITH_RANDOMX) if (WITH_RANDOMX)
add_definitions(-DWITH_RANDOMX) add_definitions(-DWITH_RANDOMX)
add_subdirectory(external/src/RandomWOW) add_subdirectory(external/src/RandomX)
set(LIBS randomx) set(LIBS randomx)
endif() endif()
@@ -118,7 +110,7 @@ include_directories(${UV_INCLUDE_DIR})
include_directories(external/src/cppzmq) include_directories(external/src/cppzmq)
include_directories(${ZMQ_INCLUDE_DIR}) include_directories(${ZMQ_INCLUDE_DIR})
if (WITH_RANDOMX) if (WITH_RANDOMX)
include_directories(external/src/RandomWOW/src) include_directories(external/src/RandomX/src)
endif() endif()
include_directories(external/src/rapidjson/include) include_directories(external/src/rapidjson/include)
include_directories(external/src/robin-hood-hashing/src/include) include_directories(external/src/robin-hood-hashing/src/include)
@@ -132,7 +124,7 @@ if (WIN32)
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(LIBS ${LIBS} pthread) set(LIBS ${LIBS} pthread)
elseif (NOT APPLE) elseif (NOT APPLE)
set(LIBS ${LIBS} pthread) set(LIBS ${LIBS} pthread dl)
endif() endif()
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC) if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
@@ -234,7 +226,7 @@ if (STATIC_BINARY OR STATIC_LIBS)
find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration) find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration)
set(STATIC_LIBS ${STATIC_LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB}) set(STATIC_LIBS ${STATIC_LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB})
else() else()
set(STATIC_LIBS ${STATIC_LIBS} pthread) set(STATIC_LIBS ${STATIC_LIBS} pthread dl)
endif() endif()
target_link_libraries(${CMAKE_PROJECT_NAME} target_link_libraries(${CMAKE_PROJECT_NAME}
+3 -8
View File
@@ -2,13 +2,7 @@
Decentralized pool for Monero mining. Decentralized pool for Monero mining.
Pool status and monitoring pages can be found at https://p2pool.io/, https://p2pool.io/mini/ and https://p2pool.observer/, https://mini.p2pool.observer/ Pool status and monitoring pages can be found at https://p2pool.io/, https://p2pool.io/mini/ and https://p2pool.observer/
# P2Pool hardfork on March 18, 2023
P2Pool (not Monero!) will hardfork to new consensus rules on March 18th at 21:00 UTC (use [this link](https://dateful.com/convert/utc?t=9pm&d=2023-03-18) to convert to your local time). The hardfork is necessary to improve P2Pool scalability and reduce the impact on Monero blockchain. You'll need to update to **P2Pool v3.0** or newer version before this time.
Reddit discussion: https://www.reddit.com/r/MoneroMining/comments/1095730/psa_p2pool_network_upgrade_aka_hardfork_on_march/
### Build Status ### Build Status
@@ -30,6 +24,7 @@ Reddit discussion: https://www.reddit.com/r/MoneroMining/comments/1095730/psa_p2
- [Monero version support](#monero-version-support) - [Monero version support](#monero-version-support)
- [How to mine on P2Pool](#how-to-mine-on-p2pool) - [How to mine on P2Pool](#how-to-mine-on-p2pool)
- [General Considerations](#general-considerations) - [General Considerations](#general-considerations)
- [GUI for P2Pool](#gui-for-p2pool)
- [GNU/Linux](#gnulinux) - [GNU/Linux](#gnulinux)
- [Windows](#windows) - [Windows](#windows)
- [Build instructions](#build-instructions) - [Build instructions](#build-instructions)
@@ -103,7 +98,7 @@ Monero network upgrade happened on August 13th, 2022 (block 2,688,888). In order
### GUI for P2Pool ### 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 - [Gupax](https://github.com/hinto-janaiyo/gupax) project aims to provide an easy to use cross-platform GUI to configure and run P2Pool & [XMRig](https://github.com/xmrig/xmrig).
### GNU/Linux ### GNU/Linux
+1 -1
View File
@@ -5,6 +5,6 @@
../external/src/cppzmq/ ../external/src/cppzmq/
../external/src/libzmq/include/ ../external/src/libzmq/include/
../external/src/curl/include ../external/src/curl/include
../external/src/RandomWOW/src/ ../external/src/RandomX/src/
../external/src/rapidjson/include ../external/src/rapidjson/include
../external/src/robin-hood-hashing/src/include ../external/src/robin-hood-hashing/src/include
Vendored Submodule
+1
Submodule external/src/RandomX added at 89031917e9
+84 -43
View File
@@ -36,56 +36,97 @@
#define MONERO_DEFAULT_LOG_CATEGORY "blockchain.hardforks" #define MONERO_DEFAULT_LOG_CATEGORY "blockchain.hardforks"
const hardfork_t mainnet_hard_forks[] = { const hardfork_t mainnet_hard_forks[] = {
{ 7, 1, 0, 1519605000 }, // version 1 from the start of the blockchain
{ 8, 6969, 0, 1524214739 }, { 1, 1, 0, 1341378000 },
{ 9, 53666, 0, 1538689773 },
{ 10, 63469, 0, 1541700352 }, // version 2 starts from block 1009827, which is on or around the 20th of March, 2016. Fork time finalised on 2015-09-20. No fork voting occurs for the v2 fork.
{ 11, 81769, 0, 1549238400 }, { 2, 1009827, 0, 1442763710 },
{ 12, 82069, 0, 1549318761 },
{ 13, 114969, 0, 1559292691 }, // version 3 starts from block 1141317, which is on or around the 24th of September, 2016. Fork time finalised on 2016-03-21.
{ 14, 115257, 0, 1559292774 }, { 3, 1141317, 0, 1458558528 },
{ 15, 160777, 0, 1573280497 },
{ 16, 253999, 0, 1600576508 }, // version 4 starts from block 1220516, which is on or around the 5th of January, 2017. Fork time finalised on 2016-09-18.
{ 17, 254287, 0, 1600576524 }, { 4, 1220516, 0, 1483574400 },
{ 18, 331170, 0, 1623245591 },
{ 19, 331458, 0, 1624793373 }, // version 5 starts from block 1288616, which is on or around the 15th of April, 2017. Fork time finalised on 2017-03-14.
{ 5, 1288616, 0, 1489520158 },
// version 6 starts from block 1400000, which is on or around the 16th of September, 2017. Fork time finalised on 2017-08-18.
{ 6, 1400000, 0, 1503046577 },
// version 7 starts from block 1546000, which is on or around the 6th of April, 2018. Fork time finalised on 2018-03-17.
{ 7, 1546000, 0, 1521303150 },
// version 8 starts from block 1685555, which is on or around the 18th of October, 2018. Fork time finalised on 2018-09-02.
{ 8, 1685555, 0, 1535889547 },
// version 9 starts from block 1686275, which is on or around the 19th of October, 2018. Fork time finalised on 2018-09-02.
{ 9, 1686275, 0, 1535889548 },
// version 10 starts from block 1788000, which is on or around the 9th of March, 2019. Fork time finalised on 2019-02-10.
{ 10, 1788000, 0, 1549792439 },
// version 11 starts from block 1788720, which is on or around the 10th of March, 2019. Fork time finalised on 2019-02-15.
{ 11, 1788720, 0, 1550225678 },
// version 12 starts from block 1978433, which is on or around the 30th of November, 2019. Fork time finalised on 2019-10-18.
{ 12, 1978433, 0, 1571419280 },
{ 13, 2210000, 0, 1598180817 },
{ 14, 2210720, 0, 1598180818 },
{ 15, 2688888, 0, 1656629117 },
{ 16, 2689608, 0, 1656629118 },
}; };
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]); const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
const uint64_t mainnet_hard_fork_version_1_till = 0; const uint64_t mainnet_hard_fork_version_1_till = 1009826;
const hardfork_t testnet_hard_forks[] = { const hardfork_t testnet_hard_forks[] = {
{ 7, 1, 0, 1519605000 }, // version 1 from the start of the blockchain
{ 8, 10, 0, 1524214739 }, { 1, 1, 0, 1341378000 },
{ 9, 20, 0, 1538689773 },
{ 10, 30, 0, 1541700352 }, // version 2 starts from block 624634, which is on or around the 23rd of November, 2015. Fork time finalised on 2015-11-20. No fork voting occurs for the v2 fork.
{ 11, 40, 0, 1549238400 }, { 2, 624634, 0, 1445355000 },
{ 12, 50, 0, 1549318761 },
{ 13, 60, 0, 1559292691 }, // versions 3-5 were passed in rapid succession from September 18th, 2016
{ 14, 70, 0, 1559292774 }, { 3, 800500, 0, 1472415034 },
{ 15, 80, 0, 1573280497 }, { 4, 801219, 0, 1472415035 },
{ 16, 90, 0, 1600576508 }, { 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6
{ 17, 100, 0, 1600576524 },
{ 18, 110, 0, 1623245591 }, { 6, 971400, 0, 1501709789 },
{ 19, 120, 0, 1624793373 }, { 7, 1057027, 0, 1512211236 },
{ 20, 130, 0, 1674457368 }, { 8, 1057058, 0, 1533211200 },
{ 9, 1057778, 0, 1533297600 },
{ 10, 1154318, 0, 1550153694 },
{ 11, 1155038, 0, 1550225678 },
{ 12, 1308737, 0, 1569582000 },
{ 13, 1543939, 0, 1599069376 },
{ 14, 1544659, 0, 1599069377 },
{ 15, 1982800, 0, 1652727000 },
{ 16, 1983520, 0, 1652813400 },
}; };
const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]); const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
const uint64_t testnet_hard_fork_version_1_till = 0; const uint64_t testnet_hard_fork_version_1_till = 624633;
const hardfork_t stagenet_hard_forks[] = { const hardfork_t stagenet_hard_forks[] = {
{ 7, 1, 0, 1519605000 }, // version 1 from the start of the blockchain
{ 8, 10, 0, 1524214739 }, { 1, 1, 0, 1341378000 },
{ 9, 20, 0, 1538689773 },
{ 10, 30, 0, 1541700352 }, // versions 2-7 in rapid succession from March 13th, 2018
{ 11, 40, 0, 1549238400 }, { 2, 32000, 0, 1521000000 },
{ 12, 50, 0, 1549318761 }, { 3, 33000, 0, 1521120000 },
{ 13, 60, 0, 1559292691 }, { 4, 34000, 0, 1521240000 },
{ 14, 70, 0, 1559292774 }, { 5, 35000, 0, 1521360000 },
{ 15, 80, 0, 1573280497 }, { 6, 36000, 0, 1521480000 },
{ 16, 90, 0, 1600576508 }, { 7, 37000, 0, 1521600000 },
{ 17, 100, 0, 1600576524 }, { 8, 176456, 0, 1537821770 },
{ 18, 110, 0, 1623245591 }, { 9, 177176, 0, 1537821771 },
{ 19, 120, 0, 1624793373 }, { 10, 269000, 0, 1550153694 },
{ 20, 130, 0, 1674457368 }, { 11, 269720, 0, 1550225678 },
{ 12, 454721, 0, 1571419280 },
{ 13, 675405, 0, 1598180817 },
{ 14, 676125, 0, 1598180818 },
{ 15, 1151000, 0, 1656629117 },
{ 16, 1151720, 0, 1656629118 },
}; };
const size_t num_stagenet_hard_forks = sizeof(stagenet_hard_forks) / sizeof(stagenet_hard_forks[0]); const size_t num_stagenet_hard_forks = sizeof(stagenet_hard_forks) / sizeof(stagenet_hard_forks[0]);
+85 -256
View File
@@ -55,12 +55,10 @@ BlockTemplate::BlockTemplate(SideChain* sidechain, RandomX_Hasher_Base* hasher)
, m_difficulty{} , m_difficulty{}
, m_seedHash{} , m_seedHash{}
, m_timestamp(0) , m_timestamp(0)
, m_txkeyPub{}
, m_txkeySec{}
, m_poolBlockTemplate(new PoolBlock()) , m_poolBlockTemplate(new PoolBlock())
, m_finalReward(0) , m_finalReward(0)
, m_minerTxKeccakState{}
, m_minerTxKeccakStateInputLength(0)
, m_sidechainHashKeccakState{}
, m_sidechainHashInputLength(0)
, m_rng(RandomDeviceSeed::instance) , m_rng(RandomDeviceSeed::instance)
{ {
// Diffuse the initial state in case it has low quality // Diffuse the initial state in case it has low quality
@@ -74,8 +72,6 @@ BlockTemplate::BlockTemplate(SideChain* sidechain, RandomX_Hasher_Base* hasher)
m_transactionHashes.reserve(8192); m_transactionHashes.reserve(8192);
m_rewards.reserve(100); m_rewards.reserve(100);
m_blockTemplateBlob.reserve(65536); m_blockTemplateBlob.reserve(65536);
m_fullDataBlob.reserve(65536);
m_sidechainHashBlob.reserve(65536);
m_merkleTreeMainBranch.reserve(HASH_SIZE * 10); m_merkleTreeMainBranch.reserve(HASH_SIZE * 10);
m_mempoolTxs.reserve(1024); m_mempoolTxs.reserve(1024);
m_mempoolTxsOrder.reserve(1024); m_mempoolTxsOrder.reserve(1024);
@@ -123,8 +119,6 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
m_templateId = b.m_templateId; m_templateId = b.m_templateId;
m_lastUpdated = b.m_lastUpdated.load(); m_lastUpdated = b.m_lastUpdated.load();
m_blockTemplateBlob = b.m_blockTemplateBlob; m_blockTemplateBlob = b.m_blockTemplateBlob;
m_fullDataBlob = b.m_fullDataBlob;
m_sidechainHashBlob = b.m_sidechainHashBlob;
m_merkleTreeMainBranch = b.m_merkleTreeMainBranch; m_merkleTreeMainBranch = b.m_merkleTreeMainBranch;
m_blockHeaderSize = b.m_blockHeaderSize; m_blockHeaderSize = b.m_blockHeaderSize;
m_minerTxOffsetInTemplate = b.m_minerTxOffsetInTemplate; m_minerTxOffsetInTemplate = b.m_minerTxOffsetInTemplate;
@@ -137,14 +131,10 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
m_difficulty = b.m_difficulty; m_difficulty = b.m_difficulty;
m_seedHash = b.m_seedHash; m_seedHash = b.m_seedHash;
m_timestamp = b.m_timestamp; m_timestamp = b.m_timestamp;
m_txkeyPub = b.m_txkeyPub;
m_txkeySec = b.m_txkeySec;
*m_poolBlockTemplate = *b.m_poolBlockTemplate; *m_poolBlockTemplate = *b.m_poolBlockTemplate;
m_finalReward = b.m_finalReward.load(); m_finalReward = b.m_finalReward;
memcpy(m_minerTxKeccakState, b.m_minerTxKeccakState, sizeof(m_minerTxKeccakState));
m_minerTxKeccakStateInputLength = b.m_minerTxKeccakStateInputLength;
memcpy(m_sidechainHashKeccakState, b.m_sidechainHashKeccakState, sizeof(m_sidechainHashKeccakState));
m_sidechainHashInputLength = b.m_sidechainHashInputLength;
m_minerTx.clear(); m_minerTx.clear();
m_blockHeader.clear(); m_blockHeader.clear();
@@ -167,7 +157,7 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
static FORCEINLINE uint64_t get_base_reward(uint64_t already_generated_coins) static FORCEINLINE uint64_t get_base_reward(uint64_t already_generated_coins)
{ {
const uint64_t result = ~already_generated_coins >> 20; const uint64_t result = ~already_generated_coins >> 19;
constexpr uint64_t min_reward = 600000000000ULL; constexpr uint64_t min_reward = 600000000000ULL;
return (result < min_reward) ? min_reward : result; return (result < min_reward) ? min_reward : result;
@@ -198,12 +188,9 @@ static FORCEINLINE uint64_t get_block_reward(uint64_t base_reward, uint64_t medi
void BlockTemplate::shuffle_tx_order() void BlockTemplate::shuffle_tx_order()
{ {
const uint64_t n = m_mempoolTxsOrder.size(); const int64_t n = static_cast<int64_t>(m_mempoolTxsOrder.size());
if (n > 1) { for (int64_t i = n - 1; i > 0; --i) {
for (uint64_t i = 0, k; i < n - 1; ++i) { std::swap(m_mempoolTxsOrder[i], m_mempoolTxsOrder[m_rng() % (i + 1)]);
umul128(m_rng(), n - i, &k);
std::swap(m_mempoolTxsOrder[i], m_mempoolTxsOrder[i + k]);
}
} }
} }
@@ -234,6 +221,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
*this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)]; *this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)];
}; };
get_tx_keys(m_txkeyPub, m_txkeySec, miner_wallet->spend_public_key(), data.prev_id);
m_height = data.height; m_height = data.height;
m_difficulty = data.difficulty; m_difficulty = data.difficulty;
m_seedHash = data.seed_hash; m_seedHash = data.seed_hash;
@@ -267,69 +256,11 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_blockHeader.insert(m_blockHeader.end(), NONCE_SIZE, 0); m_blockHeader.insert(m_blockHeader.end(), NONCE_SIZE, 0);
m_poolBlockTemplate->m_nonce = 0; m_poolBlockTemplate->m_nonce = 0;
// Fill in m_txinGenHeight here so get_shares() can use it to calculate the correct PPLNS window
m_poolBlockTemplate->m_txinGenHeight = data.height;
m_blockHeaderSize = m_blockHeader.size(); m_blockHeaderSize = m_blockHeader.size();
const int sidechain_version = m_poolBlockTemplate->get_sidechain_version(); m_sidechain->fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares);
if (sidechain_version <= 1) { // Only choose transactions that were received 10 or more seconds ago, or high fee (>= 0.006 XMR) transactions
get_tx_keys(m_poolBlockTemplate->m_txkeyPub, m_poolBlockTemplate->m_txkeySec, miner_wallet->spend_public_key(), data.prev_id);
// Both values are the same before v2
m_poolBlockTemplate->m_txkeySecSeed = m_poolBlockTemplate->m_txkeySec;
}
m_poolBlockTemplate->m_minerWallet = *miner_wallet;
m_sidechain->fill_sidechain_data(*m_poolBlockTemplate, m_shares);
// Pre-calculate outputs to speed up miner tx generation
if (!m_shares.empty()) {
struct Precalc
{
FORCEINLINE Precalc(const std::vector<MinerShare>& s, const hash& k) : txKeySec(k)
{
const size_t N = s.size();
counter = static_cast<int>(N) - 1;
shares = reinterpret_cast<std::pair<hash, hash>*>(malloc_hook(sizeof(std::pair<hash, hash>) * N));
if (shares) {
const MinerShare* src = &s[0];
std::pair<hash, hash>* dst = shares;
std::pair<hash, hash>* e = shares + N;
for (; dst < e; ++src, ++dst) {
const Wallet* w = src->m_wallet;
dst->first = w->view_public_key();
dst->second = w->spend_public_key();
}
}
}
FORCEINLINE Precalc(Precalc&& rhs) noexcept : txKeySec(rhs.txKeySec), counter(rhs.counter.load()), shares(rhs.shares) { rhs.shares = nullptr; }
FORCEINLINE ~Precalc() { free_hook(shares); }
FORCEINLINE void operator()()
{
if (shares) {
hash derivation, eph_public_key;
int i;
while ((i = counter.fetch_sub(1)) >= 0) {
uint8_t view_tag;
generate_key_derivation(shares[i].first, txKeySec, i, derivation, view_tag);
derive_public_key(derivation, i, shares[i].second, eph_public_key);
}
}
}
hash txKeySec;
std::atomic<int> counter;
std::pair<hash, hash>* shares;
};
parallel_run(uv_default_loop_checked(), Precalc(m_shares, m_poolBlockTemplate->m_txkeySec));
}
// Only choose transactions that were received 5 or more seconds ago, or high fee (>= 0.006 XMR) transactions
size_t total_mempool_transactions; size_t total_mempool_transactions;
{ {
m_mempoolTxs.clear(); m_mempoolTxs.clear();
@@ -341,7 +272,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
const uint64_t cur_time = seconds_since_epoch(); const uint64_t cur_time = seconds_since_epoch();
for (auto& it : mempool.m_transactions) { for (auto& it : mempool.m_transactions) {
if ((cur_time > it.second.time_received + 5) || (it.second.fee >= HIGH_FEE_VALUE)) { if ((cur_time >= it.second.time_received + 10) || (it.second.fee >= HIGH_FEE_VALUE)) {
m_mempoolTxs.emplace_back(it.second); m_mempoolTxs.emplace_back(it.second);
} }
} }
@@ -639,91 +570,43 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_poolBlockTemplate->m_minerWallet = *miner_wallet; m_poolBlockTemplate->m_minerWallet = *miner_wallet;
// Layout: [software id, version, random number, sidechain extra_nonce] m_poolBlockTemplate->serialize_sidechain_data();
uint32_t* sidechain_extra = m_poolBlockTemplate->m_sidechainExtraBuf; m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash();
sidechain_extra[0] = 0; const int sidechain_hash_offset = static_cast<int>(m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize) + 2;
sidechain_extra[1] = (P2POOL_VERSION_MAJOR << 16) | P2POOL_VERSION_MINOR;
sidechain_extra[2] = static_cast<uint32_t>(m_rng() >> 32);
sidechain_extra[3] = 0;
m_poolBlockTemplate->m_nonce = 0;
m_poolBlockTemplate->m_extraNonce = 0;
m_poolBlockTemplate->m_sidechainId = {};
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
const std::vector<uint8_t>& consensus_id = m_sidechain->consensus_id();
m_sidechainHashBlob = m_poolBlockTemplate->serialize_mainchain_data();
m_sidechainHashBlob.insert(m_sidechainHashBlob.end(), sidechain_data.begin(), sidechain_data.end());
m_sidechainHashBlob.insert(m_sidechainHashBlob.end(), consensus_id.begin(), consensus_id.end());
{
memset(m_sidechainHashKeccakState, 0, sizeof(m_sidechainHashKeccakState));
const size_t extra_nonce_offset = m_sidechainHashBlob.size() - HASH_SIZE - ((sidechain_version > 1) ? EXTRA_NONCE_SIZE : 0);
if (extra_nonce_offset >= KeccakParams::HASH_DATA_AREA) {
// Sidechain data is big enough to cache keccak state up to extra_nonce
m_sidechainHashInputLength = (extra_nonce_offset / KeccakParams::HASH_DATA_AREA) * KeccakParams::HASH_DATA_AREA;
const uint8_t* in = m_sidechainHashBlob.data();
int inlen = static_cast<int>(m_sidechainHashInputLength);
keccak_step(in, inlen, m_sidechainHashKeccakState);
}
else {
m_sidechainHashInputLength = 0;
}
}
m_fullDataBlob = m_blockTemplateBlob;
m_fullDataBlob.insert(m_fullDataBlob.end(), sidechain_data.begin(), sidechain_data.end());
m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(0);
if (pool_block_debug()) {
const size_t sidechain_hash_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 2;
memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE); memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
memcpy(m_fullDataBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE); memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
#if POOL_BLOCK_DEBUG
const std::vector<uint8_t> mainchain_data = m_poolBlockTemplate->serialize_mainchain_data(); 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) { 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, "mainchain_data.size() = " << mainchain_data.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(mainchain_data.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 (mainchain_data[i] != m_blockTemplateBlob[i]) { if (mainchain_data[i] != m_blockTemplateBlob[i]) {
LOGERR(1, "mainchain_data is different at offset " << i); LOGERR(1, "m_poolBlockTemplate->m_mainChainData is different at offset " << i);
break; break;
} }
} }
} }
{
std::vector<uint8_t> buf = m_blockTemplateBlob;
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
PoolBlock check; PoolBlock check;
const int result = check.deserialize(m_fullDataBlob.data(), m_fullDataBlob.size(), *m_sidechain, nullptr, false); const int result = check.deserialize(buf.data(), buf.size(), *m_sidechain, nullptr, false);
if (result != 0) { if (result != 0) {
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result); LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
} }
else { else {
LOGINFO(6, "blob size = " << m_fullDataBlob.size()); LOGINFO(6, "blob size = " << buf.size());
} }
} }
#endif
memset(m_minerTxKeccakState, 0, sizeof(m_minerTxKeccakState));
const size_t extra_nonce_offset = m_extraNonceOffsetInTemplate - m_minerTxOffsetInTemplate;
if (extra_nonce_offset >= KeccakParams::HASH_DATA_AREA) {
// Miner transaction is big enough to cache keccak state up to extra_nonce
m_minerTxKeccakStateInputLength = (extra_nonce_offset / KeccakParams::HASH_DATA_AREA) * KeccakParams::HASH_DATA_AREA;
const uint8_t* in = m_blockTemplateBlob.data() + m_minerTxOffsetInTemplate;
int inlen = static_cast<int>(m_minerTxKeccakStateInputLength);
keccak_step(in, inlen, m_minerTxKeccakState);
}
else {
m_minerTxKeccakStateInputLength = 0;
}
const hash minerTx_hash = calc_miner_tx_hash(0); const hash minerTx_hash = calc_miner_tx_hash(0);
@@ -877,7 +760,7 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
} }
else { else {
hash eph_public_key; hash eph_public_key;
if (!shares[i].m_wallet->get_eph_public_key(m_poolBlockTemplate->m_txkeySec, i, eph_public_key, view_tag)) { if (!shares[i].m_wallet->get_eph_public_key(m_txkeySec, i, eph_public_key, view_tag)) {
LOGERR(1, "get_eph_public_key failed at index " << i); LOGERR(1, "get_eph_public_key failed at index " << i);
} }
m_minerTx.insert(m_minerTx.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE); m_minerTx.insert(m_minerTx.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
@@ -900,11 +783,14 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
return -2; return -2;
} }
m_poolBlockTemplate->m_txkeyPub = m_txkeyPub;
m_poolBlockTemplate->m_txkeySec = m_txkeySec;
// TX_EXTRA begin // TX_EXTRA begin
m_minerTxExtra.clear(); m_minerTxExtra.clear();
m_minerTxExtra.push_back(TX_EXTRA_TAG_PUBKEY); m_minerTxExtra.push_back(TX_EXTRA_TAG_PUBKEY);
m_minerTxExtra.insert(m_minerTxExtra.end(), m_poolBlockTemplate->m_txkeyPub.h, m_poolBlockTemplate->m_txkeyPub.h + HASH_SIZE); m_minerTxExtra.insert(m_minerTxExtra.end(), m_txkeyPub.h, m_txkeyPub.h + HASH_SIZE);
m_minerTxExtra.push_back(TX_EXTRA_NONCE); m_minerTxExtra.push_back(TX_EXTRA_NONCE);
@@ -942,60 +828,48 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
return 1; return 1;
} }
hash BlockTemplate::calc_sidechain_hash(uint32_t sidechain_extra_nonce) const hash BlockTemplate::calc_sidechain_hash() const
{ {
// Calculate side-chain hash (all block template bytes + all side-chain bytes + consensus ID, replacing NONCE, EXTRA_NONCE and HASH itself with 0's) // Calculate side-chain hash (all block template bytes + all side-chain bytes + consensus ID, replacing NONCE, EXTRA_NONCE and HASH itself with 0's)
const int v = m_poolBlockTemplate->get_sidechain_version(); hash sidechain_hash;
const size_t size = m_sidechainHashBlob.size(); const int sidechain_hash_offset = static_cast<int>(m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize) + 2;
const size_t N = m_sidechainHashInputLength; const int blob_size = static_cast<int>(m_blockTemplateBlob.size());
const size_t sidechain_extra_nonce_offset = size - HASH_SIZE - ((v > 1) ? EXTRA_NONCE_SIZE : 0); const std::vector<uint8_t>& consensus_id = m_sidechain->consensus_id();
const uint8_t sidechain_extra_nonce_buf[EXTRA_NONCE_SIZE] = { const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
static_cast<uint8_t>(sidechain_extra_nonce >> 0),
static_cast<uint8_t>(sidechain_extra_nonce >> 8),
static_cast<uint8_t>(sidechain_extra_nonce >> 16),
static_cast<uint8_t>(sidechain_extra_nonce >> 24)
};
hash result; keccak_custom([this, sidechain_hash_offset, blob_size, consensus_id, &sidechain_data](int offset) -> uint8_t {
uint8_t buf[288]; uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(m_nonceOffset));
if (k < NONCE_SIZE) {
return 0;
}
const bool b = N && (N <= sidechain_extra_nonce_offset) && (N < size) && (size - N <= sizeof(buf)); k = static_cast<uint32_t>(offset - static_cast<int>(m_extraNonceOffsetInTemplate));
// Slow path: O(N)
if (!b || pool_block_debug()) {
keccak_custom([this, v, sidechain_extra_nonce_offset, &sidechain_extra_nonce_buf](int offset) -> uint8_t {
if (v > 1) {
const uint32_t k = static_cast<uint32_t>(offset - sidechain_extra_nonce_offset);
if (k < EXTRA_NONCE_SIZE) { if (k < EXTRA_NONCE_SIZE) {
return sidechain_extra_nonce_buf[k]; return 0;
}
}
return m_sidechainHashBlob[offset];
}, static_cast<int>(size), result.h, HASH_SIZE);
} }
// Fast path: O(1) k = static_cast<uint32_t>(offset - sidechain_hash_offset);
if (b) { if (k < HASH_SIZE) {
const int inlen = static_cast<int>(size - N); return 0;
memcpy(buf, m_sidechainHashBlob.data() + N, size - N);
if (v > 1) {
memcpy(buf + sidechain_extra_nonce_offset - N, sidechain_extra_nonce_buf, EXTRA_NONCE_SIZE);
} }
uint64_t st[25]; if (offset < blob_size) {
memcpy(st, m_sidechainHashKeccakState, sizeof(st)); return m_blockTemplateBlob[offset];
keccak_finish(buf, inlen, st);
if (pool_block_debug() && (memcmp(st, result.h, HASH_SIZE) != 0)) {
LOGERR(1, "calc_sidechain_hash fast path is broken. Fix the code!");
} }
memcpy(result.h, st, HASH_SIZE); const int side_chain_data_offsset = offset - blob_size;
const int side_chain_data_size = static_cast<int>(sidechain_data.size());
if (side_chain_data_offsset < side_chain_data_size) {
return sidechain_data[side_chain_data_offsset];
} }
return result; const int consensus_id_offset = side_chain_data_offsset - side_chain_data_size;
return consensus_id[consensus_id_offset];
},
static_cast<int>(m_blockTemplateBlob.size() + sidechain_data.size() + consensus_id.size()), sidechain_hash.h, HASH_SIZE);
return sidechain_hash;
} }
hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
@@ -1005,7 +879,7 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
const uint8_t* data = m_blockTemplateBlob.data() + m_minerTxOffsetInTemplate; const uint8_t* data = m_blockTemplateBlob.data() + m_minerTxOffsetInTemplate;
const size_t extra_nonce_offset = m_extraNonceOffsetInTemplate - m_minerTxOffsetInTemplate; const int extra_nonce_offset = static_cast<int>(m_extraNonceOffsetInTemplate - m_minerTxOffsetInTemplate);
const uint8_t extra_nonce_buf[EXTRA_NONCE_SIZE] = { const uint8_t extra_nonce_buf[EXTRA_NONCE_SIZE] = {
static_cast<uint8_t>(extra_nonce >> 0), static_cast<uint8_t>(extra_nonce >> 0),
static_cast<uint8_t>(extra_nonce >> 8), static_cast<uint8_t>(extra_nonce >> 8),
@@ -1013,57 +887,17 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
static_cast<uint8_t>(extra_nonce >> 24) static_cast<uint8_t>(extra_nonce >> 24)
}; };
// Calculate sidechain id with this extra_nonce
const hash sidechain_id = calc_sidechain_hash(extra_nonce);
const size_t sidechain_hash_offset = extra_nonce_offset + m_poolBlockTemplate->m_extraNonceSize + 2;
// 1. Prefix (everything except vin_rct_type byte in the end) // 1. Prefix (everything except vin_rct_type byte in the end)
// Apply extra_nonce in-place because we can't write to the block template here // Apply extra_nonce in-place because we can't write to the block template here
const size_t tx_size = m_minerTxSize - 1; keccak_custom([data, extra_nonce_offset, &extra_nonce_buf](int offset)
hash full_hash;
uint8_t tx_buf[288];
const size_t N = m_minerTxKeccakStateInputLength;
const bool b = N && (N <= extra_nonce_offset) && (N < tx_size) && (tx_size - N <= sizeof(tx_buf));
// Slow path: O(N)
if (!b || pool_block_debug())
{ {
keccak_custom([data, extra_nonce_offset, &extra_nonce_buf, sidechain_hash_offset, &sidechain_id](int offset) { const uint32_t k = static_cast<uint32_t>(offset - extra_nonce_offset);
uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(extra_nonce_offset));
if (k < EXTRA_NONCE_SIZE) { if (k < EXTRA_NONCE_SIZE) {
return extra_nonce_buf[k]; return extra_nonce_buf[k];
} }
k = static_cast<uint32_t>(offset - static_cast<int>(sidechain_hash_offset));
if (k < HASH_SIZE) {
return sidechain_id.h[k];
}
return data[offset]; return data[offset];
}, static_cast<int>(tx_size), full_hash.h, HASH_SIZE); },
memcpy(hashes, full_hash.h, HASH_SIZE); static_cast<int>(m_minerTxSize) - 1, hashes, HASH_SIZE);
}
// Fast path: O(1)
if (b) {
const int inlen = static_cast<int>(tx_size - N);
memcpy(tx_buf, data + N, inlen);
memcpy(tx_buf + extra_nonce_offset - N, extra_nonce_buf, EXTRA_NONCE_SIZE);
memcpy(tx_buf + sidechain_hash_offset - N, sidechain_id.h, HASH_SIZE);
uint64_t st[25];
memcpy(st, m_minerTxKeccakState, sizeof(st));
keccak_finish(tx_buf, inlen, st);
if (pool_block_debug() && (memcmp(st, full_hash.h, HASH_SIZE) != 0)) {
LOGERR(1, "calc_miner_tx_hash fast path is broken. Fix the code!");
}
memcpy(hashes, st, HASH_SIZE);
}
// 2. Base RCT, single 0 byte in miner tx // 2. Base RCT, single 0 byte in miner tx
static constexpr uint8_t known_second_hash[HASH_SIZE] = { static constexpr uint8_t known_second_hash[HASH_SIZE] = {
@@ -1076,7 +910,7 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
// Calculate miner transaction hash // Calculate miner transaction hash
hash result; hash result;
keccak(hashes, sizeof(hashes), result.h); keccak(hashes, sizeof(hashes), result.h, HASH_SIZE);
return result; return result;
} }
@@ -1086,14 +920,16 @@ void BlockTemplate::calc_merkle_tree_main_branch()
m_merkleTreeMainBranch.clear(); m_merkleTreeMainBranch.clear();
const uint64_t count = m_numTransactionHashes + 1; const uint64_t count = m_numTransactionHashes + 1;
if (count == 1) {
return;
}
const uint8_t* h = m_transactionHashes.data(); const uint8_t* h = m_transactionHashes.data();
if (count == 2) { hash root_hash;
if (count == 1) {
memcpy(root_hash.h, h, HASH_SIZE);
}
else if (count == 2) {
m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), h + HASH_SIZE, h + HASH_SIZE * 2); m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), h + HASH_SIZE, h + HASH_SIZE * 2);
keccak(h, HASH_SIZE * 2, root_hash.h, HASH_SIZE);
} }
else { else {
size_t i, j, cnt; size_t i, j, cnt;
@@ -1105,14 +941,11 @@ void BlockTemplate::calc_merkle_tree_main_branch()
std::vector<uint8_t> ints(cnt * HASH_SIZE); std::vector<uint8_t> ints(cnt * HASH_SIZE);
memcpy(ints.data(), h, (cnt * 2 - count) * HASH_SIZE); memcpy(ints.data(), h, (cnt * 2 - count) * HASH_SIZE);
hash tmp;
for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) { for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
if (i == 0) { if (i == 0) {
m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), h + HASH_SIZE, h + HASH_SIZE * 2); m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), h + HASH_SIZE, h + HASH_SIZE * 2);
} }
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, tmp.h); keccak(h + i * HASH_SIZE, HASH_SIZE * 2, ints.data() + j * HASH_SIZE, HASH_SIZE);
memcpy(ints.data() + j * HASH_SIZE, tmp.h, HASH_SIZE);
} }
while (cnt > 2) { while (cnt > 2) {
@@ -1121,12 +954,12 @@ void BlockTemplate::calc_merkle_tree_main_branch()
if (i == 0) { if (i == 0) {
m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2); m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2);
} }
keccak(ints.data() + i * HASH_SIZE, HASH_SIZE * 2, tmp.h); keccak(ints.data() + i * HASH_SIZE, HASH_SIZE * 2, ints.data() + j * HASH_SIZE, HASH_SIZE);
memcpy(ints.data() + j * HASH_SIZE, tmp.h, HASH_SIZE);
} }
} }
m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2); m_merkleTreeMainBranch.insert(m_merkleTreeMainBranch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2);
keccak(ints.data(), HASH_SIZE * 2, root_hash.h, HASH_SIZE);
} }
} }
@@ -1206,7 +1039,7 @@ uint32_t BlockTemplate::get_hashing_blob_nolock(uint32_t extra_nonce, uint8_t* b
memcpy(h, root_hash.h, HASH_SIZE); memcpy(h, root_hash.h, HASH_SIZE);
memcpy(h + HASH_SIZE, m_merkleTreeMainBranch.data() + i, HASH_SIZE); memcpy(h + HASH_SIZE, m_merkleTreeMainBranch.data() + i, HASH_SIZE);
keccak(h, HASH_SIZE * 2, root_hash.h); keccak(h, HASH_SIZE * 2, root_hash.h, HASH_SIZE);
} }
memcpy(p, root_hash.h, HASH_SIZE); memcpy(p, root_hash.h, HASH_SIZE);
@@ -1262,27 +1095,23 @@ uint32_t BlockTemplate::get_hashing_blobs(uint32_t extra_nonce_start, uint32_t c
return blob_size; return blob_size;
} }
std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id, uint32_t sidechain_extra_nonce, size_t& nonce_offset, size_t& extra_nonce_offset, size_t& sidechain_id_offset, hash& sidechain_id) const std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const
{ {
ReadLock lock(m_lock); ReadLock lock(m_lock);
if (template_id != m_templateId) { if (template_id != m_templateId) {
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)]; const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
if (old && (template_id == old->m_templateId)) { if (old && (template_id == old->m_templateId)) {
return old->get_block_template_blob(template_id, sidechain_extra_nonce, nonce_offset, extra_nonce_offset, sidechain_id_offset, sidechain_id); return old->get_block_template_blob(template_id, nonce_offset, extra_nonce_offset);
} }
nonce_offset = 0; nonce_offset = 0;
extra_nonce_offset = 0; extra_nonce_offset = 0;
sidechain_id_offset = 0;
sidechain_id = {};
return std::vector<uint8_t>(); return std::vector<uint8_t>();
} }
nonce_offset = m_nonceOffset; nonce_offset = m_nonceOffset;
extra_nonce_offset = m_extraNonceOffsetInTemplate; extra_nonce_offset = m_extraNonceOffsetInTemplate;
sidechain_id_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 2;
sidechain_id = calc_sidechain_hash(sidechain_extra_nonce);
return m_blockTemplateBlob; return m_blockTemplateBlob;
} }
@@ -1293,10 +1122,9 @@ bool 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;
m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(extra_nonce);
m_poolBlockTemplate->m_sidechainExtraBuf[3] = extra_nonce;
if (pool_block_debug()) { #if POOL_BLOCK_DEBUG
{
std::vector<uint8_t> buf = m_poolBlockTemplate->serialize_mainchain_data(); std::vector<uint8_t> buf = m_poolBlockTemplate->serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data(); const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
@@ -1321,6 +1149,7 @@ bool BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
} }
} }
} }
#endif
m_poolBlockTemplate->m_verified = true; m_poolBlockTemplate->m_verified = true;
if (!m_sidechain->block_seen(*m_poolBlockTemplate)) { if (!m_sidechain->block_seen(*m_poolBlockTemplate)) {
+6 -16
View File
@@ -48,7 +48,7 @@ public:
uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, uint64_t& sidechain_height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const; uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, uint64_t& sidechain_height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
uint32_t get_hashing_blobs(uint32_t extra_nonce_start, uint32_t count, std::vector<uint8_t>& blobs, uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const; uint32_t get_hashing_blobs(uint32_t extra_nonce_start, uint32_t count, std::vector<uint8_t>& blobs, uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
std::vector<uint8_t> get_block_template_blob(uint32_t template_id, uint32_t sidechain_extra_nonce, size_t& nonce_offset, size_t& extra_nonce_offset, size_t& sidechain_id_offset, hash& sidechain_id) const; std::vector<uint8_t> get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const;
FORCEINLINE uint64_t height() const { return m_height; } FORCEINLINE uint64_t height() const { return m_height; }
FORCEINLINE difficulty_type difficulty() const { return m_difficulty; } FORCEINLINE difficulty_type difficulty() const { return m_difficulty; }
@@ -56,12 +56,7 @@ public:
bool submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce); bool submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
FORCEINLINE const std::vector<MinerShare>& shares() const { return m_shares; } FORCEINLINE const std::vector<MinerShare>& shares() const { return m_shares; }
FORCEINLINE uint64_t get_reward() const { return m_finalReward; }
#ifdef P2POOL_UNIT_TESTS
FORCEINLINE const PoolBlock* pool_block_template() const { return m_poolBlockTemplate; } FORCEINLINE const PoolBlock* pool_block_template() const { return m_poolBlockTemplate; }
FORCEINLINE std::mt19937_64& rng() { return m_rng; }
#endif
private: private:
SideChain* m_sidechain; SideChain* m_sidechain;
@@ -69,7 +64,7 @@ private:
private: private:
int create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run); int create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run);
hash calc_sidechain_hash(uint32_t sidechain_extra_nonce) const; hash calc_sidechain_hash() const;
hash calc_miner_tx_hash(uint32_t extra_nonce) const; hash calc_miner_tx_hash(uint32_t extra_nonce) const;
void calc_merkle_tree_main_branch(); void calc_merkle_tree_main_branch();
@@ -81,7 +76,6 @@ private:
std::atomic<uint64_t> m_lastUpdated; std::atomic<uint64_t> m_lastUpdated;
std::vector<uint8_t> m_blockTemplateBlob; std::vector<uint8_t> m_blockTemplateBlob;
std::vector<uint8_t> m_fullDataBlob;
std::vector<uint8_t> m_merkleTreeMainBranch; std::vector<uint8_t> m_merkleTreeMainBranch;
size_t m_blockHeaderSize; size_t m_blockHeaderSize;
@@ -98,21 +92,17 @@ private:
uint64_t m_timestamp; uint64_t m_timestamp;
hash m_txkeyPub;
hash m_txkeySec;
PoolBlock* m_poolBlockTemplate; PoolBlock* m_poolBlockTemplate;
BlockTemplate* m_oldTemplates[4] = {}; BlockTemplate* m_oldTemplates[4] = {};
std::atomic<uint64_t> m_finalReward; uint64_t m_finalReward;
// Temp vectors, will be cleaned up after use and skipped in copy constructor/assignment operators // Temp vectors, will be cleaned up after use and skipped in copy constructor/assignment operators
std::vector<uint8_t> m_minerTx; std::vector<uint8_t> m_minerTx;
uint64_t m_minerTxKeccakState[25];
size_t m_minerTxKeccakStateInputLength;
std::vector<uint8_t> m_sidechainHashBlob;
uint64_t m_sidechainHashKeccakState[25];
size_t m_sidechainHashInputLength;
std::vector<uint8_t> m_blockHeader; std::vector<uint8_t> m_blockHeader;
std::vector<uint8_t> m_minerTxExtra; std::vector<uint8_t> m_minerTxExtra;
std::vector<uint8_t> m_transactionHashes; std::vector<uint8_t> m_transactionHashes;
+5 -10
View File
@@ -92,9 +92,9 @@
namespace p2pool { namespace p2pool {
constexpr size_t HASH_SIZE = 32; constexpr size_t HASH_SIZE = 32;
constexpr uint8_t HARDFORK_VIEW_TAGS_VERSION = 20; constexpr uint8_t HARDFORK_VIEW_TAGS_VERSION = 15;
constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 20; constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 16;
constexpr uint16_t MINER_REWARD_UNLOCK_TIME = 288; constexpr uint8_t MINER_REWARD_UNLOCK_TIME = 60;
constexpr uint8_t NONCE_SIZE = 4; constexpr uint8_t NONCE_SIZE = 4;
constexpr uint8_t EXTRA_NONCE_SIZE = 4; constexpr uint8_t EXTRA_NONCE_SIZE = 4;
constexpr uint8_t EXTRA_NONCE_MAX_SIZE = EXTRA_NONCE_SIZE + 10; constexpr uint8_t EXTRA_NONCE_MAX_SIZE = EXTRA_NONCE_SIZE + 10;
@@ -185,8 +185,8 @@ struct
#endif #endif
difficulty_type difficulty_type
{ {
FORCEINLINE constexpr difficulty_type() : lo(0), hi(0) {} FORCEINLINE difficulty_type() : lo(0), hi(0) {}
FORCEINLINE constexpr difficulty_type(uint64_t a, uint64_t b) : lo(a), hi(b) {} FORCEINLINE difficulty_type(uint64_t a, uint64_t b) : lo(a), hi(b) {}
uint64_t lo; uint64_t lo;
uint64_t hi; uint64_t hi;
@@ -254,10 +254,7 @@ struct
return (lo < other.lo); return (lo < other.lo);
} }
FORCEINLINE bool operator>(const difficulty_type& other) const { return other.operator<(*this); }
FORCEINLINE bool operator>=(const difficulty_type& other) const { return !operator<(other); } FORCEINLINE bool operator>=(const difficulty_type& other) const { return !operator<(other); }
FORCEINLINE bool operator<=(const difficulty_type& other) const { return !operator>(other); }
FORCEINLINE bool operator==(const difficulty_type& other) const { return (lo == other.lo) && (hi == other.hi); } FORCEINLINE bool operator==(const difficulty_type& other) const { return (lo == other.lo) && (hi == other.hi); }
FORCEINLINE bool operator!=(const difficulty_type& other) const { return (lo != other.lo) || (hi != other.hi); } FORCEINLINE bool operator!=(const difficulty_type& other) const { return (lo != other.lo) || (hi != other.hi); }
@@ -297,8 +294,6 @@ struct
static_assert(sizeof(difficulty_type) == sizeof(uint64_t) * 2, "struct difficulty_type has invalid size, check your compiler options"); static_assert(sizeof(difficulty_type) == sizeof(uint64_t) * 2, "struct difficulty_type has invalid size, check your compiler options");
static_assert(std::is_standard_layout<difficulty_type>::value, "struct difficulty_type is not a POD, check your compiler options"); static_assert(std::is_standard_layout<difficulty_type>::value, "struct difficulty_type is not a POD, check your compiler options");
static constexpr difficulty_type diff_max = { std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max() };
template<typename T> template<typename T>
FORCEINLINE difficulty_type operator+(const difficulty_type& a, const T& b) FORCEINLINE difficulty_type operator+(const difficulty_type& a, const T& b)
{ {
+7 -7
View File
@@ -134,7 +134,7 @@ bool check_keys(const hash& pub, const hash& sec)
static FORCEINLINE void hash_to_scalar(const uint8_t* data, int length, uint8_t (&res)[HASH_SIZE]) static FORCEINLINE void hash_to_scalar(const uint8_t* data, int length, uint8_t (&res)[HASH_SIZE])
{ {
keccak(data, length, res); keccak(data, length, res, HASH_SIZE);
sc_reduce32(res); sc_reduce32(res);
} }
@@ -266,10 +266,10 @@ public:
return true; return true;
} }
void get_tx_keys(hash& pub, hash& sec, const hash& seed, const hash& monero_block_id) void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id)
{ {
std::array<uint8_t, HASH_SIZE * 2> index; std::array<uint8_t, HASH_SIZE * 2> index;
memcpy(index.data(), seed.h, HASH_SIZE); memcpy(index.data(), wallet_spend_key.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE); memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE);
{ {
@@ -287,7 +287,7 @@ public:
uint8_t entropy[N + HASH_SIZE * 2]; uint8_t entropy[N + HASH_SIZE * 2];
memcpy(entropy, domain, N); memcpy(entropy, domain, N);
memcpy(entropy + N, seed.h, HASH_SIZE); memcpy(entropy + N, wallet_spend_key.h, HASH_SIZE);
memcpy(entropy + N + HASH_SIZE, monero_block_id.h, HASH_SIZE); memcpy(entropy + N + HASH_SIZE, monero_block_id.h, HASH_SIZE);
generate_keys_deterministic(pub, sec, entropy, sizeof(entropy)); generate_keys_deterministic(pub, sec, entropy, sizeof(entropy));
@@ -399,9 +399,9 @@ bool derive_public_key(const hash& derivation, size_t output_index, const hash&
return cache->get_public_key(derivation, output_index, base, derived_key); return cache->get_public_key(derivation, output_index, base, derived_key);
} }
void get_tx_keys(hash& pub, hash& sec, const hash& seed, const hash& monero_block_id) void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id)
{ {
cache->get_tx_keys(pub, sec, seed, monero_block_id); cache->get_tx_keys(pub, sec, wallet_spend_key, monero_block_id);
} }
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag) void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag)
@@ -416,7 +416,7 @@ void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_
writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; }); writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; });
hash view_tag_full; hash view_tag_full;
keccak(buf, static_cast<int>(p - buf), view_tag_full.h); keccak(buf, static_cast<int>(p - buf), view_tag_full.h, HASH_SIZE);
view_tag = view_tag_full.h[0]; view_tag = view_tag_full.h[0];
} }
+1 -1
View File
@@ -21,7 +21,7 @@ namespace p2pool {
void generate_keys(hash& pub, hash& sec); void generate_keys(hash& pub, hash& sec);
void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len); void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len);
void get_tx_keys(hash& pub, hash& sec, const hash& seed, const hash& monero_block_id); void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id);
bool check_keys(const hash& pub, const hash& sec); bool check_keys(const hash& pub, const hash& sec);
bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag); bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag);
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key); bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key);
+1 -1
View File
@@ -315,7 +315,7 @@ int CurlContext::on_timer(CURLM* /*multi*/, long timeout_ms)
return 0; return 0;
} }
if ((timeout_ms == 0) && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_async))) { if (timeout_ms == 0) {
// 0 ms timeout, but we can't just call on_timeout() here - we have to kick the UV loop // 0 ms timeout, but we can't just call on_timeout() here - we have to kick the UV loop
const int result = uv_async_send(&m_async); const int result = uv_async_send(&m_async);
if (result < 0) { if (result < 0) {
+16 -13
View File
@@ -24,7 +24,7 @@ namespace p2pool {
#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y)))) #define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y))))
#endif #endif
static const uint64_t keccakf_rndc[24] = const uint64_t keccakf_rndc[24] =
{ {
0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
0x8000000080008000, 0x000000000000808b, 0x0000000080000001, 0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
@@ -36,7 +36,7 @@ static const uint64_t keccakf_rndc[24] =
0x8000000000008080, 0x0000000080000001, 0x8000000080008008 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
}; };
NOINLINE void keccakf(uint64_t (&st)[25]) NOINLINE void keccakf(uint64_t* st)
{ {
for (int round = 0; round < KeccakParams::ROUNDS; ++round) { for (int round = 0; round < KeccakParams::ROUNDS; ++round) {
uint64_t bc[5]; uint64_t bc[5];
@@ -115,10 +115,14 @@ NOINLINE void keccakf(uint64_t (&st)[25])
} }
} }
NOINLINE void keccak_step(const uint8_t* &in, int &inlen, uint64_t (&st)[25]) NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen)
{ {
constexpr int rsiz = KeccakParams::HASH_DATA_AREA; uint64_t st[25];
constexpr int rsizw = rsiz / 8;
const int rsiz = sizeof(st) == mdlen ? KeccakParams::HASH_DATA_AREA : 200 - 2 * mdlen;
const int rsizw = rsiz / 8;
memset(st, 0, sizeof(st));
for (; inlen >= rsiz; inlen -= rsiz, in += rsiz) { for (; inlen >= rsiz; inlen -= rsiz, in += rsiz) {
for (int i = 0; i < rsizw; i++) { for (int i = 0; i < rsizw; i++) {
@@ -126,14 +130,6 @@ NOINLINE void keccak_step(const uint8_t* &in, int &inlen, uint64_t (&st)[25])
} }
keccakf(st); keccakf(st);
} }
}
NOINLINE void keccak_finish(const uint8_t* in, int inlen, uint64_t (&st)[25])
{
constexpr int rsiz = KeccakParams::HASH_DATA_AREA;
constexpr int rsizw = rsiz / 8;
keccak_step(in, inlen, st);
// last block and padding // last block and padding
alignas(8) uint8_t temp[144]; alignas(8) uint8_t temp[144];
@@ -148,6 +144,13 @@ NOINLINE void keccak_finish(const uint8_t* in, int inlen, uint64_t (&st)[25])
} }
keccakf(st); keccakf(st);
memcpy(md, st, mdlen);
}
void keccak(const uint8_t *in, int inlen, uint8_t (&md)[200])
{
keccak(in, inlen, md, 200);
} }
} // namespace p2pool } // namespace p2pool
+6 -15
View File
@@ -24,29 +24,20 @@ enum KeccakParams {
ROUNDS = 24, ROUNDS = 24,
}; };
void keccakf(uint64_t (&st)[25]); void keccakf(uint64_t* st);
void keccak_step(const uint8_t* &in, int &inlen, uint64_t (&st)[25]); void keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen);
void keccak_finish(const uint8_t* in, int inlen, uint64_t (&st)[25]); void keccak(const uint8_t* in, int inlen, uint8_t (&md)[200]);
template<size_t N>
FORCEINLINE void keccak(const uint8_t* in, int inlen, uint8_t (&md)[N])
{
static_assert((N == 32) || (N == 200), "invalid size");
uint64_t st[25] = {};
keccak_step(in, inlen, st);
keccak_finish(in, inlen, st);
memcpy(md, st, N);
}
template<typename T> template<typename T>
FORCEINLINE void keccak_custom(T&& in, int inlen, uint8_t* md, int mdlen) FORCEINLINE void keccak_custom(T&& in, int inlen, uint8_t* md, int mdlen)
{ {
uint64_t st[25] = {}; uint64_t st[25];
const int rsiz = sizeof(st) == mdlen ? KeccakParams::HASH_DATA_AREA : 200 - 2 * mdlen; const int rsiz = sizeof(st) == mdlen ? KeccakParams::HASH_DATA_AREA : 200 - 2 * mdlen;
const int rsizw = rsiz / 8; const int rsizw = rsiz / 8;
memset(st, 0, sizeof(st));
int offset = 0; int offset = 0;
for (; inlen >= rsiz; inlen -= rsiz, offset += rsiz) { for (; inlen >= rsiz; inlen -= rsiz, offset += rsiz) {
+5 -5
View File
@@ -364,15 +364,15 @@ template<> struct log::Stream::Entry<XMRAmount>
{ {
static NOINLINE void put(XMRAmount value, Stream* wrapper) static NOINLINE void put(XMRAmount value, Stream* wrapper)
{ {
constexpr uint64_t denomination = 100000000000ULL; constexpr uint64_t denomination = 1000000000000ULL;
const int w = wrapper->getNumberWidth(); const int w = wrapper->getNumberWidth();
wrapper->setNumberWidth(1); wrapper->setNumberWidth(1);
*wrapper << value.m_data / denomination << '.'; *wrapper << value.m_data / denomination << '.';
wrapper->setNumberWidth(11); wrapper->setNumberWidth(12);
*wrapper << value.m_data % denomination << " WOW"; *wrapper << value.m_data % denomination << " XMR";
wrapper->setNumberWidth(w); wrapper->setNumberWidth(w);
} }
@@ -492,7 +492,7 @@ struct DummyStream
MSVC_PRAGMA(warning(suppress:26444)) \ MSVC_PRAGMA(warning(suppress:26444)) \
[=]() { \ [=]() { \
log::DummyStream x; \ log::DummyStream x; \
x << (level) << __VA_ARGS__; \ x << level << __VA_ARGS__; \
}; \ }; \
} \ } \
} while (0) } while (0)
@@ -508,7 +508,7 @@ struct DummyStream
#define LOG(level, severity, ...) \ #define LOG(level, severity, ...) \
do { \ do { \
SIDE_EFFECT_CHECK(level, __VA_ARGS__); \ SIDE_EFFECT_CHECK(level, __VA_ARGS__); \
if ((level) <= log::GLOBAL_LOG_LEVEL) { \ if (level <= log::GLOBAL_LOG_LEVEL) { \
log::Writer CONCAT(log_wrapper_, __LINE__)(severity); \ log::Writer CONCAT(log_wrapper_, __LINE__)(severity); \
CONCAT(log_wrapper_, __LINE__) << log::Gray() << log_category_prefix; \ CONCAT(log_wrapper_, __LINE__) << log::Gray() << log_category_prefix; \
log::apply_severity<severity>(CONCAT(log_wrapper_, __LINE__)); \ log::apply_severity<severity>(CONCAT(log_wrapper_, __LINE__)); \
+6 -6
View File
@@ -27,9 +27,9 @@ void p2pool_usage()
printf("P2Pool %s\n" printf("P2Pool %s\n"
"\nUsage:\n\n" \ "\nUsage:\n\n" \
"--wallet Wallet address to mine to. Subaddresses and integrated addresses are not supported!\n" "--wallet Wallet address to mine to. Subaddresses and integrated addresses are not supported!\n"
"--host IP address of your Wownero node, default is 127.0.0.1\n" "--host IP address of your Monero node, default is 127.0.0.1\n"
"--rpc-port wownerod RPC API port number, default is 34568\n" "--rpc-port monerod RPC API port number, default is 18081\n"
"--zmq-port wownerod ZMQ pub port number, default is 34569 (same port as in wownerod's \"--zmq-pub\" command line parameter)\n" "--zmq-port monerod ZMQ pub port number, default is 18083 (same port as in monerod's \"--zmq-pub\" command line parameter)\n"
"--stratum Comma-separated list of IP:port for stratum server to listen on\n" "--stratum Comma-separated list of IP:port for stratum server to listen on\n"
"--p2p Comma-separated list of IP:port for p2p server to listen on\n" "--p2p Comma-separated list of IP:port for p2p server to listen on\n"
"--addpeers Comma-separated list of IP:port of other p2pool nodes to connect to\n" "--addpeers Comma-separated list of IP:port of other p2pool nodes to connect to\n"
@@ -41,19 +41,19 @@ void p2pool_usage()
"--stratum-api An alias for --local-api\n" "--stratum-api An alias for --local-api\n"
"--no-cache Disable p2pool.cache\n" "--no-cache Disable p2pool.cache\n"
"--no-color Disable colors in console output\n" "--no-color Disable colors in console output\n"
"--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to wownerod to check PoW hashes\n" "--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes\n"
"--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 450)\n" "--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 450)\n"
"--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 450)\n" "--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 450)\n"
"--start-mining N Start built-in miner using N threads (any value between 1 and 64)\n" "--start-mining N Start built-in miner using N threads (any value between 1 and 64)\n"
"--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d\n" "--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d\n"
"--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum\n" "--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum\n"
"--rpc-login Specify username[:password] required for Wownero 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" "--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 34568 --zmq-port 34569 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n", "%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",
p2pool::VERSION, p2pool::VERSION,
p2pool::log::MAX_GLOBAL_LOG_LEVEL, p2pool::log::MAX_GLOBAL_LOG_LEVEL,
p2pool::DEFAULT_P2P_PORT, p2pool::DEFAULT_P2P_PORT,
+10 -36
View File
@@ -39,17 +39,15 @@ Miner::Miner(p2pool* pool, uint32_t threads)
, m_threads(threads) , m_threads(threads)
, m_stopped{ false } , m_stopped{ false }
, m_startTimestamp(high_resolution_clock::now()) , m_startTimestamp(high_resolution_clock::now())
, m_rng(RandomDeviceSeed::instance) , m_nonce(0)
, m_fullNonce(std::numeric_limits<uint64_t>::max())
, m_nonceTimestamp(m_startTimestamp) , m_nonceTimestamp(m_startTimestamp)
, m_totalHashes(0) , m_totalHashes(0)
, m_sharesFound(0) , m_sharesFound(0)
, m_sharesFailed(0)
, m_job{} , m_job{}
, m_jobIndex{ 0 } , m_jobIndex{ 0 }
{ {
// Diffuse the initial state in case it has low quality std::random_device rd;
m_rng.discard(10000); m_extraNonce = static_cast<uint32_t>(rd());
on_block(m_pool->block_template()); on_block(m_pool->block_template());
@@ -79,24 +77,15 @@ Miner::~Miner()
void Miner::print_status() void Miner::print_status()
{ {
const uint32_t hash_count = std::numeric_limits<uint32_t>::max() - static_cast<uint32_t>(m_fullNonce.load()); const uint32_t hash_count = 0 - m_nonce.load();
const double dt = static_cast<double>(duration_cast<nanoseconds>(high_resolution_clock::now() - m_nonceTimestamp).count()) / 1e9; const double dt = static_cast<double>(duration_cast<nanoseconds>(high_resolution_clock::now() - m_nonceTimestamp).count()) / 1e9;
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0; const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
char shares_failed_buf[64] = {};
log::Stream s(shares_failed_buf);
const uint32_t shares_found = m_sharesFound;
const uint32_t shares_failed = m_sharesFailed;
if (shares_failed) {
s << log::Yellow() << "\nShares failed = " << shares_failed << log::NoColor();
}
LOGINFO(0, "status" << LOGINFO(0, "status" <<
"\nThreads = " << m_threads << "\nThreads = " << m_threads <<
"\nHashrate = " << log::Hashrate(hr) << "\nHashrate = " << log::Hashrate(hr) <<
"\nShares found = " << shares_found << static_cast<const char*>(shares_failed_buf) "\nShares found = " << m_sharesFound.load()
); );
} }
@@ -105,12 +94,9 @@ void Miner::on_block(const BlockTemplate& block)
const uint32_t next_index = m_jobIndex ^ 1; const uint32_t next_index = m_jobIndex ^ 1;
Job& j = m_job[next_index]; Job& j = m_job[next_index];
hash seed; hash seed;
j.m_blobSize = block.get_hashing_blob(m_extraNonce, j.m_blob, j.m_height, j.m_sidechainHeight, j.m_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
const uint32_t extra_nonce = PoolBlock::signal_v2_readiness(static_cast<uint32_t>(m_rng() >> 32)); const uint32_t hash_count = 0 - m_nonce.exchange(0);
j.m_blobSize = block.get_hashing_blob(extra_nonce, j.m_blob, j.m_height, j.m_sidechainHeight, j.m_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
const uint64_t next_full_nonce = (static_cast<uint64_t>(extra_nonce) << 32) | std::numeric_limits<uint32_t>::max();
const uint32_t hash_count = std::numeric_limits<uint32_t>::max() - static_cast<uint32_t>(m_fullNonce.exchange(next_full_nonce));
m_jobIndex = next_index; m_jobIndex = next_index;
const auto cur_ts = high_resolution_clock::now(); const auto cur_ts = high_resolution_clock::now();
@@ -132,7 +118,6 @@ void Miner::on_block(const BlockTemplate& block)
<< ",\"total_hashes\":" << m_totalHashes.load() << ",\"total_hashes\":" << m_totalHashes.load()
<< ",\"time_running\":" << time_running << ",\"time_running\":" << time_running
<< ",\"shares_found\":" << m_sharesFound.load() << ",\"shares_found\":" << m_sharesFound.load()
<< ",\"shares_failed\":" << m_sharesFailed.load()
<< ",\"block_reward_share_percent\":" << block_reward_share_percent << ",\"block_reward_share_percent\":" << block_reward_share_percent
<< ",\"threads\":" << m_threads << ",\"threads\":" << m_threads
<< "}"; << "}";
@@ -144,7 +129,6 @@ void Miner::reset_share_counters()
{ {
m_totalHashes = 0; m_totalHashes = 0;
m_sharesFound = 0; m_sharesFound = 0;
m_sharesFailed = 0;
} }
void Miner::run(void* data) void Miner::run(void* data)
@@ -209,19 +193,14 @@ void Miner::run(WorkerData* data)
if (first) { if (first) {
first = false; first = false;
memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job)); memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job));
job[index].set_nonce(miner->m_nonce.fetch_sub(1), miner->m_extraNonce);
const uint64_t full_nonce = miner->m_fullNonce.fetch_sub(1);
job[index].set_nonce(static_cast<uint32_t>(full_nonce), static_cast<uint32_t>(full_nonce >> 32));
randomx_calculate_hash_first(vm, job[index].m_blob, job[index].m_blobSize); randomx_calculate_hash_first(vm, job[index].m_blob, job[index].m_blobSize);
} }
const Job& j = job[index]; const Job& j = job[index];
index ^= 1; index ^= 1;
memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job)); memcpy(&job[index], &miner->m_job[miner->m_jobIndex], sizeof(Job));
job[index].set_nonce(miner->m_nonce.fetch_sub(1), miner->m_extraNonce);
const uint64_t full_nonce = miner->m_fullNonce.fetch_sub(1);
job[index].set_nonce(static_cast<uint32_t>(full_nonce), static_cast<uint32_t>(full_nonce >> 32));
hash h; hash h;
randomx_calculate_hash_next(vm, job[index].m_blob, job[index].m_blobSize, &h); randomx_calculate_hash_next(vm, job[index].m_blob, job[index].m_blobSize, &h);
@@ -234,12 +213,7 @@ void Miner::run(WorkerData* data)
if (j.m_sidechainDiff.check_pow(h)) { if (j.m_sidechainDiff.check_pow(h)) {
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << j.m_height << ", sidechain height " << j.m_sidechainHeight << ", diff " << j.m_sidechainDiff << ", worker thread " << data->m_index << '/' << data->m_count); LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << j.m_height << ", sidechain height " << j.m_sidechainHeight << ", diff " << j.m_sidechainDiff << ", worker thread " << data->m_index << '/' << data->m_count);
++m_sharesFound; ++m_sharesFound;
if (!m_pool->submit_sidechain_block(j.m_templateId, j.m_nonce, j.m_extraNonce)) { m_pool->submit_sidechain_block(j.m_templateId, j.m_nonce, j.m_extraNonce);
if (m_sharesFound > 0) {
--m_sharesFound;
}
++m_sharesFailed;
}
} }
std::this_thread::yield(); std::this_thread::yield();
+2 -4
View File
@@ -54,14 +54,12 @@ private:
std::chrono::high_resolution_clock::time_point m_startTimestamp; std::chrono::high_resolution_clock::time_point m_startTimestamp;
std::mt19937_64 m_rng; std::atomic<uint32_t> m_nonce;
std::atomic<uint64_t> m_fullNonce;
std::chrono::high_resolution_clock::time_point m_nonceTimestamp; std::chrono::high_resolution_clock::time_point m_nonceTimestamp;
uint32_t m_extraNonce;
std::atomic<uint64_t> m_totalHashes; std::atomic<uint64_t> m_totalHashes;
std::atomic<uint32_t> m_sharesFound; std::atomic<uint32_t> m_sharesFound;
std::atomic<uint32_t> m_sharesFailed;
struct Job struct Job
{ {
+19 -21
View File
@@ -30,10 +30,10 @@
#include <fstream> #include <fstream>
#include <numeric> #include <numeric>
static constexpr char log_category_prefix[] = "P2PServer-WOW "; static constexpr char log_category_prefix[] = "P2PServer ";
static constexpr char saved_peer_list_file_name[] = "p2pool_wow_peers.txt"; static constexpr char saved_peer_list_file_name[] = "p2pool_peers.txt";
static const char* seed_nodes[] = { "seeds.muchwow.lol", ""}; static const char* seed_nodes[] = { "seeds.p2pool.io", ""};
static const char* seed_nodes_mini[] = { "", "" }; static const char* seed_nodes_mini[] = { "seeds-mini.p2pool.io", "" };
static constexpr int DEFAULT_BACKLOG = 16; static constexpr int DEFAULT_BACKLOG = 16;
static constexpr uint64_t DEFAULT_BAN_TIME = 600; static constexpr uint64_t DEFAULT_BAN_TIME = 600;
@@ -75,7 +75,7 @@ P2PServer::P2PServer(p2pool* pool)
[this](bool is_v6, const std::string& /*address*/, const std::string& ip, int port) [this](bool is_v6, const std::string& /*address*/, const std::string& ip, int port)
{ {
if (!str_to_ip(is_v6, ip.c_str(), m_socks5ProxyIP)) { if (!str_to_ip(is_v6, ip.c_str(), m_socks5ProxyIP)) {
PANIC_STOP(); panic();
} }
m_socks5ProxyV6 = is_v6; m_socks5ProxyV6 = is_v6;
m_socks5ProxyPort = port; m_socks5ProxyPort = port;
@@ -95,7 +95,7 @@ P2PServer::P2PServer(p2pool* pool)
int err = uv_async_init(&m_loop, &m_broadcastAsync, on_broadcast); int err = uv_async_init(&m_loop, &m_broadcastAsync, on_broadcast);
if (err) { if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err)); LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
m_broadcastAsync.data = this; m_broadcastAsync.data = this;
m_broadcastQueue.reserve(2); m_broadcastQueue.reserve(2);
@@ -103,21 +103,21 @@ P2PServer::P2PServer(p2pool* pool)
err = uv_async_init(&m_loop, &m_connectToPeersAsync, on_connect_to_peers); err = uv_async_init(&m_loop, &m_connectToPeersAsync, on_connect_to_peers);
if (err) { if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err)); LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
m_connectToPeersAsync.data = this; m_connectToPeersAsync.data = this;
err = uv_async_init(&m_loop, &m_showPeersAsync, on_show_peers); err = uv_async_init(&m_loop, &m_showPeersAsync, on_show_peers);
if (err) { if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err)); LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
m_showPeersAsync.data = this; m_showPeersAsync.data = this;
err = uv_timer_init(&m_loop, &m_timer); err = uv_timer_init(&m_loop, &m_timer);
if (err) { if (err) {
LOGERR(1, "failed to create timer, error " << uv_err_name(err)); LOGERR(1, "failed to create timer, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
if (m_cache) { if (m_cache) {
@@ -130,7 +130,7 @@ P2PServer::P2PServer(p2pool* pool)
err = uv_timer_start(&m_timer, on_timer, 1000, m_timerInterval * 1000); err = uv_timer_start(&m_timer, on_timer, 1000, m_timerInterval * 1000);
if (err) { if (err) {
LOGERR(1, "failed to start timer, error " << uv_err_name(err)); LOGERR(1, "failed to start timer, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
load_peer_list(); load_peer_list();
@@ -329,7 +329,7 @@ void P2PServer::update_peer_connections()
// Special case: when we can't find p2pool peers, scan through monerod peers (try 25 peers at a time) // Special case: when we can't find p2pool peers, scan through monerod peers (try 25 peers at a time)
if (!has_good_peers && !m_peerListMonero.empty()) { if (!has_good_peers && !m_peerListMonero.empty()) {
LOGINFO(3, "Scanning wownerod peers, " << m_peerListMonero.size() << " left"); LOGINFO(3, "Scanning monerod peers, " << m_peerListMonero.size() << " left");
for (uint32_t i = 0; (i < 25) && !m_peerListMonero.empty(); ++i) { for (uint32_t i = 0; (i < 25) && !m_peerListMonero.empty(); ++i) {
peer_list.push_back(m_peerListMonero.back()); peer_list.push_back(m_peerListMonero.back());
m_peerListMonero.pop_back(); m_peerListMonero.pop_back();
@@ -353,7 +353,7 @@ void P2PServer::update_peer_connections()
} }
if (!has_good_peers && ((m_timerCounter % 10) == 0)) { if (!has_good_peers && ((m_timerCounter % 10) == 0)) {
LOGERR(1, "no connections to other p2pool nodes, check your wownerod/p2pool/network/firewall setup!!!"); LOGERR(1, "no connections to other p2pool nodes, check your monerod/p2pool/network/firewall setup!!!");
load_peer_list(); load_peer_list();
if (m_peerListMonero.empty()) { if (m_peerListMonero.empty()) {
load_monerod_peer_list(); load_monerod_peer_list();
@@ -667,7 +667,7 @@ void P2PServer::load_monerod_peer_list()
// Put recently active peers last in the list (it will be scanned backwards) // Put recently active peers last in the list (it will be scanned backwards)
std::sort(m_peerListMonero.begin(), m_peerListMonero.end(), [](const Peer& a, const Peer& b) { return a.m_lastSeen < b.m_lastSeen; }); std::sort(m_peerListMonero.begin(), m_peerListMonero.end(), [](const Peer& a, const Peer& b) { return a.m_lastSeen < b.m_lastSeen; });
LOGINFO(4, "wownerod peer list loaded (" << m_peerListMonero.size() << " peers)"); LOGINFO(4, "monerod peer list loaded (" << m_peerListMonero.size() << " peers)");
}, },
[](const char* data, size_t size) [](const char* data, size_t size)
{ {
@@ -1151,7 +1151,7 @@ void P2PServer::check_zmq()
if (cur_time >= last_active + 300) { if (cur_time >= last_active + 300) {
const uint64_t dt = static_cast<uint64_t>(cur_time - last_active); const uint64_t dt = static_cast<uint64_t>(cur_time - last_active);
LOGERR(1, "no ZMQ messages received from wownerod in the last " << dt << " seconds, check your wownerod/p2pool/network/firewall setup!!!"); LOGERR(1, "no ZMQ messages received from monerod in the last " << dt << " seconds, check your monerod/p2pool/network/firewall setup!!!");
m_pool->restart_zmq(); m_pool->restart_zmq();
} }
} }
@@ -1162,9 +1162,9 @@ void P2PServer::check_block_template()
return; return;
} }
// Force update block template every 20 seconds after the initial sync is done // Force update block template every 30 seconds after the initial sync is done
if (seconds_since_epoch() >= m_pool->block_template().last_updated() + 20) { if (seconds_since_epoch() >= m_pool->block_template().last_updated() + 30) {
LOGINFO(4, "block template is 20 seconds old, updating it"); LOGINFO(4, "block template is 30 seconds old, updating it");
m_pool->update_block_template_async(); m_pool->update_block_template_async();
} }
} }
@@ -1578,7 +1578,6 @@ void P2PServer::P2PClient::send_handshake_solution(const uint8_t (&challenge)[CH
P2PClient* client; P2PClient* client;
P2PServer* server; P2PServer* server;
uint32_t reset_counter; uint32_t reset_counter;
bool is_incoming;
uint8_t challenge[CHALLENGE_SIZE]; uint8_t challenge[CHALLENGE_SIZE];
uint64_t salt; uint64_t salt;
@@ -1591,7 +1590,6 @@ void P2PServer::P2PClient::send_handshake_solution(const uint8_t (&challenge)[CH
work->client = this; work->client = this;
work->server = server; work->server = server;
work->reset_counter = m_resetCounter.load(); work->reset_counter = m_resetCounter.load();
work->is_incoming = m_isIncoming;
memcpy(work->challenge, challenge, CHALLENGE_SIZE); memcpy(work->challenge, challenge, CHALLENGE_SIZE);
work->salt = server->get_random64(); work->salt = server->get_random64();
@@ -1632,7 +1630,7 @@ void P2PServer::P2PClient::send_handshake_solution(const uint8_t (&challenge)[CH
return; return;
} }
if (work->is_incoming) { if (work->client->m_isIncoming) {
// This is an incoming connection, so it must do PoW, not us // This is an incoming connection, so it must do PoW, not us
return; return;
} }
@@ -2006,7 +2004,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size,
} }
else if (peer_height > our_height) { else if (peer_height > our_height) {
if (peer_height >= our_height + 2) { if (peer_height >= our_height + 2) {
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " is ahead on mainchain (height " << peer_height << ", your height " << our_height << "). Is your wownerod stuck or lagging?"); LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " is ahead on mainchain (height " << peer_height << ", your height " << our_height << "). Is your monerod stuck or lagging?");
} }
} }
else { else {
+2 -2
View File
@@ -28,8 +28,8 @@ class BlockCache;
static constexpr size_t P2P_BUF_SIZE = 128 * 1024; static constexpr size_t P2P_BUF_SIZE = 128 * 1024;
static constexpr size_t PEER_LIST_RESPONSE_MAX_PEERS = 16; static constexpr size_t PEER_LIST_RESPONSE_MAX_PEERS = 16;
static constexpr int DEFAULT_P2P_PORT = 47889; static constexpr int DEFAULT_P2P_PORT = 37889;
static constexpr int DEFAULT_P2P_PORT_MINI = 47888; static constexpr int DEFAULT_P2P_PORT_MINI = 37888;
static constexpr uint32_t PROTOCOL_VERSION_1_0 = 0x00010000UL; static constexpr uint32_t PROTOCOL_VERSION_1_0 = 0x00010000UL;
static constexpr uint32_t PROTOCOL_VERSION_1_1 = 0x00010001UL; static constexpr uint32_t PROTOCOL_VERSION_1_1 = 0x00010001UL;
+25 -39
View File
@@ -38,7 +38,6 @@
#include "keccak.h" #include "keccak.h"
#include <thread> #include <thread>
#include <fstream> #include <fstream>
#include <numeric>
constexpr char log_category_prefix[] = "P2Pool "; constexpr char log_category_prefix[] = "P2Pool ";
constexpr int BLOCK_HEADERS_REQUIRED = 720; constexpr int BLOCK_HEADERS_REQUIRED = 720;
@@ -329,7 +328,7 @@ void p2pool::handle_miner_data(MinerData& data)
for (uint64_t h = data.height; h && (h + BLOCK_HEADERS_REQUIRED > data.height); --h) { for (uint64_t h = data.height; h && (h + BLOCK_HEADERS_REQUIRED > data.height); --h) {
auto it = m_mainchainByHeight.find(h); auto it = m_mainchainByHeight.find(h);
if ((it == m_mainchainByHeight.end()) || it->second.difficulty.empty()) { if ((it == m_mainchainByHeight.end()) || it->second.difficulty.empty()) {
LOGWARN(3, "Mainchain data for height " << h << " is missing, requesting it from wownerod again"); LOGWARN(3, "Mainchain data for height " << h << " is missing, requesting it from monerod again");
missing_heights.push_back(h); missing_heights.push_back(h);
} }
} }
@@ -518,26 +517,19 @@ void p2pool::submit_block() const
size_t nonce_offset = 0; size_t nonce_offset = 0;
size_t extra_nonce_offset = 0; size_t extra_nonce_offset = 0;
size_t sidechain_id_offset = 0;
hash sidechain_id;
bool is_external = false; bool is_external = false;
if (submit_data.blob.empty()) { if (submit_data.blob.empty()) {
submit_data.blob = m_blockTemplate->get_block_template_blob(submit_data.template_id, submit_data.extra_nonce, nonce_offset, extra_nonce_offset, sidechain_id_offset, sidechain_id); LOGINFO(0, "submit_block: height = " << height << ", template id = " << submit_data.template_id << ", nonce = " << submit_data.nonce << ", extra_nonce = " << submit_data.extra_nonce);
LOGINFO(0, log::LightGreen() << "submit_block: height = " << height
<< ", template id = " << submit_data.template_id
<< ", nonce = " << submit_data.nonce
<< ", extra_nonce = " << submit_data.extra_nonce
<< ", id = " << sidechain_id);
submit_data.blob = m_blockTemplate->get_block_template_blob(submit_data.template_id, nonce_offset, extra_nonce_offset);
if (submit_data.blob.empty()) { if (submit_data.blob.empty()) {
LOGERR(0, "submit_block: couldn't find block template with id " << submit_data.template_id); LOGERR(0, "submit_block: couldn't find block template with id " << submit_data.template_id);
return; return;
} }
} }
else { else {
LOGINFO(0, log::LightGreen() << "submit_block: height = " << height << ", external blob (" << submit_data.blob.size() << " bytes)"); LOGINFO(0, "submit_block: height = " << height << ", external blob (" << submit_data.blob.size() << " bytes)");
is_external = true; is_external = true;
} }
@@ -551,28 +543,26 @@ void p2pool::submit_block() const
const uint32_t extra_nonce = submit_data.extra_nonce; const uint32_t extra_nonce = submit_data.extra_nonce;
for (size_t i = 0; i < submit_data.blob.size(); ++i) { for (size_t i = 0; i < submit_data.blob.size(); ++i) {
uint8_t b; char buf[16];
if (nonce_offset && nonce_offset <= i && i < nonce_offset + sizeof(submit_data.nonce)) { if (nonce_offset && nonce_offset <= i && i < nonce_offset + sizeof(submit_data.nonce)) {
b = submit_data.nonce & 255; snprintf(buf, sizeof(buf), "%02x", submit_data.nonce & 255);
submit_data.nonce >>= 8; submit_data.nonce >>= 8;
} }
else if (extra_nonce_offset && extra_nonce_offset <= i && i < extra_nonce_offset + sizeof(submit_data.extra_nonce)) { else if (extra_nonce_offset && extra_nonce_offset <= i && i < extra_nonce_offset + sizeof(submit_data.extra_nonce)) {
b = submit_data.extra_nonce & 255; snprintf(buf, sizeof(buf), "%02x", submit_data.extra_nonce & 255);
submit_data.extra_nonce >>= 8; submit_data.extra_nonce >>= 8;
} }
else if (sidechain_id_offset && sidechain_id_offset <= i && i < sidechain_id_offset + HASH_SIZE) {
b = sidechain_id.h[i - sidechain_id_offset];
}
else { else {
b = submit_data.blob[i]; snprintf(buf, sizeof(buf), "%02x", submit_data.blob[i]);
} }
request.append(1, "0123456789abcdef"[b >> 4]);
request.append(1, "0123456789abcdef"[b & 15]); request.append(buf);
} }
request.append("\"]}"); request.append("\"]}");
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, request, m_params->m_rpcLogin, m_params->m_socks5Proxy, JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, request, m_params->m_rpcLogin, m_params->m_socks5Proxy,
[height, diff, template_id, nonce, extra_nonce, sidechain_id, is_external](const char* data, size_t size) [height, diff, template_id, nonce, extra_nonce, is_external](const char* data, size_t size)
{ {
rapidjson::Document doc; rapidjson::Document doc;
if (doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size).HasParseError() || !doc.IsObject()) { if (doc.Parse<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(data, size).HasParseError() || !doc.IsObject()) {
@@ -599,7 +589,7 @@ void p2pool::submit_block() const
LOGWARN(3, "submit_block (external blob): daemon returned error: " << (error_msg ? error_msg : "unknown error")); LOGWARN(3, "submit_block (external blob): daemon returned error: " << (error_msg ? error_msg : "unknown error"));
} }
else { else {
LOGERR(0, "submit_block: daemon returned error: '" << (error_msg ? error_msg : "unknown error") << "', template id = " << template_id << ", nonce = " << nonce << ", extra_nonce = " << extra_nonce << ", id = " << sidechain_id); LOGERR(0, "submit_block: daemon returned error: '" << (error_msg ? error_msg : "unknown error") << "', template id = " << template_id << ", nonce = " << nonce << ", extra_nonce = " << extra_nonce);
} }
return; return;
} }
@@ -701,14 +691,14 @@ void p2pool::download_block_headers(uint64_t current_height)
} }
else { else {
LOGERR(1, "fatal error: couldn't download block header for seed height " << height); LOGERR(1, "fatal error: couldn't download block header for seed height " << height);
PANIC_STOP(); panic();
} }
}, },
[height](const char* data, size_t size) [height](const char* data, size_t size)
{ {
if (size > 0) { if (size > 0) {
LOGERR(1, "fatal error: couldn't download block header for seed height " << height << ", error " << log::const_buf(data, size)); LOGERR(1, "fatal error: couldn't download block header for seed height " << height << ", error " << log::const_buf(data, size));
PANIC_STOP(); panic();
} }
}); });
} }
@@ -729,7 +719,7 @@ void p2pool::download_block_headers(uint64_t current_height)
} }
catch (const std::exception& e) { catch (const std::exception& e) {
LOGERR(1, "Couldn't start ZMQ reader: exception " << e.what()); LOGERR(1, "Couldn't start ZMQ reader: exception " << e.what());
PANIC_STOP(); panic();
} }
m_stratumServer = new StratumServer(this); m_stratumServer = new StratumServer(this);
@@ -916,7 +906,7 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
} }
if (info.busy_syncing || !info.synchronized) { if (info.busy_syncing || !info.synchronized) {
LOGINFO(1, "wownerod is " << (info.busy_syncing ? "busy syncing" : "not synchronized") << ", trying again in 1 second"); LOGINFO(1, "monerod is " << (info.busy_syncing ? "busy syncing" : "not synchronized") << ", trying again in 1 second");
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::this_thread::sleep_for(std::chrono::milliseconds(1000));
get_info(); get_info();
return; return;
@@ -931,8 +921,8 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
const NetworkType sidechain_network = m_sideChain->network_type(); const NetworkType sidechain_network = m_sideChain->network_type();
if (monero_network != sidechain_network) { if (monero_network != sidechain_network) {
LOGERR(1, "wownerod is on " << monero_network << ", but you're mining to a " << sidechain_network << " sidechain"); LOGERR(1, "monerod is on " << monero_network << ", but you're mining to a " << sidechain_network << " sidechain");
PANIC_STOP(); panic();
} }
get_version(); get_version();
@@ -993,7 +983,7 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
} }
constexpr uint64_t major = 3; constexpr uint64_t major = 3;
constexpr uint64_t minor = 11; constexpr uint64_t minor = 10;
constexpr uint64_t required = (major << 16) | minor; constexpr uint64_t required = (major << 16) | minor;
if (version < required) { if (version < required) {
@@ -1001,8 +991,8 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
const uint64_t version_lo = version & 65535; const uint64_t version_lo = version & 65535;
const uint64_t required_version_hi = required >> 16; const uint64_t required_version_hi = required >> 16;
const uint64_t required_version_lo = required & 65535; const uint64_t required_version_lo = required & 65535;
LOGERR(1, "wownerod RPC v" << version_hi << '.' << version_lo << " is incompatible, update to RPC >= v" << required_version_hi << '.' << required_version_lo << " (Wownero v0.11.0.0 or newer)"); LOGERR(1, "monerod RPC v" << version_hi << '.' << version_lo << " is incompatible, update to RPC >= v" << required_version_hi << '.' << required_version_lo << " (Monero v0.18.0.0 or newer)");
PANIC_STOP(); panic();
} }
get_miner_data(); get_miner_data();
@@ -1042,7 +1032,7 @@ void p2pool::parse_get_miner_data_rpc(const char* data, size_t size)
} }
hash h; hash h;
keccak(reinterpret_cast<const uint8_t*>(data), static_cast<int>(size), h.h); keccak(reinterpret_cast<const uint8_t*>(data), static_cast<int>(size), h.h, HASH_SIZE);
if (h == m_getMinerDataHash) { if (h == m_getMinerDataHash) {
LOGWARN(4, "Received a duplicate get_miner_data RPC response, ignoring it"); LOGWARN(4, "Received a duplicate get_miner_data RPC response, ignoring it");
return; return;
@@ -1233,9 +1223,6 @@ void p2pool::api_update_pool_stats()
const uint64_t miners = std::max<uint64_t>(m_sideChain->miner_count(), m_p2pServer ? m_p2pServer->peer_list_size() : 0U); const uint64_t miners = std::max<uint64_t>(m_sideChain->miner_count(), m_p2pServer ? m_p2pServer->peer_list_size() : 0U);
const difficulty_type total_hashes = m_sideChain->total_hashes(); const difficulty_type total_hashes = m_sideChain->total_hashes();
const auto& s = m_blockTemplate->shares();
const difficulty_type pplns_weight = std::accumulate(s.begin(), s.end(), difficulty_type(), [](const auto& a, const auto& b) { return a + b.m_weight; });
time_t last_block_found_time = 0; time_t last_block_found_time = 0;
uint64_t last_block_found_height = 0; uint64_t last_block_found_height = 0;
uint64_t total_blocks_found = 0; uint64_t total_blocks_found = 0;
@@ -1250,7 +1237,7 @@ void p2pool::api_update_pool_stats()
} }
m_api->set(p2pool_api::Category::POOL, "stats", m_api->set(p2pool_api::Category::POOL, "stats",
[hashrate, miners, &total_hashes, last_block_found_time, last_block_found_height, total_blocks_found, &pplns_weight](log::Stream& s) [hashrate, miners, &total_hashes, last_block_found_time, last_block_found_height, total_blocks_found](log::Stream& s)
{ {
s << "{\"pool_list\":[\"pplns\"],\"pool_statistics\":{\"hashRate\":" << hashrate s << "{\"pool_list\":[\"pplns\"],\"pool_statistics\":{\"hashRate\":" << hashrate
<< ",\"miners\":" << miners << ",\"miners\":" << miners
@@ -1258,7 +1245,6 @@ void p2pool::api_update_pool_stats()
<< ",\"lastBlockFoundTime\":" << last_block_found_time << ",\"lastBlockFoundTime\":" << last_block_found_time
<< ",\"lastBlockFound\":" << last_block_found_height << ",\"lastBlockFound\":" << last_block_found_height
<< ",\"totalBlocksFound\":" << total_blocks_found << ",\"totalBlocksFound\":" << total_blocks_found
<< ",\"pplnsWeight\":" << pplns_weight
<< "}}"; << "}}";
}); });
@@ -1577,7 +1563,7 @@ int p2pool::run()
catch (const std::exception& e) { catch (const std::exception& e) {
const char* s = e.what(); const char* s = e.what();
LOGERR(1, "exception " << s); LOGERR(1, "exception " << s);
PANIC_STOP(); panic();
} }
m_stopped = true; m_stopped = true;
+24 -75
View File
@@ -34,7 +34,7 @@ p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats)
{ {
if (m_apiPath.empty()) { if (m_apiPath.empty()) {
LOGERR(1, "api path is empty"); LOGERR(1, "api path is empty");
PANIC_STOP(); panic();
} }
if ((m_apiPath.back() != '/') if ((m_apiPath.back() != '/')
@@ -48,13 +48,13 @@ p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats)
struct stat buf; struct stat buf;
if (stat(m_apiPath.c_str(), &buf) != 0) { if (stat(m_apiPath.c_str(), &buf) != 0) {
LOGERR(1, "path " << m_apiPath << " doesn't exist"); LOGERR(1, "path " << m_apiPath << " doesn't exist");
PANIC_STOP(); panic();
} }
int result = uv_async_init(uv_default_loop_checked(), &m_dumpToFileAsync, on_dump_to_file); int result = uv_async_init(uv_default_loop_checked(), &m_dumpToFileAsync, on_dump_to_file);
if (result) { if (result) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(result)); LOGERR(1, "uv_async_init failed, error " << uv_err_name(result));
PANIC_STOP(); panic();
} }
m_dumpToFileAsync.data = this; m_dumpToFileAsync.data = this;
@@ -93,7 +93,7 @@ void p2pool_api::create_dir(const std::string& path)
result = errno; result = errno;
if (result != EEXIST) { if (result != EEXIST) {
LOGERR(1, "mkdir(" << path << ") failed, error " << result); LOGERR(1, "mkdir(" << path << ") failed, error " << result);
PANIC_STOP(); panic();
} }
} }
} }
@@ -124,10 +124,8 @@ void p2pool_api::dump_to_file_async_internal(Category category, const char* file
m_dumpData[path] = std::move(buf); m_dumpData[path] = std::move(buf);
} }
if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_dumpToFileAsync))) {
uv_async_send(&m_dumpToFileAsync); uv_async_send(&m_dumpToFileAsync);
} }
}
void p2pool_api::dump_to_file() void p2pool_api::dump_to_file()
{ {
@@ -172,108 +170,59 @@ void p2pool_api::on_fs_open(uv_fs_t* req)
buf[0].base = work->buf.data(); buf[0].base = work->buf.data();
buf[0].len = static_cast<uint32_t>(work->buf.size()); buf[0].len = static_cast<uint32_t>(work->buf.size());
int result = uv_fs_write(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), buf, 1, -1, on_fs_write); const int result = uv_fs_write(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), buf, 1, 0, on_fs_write);
if (result < 0) { if (result < 0) {
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result)); LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_error_cleanup);
if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
delete work; delete work;
} return;
} }
} }
void p2pool_api::on_fs_write(uv_fs_t* req) void p2pool_api::on_fs_write(uv_fs_t* req)
{ {
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data); DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
int result = static_cast<int>(req->result);
if (req->result < 0) {
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(static_cast<int>(req->result)));
}
uv_fs_req_cleanup(req); uv_fs_req_cleanup(req);
if (result < 0) { const int result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_close);
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
}
else if (result && (static_cast<size_t>(result) < work->buf.size())) {
work->buf.erase(work->buf.begin(), work->buf.begin() + result);
uv_buf_t buf[1];
buf[0].base = work->buf.data();
buf[0].len = static_cast<uint32_t>(work->buf.size());
result = uv_fs_write(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), buf, 1, -1, on_fs_write);
if (result < 0) {
LOGWARN(4, "failed to write to " << work->tmp_name << ", error " << uv_err_name(result));
result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_error_cleanup);
if (result < 0) { if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result)); LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
delete work; delete work;
}
}
return; return;
} }
result = uv_fs_close(uv_default_loop_checked(), &work->req, static_cast<uv_file>(work->fd), on_fs_close);
if (result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
}
} }
void p2pool_api::on_fs_close(uv_fs_t* req) void p2pool_api::on_fs_close(uv_fs_t* req)
{ {
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data); DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
int result = static_cast<int>(req->result);
if (req->result < 0) {
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(static_cast<int>(req->result)));
}
uv_fs_req_cleanup(req); uv_fs_req_cleanup(req);
if (result < 0) { const int result = uv_fs_rename(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), work->name.c_str(), on_fs_rename);
LOGWARN(4, "failed to close " << work->tmp_name << ", error " << uv_err_name(result));
}
result = uv_fs_rename(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), work->name.c_str(), on_fs_rename);
if (result < 0) { if (result < 0) {
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(result)); LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(result));
result = uv_fs_unlink(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), on_fs_error_cleanup);
if (result < 0) {
LOGWARN(4, "failed to delete " << work->tmp_name << ", error " << uv_err_name(result));
delete work; delete work;
} return;
} }
} }
void p2pool_api::on_fs_rename(uv_fs_t* req) void p2pool_api::on_fs_rename(uv_fs_t* req)
{ {
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data); DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
int result = static_cast<int>(req->result);
if (req->result < 0) {
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(static_cast<int>(req->result)));
}
uv_fs_req_cleanup(req); uv_fs_req_cleanup(req);
if (result < 0) {
LOGWARN(4, "failed to rename " << work->tmp_name << " to " << work->name << ", error " << uv_err_name(result));
result = uv_fs_unlink(uv_default_loop_checked(), &work->req, work->tmp_name.c_str(), on_fs_error_cleanup);
if (result < 0) {
LOGWARN(4, "failed to delete " << work->tmp_name << ", error " << uv_err_name(result));
delete work;
}
return;
}
delete work;
}
void p2pool_api::on_fs_error_cleanup(uv_fs_t* req)
{
DumpFileWork* work = reinterpret_cast<DumpFileWork*>(req->data);
int result = static_cast<int>(req->result);
uv_fs_req_cleanup(req);
if (result < 0) {
LOGWARN(4, "failed to cleanup after previous errors " << work->tmp_name << ", error " << uv_err_name(result));
}
delete work; delete work;
} }
-1
View File
@@ -78,7 +78,6 @@ private:
static void on_fs_write(uv_fs_t* req); static void on_fs_write(uv_fs_t* req);
static void on_fs_close(uv_fs_t* req); static void on_fs_close(uv_fs_t* req);
static void on_fs_rename(uv_fs_t* req); static void on_fs_rename(uv_fs_t* req);
static void on_fs_error_cleanup(uv_fs_t* req);
std::string m_apiPath; std::string m_apiPath;
std::string m_networkPath; std::string m_networkPath;
+2 -2
View File
@@ -28,8 +28,8 @@ struct Params
bool valid() const; bool valid() const;
std::string m_host = "127.0.0.1"; std::string m_host = "127.0.0.1";
uint32_t m_rpcPort = 34568; uint32_t m_rpcPort = 18081;
uint32_t m_zmqPort = 34569; uint32_t m_zmqPort = 18083;
bool m_lightMode = false; bool m_lightMode = false;
Wallet m_wallet{ nullptr }; Wallet m_wallet{ nullptr };
std::string m_stratumAddresses; std::string m_stratumAddresses;
+36 -88
View File
@@ -38,13 +38,11 @@ PoolBlock::PoolBlock()
, m_txkeyPub{} , m_txkeyPub{}
, m_extraNonceSize(0) , m_extraNonceSize(0)
, m_extraNonce(0) , m_extraNonce(0)
, m_txkeySecSeed{}
, m_txkeySec{} , m_txkeySec{}
, m_parent{} , m_parent{}
, m_sidechainHeight(0) , m_sidechainHeight(0)
, m_difficulty{} , m_difficulty{}
, m_cumulativeDifficulty{} , m_cumulativeDifficulty{}
, m_sidechainExtraBuf{}
, m_sidechainId{} , m_sidechainId{}
, m_depth(0) , m_depth(0)
, m_verified(false) , m_verified(false)
@@ -54,10 +52,12 @@ PoolBlock::PoolBlock()
, m_precalculated(false) , m_precalculated(false)
, m_localTimestamp(seconds_since_epoch()) , m_localTimestamp(seconds_since_epoch())
{ {
uv_mutex_init_checked(&m_lock);
} }
PoolBlock::PoolBlock(const PoolBlock& b) PoolBlock::PoolBlock(const PoolBlock& b)
{ {
uv_mutex_init_checked(&m_lock);
operator=(b); operator=(b);
} }
@@ -68,6 +68,11 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
return *this; return *this;
} }
const int lock_result = uv_mutex_trylock(&b.m_lock);
if (lock_result) {
LOGERR(1, "operator= uv_mutex_trylock failed. Fix the code!");
}
#if POOL_BLOCK_DEBUG #if POOL_BLOCK_DEBUG
m_mainChainDataDebug = b.m_mainChainDataDebug; m_mainChainDataDebug = b.m_mainChainDataDebug;
m_sideChainDataDebug = b.m_sideChainDataDebug; m_sideChainDataDebug = b.m_sideChainDataDebug;
@@ -85,14 +90,12 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_extraNonce = b.m_extraNonce; m_extraNonce = b.m_extraNonce;
m_transactions = b.m_transactions; m_transactions = b.m_transactions;
m_minerWallet = b.m_minerWallet; m_minerWallet = b.m_minerWallet;
m_txkeySecSeed = b.m_txkeySecSeed;
m_txkeySec = b.m_txkeySec; m_txkeySec = b.m_txkeySec;
m_parent = b.m_parent; m_parent = b.m_parent;
m_uncles = b.m_uncles; m_uncles = b.m_uncles;
m_sidechainHeight = b.m_sidechainHeight; m_sidechainHeight = b.m_sidechainHeight;
m_difficulty = b.m_difficulty; m_difficulty = b.m_difficulty;
m_cumulativeDifficulty = b.m_cumulativeDifficulty; m_cumulativeDifficulty = b.m_cumulativeDifficulty;
memcpy(m_sidechainExtraBuf, b.m_sidechainExtraBuf, sizeof(m_sidechainExtraBuf));
m_sidechainId = b.m_sidechainId; m_sidechainId = b.m_sidechainId;
m_depth = b.m_depth; m_depth = b.m_depth;
m_verified = b.m_verified; m_verified = b.m_verified;
@@ -103,14 +106,25 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_localTimestamp = seconds_since_epoch(); m_localTimestamp = seconds_since_epoch();
if (lock_result == 0) {
uv_mutex_unlock(&b.m_lock);
}
return *this; return *this;
} }
PoolBlock::~PoolBlock() PoolBlock::~PoolBlock()
{ {
uv_mutex_destroy(&m_lock);
} }
std::vector<uint8_t> PoolBlock::serialize_mainchain_data(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size, const uint32_t* nonce, const uint32_t* extra_nonce) const std::vector<uint8_t> PoolBlock::serialize_mainchain_data(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const
{
MutexLock lock(m_lock);
return serialize_mainchain_data_nolock(header_size, miner_tx_size, outputs_offset, outputs_blob_size);
}
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; std::vector<uint8_t> data;
data.reserve(128 + m_outputs.size() * 39 + m_transactions.size() * HASH_SIZE); data.reserve(128 + m_outputs.size() * 39 + m_transactions.size() * HASH_SIZE);
@@ -120,11 +134,7 @@ std::vector<uint8_t> PoolBlock::serialize_mainchain_data(size_t* header_size, si
data.push_back(m_minorVersion); data.push_back(m_minorVersion);
writeVarint(m_timestamp, data); writeVarint(m_timestamp, data);
data.insert(data.end(), m_prevId.h, m_prevId.h + HASH_SIZE); data.insert(data.end(), m_prevId.h, m_prevId.h + HASH_SIZE);
data.insert(data.end(), reinterpret_cast<const uint8_t*>(&m_nonce), reinterpret_cast<const uint8_t*>(&m_nonce) + NONCE_SIZE);
if (!nonce) {
nonce = &m_nonce;
}
data.insert(data.end(), reinterpret_cast<const uint8_t*>(nonce), reinterpret_cast<const uint8_t*>(nonce) + NONCE_SIZE);
const size_t header_size0 = data.size(); const size_t header_size0 = data.size();
if (header_size) { if (header_size) {
@@ -177,10 +187,7 @@ std::vector<uint8_t> PoolBlock::serialize_mainchain_data(size_t* header_size, si
*(p++) = TX_EXTRA_NONCE; *(p++) = TX_EXTRA_NONCE;
*(p++) = static_cast<uint8_t>(extra_nonce_size); *(p++) = static_cast<uint8_t>(extra_nonce_size);
if (!extra_nonce) { memcpy(p, &m_extraNonce, EXTRA_NONCE_SIZE);
extra_nonce = &m_extraNonce;
}
memcpy(p, extra_nonce, 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) {
memset(p, 0, extra_nonce_size - EXTRA_NONCE_SIZE); memset(p, 0, extra_nonce_size - EXTRA_NONCE_SIZE);
@@ -206,9 +213,9 @@ std::vector<uint8_t> PoolBlock::serialize_mainchain_data(size_t* header_size, si
data.insert(data.end(), t + HASH_SIZE, t + m_transactions.size() * HASH_SIZE); data.insert(data.end(), t + HASH_SIZE, t + m_transactions.size() * HASH_SIZE);
#if POOL_BLOCK_DEBUG #if POOL_BLOCK_DEBUG
if (!nonce && !extra_nonce && !m_mainChainDataDebug.empty() && (data != m_mainChainDataDebug)) { if (!m_mainChainDataDebug.empty() && (data != m_mainChainDataDebug)) {
LOGERR(1, "serialize_mainchain_data() has a bug, fix it!"); LOGERR(1, "serialize_mainchain_data() has a bug, fix it!");
PANIC_STOP(); panic();
} }
#endif #endif
@@ -219,23 +226,16 @@ std::vector<uint8_t> PoolBlock::serialize_sidechain_data() const
{ {
std::vector<uint8_t> data; std::vector<uint8_t> data;
data.reserve((m_uncles.size() + 4) * HASH_SIZE + 36); MutexLock lock(m_lock);
data.reserve((m_uncles.size() + 4) * HASH_SIZE + 20);
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();
const int sidechain_version = get_sidechain_version();
data.insert(data.end(), spend.h, spend.h + HASH_SIZE); data.insert(data.end(), spend.h, spend.h + HASH_SIZE);
data.insert(data.end(), view.h, view.h + HASH_SIZE); data.insert(data.end(), view.h, view.h + HASH_SIZE);
if (sidechain_version > 1) {
data.insert(data.end(), m_txkeySecSeed.h, m_txkeySecSeed.h + HASH_SIZE);
}
else {
data.insert(data.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE); data.insert(data.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE);
}
data.insert(data.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(), data); writeVarint(m_uncles.size(), data);
@@ -252,15 +252,10 @@ std::vector<uint8_t> PoolBlock::serialize_sidechain_data() const
writeVarint(m_cumulativeDifficulty.lo, data); writeVarint(m_cumulativeDifficulty.lo, data);
writeVarint(m_cumulativeDifficulty.hi, data); writeVarint(m_cumulativeDifficulty.hi, data);
if (sidechain_version > 1) {
const uint8_t* p = reinterpret_cast<const uint8_t*>(m_sidechainExtraBuf);
data.insert(data.end(), p, p + sizeof(m_sidechainExtraBuf));
}
#if POOL_BLOCK_DEBUG #if POOL_BLOCK_DEBUG
if (!m_sideChainDataDebug.empty() && (data != m_sideChainDataDebug)) { if (!m_sideChainDataDebug.empty() && (data != m_sideChainDataDebug)) {
LOGERR(1, "serialize_sidechain_data() has a bug, fix it!"); LOGERR(1, "serialize_sidechain_data() has a bug, fix it!");
PANIC_STOP(); panic();
} }
#endif #endif
@@ -301,8 +296,10 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
size_t blob_size = 0; size_t blob_size = 0;
{ {
MutexLock lock(m_lock);
size_t header_size, miner_tx_size; size_t header_size, miner_tx_size;
const std::vector<uint8_t> mainchain_data = serialize_mainchain_data(&header_size, &miner_tx_size, nullptr, nullptr, nullptr, nullptr); 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)) { 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");
@@ -313,22 +310,18 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
memcpy(blob, mainchain_data.data(), blob_size); memcpy(blob, mainchain_data.data(), blob_size);
const uint8_t* miner_tx = mainchain_data.data() + header_size; const uint8_t* miner_tx = mainchain_data.data() + header_size;
hash tmp; keccak(miner_tx, static_cast<int>(miner_tx_size) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE);
keccak(miner_tx, static_cast<int>(miner_tx_size) - 1, tmp.h);
memcpy(hashes, tmp.h, HASH_SIZE);
count = m_transactions.size(); count = m_transactions.size();
uint8_t* h = reinterpret_cast<uint8_t*>(m_transactions.data()); uint8_t* h = reinterpret_cast<uint8_t*>(m_transactions.data());
keccak(reinterpret_cast<uint8_t*>(hashes), HASH_SIZE * 3, tmp.h); keccak(reinterpret_cast<uint8_t*>(hashes), HASH_SIZE * 3, h, HASH_SIZE);
memcpy(h, tmp.h, HASH_SIZE);
if (count == 1) { if (count == 1) {
memcpy(blob + blob_size, h, HASH_SIZE); memcpy(blob + blob_size, h, HASH_SIZE);
} }
else if (count == 2) { else if (count == 2) {
keccak(h, HASH_SIZE * 2, tmp.h); keccak(h, HASH_SIZE * 2, blob + blob_size, HASH_SIZE);
memcpy(blob + blob_size, tmp.h, HASH_SIZE);
} }
else { else {
size_t i, j, cnt; size_t i, j, cnt;
@@ -341,20 +334,17 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
memcpy(tmp_ints.data(), h, (cnt * 2 - count) * HASH_SIZE); memcpy(tmp_ints.data(), h, (cnt * 2 - count) * HASH_SIZE);
for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) { for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
keccak(h + i * HASH_SIZE, HASH_SIZE * 2, tmp.h); keccak(h + i * HASH_SIZE, HASH_SIZE * 2, tmp_ints.data() + j * HASH_SIZE, HASH_SIZE);
memcpy(tmp_ints.data() + j * HASH_SIZE, tmp.h, HASH_SIZE);
} }
while (cnt > 2) { while (cnt > 2) {
cnt >>= 1; cnt >>= 1;
for (i = 0, j = 0; j < cnt; i += 2, ++j) { for (i = 0, j = 0; j < cnt; i += 2, ++j) {
keccak(tmp_ints.data() + i * HASH_SIZE, HASH_SIZE * 2, tmp.h); keccak(tmp_ints.data() + i * HASH_SIZE, HASH_SIZE * 2, tmp_ints.data() + j * HASH_SIZE, HASH_SIZE);
memcpy(tmp_ints.data() + j * HASH_SIZE, tmp.h, HASH_SIZE);
} }
} }
keccak(tmp_ints.data(), HASH_SIZE * 2, tmp.h); keccak(tmp_ints.data(), HASH_SIZE * 2, blob + blob_size, HASH_SIZE);
memcpy(blob + blob_size, tmp.h, HASH_SIZE);
} }
} }
blob_size += HASH_SIZE; blob_size += HASH_SIZE;
@@ -390,46 +380,4 @@ uint64_t PoolBlock::get_payout(const Wallet& w) const
return 0; return 0;
} }
static constexpr uint64_t VERSION2_MAINNET_TIMESTAMP = 100U; //
static constexpr uint64_t VERSION2_TESTNET_TIMESTAMP = 100U; //
uint32_t PoolBlock::signal_v2_readiness(uint32_t extra_nonce)
{
const uint64_t ts = (SideChain::network_type() == NetworkType::Mainnet) ? VERSION2_MAINNET_TIMESTAMP : VERSION2_TESTNET_TIMESTAMP;
if (time(nullptr) < static_cast<int64_t>(ts)) {
return (extra_nonce & 0x007FFFFFUL) | 0xFF000000UL;
}
return extra_nonce;
}
int PoolBlock::get_sidechain_version() const
{
const uint64_t ts = (SideChain::network_type() == NetworkType::Mainnet) ? VERSION2_MAINNET_TIMESTAMP : VERSION2_TESTNET_TIMESTAMP;
return (m_timestamp >= ts) ? 2 : 1;
}
hash PoolBlock::calculate_tx_key_seed() const
{
const char domain[] = "tx_key_seed";
const uint32_t zero = 0;
const std::vector<uint8_t> mainchain_data = serialize_mainchain_data(nullptr, nullptr, nullptr, nullptr, &zero, &zero);
const std::vector<uint8_t> sidechain_data = serialize_sidechain_data();
hash result;
keccak_custom([&domain, &mainchain_data, &sidechain_data](int offset) -> uint8_t {
size_t k = offset;
if (k < sizeof(domain)) return domain[k];
k -= sizeof(domain);
if (k < mainchain_data.size()) return mainchain_data[k];
k -= mainchain_data.size();
return sidechain_data[k];
}, static_cast<int>(sizeof(domain) + mainchain_data.size() + sidechain_data.size()), result.h, HASH_SIZE);
return result;
}
} // namespace p2pool } // namespace p2pool
+4 -15
View File
@@ -28,8 +28,6 @@
namespace p2pool { namespace p2pool {
static FORCEINLINE constexpr int pool_block_debug() { return POOL_BLOCK_DEBUG; }
class RandomX_Hasher_Base; class RandomX_Hasher_Base;
class SideChain; class SideChain;
@@ -63,6 +61,8 @@ struct PoolBlock
PoolBlock(const PoolBlock& b); PoolBlock(const PoolBlock& b);
PoolBlock& operator=(const PoolBlock& b); PoolBlock& operator=(const PoolBlock& b);
mutable uv_mutex_t m_lock;
#if POOL_BLOCK_DEBUG #if POOL_BLOCK_DEBUG
std::vector<uint8_t> m_mainChainDataDebug; std::vector<uint8_t> m_mainChainDataDebug;
std::vector<uint8_t> m_sideChainDataDebug; std::vector<uint8_t> m_sideChainDataDebug;
@@ -104,7 +104,6 @@ struct PoolBlock
// Transaction secret key // Transaction secret key
// Required to check that pub keys in the miner transaction pay out to correct miner wallet addresses // Required to check that pub keys in the miner transaction pay out to correct miner wallet addresses
hash m_txkeySecSeed;
hash m_txkeySec; hash m_txkeySec;
// Side-chain parent and uncle blocks // Side-chain parent and uncle blocks
@@ -116,9 +115,6 @@ struct PoolBlock
difficulty_type m_difficulty; difficulty_type m_difficulty;
difficulty_type m_cumulativeDifficulty; difficulty_type m_cumulativeDifficulty;
// Arbitrary extra data
uint32_t m_sidechainExtraBuf[4];
// HASH (see diagram in the comment above) // HASH (see diagram in the comment above)
hash m_sidechainId; hash m_sidechainId;
@@ -135,7 +131,8 @@ struct PoolBlock
uint64_t m_localTimestamp; uint64_t m_localTimestamp;
std::vector<uint8_t> serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr, const uint32_t* nonce = nullptr, const uint32_t* extra_nonce = nullptr) const; std::vector<uint8_t> serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr) const;
std::vector<uint8_t> serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const;
std::vector<uint8_t> serialize_sidechain_data() const; std::vector<uint8_t> serialize_sidechain_data() const;
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact); int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact);
@@ -149,12 +146,6 @@ struct PoolBlock
// but P2Pool can switch to using only TXOUT_TO_TAGGED_KEY for miner payouts starting from v15 // but P2Pool can switch to using only TXOUT_TO_TAGGED_KEY for miner payouts starting from v15
FORCEINLINE uint8_t get_tx_type() const { return (m_majorVersion < HARDFORK_VIEW_TAGS_VERSION) ? TXOUT_TO_KEY : TXOUT_TO_TAGGED_KEY; } FORCEINLINE uint8_t get_tx_type() const { return (m_majorVersion < HARDFORK_VIEW_TAGS_VERSION) ? TXOUT_TO_KEY : TXOUT_TO_TAGGED_KEY; }
// Signal hardfork readiness (only before the v2 hardfork)
// TODO: remove this code after hardfork
static uint32_t signal_v2_readiness(uint32_t extra_nonce);
int get_sidechain_version() const;
typedef std::array<uint8_t, HASH_SIZE + NONCE_SIZE + EXTRA_NONCE_SIZE> full_id; typedef std::array<uint8_t, HASH_SIZE + NONCE_SIZE + EXTRA_NONCE_SIZE> full_id;
FORCEINLINE full_id get_full_id() const FORCEINLINE full_id get_full_id() const
@@ -166,8 +157,6 @@ struct PoolBlock
memcpy(p + HASH_SIZE + NONCE_SIZE, &m_extraNonce, EXTRA_NONCE_SIZE); memcpy(p + HASH_SIZE + NONCE_SIZE, &m_extraNonce, EXTRA_NONCE_SIZE);
return key; return key;
} }
hash calculate_tx_key_seed() const;
}; };
} // namespace p2pool } // namespace p2pool
+8 -24
View File
@@ -61,6 +61,8 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
#define READ_BUF(buf, size) do { if (!read_buf((buf), (size))) return __LINE__; } while(0) #define READ_BUF(buf, size) do { if (!read_buf((buf), (size))) return __LINE__; } while(0)
MutexLock lock(m_lock);
READ_BYTE(m_majorVersion); READ_BYTE(m_majorVersion);
if (m_majorVersion > HARDFORK_SUPPORTED_VERSION) return __LINE__; if (m_majorVersion > HARDFORK_SUPPORTED_VERSION) return __LINE__;
@@ -258,20 +260,11 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__; return __LINE__;
} }
READ_BUF(m_txkeySecSeed.h, HASH_SIZE); READ_BUF(m_txkeySec.h, HASH_SIZE);
const int sidechain_version = get_sidechain_version(); if (!check_keys(m_txkeyPub, m_txkeySec)) {
if (sidechain_version > 1) {
hash pub;
get_tx_keys(pub, m_txkeySec, m_txkeySecSeed, m_prevId);
if (pub != m_txkeyPub) {
return __LINE__; return __LINE__;
} }
}
else {
// Both values are the same before v2
m_txkeySec = m_txkeySecSeed;
// Enforce deterministic tx keys starting from v15 // Enforce deterministic tx keys starting from v15
if (m_majorVersion >= HARDFORK_VIEW_TAGS_VERSION) { if (m_majorVersion >= HARDFORK_VIEW_TAGS_VERSION) {
@@ -281,11 +274,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__; return __LINE__;
} }
} }
}
if (!check_keys(m_txkeyPub, m_txkeySec)) {
return __LINE__;
}
READ_BUF(m_parent.h, HASH_SIZE); READ_BUF(m_parent.h, HASH_SIZE);
@@ -329,10 +317,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
READ_VARINT(m_cumulativeDifficulty.lo); READ_VARINT(m_cumulativeDifficulty.lo);
READ_VARINT(m_cumulativeDifficulty.hi); READ_VARINT(m_cumulativeDifficulty.hi);
if (sidechain_version > 1) {
READ_BUF(m_sidechainExtraBuf, sizeof(m_sidechainExtraBuf));
}
#undef READ_BYTE #undef READ_BYTE
#undef EXPECT_BYTE #undef EXPECT_BYTE
#undef READ_VARINT #undef READ_VARINT
@@ -398,13 +382,13 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
}, },
static_cast<int>(size + outputs_blob_size_diff + transactions_blob_size_diff + consensus_id.size()), check.h, HASH_SIZE); static_cast<int>(size + outputs_blob_size_diff + transactions_blob_size_diff + consensus_id.size()), check.h, HASH_SIZE);
#if POOL_BLOCK_DEBUG
m_sideChainDataDebug.assign(sidechain_data_begin, data_end);
#endif
if (check != m_sidechainId) { if (check != m_sidechainId) {
return __LINE__; return __LINE__;
} }
#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();
+5 -5
View File
@@ -68,7 +68,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
m_cache[i] = randomx_alloc_cache(flags); m_cache[i] = randomx_alloc_cache(flags);
if (!m_cache[i]) { if (!m_cache[i]) {
LOGERR(1, "couldn't allocate RandomX cache, aborting"); LOGERR(1, "couldn't allocate RandomX cache, aborting");
PANIC_STOP(); panic();
} }
} }
memory_allocated += RANDOMX_ARGON_MEMORY * 1024; memory_allocated += RANDOMX_ARGON_MEMORY * 1024;
@@ -206,7 +206,7 @@ void RandomX_Hasher::set_seed(const hash& seed)
m_vm[m_index].vm = randomx_create_vm(flags, m_cache[m_index], nullptr); m_vm[m_index].vm = randomx_create_vm(flags, m_cache[m_index], nullptr);
if (!m_vm[m_index].vm) { if (!m_vm[m_index].vm) {
LOGERR(1, "couldn't allocate RandomX light VM, aborting"); LOGERR(1, "couldn't allocate RandomX light VM, aborting");
PANIC_STOP(); panic();
} }
} }
} }
@@ -308,7 +308,7 @@ void RandomX_Hasher::set_old_seed(const hash& seed)
m_vm[old_index].vm = randomx_create_vm(flags, m_cache[old_index], nullptr); m_vm[old_index].vm = randomx_create_vm(flags, m_cache[old_index], nullptr);
if (!m_vm[old_index].vm) { if (!m_vm[old_index].vm) {
LOGERR(1, "couldn't allocate RandomX light VM, aborting"); LOGERR(1, "couldn't allocate RandomX light VM, aborting");
PANIC_STOP(); panic();
} }
} }
} }
@@ -376,7 +376,7 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
int err = uv_loop_init(&m_loop); int err = uv_loop_init(&m_loop);
if (err) { if (err) {
LOGERR(1, "failed to create event loop, error " << uv_err_name(err)); LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
// Init loop user data before running it // Init loop user data before running it
@@ -394,7 +394,7 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
err = uv_thread_create(&m_loopThread, loop, this); err = uv_thread_create(&m_loopThread, loop, this);
if (err) { if (err) {
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err)); LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
} }
+233 -239
View File
@@ -47,17 +47,16 @@ static constexpr size_t UNCLE_BLOCK_DEPTH = 3;
static_assert(1 <= UNCLE_BLOCK_DEPTH && UNCLE_BLOCK_DEPTH <= 10, "Invalid UNCLE_BLOCK_DEPTH"); static_assert(1 <= UNCLE_BLOCK_DEPTH && UNCLE_BLOCK_DEPTH <= 10, "Invalid UNCLE_BLOCK_DEPTH");
static constexpr uint64_t MONERO_BLOCK_TIME = 300; static constexpr uint64_t MONERO_BLOCK_TIME = 120;
namespace p2pool { namespace p2pool {
static constexpr uint8_t default_consensus_id[HASH_SIZE] = { 24,165,115,221,181,11,104,146,227,153,218,107,44,108,68,39,178,81,4,212,169,4,142,0,177,110,157,240,68,7,249,24 }; static constexpr uint8_t default_consensus_id[HASH_SIZE] = { 34,175,126,231,181,11,104,146,227,153,218,107,44,108,68,39,178,81,4,212,169,4,142,0,177,110,157,240,68,7,249,24 };
static constexpr uint8_t mini_consensus_id[HASH_SIZE] = { 47,120,200,25,139,174,199,250,66,80,189,18,108,216,194,220,136,23,63,24,64,113,221,44,219,86,39,163,53,24,126,196 }; static constexpr uint8_t mini_consensus_id[HASH_SIZE] = { 57,130,201,26,149,174,199,250,66,80,189,18,108,216,194,220,136,23,63,24,64,113,221,44,219,86,39,163,53,24,126,196 };
NetworkType SideChain::s_networkType = NetworkType::Invalid;
SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name) SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
: m_pool(pool) : m_pool(pool)
, m_networkType(type)
, m_chainTip{ nullptr } , m_chainTip{ nullptr }
, m_seenWalletsLastPruneTime(0) , m_seenWalletsLastPruneTime(0)
, m_poolName(pool_name ? pool_name : "default") , m_poolName(pool_name ? pool_name : "default")
@@ -65,28 +64,19 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
, m_minDifficulty(MIN_DIFFICULTY, 0) , m_minDifficulty(MIN_DIFFICULTY, 0)
, m_chainWindowSize(2160) , m_chainWindowSize(2160)
, m_unclePenalty(20) , m_unclePenalty(20)
, m_curDifficulty(m_minDifficulty)
, m_precalcFinished(false) , m_precalcFinished(false)
{ {
if (s_networkType == NetworkType::Invalid) { LOGINFO(1, log::LightCyan() << "network type = " << m_networkType);
s_networkType = type;
}
else if (s_networkType != type) {
LOGERR(1, "can't run both " << s_networkType << " and " << type << " at the same time");
PANIC_STOP();
}
LOGINFO(1, log::LightCyan() << "network type = " << type);
if (m_pool && !load_config(m_pool->params().m_config)) { if (m_pool && !load_config(m_pool->params().m_config)) {
PANIC_STOP(); panic();
} }
if (!check_config()) { if (!check_config()) {
PANIC_STOP(); panic();
} }
m_curDifficulty = m_minDifficulty;
uv_rwlock_init_checked(&m_sidechainLock); uv_rwlock_init_checked(&m_sidechainLock);
uv_mutex_init_checked(&m_seenWalletsLock); uv_mutex_init_checked(&m_seenWalletsLock);
uv_mutex_init_checked(&m_seenBlocksLock); uv_mutex_init_checked(&m_seenBlocksLock);
@@ -99,7 +89,7 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
char buf[log::Stream::BUF_SIZE + 1]; char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf); log::Stream s(buf);
s << s_networkType << '\0' s << m_networkType << '\0'
<< m_poolName << '\0' << m_poolName << '\0'
<< m_poolPassword << '\0' << m_poolPassword << '\0'
<< m_targetBlockTime << '\0' << m_targetBlockTime << '\0'
@@ -127,7 +117,7 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
cache = randomx_alloc_cache(flags); cache = randomx_alloc_cache(flags);
if (!cache) { if (!cache) {
LOGERR(1, "couldn't allocate RandomX cache, aborting"); LOGERR(1, "couldn't allocate RandomX cache, aborting");
PANIC_STOP(); panic();
} }
} }
@@ -151,12 +141,12 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
} }
hash id; hash id;
keccak(reinterpret_cast<uint8_t*>(scratchpad), static_cast<int>(scratchpad_size * sizeof(rx_vec_i128)), id.h); keccak(reinterpret_cast<uint8_t*>(scratchpad), static_cast<int>(scratchpad_size * sizeof(rx_vec_i128)), id.h, HASH_SIZE);
randomx_release_cache(cache); randomx_release_cache(cache);
m_consensusId.assign(id.h, id.h + HASH_SIZE); m_consensusId.assign(id.h, id.h + HASH_SIZE);
#else #else
LOGERR(1, "Can't calculate consensus ID without RandomX library"); LOGERR(1, "Can't calculate consensus ID without RandomX library");
PANIC_STOP(); panic();
#endif #endif
} }
@@ -168,8 +158,6 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
m_consensusIdDisplayStr.assign(buf); m_consensusIdDisplayStr.assign(buf);
LOGINFO(1, "consensus ID = " << log::LightCyan() << m_consensusIdDisplayStr.c_str()); LOGINFO(1, "consensus ID = " << log::LightCyan() << m_consensusIdDisplayStr.c_str());
memcpy(m_consensusHash.h, m_consensusId.data(), HASH_SIZE);
uv_cond_init_checked(&m_precalcJobsCond); uv_cond_init_checked(&m_precalcJobsCond);
uv_mutex_init_checked(&m_precalcJobsMutex); uv_mutex_init_checked(&m_precalcJobsMutex);
m_precalcJobs.reserve(16); m_precalcJobs.reserve(16);
@@ -211,17 +199,16 @@ SideChain::~SideChain()
for (const auto& it : m_blocksById) { for (const auto& it : m_blocksById) {
delete it.second; delete it.second;
} }
s_networkType = NetworkType::Invalid;
} }
void SideChain::fill_sidechain_data(PoolBlock& block, std::vector<MinerShare>& shares) const void SideChain::fill_sidechain_data(PoolBlock& block, const Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares) const
{ {
const int sidechain_version = block.get_sidechain_version();
block.m_uncles.clear();
ReadLock lock(m_sidechainLock); ReadLock lock(m_sidechainLock);
block.m_minerWallet = *w;
block.m_txkeySec = txkeySec;
block.m_uncles.clear();
const PoolBlock* tip = m_chainTip; const PoolBlock* tip = m_chainTip;
if (!tip) { if (!tip) {
@@ -230,20 +217,10 @@ void SideChain::fill_sidechain_data(PoolBlock& block, std::vector<MinerShare>& s
block.m_difficulty = m_minDifficulty; block.m_difficulty = m_minDifficulty;
block.m_cumulativeDifficulty = m_minDifficulty; block.m_cumulativeDifficulty = m_minDifficulty;
if (sidechain_version > 1) {
block.m_txkeySecSeed = m_consensusHash;
get_tx_keys(block.m_txkeyPub, block.m_txkeySec, block.m_txkeySecSeed, block.m_prevId);
}
get_shares(&block, shares); get_shares(&block, shares);
return; return;
} }
if (sidechain_version > 1) {
block.m_txkeySecSeed = (block.m_prevId == tip->m_prevId) ? tip->m_txkeySecSeed : tip->calculate_tx_key_seed();
get_tx_keys(block.m_txkeyPub, block.m_txkeySec, block.m_txkeySecSeed, block.m_prevId);
}
block.m_parent = tip->m_sidechainId; block.m_parent = tip->m_sidechainId;
block.m_sidechainHeight = tip->m_sidechainHeight + 1; block.m_sidechainHeight = tip->m_sidechainHeight + 1;
@@ -339,50 +316,23 @@ P2PServer* SideChain::p2pServer() const
return m_pool ? m_pool->p2p_server() : nullptr; return m_pool ? m_pool->p2p_server() : nullptr;
} }
bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares, uint64_t* bottom_height, bool quiet) const bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares) const
{ {
if (tip->m_txkeySecSeed.empty()) { shares.clear();
LOGERR(1, "tx key seed is not set, fix the code!"); shares.reserve(m_chainWindowSize * 2);
}
const int L = quiet ? 6 : 3;
// Collect shares from each block in the PPLNS window, starting from the "tip" // Collect shares from each block in the PPLNS window, starting from the "tip"
uint64_t block_depth = 0; uint64_t block_depth = 0;
const PoolBlock* cur = tip; const PoolBlock* cur = tip;
difficulty_type mainchain_diff
#ifdef P2POOL_UNIT_TESTS
= m_testMainChainDiff
#endif
;
if (m_pool && !tip->m_parent.empty()) {
const uint64_t h = p2pool::get_seed_height(tip->m_txinGenHeight);
if (!m_pool->get_difficulty_at_height(h, mainchain_diff)) {
LOGWARN(L, "get_shares: couldn't get mainchain difficulty for height = " << h);
return false;
}
}
// Dynamic PPLNS window starting from v2
// Limit PPLNS weight to 2x of the Monero difficulty (max 2 blocks per PPLNS window on average)
const int sidechain_version = tip->get_sidechain_version();
const difficulty_type max_pplns_weight = (sidechain_version > 1) ? (mainchain_diff * 2) : diff_max;
difficulty_type pplns_weight;
unordered_set<MinerShare> shares_set;
shares_set.reserve(m_chainWindowSize * 2);
do { do {
difficulty_type cur_weight = cur->m_difficulty; MinerShare cur_share{ cur->m_difficulty, &cur->m_minerWallet };
for (const hash& uncle_id : cur->m_uncles) { for (const hash& uncle_id : cur->m_uncles) {
auto it = m_blocksById.find(uncle_id); auto it = m_blocksById.find(uncle_id);
if (it == m_blocksById.end()) { if (it == m_blocksById.end()) {
LOGWARN(L, "get_shares: can't find uncle block at height = " << cur->m_sidechainHeight << ", id = " << uncle_id); LOGWARN(3, "get_shares: can't find uncle block at height = " << cur->m_sidechainHeight << ", id = " << uncle_id);
LOGWARN(L, "get_shares: can't calculate shares for block at height = " << tip->m_sidechainHeight << ", id = " << tip->m_sidechainId << ", mainchain height = " << tip->m_txinGenHeight); LOGWARN(3, "get_shares: can't calculate shares for block at height = " << tip->m_sidechainHeight << ", id = " << tip->m_sidechainId << ", mainchain height = " << tip->m_txinGenHeight);
return false; return false;
} }
@@ -395,34 +345,11 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares
// Take some % of uncle's weight into this share // Take some % of uncle's weight into this share
const difficulty_type uncle_penalty = uncle->m_difficulty * m_unclePenalty / 100; const difficulty_type uncle_penalty = uncle->m_difficulty * m_unclePenalty / 100;
const difficulty_type uncle_weight = uncle->m_difficulty - uncle_penalty; cur_share.m_weight += uncle_penalty;
const difficulty_type new_pplns_weight = pplns_weight + uncle_weight; shares.emplace_back(uncle->m_difficulty - uncle_penalty, &uncle->m_minerWallet);
// Skip uncles that push PPLNS weight above the limit
if (new_pplns_weight > max_pplns_weight) {
continue;
} }
cur_weight += uncle_penalty; shares.push_back(cur_share);
auto result = shares_set.emplace(uncle_weight, &uncle->m_minerWallet);
if (!result.second) {
result.first->m_weight += uncle_weight;
}
pplns_weight = new_pplns_weight;
}
// Always add non-uncle shares even if PPLNS weight goes above the limit
auto result = shares_set.emplace(cur_weight, &cur->m_minerWallet);
if (!result.second) {
result.first->m_weight += cur_weight;
}
pplns_weight += cur_weight;
// One non-uncle share can go above the limit, but it will also guarantee that "shares" is never empty
if (pplns_weight > max_pplns_weight) {
break;
}
++block_depth; ++block_depth;
if (block_depth >= m_chainWindowSize) { if (block_depth >= m_chainWindowSize) {
@@ -436,39 +363,77 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares
auto it = m_blocksById.find(cur->m_parent); auto it = m_blocksById.find(cur->m_parent);
if (it == m_blocksById.end()) { if (it == m_blocksById.end()) {
LOGWARN(L, "get_shares: can't find parent block at height = " << cur->m_sidechainHeight - 1 << ", id = " << cur->m_parent); LOGWARN(3, "get_shares: can't find parent block at height = " << cur->m_sidechainHeight - 1 << ", id = " << cur->m_parent);
LOGWARN(L, "get_shares: can't calculate shares for block at height = " << tip->m_sidechainHeight << ", id = " << tip->m_sidechainId << ", mainchain height = " << tip->m_txinGenHeight); LOGWARN(3, "get_shares: can't calculate shares for block at height = " << tip->m_sidechainHeight << ", id = " << tip->m_sidechainId << ", mainchain height = " << tip->m_txinGenHeight);
return false; return false;
} }
cur = it->second; cur = it->second;
} while (true); } while (true);
if (bottom_height) { // Combine shares with the same wallet addresses
*bottom_height = cur->m_sidechainHeight;
}
shares.assign(shares_set.begin(), shares_set.end());
std::sort(shares.begin(), shares.end(), [](const auto& a, const auto& b) { return *a.m_wallet < *b.m_wallet; }); std::sort(shares.begin(), shares.end(), [](const auto& a, const auto& b) { return *a.m_wallet < *b.m_wallet; });
const uint64_t n = shares.size(); size_t k = 0;
for (size_t i = 1, n = shares.size(); i < n; ++i)
// Shuffle shares {
if ((sidechain_version > 1) && (n > 1)) { if (*shares[i].m_wallet == *shares[k].m_wallet) {
hash h; shares[k].m_weight += shares[i].m_weight;
keccak(tip->m_txkeySecSeed.h, HASH_SIZE, h.h); }
else {
uint64_t seed = *reinterpret_cast<uint64_t*>(h.h); ++k;
if (seed == 0) seed = 1; shares[k].m_weight = shares[i].m_weight;
shares[k].m_wallet = shares[i].m_wallet;
for (uint64_t i = 0, k; i < n - 1; ++i) {
seed = xorshift64star(seed);
umul128(seed, n - i, &k);
std::swap(shares[i], shares[i + k]);
} }
} }
LOGINFO(6, "get_shares: " << n << " unique wallets in PPLNS window"); shares.resize(k + 1);
LOGINFO(6, "get_shares: " << k + 1 << " unique wallets in PPLNS window");
return true;
}
bool SideChain::get_wallets(const PoolBlock* tip, std::vector<const Wallet*>& wallets) const
{
// Collect wallets from each block in the PPLNS window, starting from the "tip"
wallets.clear();
wallets.reserve(m_chainWindowSize * 2);
uint64_t block_depth = 0;
const PoolBlock* cur = tip;
do {
wallets.push_back(&cur->m_minerWallet);
for (const hash& uncle_id : cur->m_uncles) {
auto it = m_blocksById.find(uncle_id);
if (it == m_blocksById.end()) {
return false;
}
// Skip uncles which are already out of PPLNS window
if (tip->m_sidechainHeight - it->second->m_sidechainHeight < m_chainWindowSize) {
wallets.push_back(&it->second->m_minerWallet);
}
}
++block_depth;
if ((block_depth >= m_chainWindowSize) || (cur->m_sidechainHeight == 0)) {
break;
}
auto it = m_blocksById.find(cur->m_parent);
if (it == m_blocksById.end()) {
return false;
}
cur = it->second;
} while (true);
// Remove duplicates
std::sort(wallets.begin(), wallets.end(), [](const Wallet* a, const Wallet* b) { return *a < *b; });
wallets.erase(std::unique(wallets.begin(), wallets.end(), [](const Wallet* a, const Wallet* b) { return *a == *b; }), wallets.end());
return true; return true;
} }
@@ -562,7 +527,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
// Check if it has the correct parent and difficulty to go right to monerod for checking // Check if it has the correct parent and difficulty to go right to monerod for checking
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 Wownero 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.serialize_mainchain_data()); m_pool->submit_block_async(block.serialize_mainchain_data());
} }
else { else {
@@ -571,7 +536,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
LOGWARN(3, "add_external_block: couldn't get mainchain difficulty for height = " << block.m_txinGenHeight); LOGWARN(3, "add_external_block: couldn't get mainchain difficulty for height = " << block.m_txinGenHeight);
} }
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 Wownero 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.serialize_mainchain_data()); m_pool->submit_block_async(block.serialize_mainchain_data());
} }
} }
@@ -738,18 +703,7 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
{ {
blob.clear(); blob.clear();
struct Data
{
FORCEINLINE Data() : counter(0) {}
Data(Data&&) = delete;
Data& operator=(Data&&) = delete;
std::vector<MinerShare> tmpShares; std::vector<MinerShare> tmpShares;
hash txkeySec;
std::atomic<int> counter;
};
std::shared_ptr<Data> data;
std::vector<uint64_t> tmpRewards; std::vector<uint64_t> tmpRewards;
{ {
ReadLock lock(m_sidechainLock); ReadLock lock(m_sidechainLock);
@@ -778,32 +732,80 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
return true; return true;
} }
data = std::make_shared<Data>(); if (!get_shares(block, tmpShares) || !split_reward(total_reward, tmpShares, tmpRewards) || (tmpRewards.size() != tmpShares.size())) {
data->txkeySec = block->m_txkeySec;
if (!get_shares(block, data->tmpShares) || !split_reward(total_reward, data->tmpShares, tmpRewards) || (tmpRewards.size() != data->tmpShares.size())) {
return false; return false;
} }
} }
const size_t n = data->tmpShares.size(); const size_t n = tmpShares.size();
data->counter = static_cast<int>(n) - 1;
// Helper jobs call get_eph_public_key with indices in descending order // Helper jobs call get_eph_public_key with indices in descending order
// Current thread will process indices in ascending order so when they meet, everything will be cached // Current thread will process indices in ascending order so when they meet, everything will be cached
std::atomic<int> counter{ 0 };
std::atomic<int> num_helper_jobs_finished{ 0 };
int num_helper_jobs_started = 0;
if (loop) { if (loop) {
parallel_run(loop, [data]() { uint32_t HELPER_JOBS_COUNT = std::thread::hardware_concurrency();
Data* d = data.get();
// this thread will also be running, so reduce helper job count by 1
if (HELPER_JOBS_COUNT > 0) {
--HELPER_JOBS_COUNT;
}
// No more than 8 helper jobs because our UV worker thread pool has 8 threads
if (HELPER_JOBS_COUNT > 8) {
HELPER_JOBS_COUNT = 8;
}
struct Work
{
uv_work_t req;
const std::vector<MinerShare>& tmpShares;
const hash& txkeySec;
std::atomic<int>& counter;
std::atomic<int>& num_helper_jobs_finished;
// Fix MSVC warnings
Work() = delete;
Work& operator=(Work&&) = delete;
};
counter = static_cast<int>(n) - 1;
num_helper_jobs_started = HELPER_JOBS_COUNT;
for (size_t i = 0; i < HELPER_JOBS_COUNT; ++i) {
Work* w = new Work{ {}, tmpShares, block->m_txkeySec, counter, num_helper_jobs_finished };
w->req.data = w;
const int err = uv_queue_work(loop, &w->req,
[](uv_work_t* req)
{
Work* work = reinterpret_cast<Work*>(req->data);
hash eph_public_key; hash eph_public_key;
int index; int index;
while ((index = d->counter.fetch_sub(1)) >= 0) { while ((index = work->counter.fetch_sub(1)) >= 0) {
uint8_t view_tag; uint8_t view_tag;
if (!d->tmpShares[index].m_wallet->get_eph_public_key(d->txkeySec, static_cast<size_t>(index), eph_public_key, view_tag)) { if (!work->tmpShares[index].m_wallet->get_eph_public_key(work->txkeySec, static_cast<size_t>(index), eph_public_key, view_tag)) {
LOGWARN(6, "get_eph_public_key failed at index " << index); LOGWARN(6, "get_eph_public_key failed at index " << index);
} }
} }
++work->num_helper_jobs_finished;
},
[](uv_work_t* req, int /*status*/)
{
delete reinterpret_cast<Work*>(req->data);
}); });
if (err) {
LOGERR(1, "get_outputs_blob: uv_queue_work failed, error " << uv_err_name(err));
--num_helper_jobs_started;
delete w;
}
}
} }
blob.reserve(n * 39 + 64); blob.reserve(n * 39 + 64);
@@ -818,10 +820,10 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
hash eph_public_key; hash eph_public_key;
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
// stop helper jobs when they meet with current thread // stop helper jobs when they meet with current thread
const int c = data->counter.load(); const int c = counter.load();
if ((c >= 0) && (static_cast<int>(i) >= c)) { if ((c >= 0) && (static_cast<int>(i) >= c)) {
// this will cause all helper jobs to finish immediately // this will cause all helper jobs to finish immediately
data->counter = -1; counter = -1;
} }
writeVarint(tmpRewards[i], blob); writeVarint(tmpRewards[i], blob);
@@ -829,7 +831,7 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
blob.emplace_back(tx_type); blob.emplace_back(tx_type);
uint8_t view_tag; uint8_t view_tag;
if (!data->tmpShares[i].m_wallet->get_eph_public_key(data->txkeySec, i, eph_public_key, view_tag)) { if (!tmpShares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key, view_tag)) {
LOGWARN(6, "get_eph_public_key failed at index " << i); LOGWARN(6, "get_eph_public_key failed at index " << i);
} }
blob.insert(blob.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE); blob.insert(blob.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
@@ -842,12 +844,22 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
} }
block->m_outputs.shrink_to_fit(); block->m_outputs.shrink_to_fit();
if (loop) {
// this will cause all helper jobs to finish immediately
counter = -1;
while (num_helper_jobs_finished < num_helper_jobs_started) {
std::this_thread::yield();
}
}
return true; return true;
} }
void SideChain::print_status(bool obtain_sidechain_lock) const void SideChain::print_status(bool obtain_sidechain_lock) const
{ {
unordered_set<hash> blocks_in_window; std::vector<hash> blocks_in_window;
blocks_in_window.reserve(m_chainWindowSize * 9 / 8); blocks_in_window.reserve(m_chainWindowSize * 9 / 8);
const difficulty_type diff = difficulty(); const difficulty_type diff = difficulty();
@@ -855,61 +867,51 @@ void SideChain::print_status(bool obtain_sidechain_lock) const
if (obtain_sidechain_lock) uv_rwlock_rdlock(&m_sidechainLock); if (obtain_sidechain_lock) uv_rwlock_rdlock(&m_sidechainLock);
ON_SCOPE_LEAVE([this, obtain_sidechain_lock]() { if (obtain_sidechain_lock) uv_rwlock_rdunlock(&m_sidechainLock); }); ON_SCOPE_LEAVE([this, obtain_sidechain_lock]() { if (obtain_sidechain_lock) uv_rwlock_rdunlock(&m_sidechainLock); });
const uint64_t pool_hashrate = (diff / m_targetBlockTime).lo; uint64_t rem;
uint64_t pool_hashrate = udiv128(diff.hi, diff.lo, m_targetBlockTime, &rem);
const difficulty_type network_diff = m_pool->miner_data().difficulty; difficulty_type network_diff = m_pool->miner_data().difficulty;
const uint64_t network_hashrate = (network_diff / MONERO_BLOCK_TIME).lo; uint64_t network_hashrate = udiv128(network_diff.hi, network_diff.lo, MONERO_BLOCK_TIME, &rem);
const PoolBlock* tip = m_chainTip; const PoolBlock* tip = m_chainTip;
std::vector<MinerShare> shares;
uint64_t bottom_height = 0;
if (tip) {
get_shares(tip, shares, &bottom_height, true);
}
const uint64_t window_size = (tip && bottom_height) ? (tip->m_sidechainHeight - bottom_height + 1U) : m_chainWindowSize;
uint64_t block_depth = 0; uint64_t block_depth = 0;
const PoolBlock* cur = tip; const PoolBlock* cur = tip;
const uint64_t tip_height = tip ? tip->m_sidechainHeight : 0; const uint64_t tip_height = tip ? tip->m_sidechainHeight : 0;
uint64_t total_blocks_in_window = 0; uint32_t total_blocks_in_window = 0;
uint64_t total_uncles_in_window = 0; uint32_t total_uncles_in_window = 0;
// each dot corresponds to window_size / 30 shares, with current values, 2160 / 30 = 72 // each dot corresponds to m_chainWindowSize / 30 shares, with current values, 2160 / 30 = 72
constexpr size_t N = 30; std::array<uint32_t, 30> our_blocks_in_window{};
std::array<uint64_t, N> our_blocks_in_window{}; std::array<uint32_t, 30> our_uncles_in_window{};
std::array<uint64_t, N> our_uncles_in_window{};
const Wallet& w = m_pool->params().m_wallet;
while (cur) { while (cur) {
blocks_in_window.emplace(cur->m_sidechainId); blocks_in_window.emplace_back(cur->m_sidechainId);
++total_blocks_in_window; ++total_blocks_in_window;
// "block_depth <= window_size - 1" here (see the check below), so window_index will be <= N - 1 if (cur->m_minerWallet == m_pool->params().m_wallet) {
// This will map the range [0, window_size - 1] into [0, N - 1] // this produces an integer division with quotient rounded up, avoids non-whole divisions from overflowing on total_blocks_in_window
const size_t window_index = (window_size > 1) ? (block_depth * (N - 1) / (window_size - 1)) : 0; const size_t window_index = (total_blocks_in_window - 1) / ((m_chainWindowSize + our_blocks_in_window.size() - 1) / our_blocks_in_window.size());
our_blocks_in_window[std::min(window_index, our_blocks_in_window.size() - 1)]++; // clamp window_index, even if total_blocks_in_window is not larger than m_chainWindowSize
if (cur->m_minerWallet == w) {
++our_blocks_in_window[window_index];
} }
++block_depth; ++block_depth;
if (block_depth >= window_size) { if (block_depth >= m_chainWindowSize) {
break; break;
} }
for (const hash& uncle_id : cur->m_uncles) { for (const hash& uncle_id : cur->m_uncles) {
blocks_in_window.emplace(uncle_id); blocks_in_window.emplace_back(uncle_id);
auto it = m_blocksById.find(uncle_id); auto it = m_blocksById.find(uncle_id);
if (it != m_blocksById.end()) { if (it != m_blocksById.end()) {
PoolBlock* uncle = it->second; PoolBlock* uncle = it->second;
if (tip_height - uncle->m_sidechainHeight < window_size) { if (tip_height - uncle->m_sidechainHeight < m_chainWindowSize) {
++total_uncles_in_window; ++total_uncles_in_window;
if (uncle->m_minerWallet == w) { if (uncle->m_minerWallet == m_pool->params().m_wallet) {
++our_uncles_in_window[window_index]; // this produces an integer division with quotient rounded up, avoids non-whole divisions from overflowing on total_blocks_in_window
const size_t window_index = (total_blocks_in_window - 1) / ((m_chainWindowSize + our_uncles_in_window.size() - 1) / our_uncles_in_window.size());
our_uncles_in_window[std::min(window_index, our_uncles_in_window.size() - 1)]++; // clamp window_index, even if total_blocks_in_window is not larger than m_chainWindowSize
} }
} }
} }
@@ -921,50 +923,65 @@ void SideChain::print_status(bool obtain_sidechain_lock) const
uint64_t total_orphans = 0; uint64_t total_orphans = 0;
uint64_t our_orphans = 0; uint64_t our_orphans = 0;
uint64_t your_reward = 0;
uint64_t total_reward = 0;
if (tip) { if (tip) {
for (uint64_t i = 0; (i < window_size) && (i <= tip_height); ++i) { std::sort(blocks_in_window.begin(), blocks_in_window.end());
for (uint64_t i = 0; (i < m_chainWindowSize) && (i <= tip_height); ++i) {
auto it = m_blocksByHeight.find(tip_height - i); auto it = m_blocksByHeight.find(tip_height - i);
if (it == m_blocksByHeight.end()) { if (it == m_blocksByHeight.end()) {
continue; continue;
} }
for (const PoolBlock* block : it->second) { for (const PoolBlock* block : it->second) {
if (blocks_in_window.find(block->m_sidechainId) == blocks_in_window.end()) { if (!std::binary_search(blocks_in_window.begin(), blocks_in_window.end(), block->m_sidechainId)) {
LOGINFO(4, "orphan block at height " << log::Gray() << block->m_sidechainHeight << log::NoColor() << ": " << log::Gray() << block->m_sidechainId); LOGINFO(4, "orphan block at height " << log::Gray() << block->m_sidechainHeight << log::NoColor() << ": " << log::Gray() << block->m_sidechainId);
++total_orphans; ++total_orphans;
if (block->m_minerWallet == w) { if (block->m_minerWallet == m_pool->params().m_wallet) {
++our_orphans; ++our_orphans;
} }
} }
} }
} }
const Wallet& w = m_pool->params().m_wallet;
const uint8_t tx_type = tip->get_tx_type();
hash eph_public_key;
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
const PoolBlock::TxOutput& out = tip->m_outputs[i];
if (!your_reward) {
if (tx_type == TXOUT_TO_TAGGED_KEY) {
uint8_t view_tag;
const uint8_t expected_view_tag = out.m_viewTag;
if (w.get_eph_public_key(tip->m_txkeySec, i, eph_public_key, view_tag, &expected_view_tag) && (out.m_ephPublicKey == eph_public_key)) {
your_reward = out.m_reward;
}
}
else {
uint8_t view_tag;
if (w.get_eph_public_key(tip->m_txkeySec, i, eph_public_key, view_tag) && (out.m_ephPublicKey == eph_public_key)) {
your_reward = out.m_reward;
}
}
}
total_reward += out.m_reward;
}
} }
difficulty_type your_shares_weight, pplns_weight; uint64_t product[2];
for (const MinerShare& s : shares) { product[0] = umul128(pool_hashrate, your_reward, &product[1]);
if (*s.m_wallet == w) { const uint64_t hashrate_est = total_reward ? udiv128(product[1], product[0], total_reward, &rem) : 0;
your_shares_weight = s.m_weight;
}
pplns_weight += s.m_weight;
}
if (pplns_weight == 0) {
pplns_weight = m_minDifficulty;
}
const uint64_t total_reward = m_pool->block_template().get_reward();
const uint64_t your_reward = ((your_shares_weight * total_reward) / pplns_weight).lo;
const uint64_t hashrate_est = ((your_shares_weight * pool_hashrate) / pplns_weight).lo;
const double block_share = total_reward ? ((static_cast<double>(your_reward) * 100.0) / static_cast<double>(total_reward)) : 0.0; const double block_share = total_reward ? ((static_cast<double>(your_reward) * 100.0) / static_cast<double>(total_reward)) : 0.0;
const uint64_t our_blocks_in_window_total = std::accumulate(our_blocks_in_window.begin(), our_blocks_in_window.end(), 0ULL); const uint32_t our_blocks_in_window_total = std::accumulate(our_blocks_in_window.begin(), our_blocks_in_window.end(), 0U);
const uint64_t our_uncles_in_window_total = std::accumulate(our_uncles_in_window.begin(), our_uncles_in_window.end(), 0ULL); const uint32_t our_uncles_in_window_total = std::accumulate(our_uncles_in_window.begin(), our_uncles_in_window.end(), 0U);
std::string our_blocks_in_window_chart; std::string our_blocks_in_window_chart;
if (our_blocks_in_window_total) { if (our_blocks_in_window_total) {
our_blocks_in_window_chart.reserve(our_blocks_in_window.size() + 32); our_blocks_in_window_chart.reserve(our_blocks_in_window.size() + 32);
our_blocks_in_window_chart = "\nYour shares position = ["; our_blocks_in_window_chart = "\nYour shares position = [";
for (uint64_t p : our_blocks_in_window) { for (uint32_t p : our_blocks_in_window) {
our_blocks_in_window_chart += (p ? ((p > 9) ? '+' : static_cast<char>('0' + p)) : '.'); our_blocks_in_window_chart += (p ? ((p > 9) ? '+' : static_cast<char>('0' + p)) : '.');
} }
our_blocks_in_window_chart += ']'; our_blocks_in_window_chart += ']';
@@ -974,7 +991,7 @@ void SideChain::print_status(bool obtain_sidechain_lock) const
if (our_uncles_in_window_total) { if (our_uncles_in_window_total) {
our_uncles_in_window_chart.reserve(our_uncles_in_window.size() + 32); our_uncles_in_window_chart.reserve(our_uncles_in_window.size() + 32);
our_uncles_in_window_chart = "\nYour uncles position = ["; our_uncles_in_window_chart = "\nYour uncles position = [";
for (uint64_t p : our_uncles_in_window) { for (uint32_t p : our_uncles_in_window) {
our_uncles_in_window_chart += (p ? ((p > 9) ? '+' : static_cast<char>('0' + p)) : '.'); our_uncles_in_window_chart += (p ? ((p > 9) ? '+' : static_cast<char>('0' + p)) : '.');
} }
our_uncles_in_window_chart += ']'; our_uncles_in_window_chart += ']';
@@ -988,8 +1005,7 @@ void SideChain::print_status(bool obtain_sidechain_lock) const
"\nSide chain hashrate = " << log::Hashrate(pool_hashrate) << "\nSide chain hashrate = " << log::Hashrate(pool_hashrate) <<
(hashrate_est ? "\nYour hashrate (pool-side) = " : "") << (hashrate_est ? log::Hashrate(hashrate_est) : log::Hashrate()) << (hashrate_est ? "\nYour hashrate (pool-side) = " : "") << (hashrate_est ? log::Hashrate(hashrate_est) : log::Hashrate()) <<
"\nPPLNS window = " << total_blocks_in_window << " blocks (+" << total_uncles_in_window << " uncles, " << total_orphans << " orphans)" << "\nPPLNS window = " << total_blocks_in_window << " blocks (+" << total_uncles_in_window << " uncles, " << total_orphans << " orphans)" <<
"\nPPLNS window duration = " << log::Duration((pplns_weight / pool_hashrate).lo) << "\nYour wallet address = " << m_pool->params().m_wallet <<
"\nYour wallet address = " << w <<
"\nYour shares = " << our_blocks_in_window_total << " blocks (+" << our_uncles_in_window_total << " uncles, " << our_orphans << " orphans)" "\nYour shares = " << our_blocks_in_window_total << " blocks (+" << our_uncles_in_window_total << " uncles, " << our_orphans << " orphans)"
<< our_blocks_in_window_chart << our_uncles_in_window_chart << << our_blocks_in_window_chart << our_uncles_in_window_chart <<
"\nBlock reward share = " << block_share << "% (" << log::XMRAmount(your_reward) << ')' "\nBlock reward share = " << block_share << "% (" << log::XMRAmount(your_reward) << ')'
@@ -1031,12 +1047,12 @@ double SideChain::get_reward_share(const Wallet& w) const
return total_reward ? (static_cast<double>(reward) / static_cast<double>(total_reward)) : 0.0; return total_reward ? (static_cast<double>(reward) / static_cast<double>(total_reward)) : 0.0;
} }
uint64_t SideChain::network_major_version(uint64_t height) uint64_t SideChain::network_major_version(uint64_t height) const
{ {
const hardfork_t* hard_forks; const hardfork_t* hard_forks;
size_t num_hard_forks; size_t num_hard_forks;
switch (s_networkType) switch (m_networkType)
{ {
case NetworkType::Mainnet: case NetworkType::Mainnet:
default: default:
@@ -1322,15 +1338,12 @@ void SideChain::verify_loop(PoolBlock* block)
void SideChain::verify(PoolBlock* block) void SideChain::verify(PoolBlock* block)
{ {
const int sidechain_version = block->get_sidechain_version();
// Genesis block // Genesis block
if (block->m_sidechainHeight == 0) { if (block->m_sidechainHeight == 0) {
if (!block->m_parent.empty() || if (!block->m_parent.empty() ||
!block->m_uncles.empty() || !block->m_uncles.empty() ||
(block->m_difficulty != m_minDifficulty) || (block->m_difficulty != m_minDifficulty) ||
(block->m_cumulativeDifficulty != m_minDifficulty) || (block->m_cumulativeDifficulty != m_minDifficulty))
((sidechain_version > 1) && (block->m_txkeySecSeed != m_consensusHash)))
{ {
block->m_invalid = true; block->m_invalid = true;
} }
@@ -1376,24 +1389,12 @@ void SideChain::verify(PoolBlock* block)
return; return;
} }
if (sidechain_version > 1) {
// Check m_txkeySecSeed
const hash h = (block->m_prevId == parent->m_prevId) ? parent->m_txkeySecSeed : parent->calculate_tx_key_seed();
if (block->m_txkeySecSeed != h) {
LOGWARN(3, "block " << block->m_sidechainId << " has invalid tx key seed: expected " << h << ", got " << block->m_txkeySecSeed);
block->m_verified = true;
block->m_invalid = true;
return;
}
}
const uint64_t expectedHeight = parent->m_sidechainHeight + 1; const uint64_t expectedHeight = parent->m_sidechainHeight + 1;
if (block->m_sidechainHeight != expectedHeight) { if (block->m_sidechainHeight != expectedHeight) {
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 <<
" has wrong height: expected " << expectedHeight); " has wrong height: expected " << expectedHeight);
block->m_verified = true;
block->m_invalid = true; block->m_invalid = true;
return; return;
} }
@@ -1657,11 +1658,6 @@ void SideChain::update_chain_tip(const PoolBlock* block)
const PoolBlock* tip = m_chainTip; const PoolBlock* tip = m_chainTip;
if (block == tip) {
LOGINFO(5, "Trying to update chain tip to the same block again. Ignoring it.");
return;
}
bool is_alternative; bool is_alternative;
if (is_longer_chain(tip, block, is_alternative)) { if (is_longer_chain(tip, block, is_alternative)) {
difficulty_type diff; difficulty_type diff;
@@ -2020,7 +2016,7 @@ bool SideChain::load_config(const std::string& filename)
parseValue(doc, "block_time", m_targetBlockTime); parseValue(doc, "block_time", m_targetBlockTime);
uint64_t min_diff; uint64_t min_diff;
if (parseValue(doc, "min_diff", min_diff) && min_diff) { if (parseValue(doc, "min_diff", min_diff)) {
m_minDifficulty = { min_diff, 0 }; m_minDifficulty = { min_diff, 0 };
} }
@@ -2052,7 +2048,6 @@ bool SideChain::check_config() const
return false; return false;
} }
if (s_networkType == NetworkType::Mainnet) {
const difficulty_type min_diff{ MIN_DIFFICULTY, 0 }; const difficulty_type min_diff{ MIN_DIFFICULTY, 0 };
const difficulty_type max_diff{ 1000000000, 0 }; const difficulty_type max_diff{ 1000000000, 0 };
@@ -2060,7 +2055,6 @@ bool SideChain::check_config() const
LOGERR(1, "min_diff is invalid (must be between " << min_diff << " and " << max_diff << ')'); LOGERR(1, "min_diff is invalid (must be between " << min_diff << " and " << max_diff << ')');
return false; return false;
} }
}
if ((m_chainWindowSize < 60) || (m_chainWindowSize > 2160)) { if ((m_chainWindowSize < 60) || (m_chainWindowSize > 2160)) {
LOGERR(1, "pplns_window is invalid (must be between 60 and 2160)"); LOGERR(1, "pplns_window is invalid (must be between 60 and 2160)");
@@ -2096,10 +2090,10 @@ void SideChain::launch_precalc(const PoolBlock* block)
if (b->m_precalculated) { if (b->m_precalculated) {
continue; continue;
} }
std::vector<MinerShare> shares; std::vector<const Wallet*> wallets;
if (get_shares(b, shares, nullptr, true)) { if (get_wallets(b, wallets)) {
b->m_precalculated = true; b->m_precalculated = true;
PrecalcJob* job = new PrecalcJob{ b, std::move(shares) }; PrecalcJob* job = new PrecalcJob{ b, std::move(wallets) };
{ {
MutexLock lock2(m_precalcJobsMutex); MutexLock lock2(m_precalcJobsMutex);
m_precalcJobs.push_back(job); m_precalcJobs.push_back(job);
@@ -2136,20 +2130,20 @@ void SideChain::precalc_worker()
uint8_t t[HASH_SIZE * 2 + sizeof(size_t)]; uint8_t t[HASH_SIZE * 2 + sizeof(size_t)];
memcpy(t, job->b->m_txkeySec.h, HASH_SIZE); memcpy(t, job->b->m_txkeySec.h, HASH_SIZE);
for (size_t i = 0, n = job->shares.size(); i < n; ++i) { for (size_t i = 0, n = job->wallets.size(); i < n; ++i) {
memcpy(t + HASH_SIZE, job->shares[i].m_wallet->view_public_key().h, HASH_SIZE); memcpy(t + HASH_SIZE, job->wallets[i]->view_public_key().h, HASH_SIZE);
memcpy(t + HASH_SIZE * 2, &i, sizeof(i)); memcpy(t + HASH_SIZE * 2, &i, sizeof(i));
if (!m_uniquePrecalcInputs->insert(robin_hood::hash_bytes(t, array_size(t))).second) { if (!m_uniquePrecalcInputs->insert(robin_hood::hash_bytes(t, array_size(t))).second) {
job->shares[i].m_wallet = nullptr; job->wallets[i] = nullptr;
} }
} }
} }
for (size_t i = 0, n = job->shares.size(); i < n; ++i) { for (size_t i = 0, n = job->wallets.size(); i < n; ++i) {
if (job->shares[i].m_wallet) { if (job->wallets[i]) {
hash eph_public_key; hash eph_public_key;
uint8_t view_tag; uint8_t view_tag;
job->shares[i].m_wallet->get_eph_public_key(job->b->m_txkeySec, i, eph_public_key, view_tag); job->wallets[i]->get_eph_public_key(job->b->m_txkeySec, i, eph_public_key, view_tag);
} }
} }
delete job; delete job;
+7 -27
View File
@@ -32,8 +32,6 @@ struct MinerShare
FORCEINLINE MinerShare() : m_weight(), m_wallet(nullptr) {} FORCEINLINE MinerShare() : m_weight(), m_wallet(nullptr) {}
FORCEINLINE MinerShare(const difficulty_type& w, const Wallet* x) : m_weight(w), m_wallet(x) {} FORCEINLINE MinerShare(const difficulty_type& w, const Wallet* x) : m_weight(w), m_wallet(x) {}
FORCEINLINE bool operator==(const MinerShare& s) const { return *m_wallet == *s.m_wallet; }
difficulty_type m_weight; difficulty_type m_weight;
const Wallet* m_wallet; const Wallet* m_wallet;
}; };
@@ -44,7 +42,7 @@ public:
SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr); SideChain(p2pool* pool, NetworkType type, const char* pool_name = nullptr);
~SideChain(); ~SideChain();
void fill_sidechain_data(PoolBlock& block, std::vector<MinerShare>& shares) const; void fill_sidechain_data(PoolBlock& block, const Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares) const;
bool block_seen(const PoolBlock& block); bool block_seen(const PoolBlock& block);
void unsee_block(const PoolBlock& block); void unsee_block(const PoolBlock& block);
@@ -66,8 +64,8 @@ public:
// Consensus ID can therefore be used as a password to create private P2Pools // Consensus ID can therefore be used as a password to create private P2Pools
const std::vector<uint8_t>& consensus_id() const { return m_consensusId; } const std::vector<uint8_t>& consensus_id() const { return m_consensusId; }
uint64_t chain_window_size() const { return m_chainWindowSize; } uint64_t chain_window_size() const { return m_chainWindowSize; }
static NetworkType network_type() { return s_networkType; } NetworkType network_type() const { return m_networkType; }
static uint64_t network_major_version(uint64_t height); uint64_t network_major_version(uint64_t height) const;
FORCEINLINE difficulty_type difficulty() const { ReadLock lock(m_curDifficultyLock); return m_curDifficulty; } FORCEINLINE difficulty_type difficulty() const { ReadLock lock(m_curDifficultyLock); return m_curDifficulty; }
difficulty_type total_hashes() const; difficulty_type total_hashes() const;
uint64_t block_time() const { return m_targetBlockTime; } uint64_t block_time() const { return m_targetBlockTime; }
@@ -79,20 +77,17 @@ public:
const PoolBlock* chainTip() const { return m_chainTip; } const PoolBlock* chainTip() const { return m_chainTip; }
bool precalcFinished() const { return m_precalcFinished.load(); } bool precalcFinished() const { return m_precalcFinished.load(); }
#ifdef P2POOL_UNIT_TESTS
difficulty_type m_testMainChainDiff;
#endif
static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards); static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards);
private: private:
p2pool* m_pool; p2pool* m_pool;
P2PServer* p2pServer() const; P2PServer* p2pServer() const;
static NetworkType s_networkType; NetworkType m_networkType;
private: private:
bool get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares, uint64_t* bottom_height = nullptr, bool quiet = false) const; bool get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares) const;
bool get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const; bool get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>& difficultyData, difficulty_type& curDifficulty) const;
bool get_wallets(const PoolBlock* tip, std::vector<const Wallet*>& wallets) const;
void verify_loop(PoolBlock* block); void verify_loop(PoolBlock* block);
void verify(PoolBlock* block); void verify(PoolBlock* block);
void update_chain_tip(const PoolBlock* block); void update_chain_tip(const PoolBlock* block);
@@ -139,7 +134,7 @@ private:
struct PrecalcJob struct PrecalcJob
{ {
const PoolBlock* b; const PoolBlock* b;
std::vector<MinerShare> shares; std::vector<const Wallet*> wallets;
}; };
uv_cond_t m_precalcJobsCond; uv_cond_t m_precalcJobsCond;
@@ -151,24 +146,9 @@ private:
std::atomic<bool> m_precalcFinished; std::atomic<bool> m_precalcFinished;
hash m_consensusHash;
void launch_precalc(const PoolBlock* block); void launch_precalc(const PoolBlock* block);
void precalc_worker(); void precalc_worker();
void finish_precalc(); void finish_precalc();
}; };
} // namespace p2pool } // namespace p2pool
namespace robin_hood {
template<>
struct hash<p2pool::MinerShare>
{
FORCEINLINE size_t operator()(const p2pool::MinerShare& value) const noexcept
{
return hash_bytes(value.m_wallet->spend_public_key().h, p2pool::HASH_SIZE);
}
};
} // namespace robin_hood
+12 -22
View File
@@ -60,14 +60,14 @@ 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);
uv_mutex_init_checked(&m_rngLock); uv_mutex_init_checked(&m_rngLock);
uv_rwlock_init_checked(&m_hashrateDataLock); uv_rwlock_init_checked(&m_hashrateDataLock);
m_extraNonce = PoolBlock::signal_v2_readiness(get_random32());
m_submittedSharesPool.resize(10); m_submittedSharesPool.resize(10);
for (size_t i = 0; i < m_submittedSharesPool.size(); ++i) { for (size_t i = 0; i < m_submittedSharesPool.size(); ++i) {
m_submittedSharesPool[i] = new SubmittedShare{}; m_submittedSharesPool[i] = new SubmittedShare{};
@@ -76,7 +76,7 @@ StratumServer::StratumServer(p2pool* pool)
const int err = uv_async_init(&m_loop, &m_blobsAsync, on_blobs_ready); const int err = uv_async_init(&m_loop, &m_blobsAsync, on_blobs_ready);
if (err) { if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err)); LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
m_blobsAsync.data = this; m_blobsAsync.data = this;
m_blobsQueue.reserve(2); m_blobsQueue.reserve(2);
@@ -107,7 +107,7 @@ void StratumServer::on_block(const BlockTemplate& block)
return; return;
} }
const uint32_t extra_nonce_start = PoolBlock::signal_v2_readiness(get_random32()); const uint32_t extra_nonce_start = static_cast<uint32_t>(get_random64());
m_extraNonce.exchange(extra_nonce_start + num_connections); m_extraNonce.exchange(extra_nonce_start + num_connections);
BlobsData* blobs_data = new BlobsData{}; BlobsData* blobs_data = new BlobsData{};
@@ -283,7 +283,7 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
[client, id, &hashing_blob, job_id, blob_size, target, height, &seed_hash](void* buf, size_t buf_size) [client, id, &hashing_blob, job_id, blob_size, target, height, &seed_hash](void* buf, size_t buf_size)
{ {
do { do {
client->m_rpcId = static_cast<StratumServer*>(client->m_owner)->get_random32(); client->m_rpcId = static_cast<uint32_t>(static_cast<StratumServer*>(client->m_owner)->get_random64());
} while (!client->m_rpcId); } while (!client->m_rpcId);
log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t)); log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t));
@@ -298,7 +298,7 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
s << log::Hex(client->m_rpcId) << "\",\"job\":{\"blob\":\""; s << log::Hex(client->m_rpcId) << "\",\"job\":{\"blob\":\"";
s << log::hex_buf(hashing_blob, blob_size) << "\",\"job_id\":\""; s << log::hex_buf(hashing_blob, blob_size) << "\",\"job_id\":\"";
s << log::Hex(job_id) << "\",\"target\":\""; s << log::Hex(job_id) << "\",\"target\":\"";
s << target_hex << "\",\"algo\":\"rx/wow\",\"height\":"; s << target_hex << "\",\"algo\":\"rx/0\",\"height\":";
s << height << ",\"seed_hash\":\""; s << height << ",\"seed_hash\":\"";
s << seed_hash << "\"},\"extensions\":[\"algo\"],\"status\":\"OK\"}}\n"; s << seed_hash << "\"},\"extensions\":[\"algo\"],\"status\":\"OK\"}}\n";
return s.m_pos; return s.m_pos;
@@ -463,10 +463,10 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
return result; return result;
} }
uint32_t StratumServer::get_random32() uint64_t StratumServer::get_random64()
{ {
MutexLock lock(m_rngLock); MutexLock lock(m_rngLock);
return static_cast<uint32_t>(m_rng() >> 32); return m_rng();
} }
void StratumServer::print_status() void StratumServer::print_status()
@@ -585,7 +585,7 @@ void StratumServer::print_stratum_status() const
"\nHashrate (1h est) = " << log::Hashrate(hashrate_1h) << "\nHashrate (1h est) = " << log::Hashrate(hashrate_1h) <<
"\nHashrate (24h est) = " << log::Hashrate(hashrate_24h) << "\nHashrate (24h est) = " << log::Hashrate(hashrate_24h) <<
"\nTotal hashes = " << total_hashes << "\nTotal hashes = " << total_hashes <<
"\nShares found = " << shares_found << static_cast<const char*>(shares_failed_buf) << "\nShares found = " << shares_found << shares_failed_buf <<
"\nAverage effort = " << average_effort << '%' << "\nAverage effort = " << average_effort << '%' <<
"\nCurrent effort = " << static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double() << '%' << "\nCurrent effort = " << static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double() << '%' <<
"\nConnections = " << m_numConnections.load() << " (" << m_numIncomingConnections.load() << " incoming)" "\nConnections = " << m_numConnections.load() << " (" << m_numIncomingConnections.load() << " incoming)"
@@ -758,7 +758,7 @@ void StratumServer::on_blobs_ready()
} }
const bool result = send(client, const bool result = send(client,
[data, target, hashing_blob, job_id](void* buf, size_t buf_size) [data, target, hashing_blob, &job_id](void* buf, size_t buf_size)
{ {
log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t)); log::hex_buf target_hex(reinterpret_cast<const uint8_t*>(&target), sizeof(uint64_t));
@@ -771,7 +771,7 @@ void StratumServer::on_blobs_ready()
s << "{\"jsonrpc\":\"2.0\",\"method\":\"job\",\"params\":{\"blob\":\""; s << "{\"jsonrpc\":\"2.0\",\"method\":\"job\",\"params\":{\"blob\":\"";
s << log::hex_buf(hashing_blob, data->m_blobSize) << "\",\"job_id\":\""; s << log::hex_buf(hashing_blob, data->m_blobSize) << "\",\"job_id\":\"";
s << log::Hex(job_id) << "\",\"target\":\""; s << log::Hex(job_id) << "\",\"target\":\"";
s << target_hex << "\",\"algo\":\"rx/wow\",\"height\":"; s << target_hex << "\",\"algo\":\"rx/0\",\"height\":";
s << data->m_height << ",\"seed_hash\":\""; s << data->m_height << ",\"seed_hash\":\"";
s << data->m_seedHash << "\"}}\n"; s << data->m_seedHash << "\"}}\n";
return s.m_pos; return s.m_pos;
@@ -828,19 +828,12 @@ void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp)
void StratumServer::on_share_found(uv_work_t* req) void StratumServer::on_share_found(uv_work_t* req)
{ {
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data); SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
StratumServer* server = share->m_server;
if (server->is_banned(share->m_clientAddr)) {
share->m_highEnoughDifficulty = false;
share->m_result = SubmittedShare::Result::BANNED;
return;
}
if (share->m_highEnoughDifficulty) { if (share->m_highEnoughDifficulty) {
BACKGROUND_JOB_START(StratumServer::on_share_found); BACKGROUND_JOB_START(StratumServer::on_share_found);
} }
StratumClient* client = share->m_client; StratumClient* client = share->m_client;
StratumServer* server = share->m_server;
p2pool* pool = server->m_pool; p2pool* pool = server->m_pool;
const uint64_t target = share->m_target; const uint64_t target = share->m_target;
@@ -959,9 +952,6 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
case SubmittedShare::Result::INVALID_POW: case SubmittedShare::Result::INVALID_POW:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Invalid PoW\"}}\n"; s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Invalid PoW\"}}\n";
break; break;
case SubmittedShare::Result::BANNED:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Banned\"}}\n";
break;
case SubmittedShare::Result::OK: case SubmittedShare::Result::OK:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":null,\"result\":{\"status\":\"OK\"}}\n"; s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":null,\"result\":{\"status\":\"OK\"}}\n";
break; break;
+1 -2
View File
@@ -84,7 +84,7 @@ public:
bool on_login(StratumClient* client, uint32_t id, const char* login); bool on_login(StratumClient* client, uint32_t id, const char* login);
bool on_submit(StratumClient* client, uint32_t id, const char* job_id_str, const char* nonce_str, const char* result_str); bool on_submit(StratumClient* client, uint32_t id, const char* job_id_str, const char* nonce_str, const char* result_str);
uint32_t get_random32(); uint64_t get_random64();
void print_status() override; void print_status() override;
void show_workers(); void show_workers();
@@ -152,7 +152,6 @@ private:
COULDNT_CHECK_POW, COULDNT_CHECK_POW,
LOW_DIFF, LOW_DIFF,
INVALID_POW, INVALID_POW,
BANNED,
OK OK
} m_result; } m_result;
}; };
+2 -3
View File
@@ -102,10 +102,9 @@ public:
struct WriteBuf struct WriteBuf
{ {
uv_write_t m_write = {};
Client* m_client = nullptr; Client* m_client = nullptr;
void* m_data = nullptr; uv_write_t m_write = {};
size_t m_dataCapacity = 0; std::vector<uint8_t> m_data;
}; };
std::vector<WriteBuf*> m_writeBuffers; std::vector<WriteBuf*> m_writeBuffers;
+21 -31
View File
@@ -41,7 +41,7 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
int err = uv_loop_init(&m_loop); int err = uv_loop_init(&m_loop);
if (err) { if (err) {
LOGERR(1, "failed to create event loop, error " << uv_err_name(err)); LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
// Init loop user data before running it // Init loop user data before running it
@@ -50,14 +50,14 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
err = uv_async_init(&m_loop, &m_dropConnectionsAsync, on_drop_connections); err = uv_async_init(&m_loop, &m_dropConnectionsAsync, on_drop_connections);
if (err) { if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err)); LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
m_dropConnectionsAsync.data = this; m_dropConnectionsAsync.data = this;
err = uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown); err = uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
if (err) { if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err)); LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
m_shutdownAsync.data = this; m_shutdownAsync.data = this;
@@ -133,7 +133,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
{ {
if (listen_addresses.empty()) { if (listen_addresses.empty()) {
LOGERR(1, "listen address not set"); LOGERR(1, "listen address not set");
PANIC_STOP(); panic();
} }
parse_address_list(listen_addresses, parse_address_list(listen_addresses,
@@ -144,7 +144,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
} }
else if (m_listenPort != port) { else if (m_listenPort != port) {
LOGERR(1, "all sockets must be listening on the same port number, fix the command line"); LOGERR(1, "all sockets must be listening on the same port number, fix the command line");
PANIC_STOP(); panic();
} }
uv_tcp_t* socket = new uv_tcp_t(); uv_tcp_t* socket = new uv_tcp_t();
@@ -159,14 +159,14 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
int err = uv_tcp_init(&m_loop, socket); int err = uv_tcp_init(&m_loop, socket);
if (err) { if (err) {
LOGERR(1, "failed to create tcp server handle, error " << uv_err_name(err)); LOGERR(1, "failed to create tcp server handle, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
socket->data = this; socket->data = this;
err = uv_tcp_nodelay(socket, 1); err = uv_tcp_nodelay(socket, 1);
if (err) { if (err) {
LOGERR(1, "failed to set tcp_nodelay on tcp server handle, error " << uv_err_name(err)); LOGERR(1, "failed to set tcp_nodelay on tcp server handle, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
if (is_v6) { if (is_v6) {
@@ -174,13 +174,13 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
err = uv_ip6_addr(ip.c_str(), port, &addr6); err = uv_ip6_addr(ip.c_str(), port, &addr6);
if (err) { if (err) {
LOGERR(1, "failed to parse IPv6 address " << ip << ", error " << uv_err_name(err)); LOGERR(1, "failed to parse IPv6 address " << ip << ", error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
err = uv_tcp_bind(socket, reinterpret_cast<sockaddr*>(&addr6), UV_TCP_IPV6ONLY); err = uv_tcp_bind(socket, reinterpret_cast<sockaddr*>(&addr6), UV_TCP_IPV6ONLY);
if (err) { if (err) {
LOGERR(1, "failed to bind tcp server IPv6 socket " << address << ", error " << uv_err_name(err)); LOGERR(1, "failed to bind tcp server IPv6 socket " << address << ", error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
} }
else { else {
@@ -188,20 +188,20 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
err = uv_ip4_addr(ip.c_str(), port, &addr); err = uv_ip4_addr(ip.c_str(), port, &addr);
if (err) { if (err) {
LOGERR(1, "failed to parse IPv4 address " << ip << ", error " << uv_err_name(err)); LOGERR(1, "failed to parse IPv4 address " << ip << ", error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
err = uv_tcp_bind(socket, reinterpret_cast<sockaddr*>(&addr), 0); err = uv_tcp_bind(socket, reinterpret_cast<sockaddr*>(&addr), 0);
if (err) { if (err) {
LOGERR(1, "failed to bind tcp server IPv4 socket " << address << ", error " << uv_err_name(err)); LOGERR(1, "failed to bind tcp server IPv4 socket " << address << ", error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
} }
err = uv_listen(reinterpret_cast<uv_stream_t*>(socket), DEFAULT_BACKLOG, on_new_connection); err = uv_listen(reinterpret_cast<uv_stream_t*>(socket), DEFAULT_BACKLOG, on_new_connection);
if (err) { if (err) {
LOGERR(1, "failed to listen on tcp server socket " << address << ", error " << uv_err_name(err)); LOGERR(1, "failed to listen on tcp server socket " << address << ", error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
LOGINFO(1, "listening on " << log::Gray() << address); LOGINFO(1, "listening on " << log::Gray() << address);
@@ -210,7 +210,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
const int err = uv_thread_create(&m_loopThread, loop, this); const int err = uv_thread_create(&m_loopThread, loop, this);
if (err) { if (err) {
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err)); LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
} }
@@ -376,7 +376,7 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::connect_to_peer(Client* client)
err = uv_tcp_connect(connect_request, &client->m_socket, reinterpret_cast<sockaddr*>(&addr), on_connect); err = uv_tcp_connect(connect_request, &client->m_socket, reinterpret_cast<sockaddr*>(&addr), on_connect);
if (err) { if (err) {
LOGWARN(5, "failed to initiate tcp connection to " << static_cast<const char*>(client->m_addrString) << ", error " << uv_err_name(err)); LOGERR(1, "failed to initiate tcp connection to " << static_cast<const char*>(client->m_addrString) << ", error " << uv_err_name(err));
m_pendingConnections.erase(client->m_addr); m_pendingConnections.erase(client->m_addr);
uv_close(reinterpret_cast<uv_handle_t*>(&client->m_socket), on_connection_error); uv_close(reinterpret_cast<uv_handle_t*>(&client->m_socket), on_connection_error);
return false; return false;
@@ -509,7 +509,7 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
if (bytes_written > WRITE_BUF_SIZE) { if (bytes_written > WRITE_BUF_SIZE) {
LOGERR(0, "send callback wrote " << bytes_written << " bytes, expected no more than " << WRITE_BUF_SIZE << " bytes"); LOGERR(0, "send callback wrote " << bytes_written << " bytes, expected no more than " << WRITE_BUF_SIZE << " bytes");
PANIC_STOP(); panic();
} }
if (bytes_written == 0) { if (bytes_written == 0) {
@@ -518,22 +518,13 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
return true; return true;
} }
buf->m_write.data = buf;
buf->m_client = client; buf->m_client = client;
buf->m_write.data = buf;
if (buf->m_dataCapacity < bytes_written) { buf->m_data.reserve(round_up(bytes_written, 64));
buf->m_dataCapacity = round_up(bytes_written, 64); buf->m_data.assign(callback_buf, callback_buf + bytes_written);
buf->m_data = realloc_hook(buf->m_data, buf->m_dataCapacity);
if (!buf->m_data) {
LOGERR(0, "failed to allocate " << buf->m_dataCapacity << " bytes to send data");
PANIC_STOP();
}
}
memcpy(buf->m_data, callback_buf, bytes_written);
uv_buf_t bufs[1]; uv_buf_t bufs[1];
bufs[0].base = reinterpret_cast<char*>(buf->m_data); bufs[0].base = reinterpret_cast<char*>(buf->m_data.data());
bufs[0].len = static_cast<int>(bytes_written); bufs[0].len = static_cast<int>(bytes_written);
const int err = uv_write(&buf->m_write, reinterpret_cast<uv_stream_t*>(&client->m_socket), bufs, 1, Client::on_write); const int err = uv_write(&buf->m_write, reinterpret_cast<uv_stream_t*>(&client->m_socket), bufs, 1, Client::on_write);
@@ -571,7 +562,6 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::loop(void* data)
} }
for (WriteBuf* buf : server->m_writeBuffers) { for (WriteBuf* buf : server->m_writeBuffers) {
free_hook(buf->m_data);
delete buf; delete buf;
} }
server->m_writeBuffers.clear(); server->m_writeBuffers.clear();
@@ -820,7 +810,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_shutdown(uv_async_t* async)
delete GetLoopUserData(&s->m_loop, false); delete GetLoopUserData(&s->m_loop, false);
s->m_numHandles = 0; s->m_numHandles = 0;
uv_walk(&s->m_loop, [](uv_handle_t*, void* n) { (*reinterpret_cast<uint32_t*>(n))++; }, &s->m_numHandles); 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); uv_prepare_init(&s->m_loop, &s->m_shutdownPrepare);
s->m_shutdownPrepare.data = s; s->m_shutdownPrepare.data = s;
@@ -853,7 +843,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_shutdown(uv_async_t* async)
TCPServer* s = reinterpret_cast<TCPServer*>(h->data); TCPServer* s = reinterpret_cast<TCPServer*>(h->data);
s->m_numHandles = 0; s->m_numHandles = 0;
uv_walk(&s->m_loop, [](uv_handle_t*, void* n) { (*reinterpret_cast<uint32_t*>(n))++; }, &s->m_numHandles); uv_walk(&s->m_loop, [](uv_handle_t*, void* n) { (*reinterpret_cast<size_t*>(n))++; }, &s->m_numHandles);
if (s->m_numHandles > 2) { if (s->m_numHandles > 2) {
// Don't count m_shutdownTimer and m_shutdownPrepare // Don't count m_shutdownTimer and m_shutdownPrepare
+11 -7
View File
@@ -29,6 +29,9 @@ static constexpr char log_category_prefix[] = "Util ";
namespace p2pool { namespace p2pool {
#define STR2(X) STR(X)
#define STR(X) #X
const char* VERSION = "v" STR2(P2POOL_VERSION_MAJOR) "." STR2(P2POOL_VERSION_MINOR) " (built" const char* VERSION = "v" STR2(P2POOL_VERSION_MAJOR) "." STR2(P2POOL_VERSION_MINOR) " (built"
#if defined(__clang__) #if defined(__clang__)
" with clang/" __clang_version__ " with clang/" __clang_version__
@@ -39,12 +42,13 @@ const char* VERSION = "v" STR2(P2POOL_VERSION_MAJOR) "." STR2(P2POOL_VERSION_MIN
#endif #endif
" on " __DATE__ ")"; " on " __DATE__ ")";
#undef STR2
#undef STR
MinerCallbackHandler::~MinerCallbackHandler() {} MinerCallbackHandler::~MinerCallbackHandler() {}
void panic_stop(const char* message) void panic()
{ {
fprintf(stderr, "P2Pool can't continue execution: panic at %s\n", message);
p2pool::log::stop(); p2pool::log::stop();
do { do {
#ifdef _WIN32 #ifdef _WIN32
@@ -250,7 +254,7 @@ void uv_cond_init_checked(uv_cond_t* cond)
const int result = uv_cond_init(cond); const int result = uv_cond_init(cond);
if (result) { if (result) {
LOGERR(1, "failed to create conditional variable, error " << uv_err_name(result)); LOGERR(1, "failed to create conditional variable, error " << uv_err_name(result));
PANIC_STOP(); panic();
} }
} }
@@ -259,7 +263,7 @@ void uv_mutex_init_checked(uv_mutex_t* mutex)
const int result = uv_mutex_init(mutex); const int result = uv_mutex_init(mutex);
if (result) { if (result) {
LOGERR(1, "failed to create mutex, error " << uv_err_name(result)); LOGERR(1, "failed to create mutex, error " << uv_err_name(result));
PANIC_STOP(); panic();
} }
} }
@@ -268,7 +272,7 @@ void uv_rwlock_init_checked(uv_rwlock_t* lock)
const int result = uv_rwlock_init(lock); const int result = uv_rwlock_init(lock);
if (result) { if (result) {
LOGERR(1, "failed to create rwlock, error " << uv_err_name(result)); LOGERR(1, "failed to create rwlock, error " << uv_err_name(result));
PANIC_STOP(); panic();
} }
} }
@@ -277,7 +281,7 @@ void uv_async_init_checked(uv_loop_t* loop, uv_async_t* async, uv_async_cb async
const int err = uv_async_init(loop, async, async_cb); const int err = uv_async_init(loop, async, async_cb);
if (err) { if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err)); LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
PANIC_STOP(); panic();
} }
} }
+1 -14
View File
@@ -149,12 +149,7 @@ FORCEINLINE T read_unaligned(const T* p)
template<typename T, size_t N> FORCEINLINE constexpr size_t array_size(T(&)[N]) { return N; } template<typename T, size_t N> FORCEINLINE constexpr size_t array_size(T(&)[N]) { return N; }
template<typename T, typename U, size_t N> FORCEINLINE constexpr size_t array_size(T(U::*)[N]) { return N; } template<typename T, typename U, size_t N> FORCEINLINE constexpr size_t array_size(T(U::*)[N]) { return N; }
[[noreturn]] void panic_stop(const char* message); [[noreturn]] void panic();
#define STR(X) #X
#define STR2(X) STR(X)
#define PANIC_STOP(...) panic_stop(__FILE__ ":" STR2(__LINE__))
void make_thread_background(); void make_thread_background();
@@ -213,14 +208,6 @@ struct RandomDeviceSeed
static RandomDeviceSeed instance; static RandomDeviceSeed instance;
}; };
FORCEINLINE uint64_t xorshift64star(uint64_t x)
{
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
return x * 0x2545F4914F6CDD1DULL;
}
FORCEINLINE uint64_t seconds_since_epoch() FORCEINLINE uint64_t seconds_since_epoch()
{ {
using namespace std::chrono; using namespace std::chrono;
-61
View File
@@ -18,7 +18,6 @@
#pragma once #pragma once
#include <uv.h> #include <uv.h>
#include <thread>
static_assert(sizeof(in6_addr) == 16, "struct in6_addr has invalid size"); static_assert(sizeof(in6_addr) == 16, "struct in6_addr has invalid size");
static_assert(sizeof(in_addr) == 4, "struct in_addr has invalid size"); static_assert(sizeof(in_addr) == 4, "struct in_addr has invalid size");
@@ -176,64 +175,4 @@ bool CallOnLoop(uv_loop_t* loop, T&& callback)
return false; return false;
} }
template<typename T>
void parallel_run(uv_loop_t* loop, T&& callback, bool wait = false)
{
uint32_t THREAD_COUNT = std::thread::hardware_concurrency();
if (THREAD_COUNT > 0) {
--THREAD_COUNT;
}
// No more than 8 threads because our UV worker thread pool has 8 threads
if (THREAD_COUNT > 8) {
THREAD_COUNT = 8;
}
struct Callback
{
explicit FORCEINLINE Callback(T&& f) : m_func(std::move(f)) {}
Callback& operator=(Callback&&) = delete;
T m_func;
};
std::shared_ptr<Callback> cb = std::make_shared<Callback>(std::move(callback));
struct Work
{
uv_work_t req;
std::shared_ptr<Callback> cb;
};
for (size_t i = 0; i < THREAD_COUNT; ++i) {
Work* w = new Work{ {}, cb };
w->req.data = w;
const int err = uv_queue_work(loop, &w->req,
[](uv_work_t* req)
{
std::shared_ptr<Callback>& cb = reinterpret_cast<Work*>(req->data)->cb;
cb->m_func();
cb.reset();
},
[](uv_work_t* req, int)
{
delete reinterpret_cast<Work*>(req->data);
});
if (err) {
delete w;
}
}
if (wait) {
cb->m_func();
while (cb.use_count() > 1) {
std::this_thread::yield();
}
}
}
} // namespace p2pool } // namespace p2pool
+4 -6
View File
@@ -5,19 +5,17 @@ option(STATIC_LIBS "Use locally built libuv and libzmq static libs" OFF)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
if (${CMAKE_VERSION} VERSION_GREATER "3.5.2") if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.6.0")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool_tests) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool_tests)
endif() endif()
add_subdirectory(googletest) add_subdirectory(googletest)
set(LIBS gtest) set(LIBS gtest)
add_subdirectory(../external/src/RandomWOW RandomWOW) add_subdirectory(../external/src/RandomX RandomX)
set(LIBS ${LIBS} randomx) set(LIBS ${LIBS} randomx)
add_definitions(-DWITH_RANDOMX) add_definitions(-DWITH_RANDOMX)
add_definitions(-DP2POOL_UNIT_TESTS)
include(cmake/flags.cmake) include(cmake/flags.cmake)
set(HEADERS set(HEADERS
@@ -83,7 +81,7 @@ include_directories(../external/src/cryptonote)
include_directories(${UV_INCLUDE_DIR}) include_directories(${UV_INCLUDE_DIR})
include_directories(../external/src/cppzmq) include_directories(../external/src/cppzmq)
include_directories(${ZMQ_INCLUDE_DIR}) include_directories(${ZMQ_INCLUDE_DIR})
include_directories(../external/src/RandomWOW/src) include_directories(../external/src/RandomX/src)
include_directories(../external/src/rapidjson/include) include_directories(../external/src/rapidjson/include)
include_directories(../external/src/robin-hood-hashing/src/include) include_directories(../external/src/robin-hood-hashing/src/include)
include_directories(src) include_directories(src)
@@ -96,7 +94,7 @@ if (WIN32)
endif() endif()
add_definitions(-DCURL_STATICLIB) add_definitions(-DCURL_STATICLIB)
elseif (NOT APPLE) elseif (NOT APPLE)
set(LIBS ${LIBS} pthread) set(LIBS ${LIBS} pthread gss dl)
endif() endif()
if (STATIC_LIBS) if (STATIC_LIBS)
+3 -4
View File
@@ -32,7 +32,6 @@ TEST(block_template, update)
SideChain sidechain(nullptr, NetworkType::Mainnet); SideChain sidechain(nullptr, NetworkType::Mainnet);
BlockTemplate tpl(&sidechain, nullptr); BlockTemplate tpl(&sidechain, nullptr);
tpl.rng().seed(123);
auto H = [](const char* s) auto H = [](const char* s)
{ {
@@ -60,7 +59,7 @@ TEST(block_template, update)
tpl.update(data, mempool, &wallet); tpl.update(data, mempool, &wallet);
const PoolBlock* b = tpl.pool_block_template(); const PoolBlock* b = tpl.pool_block_template();
ASSERT_EQ(b->m_sidechainId, H("16d6a5c45d452288fcc439e5a258e8230798dfb6bbfd32220303efe932061aa9")); ASSERT_EQ(b->m_sidechainId, H("b708e3e456d97c43a7fcbd7b4e7aa29bdf45cd909bba07f915cb5f1d805433e6"));
std::vector<uint8_t> blobs; std::vector<uint8_t> blobs;
uint64_t height; uint64_t height;
@@ -78,8 +77,8 @@ TEST(block_template, update)
ASSERT_EQ(template_id, 1); ASSERT_EQ(template_id, 1);
hash blobs_hash; hash blobs_hash;
keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h); keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h, HASH_SIZE);
ASSERT_EQ(blobs_hash, H("27bd8678420c8a0948f71c71356252be790899d61f14e35b2d0440a30d730f4c")); ASSERT_EQ(blobs_hash, H("e9154971a27c412175562d23ab458b0d3cf780a8bcecf62ff3f667fed9d3bc1d"));
// Test 2: mempool with high fee and low fee transactions, it must choose high fee transactions // Test 2: mempool with high fee and low fee transactions, it must choose high fee transactions
for (uint64_t i = 0; i < 512; ++i) { for (uint64_t i = 0; i < 512; ++i) {
+1 -1
View File
@@ -27,7 +27,7 @@ TEST(keccak, hashing)
hash output; hash output;
const uint8_t* data = reinterpret_cast<const uint8_t*>(input); const uint8_t* data = reinterpret_cast<const uint8_t*>(input);
const int len = static_cast<int>(size); const int len = static_cast<int>(size);
keccak(data, len, output.h); keccak(data, len, output.h, HASH_SIZE);
char buf[log::Stream::BUF_SIZE + 1]; char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf); log::Stream s(buf);
-4
View File
@@ -27,8 +27,6 @@ namespace p2pool {
TEST(pool_block, deserialize) TEST(pool_block, deserialize)
{ {
init_crypto_cache();
PoolBlock b; PoolBlock b;
SideChain sidechain(nullptr, NetworkType::Mainnet, "mainnet test 2"); SideChain sidechain(nullptr, NetworkType::Mainnet, "mainnet test 2");
@@ -103,8 +101,6 @@ TEST(pool_block, deserialize)
ASSERT_EQ(s.str(), "f76d731c61c9c9b6c3f46be2e60c9478930b49b4455feecd41ecb9420d000000"); ASSERT_EQ(s.str(), "f76d731c61c9c9b6c3f46be2e60c9478930b49b4455feecd41ecb9420d000000");
ASSERT_EQ(b.m_difficulty.check_pow(pow_hash), true); ASSERT_EQ(b.m_difficulty.check_pow(pow_hash), true);
destroy_crypto_cache();
} }
TEST(pool_block, verify) TEST(pool_block, verify)