Compare commits

..
43 Commits
Author SHA1 Message Date
SChernykh 8aebcdc580 p2pool v2.0 2022-04-30 17:50:27 +02:00
SChernykh e23a2e77a8 Updated dependencies 2022-04-30 12:10:09 +02:00
SChernykh 0f9362e14e More tests for derive_view_tag 2022-04-29 22:25:22 +02:00
SChernykh ab974b9684 Monero network upgrade information 2022-04-29 14:35:06 +02:00
SChernykhandGitHub 54acfee036 Merge pull request #144 from SChernykh/view_tags
v15 hardfork changes
2022-04-27 19:56:42 +02:00
SChernykh a78ddb50d5 Fixed logger thread timing logic 2022-04-24 16:22:22 +02:00
SChernykh b2f86a5f6b Ban peers with stuck or lagging monerod 2022-04-23 11:29:17 +02:00
SChernykh 2a77695832 Fixed data races in miner 2022-04-09 18:25:12 +02:00
SChernykh 21d8cb917b Fixed data race in logger 2022-04-09 10:41:20 +02:00
SChernykh 2c70bf8616 View tags support
See http://github.com/monero-project/monero/pull/8061
2022-04-09 00:56:42 +02:00
SChernykh 7522111bb7 Fixed data races 2022-04-09 00:38:15 +02:00
SChernykh 7b5b910f3f Undefined behaviour fixes 2022-04-08 22:49:08 +02:00
SChernykh afb52e4235 Check return value (Coverity fix) 2022-04-07 19:33:39 +02:00
SChernykh 93cd9a659a Code cleanup 2022-04-07 19:11:20 +02:00
SChernykh 9e287ab4f0 Logger fixes
- Fixed a race condition when stopping the logger
- Flush log file only once per batch of log lines
2022-04-07 16:55:35 +02:00
SChernykh 094209f311 P2PServer: optimized update_peer_connections() 2022-04-07 16:48:01 +02:00
SChernykh dee66c3f32 Added varint unit tests 2022-04-06 16:34:16 +02:00
SChernykh 32533e3c96 FreeBSD build 2022-04-05 19:36:37 +02:00
SChernykh a073923a82 Updated dependencies 2022-04-05 11:32:39 +02:00
SChernykh 3484b24caa Updated dependencies 2022-04-04 19:56:42 +02:00
SChernykh 1b3c1eb850 P2PServer: increased peer timeout to 15 minutes 2022-03-30 22:47:10 +02:00
SChernykh 7fe032915f Ignore SIGPIPE 2022-03-30 21:53:52 +02:00
SChernykh 5b9b73abcc p2pool v1.9 2022-03-30 20:15:47 +02:00
SChernykh a2afa29052 Updated copyright 2022-03-30 14:42:26 +02:00
SChernykh a2d3dbad5e Use old bock template when update fails 2022-03-30 10:11:35 +02:00
SChernykh e50cf060cb Miner: fixed share counter initialization 2022-03-30 08:34:22 +02:00
SChernykh 915988d694 Added logging for the extra_nonce fix 2022-03-30 07:59:59 +02:00
SChernykh 345c231a9a Update config.json 2022-03-28 19:12:21 +02:00
SChernykh a7aed2f221 Fixed miner tx creation 2022-03-28 11:28:56 +02:00
SChernykh 292e2580e5 Make sure dataset init doesn't block start 2022-03-24 20:55:35 +01:00
SChernykh 028a5d0c88 Fixed memory leak after loading block cache 2022-03-24 18:30:23 +01:00
SChernykh 8b27faad6d BlockCache: fixed collisions of same height blocks 2022-03-24 16:03:12 +01:00
SChernykh cdc3206ee8 Optimized keccak 2022-03-24 12:52:57 +01:00
SChernykh 0c2b7d6010 More time handling fixes 2022-03-23 15:49:24 +01:00
SChernykh ef15c3b54f Show sidechain ID in status 2022-03-23 14:17:40 +01:00
SChernykh f4bcdc7fe9 Use steady_clock to time peer list requests 2022-03-23 11:37:05 +01:00
SChernykh aada1bb5cc Use chrono::steady_clock for internal timestamps 2022-03-23 11:30:38 +01:00
SChernykh 796850d8c5 Update CMakeLists.txt 2022-03-18 14:53:30 +01:00
SChernykh ab0bc0488e Removed libsodium as it's not used in compilation 2022-03-18 14:43:23 +01:00
SChernykhandGitHub f3af02111d Update README.md 2022-03-18 10:31:06 +01:00
SChernykh c21d052d7a Refactored RNG usage across the code 2022-03-17 16:14:29 +01:00
SChernykh 62b1690780 Added an option to disable RandomX for the build 2022-03-15 17:11:45 +01:00
SChernykh 52050bbcfb Update difficulty_type_tests.cpp 2022-03-10 19:11:49 +01:00
71 changed files with 1107 additions and 457 deletions
+5 -4
View File
@@ -106,8 +106,9 @@ jobs:
strategy: strategy:
matrix: matrix:
config: config:
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\"} - {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\", rx: "ON"}
- {vs: Visual Studio 17 2022, os: 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\"} - {vs: Visual Studio 17 2022, os: 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\", rx: "ON"}
- {vs: Visual Studio 17 2022, os: 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\", rx: "OFF"}
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -122,7 +123,7 @@ jobs:
run: | run: |
mkdir build mkdir build
cd build cd build
cmake .. -G "${{ matrix.config.vs }}" cmake .. -G "${{ matrix.config.vs }}" -DWITH_RANDOMX=${{ matrix.config.rx }}
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool.vcxproj & "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool.vcxproj
- name: Build tests - name: Build tests
@@ -141,7 +142,7 @@ jobs:
- name: Archive binary - name: Archive binary
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: p2pool-msbuild-${{ matrix.config.os }}.exe name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}.exe
path: build/Release/p2pool.exe path: build/Release/p2pool.exe
build-macos: build-macos:
-3
View File
@@ -10,9 +10,6 @@
[submodule "external/src/cppzmq"] [submodule "external/src/cppzmq"]
path = external/src/cppzmq path = external/src/cppzmq
url = https://github.com/SChernykh/cppzmq url = https://github.com/SChernykh/cppzmq
[submodule "external/src/libsodium"]
path = external/src/libsodium
url = https://github.com/SChernykh/libsodium
[submodule "external/src/libuv"] [submodule "external/src/libuv"]
path = external/src/libuv path = external/src/libuv
url = https://github.com/SChernykh/libuv url = https://github.com/SChernykh/libuv
+23 -7
View File
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 2.8.12)
project(p2pool) project(p2pool)
option(STATIC_BINARY "Build static binary" OFF) option(STATIC_BINARY "Build static binary" OFF)
option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned off, p2pool will rely on monerod for verifying RandomX hashes" ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
@@ -9,8 +10,11 @@ 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() endif()
add_subdirectory(external/src/RandomX) if (WITH_RANDOMX)
set(LIBS randomx) add_definitions(-DWITH_RANDOMX)
add_subdirectory(external/src/RandomX)
set(LIBS randomx)
endif()
include(cmake/flags.cmake) include(cmake/flags.cmake)
@@ -27,7 +31,6 @@ set(HEADERS
src/keccak.h src/keccak.h
src/log.h src/log.h
src/mempool.h src/mempool.h
src/miner.h
src/p2p_server.h src/p2p_server.h
src/p2pool.h src/p2pool.h
src/p2pool_api.h src/p2pool_api.h
@@ -61,7 +64,6 @@ set(SOURCES
src/main.cpp src/main.cpp
src/memory_leak_debug.cpp src/memory_leak_debug.cpp
src/mempool.cpp src/mempool.cpp
src/miner.cpp
src/p2p_server.cpp src/p2p_server.cpp
src/p2pool.cpp src/p2pool.cpp
src/p2pool_api.cpp src/p2pool_api.cpp
@@ -75,6 +77,11 @@ set(SOURCES
src/zmq_reader.cpp src/zmq_reader.cpp
) )
if (WITH_RANDOMX)
set(HEADERS ${HEADERS} src/miner.h)
set(SOURCES ${SOURCES} src/miner.cpp)
endif()
include_directories(src) include_directories(src)
include_directories(external/src) include_directories(external/src)
include_directories(external/src/cryptonote) include_directories(external/src/cryptonote)
@@ -82,12 +89,16 @@ include_directories(external/src/libuv/include)
include_directories(external/src/cppzmq) include_directories(external/src/cppzmq)
include_directories(external/src/libzmq/include) include_directories(external/src/libzmq/include)
include_directories(external/src/llhttp) include_directories(external/src/llhttp)
include_directories(external/src/RandomX/src) if (WITH_RANDOMX)
include_directories(external/src/RandomX/src)
endif()
include_directories(external/src/rapidjson/include) include_directories(external/src/rapidjson/include)
include_directories(external/src/robin-hood-hashing/src/include) include_directories(external/src/robin-hood-hashing/src/include)
if (WIN32) if (WIN32)
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi) set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(LIBS ${LIBS} pthread)
elseif (NOT APPLE) elseif (NOT APPLE)
set(LIBS ${LIBS} pthread gss dl) set(LIBS ${LIBS} pthread gss dl)
endif() endif()
@@ -127,8 +138,13 @@ add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
if (STATIC_BINARY) if (STATIC_BINARY)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME}) add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME})
set(STATIC_LIBS randomx) if (WITH_RANDOMX)
if (NOT APPLE) set(STATIC_LIBS randomx)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(STATIC_LIBS ${STATIC_LIBS} pthread)
elseif (NOT APPLE)
set(STATIC_LIBS ${STATIC_LIBS} pthread dl) set(STATIC_LIBS ${STATIC_LIBS} pthread dl)
endif() endif()
+24 -2
View File
@@ -2,7 +2,7 @@
Decentralized pool for Monero mining. Decentralized pool for Monero mining.
Pool status and monitoring pages can be found at https://p2pool.io/ and https://p2pool.observer/ Pool status and monitoring pages can be found at https://p2pool.io/, https://p2pool.io/mini/ and https://p2pool.observer/
### Build Status ### Build Status
@@ -19,6 +19,7 @@ Pool status and monitoring pages can be found at https://p2pool.io/ and https://
- [Features](#features) - [Features](#features)
- [How PPLNS works in P2Pool](#how-pplns-works-in-p2pool) - [How PPLNS works in P2Pool](#how-pplns-works-in-p2pool)
- [Default P2Pool parameters](#default-p2pool-parameters) - [Default P2Pool parameters](#default-p2pool-parameters)
- [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)
- [GNU/Linux](#gnulinux) - [GNU/Linux](#gnulinux)
@@ -63,12 +64,21 @@ First you need to find a pool share. This share will stay in PPLNS window for 21
* PPLNS window: 2160 blocks (6 hours) * PPLNS window: 2160 blocks (6 hours)
* Minimum payout = Monero block reward/2160, ~0.0003 XMR * Minimum payout = Monero block reward/2160, ~0.0003 XMR
## Monero version support
Monero will undergo a network upgrade on July 16th, 2022 (block 2,668,888). In order to continue mining after that date, you must update both Monero and P2Pool software to the latest available versions as soon as they are released.
|Monero protocol version|Required Monero software version|Required P2Pool version
|-|-|-|
|v14 (active until July 16th, 2022)|v0.17.3.0 or newer|v1.0 or newer
|v15, v16 (active after July 16th, 2022)|v0.18.0.0 or newer|v2.0 or newer
## How to mine on P2Pool ## How to mine on P2Pool
### General Considerations ### General Considerations
- In order to mine on P2Pool, a synced Monero node using monerod v0.17.3.0 or newer is required. If you do not currently have one configured, you can find instructions to do so [here](https://sethforprivacy.com/guides/run-a-monero-node-advanced/). - In order to mine on P2Pool, a synced Monero node using monerod v0.17.3.0 or newer is required. If you do not currently have one configured, you can find instructions to do so [here](https://sethforprivacy.com/guides/run-a-monero-node-advanced/).
- It is highly recommended that you create a separate restricted user account for mining. P2Pool is still relatively new and may still have serious bugs/vulnerabilities. - It is highly recommended that you create a separate restricted user account for mining. While P2Pool has been battle-tested for a long time now, any software may have unknown bugs/vulnerabilities.
- You have to use a primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining. - You have to use a primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
- Starting from P2Pool v1.7, you can add the `--mini` parameter to your P2Pool command to connect to the **p2pool-mini** sidechain. Note that it will also change the default p2p port from 37889 to 37888. - Starting from P2Pool v1.7, you can add the `--mini` parameter to your P2Pool command to connect to the **p2pool-mini** sidechain. Note that it will also change the default p2p port from 37889 to 37888.
- Check that ports 18080 (Monero p2p port) and 37889/37888 (P2Pool/P2Pool mini p2p port) are open in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine. - Check that ports 18080 (Monero p2p port) and 37889/37888 (P2Pool/P2Pool mini p2p port) are open in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine.
@@ -249,6 +259,18 @@ cmake ..
make -j$(sysctl -n hw.logicalcpu) make -j$(sysctl -n hw.logicalcpu)
``` ```
### FreeBSD
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on FreeBSD:
```
pkg install git cmake libuv libzmq4
git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool
mkdir build && cd build
cmake ..
make
```
## Donations ## Donations
If you'd like to support further development of Monero P2Pool, you're welcome to send any amount of XMR to the following address: If you'd like to support further development of Monero P2Pool, you're welcome to send any amount of XMR to the following address:
+1 -1
View File
@@ -5,7 +5,7 @@
// Fixed difficulty for miners that connect to your node must be set in their config.json // Fixed difficulty for miners that connect to your node must be set in their config.json
// //
{ {
"name": "mainnet test 2", "name": "default",
"password": "", "password": "",
"block_time": 10, "block_time": 10,
"min_diff": 100000, "min_diff": 100000,
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+44 -8
View File
@@ -3831,15 +3831,51 @@ int sc_isnonzero(const unsigned char *s) {
s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1; s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1;
} }
int ge_p3_is_point_at_infinity(const ge_p3 *p) { int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) {
// X = 0 and Y == Z // https://eprint.iacr.org/2008/522
int n; // X == T == 0 and Y/Z == 1
for (n = 0; n < 10; ++n) // note: convert all pieces to canonical bytes in case rounding is required (i.e. an element is > q)
// note2: even though T = XY/Z is true for valid point representations (implying it isn't necessary to
// test T == 0), the input to this function might NOT be valid, so we must test T == 0
char result_X_bytes[32];
fe_tobytes((unsigned char*)&result_X_bytes, p->X);
// X != 0
for (int i = 0; i < 32; ++i)
{ {
if (p->X[n] | p->T[n]) if (result_X_bytes[i])
return 0;
if (p->Y[n] != p->Z[n])
return 0; return 0;
} }
return 1;
char result_T_bytes[32];
fe_tobytes((unsigned char*)&result_T_bytes, p->T);
// T != 0
for (int i = 0; i < 32; ++i)
{
if (result_T_bytes[i])
return 0;
}
char result_Y_bytes[32];
char result_Z_bytes[32];
fe_tobytes((unsigned char*)&result_Y_bytes, p->Y);
fe_tobytes((unsigned char*)&result_Z_bytes, p->Z);
// Y != Z
for (int i = 0; i < 32; ++i)
{
if (result_Y_bytes[i] != result_Z_bytes[i])
return 0;
}
// is Y nonzero? then Y/Z == 1
for (int i = 0; i < 32; ++i)
{
if (result_Y_bytes[i] != 0)
return 1;
}
// Y/Z = 0/0
return 0;
} }
+1 -1
View File
@@ -162,4 +162,4 @@ void fe_add(fe h, const fe f, const fe g);
void fe_tobytes(unsigned char *, const fe); void fe_tobytes(unsigned char *, const fe);
void fe_invert(fe out, const fe z); void fe_invert(fe out, const fe z);
int ge_p3_is_point_at_infinity(const ge_p3 *p); int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p);
+3 -2
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -154,6 +154,7 @@ struct BlockCache::Impl : public nocopy_nomove
BlockCache::BlockCache() BlockCache::BlockCache()
: m_impl(new Impl()) : m_impl(new Impl())
, m_flushRunning(0) , m_flushRunning(0)
, m_storeIndex(0)
{ {
} }
@@ -171,7 +172,7 @@ void BlockCache::store(const PoolBlock& block)
return; return;
} }
uint8_t* data = m_impl->m_data + (static_cast<size_t>(block.m_sidechainHeight % NUM_BLOCKS) * BLOCK_SIZE); uint8_t* data = m_impl->m_data + (static_cast<size_t>((m_storeIndex++) % NUM_BLOCKS) * BLOCK_SIZE);
*reinterpret_cast<uint32_t*>(data) = static_cast<uint32_t>(n1 + n2); *reinterpret_cast<uint32_t*>(data) = static_cast<uint32_t>(n1 + n2);
memcpy(data + sizeof(uint32_t), block.m_mainChainData.data(), n1); memcpy(data + sizeof(uint32_t), block.m_mainChainData.data(), n1);
+2 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -37,6 +37,7 @@ private:
struct Impl; struct Impl;
Impl* m_impl; Impl* m_impl;
std::atomic<uint32_t> m_flushRunning; std::atomic<uint32_t> m_flushRunning;
std::atomic<uint32_t> m_storeIndex;
}; };
} // namespace p2pool } // namespace p2pool
+92 -21
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Portions Copyright (c) 2012-2013 The Cryptonote developers * Portions Copyright (c) 2012-2013 The Cryptonote developers
* Portions Copyright (c) 2014-2021 The Monero Project * Portions Copyright (c) 2014-2021 The Monero Project
* Portions Copyright (c) 2021 XMRig <https://github.com/xmrig> * Portions Copyright (c) 2021 XMRig <https://github.com/xmrig>
@@ -190,12 +190,17 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
++m_templateId; ++m_templateId;
// When block template generation fails for any reason
auto use_old_template = [this]() {
const uint32_t id = m_templateId - 1;
LOGWARN(4, "using old block template with ID = " << id);
*this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)];
};
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;
const time_t cur_time = time(nullptr);
// Only choose transactions that were received 10 or more seconds ago // Only choose transactions that were received 10 or more seconds ago
size_t total_mempool_transactions; size_t total_mempool_transactions;
{ {
@@ -205,6 +210,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
total_mempool_transactions = mempool.m_transactions.size(); total_mempool_transactions = mempool.m_transactions.size();
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 + 10) { if (cur_time >= it.second.time_received + 10) {
m_mempoolTxs.emplace_back(it.second); m_mempoolTxs.emplace_back(it.second);
@@ -252,7 +259,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_poolBlockTemplate->m_minorVersion = HARDFORK_SUPPORTED_VERSION; m_poolBlockTemplate->m_minorVersion = HARDFORK_SUPPORTED_VERSION;
// Timestamp // Timestamp
m_timestamp = cur_time; m_timestamp = time(nullptr);
if (m_timestamp <= data.median_timestamp) { if (m_timestamp <= data.median_timestamp) {
LOGWARN(2, "timestamp adjusted from " << m_timestamp << " to " << data.median_timestamp + 1 << ". Fix your system time!"); LOGWARN(2, "timestamp adjusted from " << m_timestamp << " to " << data.median_timestamp + 1 << ". Fix your system time!");
m_timestamp = data.median_timestamp + 1; m_timestamp = data.median_timestamp + 1;
@@ -275,21 +282,26 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_pool->side_chain().fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares); m_pool->side_chain().fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares);
if (!SideChain::split_reward(max_reward, m_shares, m_rewards)) { if (!SideChain::split_reward(max_reward, m_shares, m_rewards)) {
use_old_template();
return; return;
} }
const uint64_t max_reward_amounts_weight = std::accumulate(m_rewards.begin(), m_rewards.end(), 0ULL, auto get_reward_amounts_weight = [this]() {
[](uint64_t a, uint64_t b) return std::accumulate(m_rewards.begin(), m_rewards.end(), 0ULL,
{ [](uint64_t a, uint64_t b)
writeVarint(b, [&a](uint8_t) { ++a; }); {
return a; writeVarint(b, [&a](uint8_t) { ++a; });
}); return a;
});
};
uint64_t max_reward_amounts_weight = get_reward_amounts_weight();
if (!create_miner_tx(data, m_shares, max_reward_amounts_weight, true)) { if (create_miner_tx(data, m_shares, max_reward_amounts_weight, true) < 0) {
use_old_template();
return; return;
} }
const uint64_t miner_tx_weight = m_minerTx.size(); uint64_t miner_tx_weight = m_minerTx.size();
// Select transactions from the mempool // Select transactions from the mempool
uint64_t final_reward, final_fees, final_weight; uint64_t final_reward, final_fees, final_weight;
@@ -427,17 +439,64 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
} }
if (!SideChain::split_reward(final_reward, m_shares, m_rewards)) { if (!SideChain::split_reward(final_reward, m_shares, m_rewards)) {
use_old_template();
return; return;
} }
m_finalReward = final_reward; m_finalReward = final_reward;
if (!create_miner_tx(data, m_shares, max_reward_amounts_weight, false)) { const int create_miner_tx_result = create_miner_tx(data, m_shares, max_reward_amounts_weight, false);
return; if (create_miner_tx_result < 0) {
if (create_miner_tx_result == -3) {
// Too many extra bytes were added, refine max_reward_amounts_weight and miner_tx_weight
LOGINFO(4, "Readjusting miner_tx to reduce extra nonce size");
// The difference between max possible reward and the actual reward can't reduce the size of output amount varints by more than 1 byte each
// So block weight will be >= current weight - number of outputs
const uint64_t w = (final_weight > m_rewards.size()) ? (final_weight - m_rewards.size()) : 0;
// Block reward will be <= r due to how block size penalty works
const uint64_t r = get_block_reward(base_reward, data.median_weight, final_fees, w);
if (!SideChain::split_reward(r, m_shares, m_rewards)) {
use_old_template();
return;
}
max_reward_amounts_weight = get_reward_amounts_weight();
if (create_miner_tx(data, m_shares, max_reward_amounts_weight, true) < 0) {
use_old_template();
return;
}
final_weight -= miner_tx_weight;
final_weight += m_minerTx.size();
miner_tx_weight = m_minerTx.size();
final_reward = get_block_reward(base_reward, data.median_weight, final_fees, final_weight);
if (!SideChain::split_reward(final_reward, m_shares, m_rewards)) {
use_old_template();
return;
}
if (create_miner_tx(data, m_shares, max_reward_amounts_weight, false) < 0) {
use_old_template();
return;
}
LOGINFO(4, "New extra nonce size = " << m_poolBlockTemplate->m_extraNonceSize);
}
else {
use_old_template();
return;
}
} }
if (m_minerTx.size() != miner_tx_weight) { if (m_minerTx.size() != miner_tx_weight) {
LOGERR(1, "miner tx size changed after adjusting reward"); LOGERR(1, "miner tx size changed after adjusting reward");
use_old_template();
return; return;
} }
@@ -600,7 +659,7 @@ void BlockTemplate::fill_optimal_knapsack(const MinerData& data, uint64_t base_r
} }
#endif #endif
bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run) int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run)
{ {
// Miner transaction (coinbase) // Miner transaction (coinbase)
m_minerTx.clear(); m_minerTx.clear();
@@ -630,6 +689,8 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
m_poolBlockTemplate->m_outputs.clear(); m_poolBlockTemplate->m_outputs.clear();
m_poolBlockTemplate->m_outputs.reserve(num_outputs); m_poolBlockTemplate->m_outputs.reserve(num_outputs);
const uint8_t tx_type = m_poolBlockTemplate->get_tx_type();
uint64_t reward_amounts_weight = 0; uint64_t reward_amounts_weight = 0;
for (size_t i = 0; i < num_outputs; ++i) { for (size_t i = 0; i < num_outputs; ++i) {
writeVarint(m_rewards[i], [this, &reward_amounts_weight](uint8_t b) writeVarint(m_rewards[i], [this, &reward_amounts_weight](uint8_t b)
@@ -637,30 +698,36 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
m_minerTx.push_back(b); m_minerTx.push_back(b);
++reward_amounts_weight; ++reward_amounts_weight;
}); });
m_minerTx.push_back(TXOUT_TO_KEY); m_minerTx.push_back(tx_type);
uint8_t view_tag = 0;
if (dry_run) { if (dry_run) {
m_minerTx.insert(m_minerTx.end(), HASH_SIZE, 0); m_minerTx.insert(m_minerTx.end(), HASH_SIZE, 0);
} }
else { else {
hash eph_public_key; hash eph_public_key;
if (!shares[i].m_wallet->get_eph_public_key(m_txkeySec, i, eph_public_key)) { 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);
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key); m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key, tx_type, view_tag);
}
if (tx_type == TXOUT_TO_TAGGED_KEY) {
m_minerTx.emplace_back(view_tag);
} }
} }
if (dry_run) { if (dry_run) {
if (reward_amounts_weight != max_reward_amounts_weight) { if (reward_amounts_weight != max_reward_amounts_weight) {
LOGERR(1, "create_miner_tx: incorrect miner rewards during the dry run (" << reward_amounts_weight << " != " << max_reward_amounts_weight << ")"); LOGERR(1, "create_miner_tx: incorrect miner rewards during the dry run (" << reward_amounts_weight << " != " << max_reward_amounts_weight << ")");
return false; return -1;
} }
} }
else if (reward_amounts_weight > max_reward_amounts_weight) { else if (reward_amounts_weight > max_reward_amounts_weight) {
LOGERR(1, "create_miner_tx: incorrect miner rewards during the real run (" << reward_amounts_weight << " > " << max_reward_amounts_weight << ")"); LOGERR(1, "create_miner_tx: incorrect miner rewards during the real run (" << reward_amounts_weight << " > " << max_reward_amounts_weight << ")");
return false; return -2;
} }
m_poolBlockTemplate->m_txkeyPub = m_txkeyPub; m_poolBlockTemplate->m_txkeyPub = m_txkeyPub;
@@ -676,6 +743,10 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
const uint64_t corrected_extra_nonce_size = EXTRA_NONCE_SIZE + max_reward_amounts_weight - reward_amounts_weight; const uint64_t corrected_extra_nonce_size = EXTRA_NONCE_SIZE + max_reward_amounts_weight - reward_amounts_weight;
if (corrected_extra_nonce_size > EXTRA_NONCE_SIZE) { if (corrected_extra_nonce_size > EXTRA_NONCE_SIZE) {
if (corrected_extra_nonce_size > EXTRA_NONCE_MAX_SIZE) {
LOGWARN(4, "create_miner_tx: corrected_extra_nonce_size (" << corrected_extra_nonce_size << ") is too large");
return -3;
}
LOGINFO(4, "increased EXTRA_NONCE from " << EXTRA_NONCE_SIZE << " to " << corrected_extra_nonce_size << " bytes to maintain miner tx weight"); LOGINFO(4, "increased EXTRA_NONCE from " << EXTRA_NONCE_SIZE << " to " << corrected_extra_nonce_size << " bytes to maintain miner tx weight");
} }
writeVarint(corrected_extra_nonce_size, m_minerTxExtra); writeVarint(corrected_extra_nonce_size, m_minerTxExtra);
@@ -701,7 +772,7 @@ bool BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Min
// Not a part of transaction hash data // Not a part of transaction hash data
m_minerTx.push_back(0); m_minerTx.push_back(0);
return true; return 1;
} }
hash BlockTemplate::calc_sidechain_hash() const hash BlockTemplate::calc_sidechain_hash() const
+3 -3
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ public:
void update_tx_keys(); void update_tx_keys();
FORCEINLINE uint64_t height() const { return m_height; } FORCEINLINE uint64_t height() const { return m_height; }
FORCEINLINE time_t timestamp() const { return m_timestamp; } FORCEINLINE uint64_t timestamp() const { return m_timestamp; }
FORCEINLINE difficulty_type difficulty() const { return m_difficulty; } FORCEINLINE difficulty_type difficulty() const { return m_difficulty; }
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce); void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
@@ -61,7 +61,7 @@ private:
p2pool* m_pool; p2pool* m_pool;
private: private:
bool create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run); int create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run);
hash calc_sidechain_hash() const; hash calc_sidechain_hash() 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();
+8 -4
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -54,6 +54,7 @@
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include <random>
#include <signal.h> #include <signal.h>
@@ -80,13 +81,16 @@
namespace p2pool { namespace p2pool {
constexpr size_t HASH_SIZE = 32; constexpr size_t HASH_SIZE = 32;
constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 14; constexpr uint8_t HARDFORK_VIEW_TAGS_VERSION = 15;
constexpr uint8_t HARDFORK_SUPPORTED_VERSION = 16;
constexpr uint8_t MINER_REWARD_UNLOCK_TIME = 60; 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 TX_VERSION = 2; constexpr uint8_t TX_VERSION = 2;
constexpr uint8_t TXIN_GEN = 0xFF; constexpr uint8_t TXIN_GEN = 0xFF;
constexpr uint8_t TXOUT_TO_KEY = 2; constexpr uint8_t TXOUT_TO_KEY = 2;
constexpr uint8_t TXOUT_TO_TAGGED_KEY = 3;
constexpr uint8_t TX_EXTRA_TAG_PUBKEY = 1; constexpr uint8_t TX_EXTRA_TAG_PUBKEY = 1;
constexpr uint8_t TX_EXTRA_NONCE = 2; constexpr uint8_t TX_EXTRA_NONCE = 2;
constexpr uint8_t TX_EXTRA_MERGE_MINING_TAG = 3; constexpr uint8_t TX_EXTRA_MERGE_MINING_TAG = 3;
@@ -234,7 +238,7 @@ struct TxMempoolData
uint64_t blob_size; uint64_t blob_size;
uint64_t weight; uint64_t weight;
uint64_t fee; uint64_t fee;
time_t time_received; uint64_t time_received;
}; };
struct MinerData struct MinerData
@@ -260,7 +264,7 @@ struct MinerData
uint64_t median_timestamp; uint64_t median_timestamp;
std::vector<TxMempoolData> tx_backlog; std::vector<TxMempoolData> tx_backlog;
std::chrono::system_clock::time_point time_received; std::chrono::high_resolution_clock::time_point time_received;
}; };
struct ChainMain struct ChainMain
+15 -5
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2021 hyc <https://github.com/hyc> * Copyright (c) 2021 hyc <https://github.com/hyc>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@@ -21,7 +21,9 @@
#include "p2pool.h" #include "p2pool.h"
#include "stratum_server.h" #include "stratum_server.h"
#include "p2p_server.h" #include "p2p_server.h"
#ifdef WITH_RANDOMX
#include "miner.h" #include "miner.h"
#endif
#include "side_chain.h" #include "side_chain.h"
#include <iostream> #include <iostream>
@@ -70,7 +72,11 @@ typedef struct cmd {
cmdfunc *func; cmdfunc *func;
} cmd; } cmd;
static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_showpeers, do_showbans, do_outpeers, do_inpeers, do_start_mining, do_stop_mining, do_exit; static cmdfunc do_help, do_status, do_loglevel, do_addpeers, do_droppeers, do_showpeers, do_showbans, do_outpeers, do_inpeers, do_exit;
#ifdef WITH_RANDOMX
static cmdfunc do_start_mining, do_stop_mining;
#endif
static cmd cmds[] = { static cmd cmds[] = {
{ STRCONST("help"), "", "display list of commands", do_help }, { STRCONST("help"), "", "display list of commands", do_help },
@@ -82,8 +88,10 @@ static cmd cmds[] = {
{ STRCONST("bans"), "", "show all banned IPs", do_showbans }, { STRCONST("bans"), "", "show all banned IPs", do_showbans },
{ STRCONST("outpeers"), "", "set maximum number of outgoing connections", do_outpeers }, { STRCONST("outpeers"), "", "set maximum number of outgoing connections", do_outpeers },
{ STRCONST("inpeers"), "", "set maximum number of incoming connections", do_inpeers }, { STRCONST("inpeers"), "", "set maximum number of incoming connections", do_inpeers },
#ifdef WITH_RANDOMX
{ STRCONST("start_mining"), "<threads>", "start mining", do_start_mining }, { STRCONST("start_mining"), "<threads>", "start mining", do_start_mining },
{ STRCONST("stop_mining"), "", "stop mining", do_stop_mining }, { STRCONST("stop_mining"), "", "stop mining", do_stop_mining },
#endif
{ STRCONST("exit"), "", "terminate p2pool", do_exit }, { STRCONST("exit"), "", "terminate p2pool", do_exit },
{ STRCNULL, NULL, NULL, NULL } { STRCNULL, NULL, NULL, NULL }
}; };
@@ -106,9 +114,9 @@ static int do_status(p2pool *m_pool, const char * /* args */)
if (m_pool->p2p_server()) { if (m_pool->p2p_server()) {
m_pool->p2p_server()->print_status(); m_pool->p2p_server()->print_status();
} }
if (m_pool->miner()) { #ifdef WITH_RANDOMX
m_pool->miner()->print_status(); m_pool->print_miner_status();
} #endif
bkg_jobs_tracker.print_status(); bkg_jobs_tracker.print_status();
return 0; return 0;
} }
@@ -175,6 +183,7 @@ static int do_inpeers(p2pool* m_pool, const char* args)
return 0; return 0;
} }
#ifdef WITH_RANDOMX
static int do_start_mining(p2pool* m_pool, const char* args) static int do_start_mining(p2pool* m_pool, const char* args)
{ {
uint32_t threads = strtoul(args, nullptr, 10); uint32_t threads = strtoul(args, nullptr, 10);
@@ -188,6 +197,7 @@ static int do_stop_mining(p2pool* m_pool, const char* /*args*/)
m_pool->stop_mining(); m_pool->stop_mining();
return 0; return 0;
} }
#endif
static int do_exit(p2pool *m_pool, const char * /* args */) static int do_exit(p2pool *m_pool, const char * /* args */)
{ {
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+41 -37
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
#include "crypto.h" #include "crypto.h"
#include "keccak.h" #include "keccak.h"
#include "uv_util.h" #include "uv_util.h"
#include <random>
extern "C" { extern "C" {
#include "crypto-ops.h" #include "crypto-ops.h"
@@ -32,7 +31,7 @@ namespace {
class RandomBytes class RandomBytes
{ {
public: public:
RandomBytes() : rng(s), dist(0, 255) RandomBytes() : rng(RandomDeviceSeed::instance), dist(0, 255)
{ {
uv_mutex_init_checked(&m); uv_mutex_init_checked(&m);
@@ -57,22 +56,6 @@ public:
private: private:
uv_mutex_t m; uv_mutex_t m;
// Fills the whole initial MT19937-64 state with non-deterministic random numbers
struct SeedSequence
{
using result_type = std::random_device::result_type;
template<typename T>
static void generate(T begin, T end)
{
std::random_device rd;
for (T i = begin; i != end; ++i) {
*i = rd();
}
}
};
SeedSequence s;
std::mt19937_64 rng; std::mt19937_64 rng;
std::uniform_int_distribution<> dist; std::uniform_int_distribution<> dist;
}; };
@@ -126,9 +109,9 @@ bool check_keys(const hash& pub, const hash& sec)
return pub == pub_check; return pub == pub_check;
} }
static FORCEINLINE void hash_to_scalar(const uint8_t* data, size_t 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, static_cast<int>(length), res, HASH_SIZE); keccak(data, length, res, HASH_SIZE);
sc_reduce32(res); sc_reduce32(res);
} }
@@ -139,18 +122,13 @@ static FORCEINLINE void derivation_to_scalar(const hash& derivation, size_t outp
uint8_t output_index[(sizeof(size_t) * 8 + 6) / 7]; uint8_t output_index[(sizeof(size_t) * 8 + 6) / 7];
} buf; } buf;
uint8_t* begin = buf.derivation;
uint8_t* end = buf.output_index;
memcpy(buf.derivation, derivation.h, sizeof(buf.derivation)); memcpy(buf.derivation, derivation.h, sizeof(buf.derivation));
size_t k = output_index; uint8_t* p = buf.output_index;
while (k >= 0x80) { writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; });
*(end++) = (static_cast<uint8_t>(k) & 0x7F) | 0x80;
k >>= 7;
}
*(end++) = static_cast<uint8_t>(k);
hash_to_scalar(begin, end - begin, res); const uint8_t* data = buf.derivation;
hash_to_scalar(data, static_cast<int>(p - data), res);
} }
class Cache class Cache
@@ -166,17 +144,19 @@ public:
uv_mutex_destroy(&m); uv_mutex_destroy(&m);
} }
bool get_derivation(const hash& key1, const hash& key2, hash& derivation) bool get_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag)
{ {
std::array<uint8_t, HASH_SIZE * 2> index; std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)> index;
memcpy(index.data(), key1.h, HASH_SIZE); memcpy(index.data(), key1.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE, key2.h, HASH_SIZE); memcpy(index.data() + HASH_SIZE, key2.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE * 2, &output_index, sizeof(size_t));
{ {
MutexLock lock(m); MutexLock lock(m);
auto it = derivations.find(index); auto it = derivations.find(index);
if (it != derivations.end()) { if (it != derivations.end()) {
derivation = it->second; derivation = it->second.derivation;
view_tag = it->second.view_tag;
return true; return true;
} }
} }
@@ -194,9 +174,11 @@ public:
ge_p1p1_to_p2(&point2, &point3); ge_p1p1_to_p2(&point2, &point3);
ge_tobytes(reinterpret_cast<uint8_t*>(&derivation), &point2); ge_tobytes(reinterpret_cast<uint8_t*>(&derivation), &point2);
derive_view_tag(derivation, output_index, view_tag);
{ {
MutexLock lock(m); MutexLock lock(m);
derivations.emplace(index, derivation); derivations.emplace(index, DerivationEntry{ derivation, view_tag } );
} }
return true; return true;
@@ -253,16 +235,22 @@ public:
} }
private: private:
struct DerivationEntry
{
hash derivation;
uint8_t view_tag;
};
uv_mutex_t m; uv_mutex_t m;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, hash> derivations; unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, DerivationEntry> derivations;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys; unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
}; };
static Cache* cache = nullptr; static Cache* cache = nullptr;
bool generate_key_derivation(const hash& key1, const hash& key2, hash& derivation) bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag)
{ {
return cache->get_derivation(key1, key2, derivation); return cache->get_derivation(key1, key2, output_index, derivation, 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)
@@ -270,6 +258,22 @@ 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 derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag)
{
constexpr uint8_t salt[] = "view_tag";
constexpr size_t SALT_SIZE = sizeof(salt) - 1;
uint8_t buf[64];
memcpy(buf, salt, SALT_SIZE);
memcpy(buf + SALT_SIZE, derivation.h, HASH_SIZE);
uint8_t* p = buf + SALT_SIZE + HASH_SIZE;
writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; });
hash view_tag_full;
keccak(buf, static_cast<int>(p - buf), view_tag_full.h, HASH_SIZE);
view_tag = view_tag_full.h[0];
}
void init_crypto_cache() void init_crypto_cache()
{ {
if (!cache) { if (!cache) {
+3 -2
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -21,8 +21,9 @@ namespace p2pool {
void generate_keys(hash& pub, hash& sec); void generate_keys(hash& pub, hash& sec);
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, hash& derivation); 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);
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag);
void init_crypto_cache(); void init_crypto_cache();
void destroy_crypto_cache(); void destroy_crypto_cache();
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+14 -10
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -48,17 +48,21 @@ NOINLINE void keccakf(uint64_t* st)
bc[3] = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23]; bc[3] = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
bc[4] = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24]; bc[4] = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
for (int i = 0; i < 5; ++i) { #define X(i) { \
uint64_t t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); const uint64_t t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); \
st[i + 0 ] ^= t; st[i + 0 ] ^= t; \
st[i + 5] ^= t; st[i + 5] ^= t; \
st[i + 10] ^= t; st[i + 10] ^= t; \
st[i + 15] ^= t; st[i + 15] ^= t; \
st[i + 20] ^= t; st[i + 20] ^= t; \
} }
X(0); X(1); X(2); X(3); X(4);
#undef X
// Rho Pi // Rho Pi
uint64_t t = st[1]; const uint64_t t = st[1];
st[ 1] = ROTL64(st[ 6], 44); st[ 1] = ROTL64(st[ 6], 44);
st[ 6] = ROTL64(st[ 9], 20); st[ 6] = ROTL64(st[ 9], 20);
st[ 9] = ROTL64(st[22], 61); st[ 9] = ROTL64(st[22], 61);
@@ -155,7 +159,7 @@ NOINLINE void keccak(const uint8_t* in, int inlen, uint8_t* md, int mdlen)
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++) {
st[i] ^= ((uint64_t*)in)[i]; st[i] ^= read_unaligned(reinterpret_cast<const uint64_t*>(in) + i);
} }
keccakf(st); keccakf(st);
} }
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+26 -11
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
#include "uv_util.h" #include "uv_util.h"
#include <ctime> #include <ctime>
#include <fstream> #include <fstream>
#include <thread>
static constexpr char log_category_prefix[] = "Log "; static constexpr char log_category_prefix[] = "Log ";
static constexpr char log_file_name[] = "p2pool.log"; static constexpr char log_file_name[] = "p2pool.log";
@@ -32,7 +33,7 @@ bool CONSOLE_COLORS = true;
#ifndef P2POOL_LOG_DISABLE #ifndef P2POOL_LOG_DISABLE
static volatile bool stopped = false; static std::atomic<bool> stopped{ false };
static volatile bool worker_started = false; static volatile bool worker_started = false;
#ifdef _WIN32 #ifdef _WIN32
@@ -47,7 +48,7 @@ public:
enum params : int enum params : int
{ {
SLOT_SIZE = 1024, SLOT_SIZE = 1024,
BUF_SIZE = SLOT_SIZE * 16384, BUF_SIZE = SLOT_SIZE * 8192,
}; };
FORCEINLINE Worker() FORCEINLINE Worker()
@@ -72,7 +73,9 @@ public:
abort(); abort();
} }
do {} while (!worker_started); while (!worker_started) {
std::this_thread::yield();
}
#ifdef _WIN32 #ifdef _WIN32
DWORD dwConsoleMode; DWORD dwConsoleMode;
@@ -102,11 +105,10 @@ public:
FORCEINLINE void stop() FORCEINLINE void stop()
{ {
if (stopped) { if (stopped.exchange(true)) {
return; return;
} }
stopped = true;
LOGINFO(0, "stopped"); LOGINFO(0, "stopped");
uv_thread_join(&m_worker); uv_thread_join(&m_worker);
uv_cond_destroy(&m_cond); uv_cond_destroy(&m_cond);
@@ -131,6 +133,7 @@ public:
memcpy(p + 1, buf + 1, size - 1); memcpy(p + 1, buf + 1, size - 1);
// Ensure memory order in the writer thread
std::atomic_thread_fence(std::memory_order_seq_cst); std::atomic_thread_fence(std::memory_order_seq_cst);
// Mark that everything is written into this log slot // Mark that everything is written into this log slot
@@ -149,18 +152,26 @@ private:
do { do {
uv_mutex_lock(&m_mutex); uv_mutex_lock(&m_mutex);
uv_cond_wait(&m_cond, &m_mutex); if (m_readPos == m_writePos.load()) {
// Nothing to do, wait for the signal
uv_cond_wait(&m_cond, &m_mutex);
}
uv_mutex_unlock(&m_mutex); uv_mutex_unlock(&m_mutex);
for (uint32_t writePos = m_writePos.load(); m_readPos < writePos; writePos = m_writePos.load()) { for (uint32_t writePos = m_writePos.load(); m_readPos != writePos; writePos = m_writePos.load()) {
// We have at least one log slot pending, possibly more than one // We have at least one log slot pending, possibly more than one
// Process everything in a loop before reading m_writePos again // Process everything in a loop before reading m_writePos again
do { do {
char* p = m_buf.data() + (m_readPos % BUF_SIZE); char* p = m_buf.data() + (m_readPos % BUF_SIZE);
// Wait until everything is written into this log slot // Wait until everything is written into this log slot
volatile char& severity = *p; volatile char& severity = p[0];
while (!severity) {} while (!severity) {
std::this_thread::yield();
}
// Ensure memory order in the reader thread
std::atomic_thread_fence(std::memory_order_seq_cst);
uint32_t size = static_cast<uint8_t>(p[2]); uint32_t size = static_cast<uint8_t>(p[2]);
size = (size << 8) + static_cast<uint8_t>(p[1]); size = (size << 8) + static_cast<uint8_t>(p[1]);
@@ -208,7 +219,6 @@ private:
} }
m_logFile.write(p, size); m_logFile.write(p, size);
m_logFile.flush();
} }
} }
@@ -218,6 +228,11 @@ private:
m_readPos += SLOT_SIZE; m_readPos += SLOT_SIZE;
} while (m_readPos < writePos); } while (m_readPos < writePos);
} }
// Flush the log file only after all pending log lines have been written
if (m_logFile.is_open()) {
m_logFile.flush();
}
} while (!stopped); } while (!stopped);
} }
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+2 -2
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -44,7 +44,7 @@ void Mempool::add(const TxMempoolData& tx)
void Mempool::swap(std::vector<TxMempoolData>& transactions) void Mempool::swap(std::vector<TxMempoolData>& transactions)
{ {
const time_t cur_time = time(nullptr); const uint64_t cur_time = seconds_since_epoch();
WriteLock lock(m_lock); WriteLock lock(m_lock);
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+4 -3
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -35,14 +35,15 @@ namespace p2pool {
Miner::Miner(p2pool* pool, uint32_t threads) Miner::Miner(p2pool* pool, uint32_t threads)
: m_pool(pool) : m_pool(pool)
, m_threads(threads) , m_threads(threads)
, m_stopped(false) , m_stopped{ false }
, m_startTimestamp(high_resolution_clock::now()) , m_startTimestamp(high_resolution_clock::now())
, m_nonce(0) , m_nonce(0)
, m_nonceTimestamp(m_startTimestamp) , m_nonceTimestamp(m_startTimestamp)
, m_extraNonce(0xF19E3779U) , m_extraNonce(0xF19E3779U)
, m_totalHashes(0) , m_totalHashes(0)
, m_sharesFound(0)
, m_job{} , m_job{}
, m_jobIndex(0) , m_jobIndex{ 0 }
{ {
on_block(m_pool->block_template()); on_block(m_pool->block_template());
+5 -5
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -49,12 +49,12 @@ private:
}; };
std::vector<WorkerData*> m_minerThreads; std::vector<WorkerData*> m_minerThreads;
volatile bool m_stopped; std::atomic<bool> m_stopped;
std::chrono::time_point<std::chrono::high_resolution_clock> m_startTimestamp; std::chrono::high_resolution_clock::time_point m_startTimestamp;
std::atomic<uint32_t> m_nonce; std::atomic<uint32_t> m_nonce;
std::chrono::time_point<std::chrono::high_resolution_clock> m_nonceTimestamp; std::chrono::high_resolution_clock::time_point m_nonceTimestamp;
const uint32_t m_extraNonce; const uint32_t m_extraNonce;
uint64_t m_totalHashes; uint64_t m_totalHashes;
@@ -75,7 +75,7 @@ private:
void set_nonce(uint32_t nonce, uint32_t extra_nonce); void set_nonce(uint32_t nonce, uint32_t extra_nonce);
}; };
Job m_job[2]; Job m_job[2];
volatile uint32_t m_jobIndex; std::atomic<uint32_t> m_jobIndex;
void run(WorkerData* data); void run(WorkerData* data);
}; };
+125 -73
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -47,17 +47,23 @@ P2PServer::P2PServer(p2pool* pool)
, m_cache(pool->params().m_blockCache ? new BlockCache() : nullptr) , m_cache(pool->params().m_blockCache ? new BlockCache() : nullptr)
, m_cacheLoaded(false) , m_cacheLoaded(false)
, m_initialPeerList(pool->params().m_p2pPeerList) , m_initialPeerList(pool->params().m_p2pPeerList)
, m_rd{} , m_cachedBlocks(nullptr)
, m_rng(m_rd()) , m_rng(RandomDeviceSeed::instance)
, m_block(new PoolBlock()) , m_block(new PoolBlock())
, m_timer{} , m_timer{}
, m_timerCounter(0) , m_timerCounter(0)
, m_timerInterval(2) , m_timerInterval(2)
, m_peerId(m_rng())
, m_peerListLastSaved(0) , m_peerListLastSaved(0)
{ {
set_max_outgoing_peers(pool->params().m_maxOutgoingPeers); // Diffuse the initial state in case it has low quality
set_max_incoming_peers(pool->params().m_maxIncomingPeers); m_rng.discard(10000);
m_peerId = m_rng();
const Params& params = pool->params();
set_max_outgoing_peers(params.m_maxOutgoingPeers);
set_max_incoming_peers(params.m_maxIncomingPeers);
uv_mutex_init_checked(&m_rngLock); uv_mutex_init_checked(&m_rngLock);
uv_mutex_init_checked(&m_blockLock); uv_mutex_init_checked(&m_blockLock);
@@ -94,7 +100,7 @@ P2PServer::P2PServer(p2pool* pool)
} }
load_peer_list(); load_peer_list();
start_listening(pool->params().m_p2pAddresses); start_listening(params.m_p2pAddresses);
} }
P2PServer::~P2PServer() P2PServer::~P2PServer()
@@ -125,18 +131,34 @@ void P2PServer::add_cached_block(const PoolBlock& block)
return; return;
} }
PoolBlock* new_block = new PoolBlock(block); if (!m_cachedBlocks) {
m_cachedBlocks.insert({ new_block->m_sidechainId, new_block }); m_cachedBlocks = new unordered_map<hash, PoolBlock*>();
}
if (m_cachedBlocks->find(block.m_sidechainId) == m_cachedBlocks->end()) {
PoolBlock* new_block = new PoolBlock(block);
m_cachedBlocks->insert({ new_block->m_sidechainId, new_block });
}
} }
void P2PServer::clear_cached_blocks() void P2PServer::clear_cached_blocks()
{ {
if (!m_cachedBlocks) {
return;
}
WriteLock lock(m_cachedBlocksLock); WriteLock lock(m_cachedBlocksLock);
for (auto it : m_cachedBlocks) { if (!m_cachedBlocks) {
return;
}
for (auto it : *m_cachedBlocks) {
delete it.second; delete it.second;
} }
m_cachedBlocks.clear();
delete m_cachedBlocks;
m_cachedBlocks = nullptr;
} }
void P2PServer::store_in_cache(const PoolBlock& block) void P2PServer::store_in_cache(const PoolBlock& block)
@@ -174,12 +196,12 @@ void P2PServer::on_connect_failed(bool is_v6, const raw_ip& ip, int port)
void P2PServer::update_peer_connections() void P2PServer::update_peer_connections()
{ {
const time_t cur_time = time(nullptr); const uint64_t cur_time = seconds_since_epoch();
const time_t last_updated = m_pool->side_chain().last_updated(); const uint64_t last_updated = m_pool->side_chain().last_updated();
bool has_good_peers = false; bool has_good_peers = false;
std::vector<raw_ip> connected_clients; unordered_set<raw_ip> connected_clients;
{ {
MutexLock lock(m_clientsListLock); MutexLock lock(m_clientsListLock);
connected_clients.reserve(m_numConnections); connected_clients.reserve(m_numConnections);
@@ -195,12 +217,12 @@ void P2PServer::update_peer_connections()
} }
if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) { if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) {
// - Side chain is at least 5 minutes newer (last_updated >= client->m_lastBroadcastTimestamp + 300) // - Side chain is at least 15 minutes newer (last_updated >= client->m_lastBroadcastTimestamp + 900)
// - It's been at least 10 seconds since side chain updated (cur_time >= last_updated + 10) // - It's been at least 10 seconds since side chain updated (cur_time >= last_updated + 10)
// - It's been at least 10 seconds since the last block request (peer is not syncing) // - It's been at least 10 seconds since the last block request (peer is not syncing)
// - Peer should have sent a broadcast by now // - Peer should have sent a broadcast by now
if (last_updated && (cur_time >= std::max(last_updated, client->m_lastBlockrequestTimestamp) + 10) && (last_updated >= client->m_lastBroadcastTimestamp + 300)) { if (last_updated && (cur_time >= std::max(last_updated, client->m_lastBlockrequestTimestamp) + 10) && (last_updated >= client->m_lastBroadcastTimestamp + 900)) {
const time_t dt = last_updated - client->m_lastBroadcastTimestamp; const uint64_t dt = last_updated - client->m_lastBroadcastTimestamp;
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " is not broadcasting blocks (last update " << dt << " seconds ago)"); LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " is not broadcasting blocks (last update " << dt << " seconds ago)");
client->ban(DEFAULT_BAN_TIME); client->ban(DEFAULT_BAN_TIME);
remove_peer_from_list(client); remove_peer_from_list(client);
@@ -210,7 +232,7 @@ void P2PServer::update_peer_connections()
} }
if (!disconnected) { if (!disconnected) {
connected_clients.emplace_back(client->m_addr); connected_clients.insert(client->m_addr);
if (client->m_handshakeComplete && !client->m_handshakeInvalid && (client->m_listenPort >= 0)) { if (client->m_handshakeComplete && !client->m_handshakeInvalid && (client->m_listenPort >= 0)) {
has_good_peers = true; has_good_peers = true;
} }
@@ -225,7 +247,7 @@ void P2PServer::update_peer_connections()
if ((m_timerCounter % 30) == 1) { if ((m_timerCounter % 30) == 1) {
// Update last seen time for currently connected peers // Update last seen time for currently connected peers
for (Peer& p : m_peerList) { for (Peer& p : m_peerList) {
if (std::find_if(connected_clients.begin(), connected_clients.end(), [&p](const raw_ip& addr) { return p.m_addr == addr; }) != connected_clients.end()) { if (connected_clients.find(p.m_addr) != connected_clients.end()) {
p.m_lastSeen = cur_time; p.m_lastSeen = cur_time;
} }
} }
@@ -254,15 +276,7 @@ void P2PServer::update_peer_connections()
const uint64_t k = get_random64() % peer_list.size(); const uint64_t k = get_random64() % peer_list.size();
const Peer& peer = peer_list[k]; const Peer& peer = peer_list[k];
bool already_connected = false; if ((connected_clients.find(peer.m_addr) == connected_clients.end()) && connect_to_peer(peer.m_isV6, peer.m_addr, peer.m_port)) {
for (const raw_ip& ip : connected_clients) {
if (ip == peer.m_addr) {
already_connected = true;
break;
}
}
if (!already_connected && connect_to_peer(peer.m_isV6, peer.m_addr, peer.m_port)) {
++i; ++i;
} }
@@ -282,6 +296,7 @@ void P2PServer::update_peer_connections()
void P2PServer::update_peer_list() void P2PServer::update_peer_list()
{ {
const uint64_t cur_time = seconds_since_epoch();
{ {
MutexLock lock(m_clientsListLock); MutexLock lock(m_clientsListLock);
@@ -290,9 +305,9 @@ void P2PServer::update_peer_list()
continue; continue;
} }
if (m_timerCounter >= client->m_nextOutgoingPeerListRequest) { if (cur_time >= client->m_nextOutgoingPeerListRequest) {
// Send peer list requests at random intervals (60-120 seconds) // Send peer list requests at random intervals (60-120 seconds)
client->m_nextOutgoingPeerListRequest = m_timerCounter + (60 + (get_random64() % 61)) / m_timerInterval; client->m_nextOutgoingPeerListRequest = cur_time + (60 + (get_random64() % 61));
const bool result = send(client, const bool result = send(client,
[](void* buf) [](void* buf)
@@ -313,7 +328,7 @@ void P2PServer::update_peer_list()
void P2PServer::save_peer_list_async() void P2PServer::save_peer_list_async()
{ {
const time_t cur_time = time(nullptr); const uint64_t cur_time = seconds_since_epoch();
if (cur_time < m_peerListLastSaved + 300) { if (cur_time < m_peerListLastSaved + 300) {
return; return;
} }
@@ -386,7 +401,7 @@ void P2PServer::save_peer_list()
f.close(); f.close();
LOGINFO(5, "peer list saved (" << peer_list.size() << " peers)"); LOGINFO(5, "peer list saved (" << peer_list.size() << " peers)");
m_peerListLastSaved = time(nullptr); m_peerListLastSaved = seconds_since_epoch();
} }
void P2PServer::load_peer_list() void P2PServer::load_peer_list()
@@ -510,7 +525,7 @@ void P2PServer::load_peer_list()
p.m_port = port; p.m_port = port;
p.m_numFailedConnections = 0; p.m_numFailedConnections = 0;
p.m_lastSeen = time(nullptr); p.m_lastSeen = seconds_since_epoch();
if (!already_added && !is_banned(p.m_addr)) { if (!already_added && !is_banned(p.m_addr)) {
m_peerList.push_back(p); m_peerList.push_back(p);
@@ -617,7 +632,7 @@ void P2PServer::load_monerod_peer_list()
void P2PServer::update_peer_in_list(bool is_v6, const raw_ip& ip, int port) void P2PServer::update_peer_in_list(bool is_v6, const raw_ip& ip, int port)
{ {
const time_t cur_time = time(nullptr); const uint64_t cur_time = seconds_since_epoch();
MutexLock lock(m_peerListLock); MutexLock lock(m_peerListLock);
@@ -663,13 +678,15 @@ void P2PServer::remove_peer_from_list(const raw_ip& ip)
void P2PServer::broadcast(const PoolBlock& block) void P2PServer::broadcast(const PoolBlock& block)
{ {
if (block.m_txinGenHeight + 2 < m_pool->miner_data().height) { MinerData miner_data = m_pool->miner_data();
LOGWARN(3, "Trying to broadcast a stale block " << block.m_sidechainId << " (mainchain height " << block.m_txinGenHeight << ", current height is " << m_pool->miner_data().height << ')');
if (block.m_txinGenHeight + 2 < miner_data.height) {
LOGWARN(3, "Trying to broadcast a stale block " << block.m_sidechainId << " (mainchain height " << block.m_txinGenHeight << ", current height is " << miner_data.height << ')');
return; return;
} }
if (block.m_txinGenHeight > m_pool->miner_data().height + 2) { if (block.m_txinGenHeight > miner_data.height + 2) {
LOGWARN(3, "Trying to broadcast a block " << block.m_sidechainId << " ahead on mainchain (mainchain height " << block.m_txinGenHeight << ", current height is " << m_pool->miner_data().height << ')'); LOGWARN(3, "Trying to broadcast a block " << block.m_sidechainId << " ahead on mainchain (mainchain height " << block.m_txinGenHeight << ", current height is " << miner_data.height << ')');
return; return;
} }
@@ -783,21 +800,27 @@ void P2PServer::on_broadcast()
LOGINFO(6, "sending BLOCK_BROADCAST (pruned) to " << log::Gray() << static_cast<char*>(client->m_addrString)); LOGINFO(6, "sending BLOCK_BROADCAST (pruned) to " << log::Gray() << static_cast<char*>(client->m_addrString));
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST); *(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
*reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(data->pruned_blob.size()); const uint32_t len = static_cast<uint32_t>(data->pruned_blob.size());
memcpy(p, &len, sizeof(uint32_t));
p += sizeof(uint32_t); p += sizeof(uint32_t);
memcpy(p, data->pruned_blob.data(), data->pruned_blob.size()); if (len) {
p += data->pruned_blob.size(); memcpy(p, data->pruned_blob.data(), len);
p += len;
}
} }
else { else {
LOGINFO(5, "sending BLOCK_BROADCAST (full) to " << log::Gray() << static_cast<char*>(client->m_addrString)); LOGINFO(5, "sending BLOCK_BROADCAST (full) to " << log::Gray() << static_cast<char*>(client->m_addrString));
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST); *(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
*reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(data->blob.size()); const uint32_t len = static_cast<uint32_t>(data->blob.size());
memcpy(p, &len, sizeof(uint32_t));
p += sizeof(uint32_t); p += sizeof(uint32_t);
memcpy(p, data->blob.data(), data->blob.size()); if (len) {
p += data->blob.size(); memcpy(p, data->blob.data(), len);
p += len;
}
} }
return p - p0; return p - p0;
@@ -814,7 +837,7 @@ uint64_t P2PServer::get_random64()
void P2PServer::print_status() void P2PServer::print_status()
{ {
const int64_t uptime = time(nullptr) - m_pool->start_time(); const int64_t uptime = seconds_since_epoch() - m_pool->start_time();
const int64_t s = uptime % 60; const int64_t s = uptime % 60;
const int64_t m = (uptime / 60) % 60; const int64_t m = (uptime / 60) % 60;
@@ -930,12 +953,14 @@ void P2PServer::download_missing_blocks()
return; return;
} }
ReadLock lock2(m_cachedBlocksLock);
// Try to download each block from a random client // Try to download each block from a random client
for (const hash& id : missing_blocks) { for (const hash& id : missing_blocks) {
P2PClient* client = clients[get_random64() % clients.size()]; P2PClient* client = clients[get_random64() % clients.size()];
{ {
MutexLock lock2(m_missingBlockRequestsLock); MutexLock lock3(m_missingBlockRequestsLock);
const uint64_t truncated_block_id = *reinterpret_cast<const uint64_t*>(id.h); const uint64_t truncated_block_id = *reinterpret_cast<const uint64_t*>(id.h);
if (!m_missingBlockRequests.insert({ client->m_peerId, truncated_block_id }).second) { if (!m_missingBlockRequests.insert({ client->m_peerId, truncated_block_id }).second) {
@@ -945,6 +970,15 @@ void P2PServer::download_missing_blocks()
} }
} }
if (m_cachedBlocks) {
auto it = m_cachedBlocks->find(id);
if (it != m_cachedBlocks->end()) {
LOGINFO(5, "using cached block for id = " << id);
client->handle_incoming_block_async(it->second);
continue;
}
}
const bool result = send(client, const bool result = send(client,
[&id](void* buf) [&id](void* buf)
{ {
@@ -972,8 +1006,8 @@ void P2PServer::check_zmq()
return; return;
} }
const time_t cur_time = time(nullptr); const uint64_t cur_time = seconds_since_epoch();
const time_t last_active = m_pool->zmq_last_active(); const uint64_t last_active = m_pool->zmq_last_active();
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);
@@ -990,12 +1024,13 @@ P2PServer::P2PClient::P2PClient()
, m_handshakeInvalid(false) , m_handshakeInvalid(false)
, m_listenPort(-1) , m_listenPort(-1)
, m_fastPeerListRequestCount(0) , m_fastPeerListRequestCount(0)
, m_prevIncomingPeerListRequest{} , m_prevIncomingPeerListRequest(0)
, m_nextOutgoingPeerListRequest(0) , m_nextOutgoingPeerListRequest(0)
, m_lastPeerListRequestTime{} , m_lastPeerListRequestTime{}
, m_peerListPendingRequests(0) , m_peerListPendingRequests(0)
, m_pingTime(0) , m_pingTime(0)
, m_blockPendingRequests(0) , m_blockPendingRequests(0)
, m_chainTipBlockRequest(false)
, m_lastAlive(0) , m_lastAlive(0)
, m_lastBroadcastTimestamp(0) , m_lastBroadcastTimestamp(0)
, m_lastBlockrequestTimestamp(0) , m_lastBlockrequestTimestamp(0)
@@ -1019,12 +1054,13 @@ void P2PServer::P2PClient::reset()
m_handshakeInvalid = false; m_handshakeInvalid = false;
m_listenPort = -1; m_listenPort = -1;
m_fastPeerListRequestCount = 0; m_fastPeerListRequestCount = 0;
m_prevIncomingPeerListRequest = {}; m_prevIncomingPeerListRequest = 0;
m_nextOutgoingPeerListRequest = 0; m_nextOutgoingPeerListRequest = 0;
m_lastPeerListRequestTime = {}; m_lastPeerListRequestTime = {};
m_peerListPendingRequests = 0; m_peerListPendingRequests = 0;
m_pingTime = 0; m_pingTime = 0;
m_blockPendingRequests = 0; m_blockPendingRequests = 0;
m_chainTipBlockRequest = false;
m_lastAlive = 0; m_lastAlive = 0;
m_lastBroadcastTimestamp = 0; m_lastBroadcastTimestamp = 0;
m_lastBlockrequestTimestamp = 0; m_lastBlockrequestTimestamp = 0;
@@ -1057,7 +1093,7 @@ bool P2PServer::P2PClient::on_connect()
} }
} }
m_lastAlive = time(nullptr); m_lastAlive = seconds_since_epoch();
return send_handshake_challenge(); return send_handshake_challenge();
} }
@@ -1191,7 +1227,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_RESPONSE"); LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_RESPONSE");
if (bytes_left >= 1 + sizeof(uint32_t)) { if (bytes_left >= 1 + sizeof(uint32_t)) {
const uint32_t block_size = *reinterpret_cast<uint32_t*>(buf + 1); const uint32_t block_size = read_unaligned(reinterpret_cast<uint32_t*>(buf + 1));
if (bytes_left >= 1 + sizeof(uint32_t) + block_size) { if (bytes_left >= 1 + sizeof(uint32_t) + block_size) {
bytes_read = 1 + sizeof(uint32_t) + block_size; bytes_read = 1 + sizeof(uint32_t) + block_size;
@@ -1209,7 +1245,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
LOGINFO(6, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_BROADCAST"); LOGINFO(6, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_BROADCAST");
if (bytes_left >= 1 + sizeof(uint32_t)) { if (bytes_left >= 1 + sizeof(uint32_t)) {
const uint32_t block_size = *reinterpret_cast<uint32_t*>(buf + 1); const uint32_t block_size = read_unaligned(reinterpret_cast<uint32_t*>(buf + 1));
if (bytes_left >= 1 + sizeof(uint32_t) + block_size) { if (bytes_left >= 1 + sizeof(uint32_t) + block_size) {
bytes_read = 1 + sizeof(uint32_t) + block_size; bytes_read = 1 + sizeof(uint32_t) + block_size;
if (!on_block_broadcast(buf + 1 + sizeof(uint32_t), block_size)) { if (!on_block_broadcast(buf + 1 + sizeof(uint32_t), block_size)) {
@@ -1273,7 +1309,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
if (bytes_read) { if (bytes_read) {
buf += bytes_read; buf += bytes_read;
bytes_left -= bytes_read; bytes_left -= bytes_read;
m_lastAlive = time(nullptr); m_lastAlive = seconds_since_epoch();
} }
} while (bytes_read && bytes_left); } while (bytes_read && bytes_left);
@@ -1608,7 +1644,8 @@ void P2PServer::P2PClient::on_after_handshake(uint8_t* &p)
p += HASH_SIZE; p += HASH_SIZE;
++m_blockPendingRequests; ++m_blockPendingRequests;
m_lastBroadcastTimestamp = time(nullptr); m_chainTipBlockRequest = true;
m_lastBroadcastTimestamp = seconds_since_epoch();
} }
bool P2PServer::P2PClient::on_listen_port(const uint8_t* buf) bool P2PServer::P2PClient::on_listen_port(const uint8_t* buf)
@@ -1629,7 +1666,7 @@ bool P2PServer::P2PClient::on_listen_port(const uint8_t* buf)
bool P2PServer::P2PClient::on_block_request(const uint8_t* buf) bool P2PServer::P2PClient::on_block_request(const uint8_t* buf)
{ {
m_lastBlockrequestTimestamp = time(nullptr); m_lastBlockrequestTimestamp = seconds_since_epoch();
hash id; hash id;
memcpy(id.h, buf, HASH_SIZE); memcpy(id.h, buf, HASH_SIZE);
@@ -1650,11 +1687,14 @@ bool P2PServer::P2PClient::on_block_request(const uint8_t* buf)
LOGINFO(5, "sending BLOCK_RESPONSE"); LOGINFO(5, "sending BLOCK_RESPONSE");
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_RESPONSE); *(p++) = static_cast<uint8_t>(MessageId::BLOCK_RESPONSE);
*reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(blob.size()); const uint32_t len = static_cast<uint32_t>(blob.size());
memcpy(p, &len, sizeof(uint32_t));
p += sizeof(uint32_t); p += sizeof(uint32_t);
memcpy(p, blob.data(), blob.size()); if (len) {
p += blob.size(); memcpy(p, blob.data(), len);
p += len;
}
return p - p0; return p - p0;
}); });
@@ -1677,6 +1717,18 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size)
return false; return false;
} }
if (m_chainTipBlockRequest) {
m_chainTipBlockRequest = false;
const uint64_t peer_height = server->m_block->m_txinGenHeight;
const uint64_t our_height = server->m_pool->miner_data().height;
if (peer_height + 2 < our_height) {
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " is mining on top of a stale block (mainchain height " << peer_height << ", expected >= " << our_height << ')');
return false;
}
}
return handle_incoming_block_async(server->m_block); return handle_incoming_block_async(server->m_block);
} }
@@ -1699,7 +1751,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
m_broadcastedHashes[m_broadcastedHashesIndex.fetch_add(1) % array_size(&P2PClient::m_broadcastedHashes)] = server->m_block->m_sidechainId; m_broadcastedHashes[m_broadcastedHashesIndex.fetch_add(1) % array_size(&P2PClient::m_broadcastedHashes)] = server->m_block->m_sidechainId;
const MinerData& miner_data = server->m_pool->miner_data(); MinerData miner_data = server->m_pool->miner_data();
if (server->m_block->m_prevId != miner_data.prev_id) { if (server->m_block->m_prevId != miner_data.prev_id) {
// This peer is mining on top of a different Monero block, investigate it // This peer is mining on top of a different Monero block, investigate it
@@ -1709,7 +1761,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
if (peer_height < our_height) { if (peer_height < our_height) {
if (our_height - peer_height < 5) { if (our_height - peer_height < 5) {
using namespace std::chrono; using namespace std::chrono;
const int64_t elapsed_ms = duration_cast<milliseconds>(system_clock::now() - miner_data.time_received).count(); const int64_t elapsed_ms = duration_cast<milliseconds>(high_resolution_clock::now() - miner_data.time_received).count();
if (our_height - peer_height > 1) { if (our_height - peer_height > 1) {
LOGWARN(5, "peer " << static_cast<char*>(m_addrString) << " broadcasted a stale block (" << elapsed_ms << " ms late, mainchain height " << peer_height << ", expected >= " << our_height << "), ignoring it"); LOGWARN(5, "peer " << static_cast<char*>(m_addrString) << " broadcasted a stale block (" << elapsed_ms << " ms late, mainchain height " << peer_height << ", expected >= " << our_height << "), ignoring it");
return true; return true;
@@ -1719,7 +1771,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
} }
} }
else { else {
LOGWARN(5, "peer " << static_cast<char*>(m_addrString) << " broadcasted an unreasonably stale block (mainchain height " << peer_height << ", expected >= " << our_height << ')'); LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " broadcasted an unreasonably stale block (mainchain height " << peer_height << ", expected >= " << our_height << ')');
return false; return false;
} }
} }
@@ -1735,20 +1787,18 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
server->m_block->m_wantBroadcast = true; server->m_block->m_wantBroadcast = true;
m_lastBroadcastTimestamp = time(nullptr); m_lastBroadcastTimestamp = seconds_since_epoch();
return handle_incoming_block_async(server->m_block); return handle_incoming_block_async(server->m_block);
} }
bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*) bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
{ {
using namespace std::chrono;
P2PServer* server = static_cast<P2PServer*>(m_owner); P2PServer* server = static_cast<P2PServer*>(m_owner);
const auto cur_time = steady_clock::now(); const uint64_t cur_time = seconds_since_epoch();
// Allow peer list requests no more than once every 30 seconds // Allow peer list requests no more than once every 30 seconds
if (duration_cast<seconds>(cur_time - m_prevIncomingPeerListRequest).count() < 30) { if (cur_time - m_prevIncomingPeerListRequest < 30) {
++m_fastPeerListRequestCount; ++m_fastPeerListRequestCount;
if (m_fastPeerListRequestCount >= 3) { if (m_fastPeerListRequestCount >= 3) {
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " is sending PEER_LIST_REQUEST too often"); LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " is sending PEER_LIST_REQUEST too often");
@@ -1819,7 +1869,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
{ {
P2PServer* server = static_cast<P2PServer*>(m_owner); P2PServer* server = static_cast<P2PServer*>(m_owner);
const time_t cur_time = time(nullptr); const uint64_t cur_time = seconds_since_epoch();
MutexLock lock(server->m_peerListLock); MutexLock lock(server->m_peerListLock);
@@ -1942,11 +1992,13 @@ void P2PServer::P2PClient::post_handle_incoming_block(const uint32_t reset_count
ReadLock lock(server->m_cachedBlocksLock); ReadLock lock(server->m_cachedBlocksLock);
for (const hash& id : missing_blocks) { for (const hash& id : missing_blocks) {
auto it = server->m_cachedBlocks.find(id); if (server->m_cachedBlocks) {
if (it != server->m_cachedBlocks.end()) { auto it = server->m_cachedBlocks->find(id);
LOGINFO(5, "using cached block for id = " << id); if (it != server->m_cachedBlocks->end()) {
handle_incoming_block_async(it->second); LOGINFO(5, "using cached block for id = " << id);
continue; handle_incoming_block_async(it->second);
continue;
}
} }
const bool result = m_owner->send(this, const bool result = m_owner->send(this,
+9 -10
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
#pragma once #pragma once
#include "tcp_server.h" #include "tcp_server.h"
#include <random>
namespace p2pool { namespace p2pool {
@@ -111,17 +110,18 @@ public:
int m_listenPort; int m_listenPort;
uint32_t m_fastPeerListRequestCount; uint32_t m_fastPeerListRequestCount;
std::chrono::steady_clock::time_point m_prevIncomingPeerListRequest; uint64_t m_prevIncomingPeerListRequest;
uint64_t m_nextOutgoingPeerListRequest; uint64_t m_nextOutgoingPeerListRequest;
std::chrono::high_resolution_clock::time_point m_lastPeerListRequestTime; std::chrono::high_resolution_clock::time_point m_lastPeerListRequestTime;
int m_peerListPendingRequests; int m_peerListPendingRequests;
int64_t m_pingTime; int64_t m_pingTime;
int m_blockPendingRequests; int m_blockPendingRequests;
bool m_chainTipBlockRequest;
time_t m_lastAlive; uint64_t m_lastAlive;
time_t m_lastBroadcastTimestamp; uint64_t m_lastBroadcastTimestamp;
time_t m_lastBlockrequestTimestamp; uint64_t m_lastBlockrequestTimestamp;
hash m_broadcastedHashes[8]; hash m_broadcastedHashes[8];
std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 }; std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 };
@@ -150,7 +150,7 @@ private:
uint32_t m_maxIncomingPeers; uint32_t m_maxIncomingPeers;
uv_rwlock_t m_cachedBlocksLock; uv_rwlock_t m_cachedBlocksLock;
unordered_map<hash, PoolBlock*> m_cachedBlocks; unordered_map<hash, PoolBlock*>* m_cachedBlocks;
private: private:
static void on_timer(uv_timer_t* timer) { reinterpret_cast<P2PServer*>(timer->data)->on_timer(); } static void on_timer(uv_timer_t* timer) { reinterpret_cast<P2PServer*>(timer->data)->on_timer(); }
@@ -170,7 +170,6 @@ private:
void remove_peer_from_list(const raw_ip& ip); void remove_peer_from_list(const raw_ip& ip);
uv_mutex_t m_rngLock; uv_mutex_t m_rngLock;
std::random_device m_rd;
std::mt19937_64 m_rng; std::mt19937_64 m_rng;
uv_mutex_t m_blockLock; uv_mutex_t m_blockLock;
@@ -190,12 +189,12 @@ private:
raw_ip m_addr; raw_ip m_addr;
int m_port; int m_port;
uint32_t m_numFailedConnections; uint32_t m_numFailedConnections;
time_t m_lastSeen; uint64_t m_lastSeen;
}; };
std::vector<Peer> m_peerList; std::vector<Peer> m_peerList;
std::vector<Peer> m_peerListMonero; std::vector<Peer> m_peerListMonero;
time_t m_peerListLastSaved; uint64_t m_peerListLastSaved;
struct Broadcast struct Broadcast
{ {
+100 -30
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -27,7 +27,9 @@
#include "side_chain.h" #include "side_chain.h"
#include "stratum_server.h" #include "stratum_server.h"
#include "p2p_server.h" #include "p2p_server.h"
#ifdef WITH_RANDOMX
#include "miner.h" #include "miner.h"
#endif
#include "params.h" #include "params.h"
#include "console_commands.h" #include "console_commands.h"
#include "crypto.h" #include "crypto.h"
@@ -52,7 +54,7 @@ p2pool::p2pool(int argc, char* argv[])
, m_updateSeed(true) , m_updateSeed(true)
, m_submitBlockData{} , m_submitBlockData{}
, m_zmqLastActive(0) , m_zmqLastActive(0)
, m_startTime(time(nullptr)) , m_startTime(seconds_since_epoch())
{ {
LOGINFO(1, log::LightCyan() << VERSION); LOGINFO(1, log::LightCyan() << VERSION);
@@ -70,7 +72,8 @@ p2pool::p2pool(int argc, char* argv[])
hash pub, sec, eph_public_key; hash pub, sec, eph_public_key;
generate_keys(pub, sec); generate_keys(pub, sec);
if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key)) { uint8_t view_tag;
if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key, view_tag)) {
LOGERR(1, "Invalid wallet address: get_eph_public_key failed"); LOGERR(1, "Invalid wallet address: get_eph_public_key failed");
panic(); panic();
} }
@@ -106,7 +109,11 @@ p2pool::p2pool(int argc, char* argv[])
m_stopAsync.data = this; m_stopAsync.data = this;
uv_rwlock_init_checked(&m_mainchainLock); uv_rwlock_init_checked(&m_mainchainLock);
uv_rwlock_init_checked(&m_minerDataLock);
uv_mutex_init_checked(&m_foundBlocksLock); uv_mutex_init_checked(&m_foundBlocksLock);
#ifdef WITH_RANDOMX
uv_mutex_init_checked(&m_minerLock);
#endif
uv_mutex_init_checked(&m_submitBlockDataLock); uv_mutex_init_checked(&m_submitBlockDataLock);
m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath, m_params->m_localStats); m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath, m_params->m_localStats);
@@ -128,12 +135,16 @@ p2pool::p2pool(int argc, char* argv[])
m_params->m_p2pAddresses = buf; m_params->m_p2pAddresses = buf;
} }
#ifdef WITH_RANDOMX
if (m_params->m_disableRandomX) { if (m_params->m_disableRandomX) {
m_hasher = new RandomX_Hasher_RPC(this); m_hasher = new RandomX_Hasher_RPC(this);
} }
else { else {
m_hasher = new RandomX_Hasher(this); m_hasher = new RandomX_Hasher(this);
} }
#else
m_hasher = new RandomX_Hasher_RPC(this);
#endif
m_blockTemplate = new BlockTemplate(this); m_blockTemplate = new BlockTemplate(this);
m_mempool = new Mempool(); m_mempool = new Mempool();
@@ -143,7 +154,11 @@ p2pool::p2pool(int argc, char* argv[])
p2pool::~p2pool() p2pool::~p2pool()
{ {
uv_rwlock_destroy(&m_mainchainLock); uv_rwlock_destroy(&m_mainchainLock);
uv_rwlock_destroy(&m_minerDataLock);
uv_mutex_destroy(&m_foundBlocksLock); uv_mutex_destroy(&m_foundBlocksLock);
#ifdef WITH_RANDOMX
uv_mutex_destroy(&m_minerLock);
#endif
uv_mutex_destroy(&m_submitBlockDataLock); uv_mutex_destroy(&m_submitBlockDataLock);
delete m_api; delete m_api;
@@ -181,6 +196,17 @@ bool p2pool::get_seed(uint64_t height, hash& seed) const
return true; return true;
} }
#ifdef WITH_RANDOMX
void p2pool::print_miner_status()
{
MutexLock lock(m_minerLock);
if (m_miner) {
m_miner->print_status();
}
}
#endif
void p2pool::handle_tx(TxMempoolData& tx) void p2pool::handle_tx(TxMempoolData& tx)
{ {
if (!tx.weight || !tx.fee) { if (!tx.weight || !tx.fee) {
@@ -197,10 +223,10 @@ void p2pool::handle_tx(TxMempoolData& tx)
", fee = " << log::Gray() << static_cast<double>(tx.fee) / 1e6 << " um"); ", fee = " << log::Gray() << static_cast<double>(tx.fee) / 1e6 << " um");
#if TEST_MEMPOOL_PICKING_ALGORITHM #if TEST_MEMPOOL_PICKING_ALGORITHM
m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet); m_blockTemplate->update(miner_data(), *m_mempool, &m_params->m_wallet);
#endif #endif
m_zmqLastActive = time(nullptr); m_zmqLastActive = seconds_since_epoch();
} }
void p2pool::handle_miner_data(MinerData& data) void p2pool::handle_miner_data(MinerData& data)
@@ -232,8 +258,11 @@ void p2pool::handle_miner_data(MinerData& data)
} }
data.tx_backlog.clear(); data.tx_backlog.clear();
data.time_received = std::chrono::system_clock::now(); data.time_received = std::chrono::high_resolution_clock::now();
m_minerData = data; {
WriteLock lock(m_minerDataLock);
m_minerData = data;
}
m_updateSeed = true; m_updateSeed = true;
update_median_timestamp(); update_median_timestamp();
@@ -257,7 +286,7 @@ void p2pool::handle_miner_data(MinerData& data)
update_block_template(); update_block_template();
} }
m_zmqLastActive = time(nullptr); m_zmqLastActive = seconds_since_epoch();
if (m_serversStarted.load()) { if (m_serversStarted.load()) {
std::vector<uint64_t> missing_heights; std::vector<uint64_t> missing_heights;
@@ -361,7 +390,7 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
api_update_network_stats(); api_update_network_stats();
m_zmqLastActive = time(nullptr); m_zmqLastActive = seconds_since_epoch();
} }
void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce) void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
@@ -543,11 +572,13 @@ void p2pool::update_block_template_async()
void p2pool::update_block_template() void p2pool::update_block_template()
{ {
MinerData data = miner_data();
if (m_updateSeed) { if (m_updateSeed) {
m_hasher->set_seed_async(m_minerData.seed_hash); m_hasher->set_seed_async(data.seed_hash);
m_updateSeed = false; m_updateSeed = false;
} }
m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet); m_blockTemplate->update(data, *m_mempool, &m_params->m_wallet);
stratum_on_block(); stratum_on_block();
api_update_pool_stats(); api_update_pool_stats();
} }
@@ -590,33 +621,37 @@ void p2pool::download_block_headers(uint64_t current_height)
}); });
} }
const uint64_t start_height = (current_height > BLOCK_HEADERS_REQUIRED) ? (current_height - BLOCK_HEADERS_REQUIRED) : 0;
s.m_pos = 0; s.m_pos = 0;
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_headers_range\",\"params\":{\"start_height\":" << current_height - BLOCK_HEADERS_REQUIRED << ",\"end_height\":" << current_height - 1 << "}}\0"; s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_headers_range\",\"params\":{\"start_height\":" << start_height << ",\"end_height\":" << current_height - 1 << "}}\0";
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf, JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
[this, current_height](const char* data, size_t size) [this, start_height, current_height](const char* data, size_t size)
{ {
if (parse_block_headers_range(data, size) == BLOCK_HEADERS_REQUIRED) { if (parse_block_headers_range(data, size) == current_height - start_height) {
update_median_timestamp(); update_median_timestamp();
if (m_serversStarted.exchange(1) == 0) { if (m_serversStarted.exchange(1) == 0) {
m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this); m_ZMQReader = new ZMQReader(m_params->m_host.c_str(), m_params->m_zmqPort, this);
m_stratumServer = new StratumServer(this); m_stratumServer = new StratumServer(this);
m_p2pServer = new P2PServer(this); m_p2pServer = new P2PServer(this);
#ifdef WITH_RANDOMX
if (m_params->m_minerThreads) { if (m_params->m_minerThreads) {
start_mining(m_params->m_minerThreads); start_mining(m_params->m_minerThreads);
} }
#endif
api_update_network_stats(); api_update_network_stats();
} }
} }
else { else {
LOGERR(1, "fatal error: couldn't download block headers for heights " << current_height - BLOCK_HEADERS_REQUIRED << " - " << current_height - 1); LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1);
panic(); panic();
} }
}, },
[current_height](const char* data, size_t size) [start_height, current_height](const char* data, size_t size)
{ {
if (size > 0) { if (size > 0) {
LOGERR(1, "fatal error: couldn't download block headers for heights " << current_height - BLOCK_HEADERS_REQUIRED << " - " << current_height - 1 << ", error " << log::const_buf(data, size)); LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1 << ", error " << log::const_buf(data, size));
panic(); panic();
} }
}); });
@@ -658,6 +693,7 @@ void p2pool::update_median_timestamp()
uint64_t timestamps[TIMESTAMP_WINDOW]; uint64_t timestamps[TIMESTAMP_WINDOW];
if (!get_timestamps(timestamps)) if (!get_timestamps(timestamps))
{ {
WriteLock lock(m_minerDataLock);
m_minerData.median_timestamp = 0; m_minerData.median_timestamp = 0;
return; return;
} }
@@ -665,15 +701,25 @@ void p2pool::update_median_timestamp()
std::sort(timestamps, timestamps + TIMESTAMP_WINDOW); std::sort(timestamps, timestamps + TIMESTAMP_WINDOW);
// Shift it +1 block compared to Monero's code because we don't have the latest block yet when we receive new miner data // Shift it +1 block compared to Monero's code because we don't have the latest block yet when we receive new miner data
m_minerData.median_timestamp = (timestamps[TIMESTAMP_WINDOW / 2] + timestamps[TIMESTAMP_WINDOW / 2 + 1]) / 2; const uint64_t ts = (timestamps[TIMESTAMP_WINDOW / 2] + timestamps[TIMESTAMP_WINDOW / 2 + 1]) / 2;
LOGINFO(4, "median timestamp updated to " << log::Gray() << m_minerData.median_timestamp); LOGINFO(4, "median timestamp updated to " << log::Gray() << ts);
WriteLock lock(m_minerDataLock);
m_minerData.median_timestamp = ts;
} }
void p2pool::stratum_on_block() void p2pool::stratum_on_block()
{ {
if (m_miner) { #ifdef WITH_RANDOMX
m_miner->on_block(*m_blockTemplate); {
MutexLock lock(m_minerLock);
if (m_miner) {
m_miner->on_block(*m_blockTemplate);
}
} }
#endif
if (m_stratumServer) { if (m_stratumServer) {
m_stratumServer->on_block(*m_blockTemplate); m_stratumServer->on_block(*m_blockTemplate);
} }
@@ -1011,10 +1057,16 @@ void p2pool::api_update_network_stats()
return; return;
} }
hash prev_id;
{
ReadLock lock(m_minerDataLock);
prev_id = m_minerData.prev_id;
}
ChainMain mainnet_tip; ChainMain mainnet_tip;
{ {
ReadLock lock(m_mainchainLock); ReadLock lock(m_mainchainLock);
mainnet_tip = m_mainchainByHash[m_minerData.prev_id]; mainnet_tip = m_mainchainByHash[prev_id];
} }
m_api->set(p2pool_api::Category::NETWORK, "stats", m_api->set(p2pool_api::Category::NETWORK, "stats",
@@ -1076,10 +1128,16 @@ void p2pool::api_update_stats_mod()
return; return;
} }
hash prev_id;
{
ReadLock lock(m_minerDataLock);
prev_id = m_minerData.prev_id;
}
ChainMain mainnet_tip; ChainMain mainnet_tip;
{ {
ReadLock lock(m_mainchainLock); ReadLock lock(m_mainchainLock);
mainnet_tip = m_mainchainByHash[m_minerData.prev_id]; mainnet_tip = m_mainchainByHash[prev_id];
} }
time_t last_block_found_time = 0; time_t last_block_found_time = 0;
@@ -1090,10 +1148,11 @@ void p2pool::api_update_stats_mod()
{ {
MutexLock lock(m_foundBlocksLock); MutexLock lock(m_foundBlocksLock);
if (!m_foundBlocks.empty()) { if (!m_foundBlocks.empty()) {
last_block_found_time = m_foundBlocks.back().timestamp; const FoundBlock& b = m_foundBlocks.back();
last_block_found_height = m_foundBlocks.back().height; last_block_found_time = b.timestamp;
last_block_found_hash = m_foundBlocks.back().id; last_block_found_height = b.height;
last_block_total_hashes = m_foundBlocks.back().total_hashes; last_block_found_hash = b.id;
last_block_total_hashes = b.total_hashes;
} }
} }
@@ -1219,20 +1278,25 @@ bool p2pool::get_difficulty_at_height(uint64_t height, difficulty_type& diff)
return true; return true;
} }
#ifdef WITH_RANDOMX
void p2pool::start_mining(uint32_t threads) void p2pool::start_mining(uint32_t threads)
{ {
stop_mining(); stop_mining();
MutexLock lock(m_minerLock);
m_miner = new Miner(this, threads); m_miner = new Miner(this, threads);
} }
void p2pool::stop_mining() void p2pool::stop_mining()
{ {
Miner* miner = m_miner; MutexLock lock(m_minerLock);
if (miner) {
if (m_miner) {
delete m_miner;
m_miner = nullptr; m_miner = nullptr;
delete miner;
} }
} }
#endif
static void on_signal(uv_signal_t* handle, int signum) static void on_signal(uv_signal_t* handle, int signum)
{ {
@@ -1276,6 +1340,10 @@ static bool init_uv_threadpool()
static bool init_signals(p2pool* pool) static bool init_signals(p2pool* pool)
{ {
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
constexpr int signal_names[] = { constexpr int signal_names[] = {
SIGHUP, SIGHUP,
SIGINT, SIGINT,
@@ -1342,7 +1410,9 @@ int p2pool::run()
bkg_jobs_tracker.wait(); bkg_jobs_tracker.wait();
#ifdef WITH_RANDOMX
delete m_miner; delete m_miner;
#endif
delete m_stratumServer; delete m_stratumServer;
delete m_p2pServer; delete m_p2pServer;
+24 -8
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -48,7 +48,12 @@ public:
const Params& params() const { return *m_params; } const Params& params() const { return *m_params; }
BlockTemplate& block_template() { return *m_blockTemplate; } BlockTemplate& block_template() { return *m_blockTemplate; }
SideChain& side_chain() { return *m_sideChain; } SideChain& side_chain() { return *m_sideChain; }
const MinerData& miner_data() const { return m_minerData; }
FORCEINLINE MinerData miner_data() const
{
ReadLock lock(m_minerDataLock);
return m_minerData;
}
p2pool_api* api() const { return m_api; } p2pool_api* api() const { return m_api; }
@@ -59,7 +64,10 @@ public:
StratumServer* stratum_server() const { return m_stratumServer; } StratumServer* stratum_server() const { return m_stratumServer; }
P2PServer* p2p_server() const { return m_p2pServer; } P2PServer* p2p_server() const { return m_p2pServer; }
Miner* miner() const { return m_miner; }
#ifdef WITH_RANDOMX
void print_miner_status();
#endif
virtual void handle_tx(TxMempoolData& tx) override; virtual void handle_tx(TxMempoolData& tx) override;
virtual void handle_miner_data(MinerData& data) override; virtual void handle_miner_data(MinerData& data) override;
@@ -80,11 +88,13 @@ public:
bool get_difficulty_at_height(uint64_t height, difficulty_type& diff); bool get_difficulty_at_height(uint64_t height, difficulty_type& diff);
#ifdef WITH_RANDOMX
void start_mining(uint32_t threads); void start_mining(uint32_t threads);
void stop_mining(); void stop_mining();
#endif
time_t zmq_last_active() const { return m_zmqLastActive; } uint64_t zmq_last_active() const { return m_zmqLastActive; }
time_t start_time() const { return m_startTime; } uint64_t start_time() const { return m_startTime; }
private: private:
p2pool(const p2pool&) = delete; p2pool(const p2pool&) = delete;
@@ -104,7 +114,6 @@ private:
SideChain* m_sideChain; SideChain* m_sideChain;
RandomX_Hasher_Base* m_hasher; RandomX_Hasher_Base* m_hasher;
BlockTemplate* m_blockTemplate; BlockTemplate* m_blockTemplate;
MinerData m_minerData;
bool m_updateSeed; bool m_updateSeed;
Mempool* m_mempool; Mempool* m_mempool;
@@ -112,6 +121,9 @@ private:
std::map<uint64_t, ChainMain> m_mainchainByHeight; std::map<uint64_t, ChainMain> m_mainchainByHeight;
unordered_map<hash, ChainMain> m_mainchainByHash; unordered_map<hash, ChainMain> m_mainchainByHash;
mutable uv_rwlock_t m_minerDataLock;
MinerData m_minerData;
enum { TIMESTAMP_WINDOW = 60 }; enum { TIMESTAMP_WINDOW = 60 };
bool get_timestamps(uint64_t (&timestamps)[TIMESTAMP_WINDOW]) const; bool get_timestamps(uint64_t (&timestamps)[TIMESTAMP_WINDOW]) const;
void update_median_timestamp(); void update_median_timestamp();
@@ -160,7 +172,11 @@ private:
std::atomic<uint32_t> m_serversStarted{ 0 }; std::atomic<uint32_t> m_serversStarted{ 0 };
StratumServer* m_stratumServer = nullptr; StratumServer* m_stratumServer = nullptr;
P2PServer* m_p2pServer = nullptr; P2PServer* m_p2pServer = nullptr;
#ifdef WITH_RANDOMX
uv_mutex_t m_minerLock;
Miner* m_miner = nullptr; Miner* m_miner = nullptr;
#endif
ConsoleCommands* m_consoleCommands; ConsoleCommands* m_consoleCommands;
@@ -179,8 +195,8 @@ private:
uv_async_t m_blockTemplateAsync; uv_async_t m_blockTemplateAsync;
uv_async_t m_stopAsync; uv_async_t m_stopAsync;
time_t m_zmqLastActive; std::atomic<uint64_t> m_zmqLastActive;
time_t m_startTime; uint64_t m_startTime;
ZMQReader* m_ZMQReader = nullptr; ZMQReader* m_ZMQReader = nullptr;
}; };
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+5 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -39,7 +39,11 @@ struct Params
std::string m_apiPath; std::string m_apiPath;
bool m_localStats = false; bool m_localStats = false;
bool m_blockCache = true; bool m_blockCache = true;
#ifdef WITH_RANDOMX
bool m_disableRandomX = false; bool m_disableRandomX = false;
#else
bool m_disableRandomX = true;
#endif
uint32_t m_maxOutgoingPeers = 10; uint32_t m_maxOutgoingPeers = 10;
uint32_t m_maxIncomingPeers = 1000; uint32_t m_maxIncomingPeers = 1000;
uint32_t m_minerThreads = 0; uint32_t m_minerThreads = 0;
+20 -6
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -53,7 +53,7 @@ PoolBlock::PoolBlock()
, m_invalid(false) , m_invalid(false)
, m_broadcasted(false) , m_broadcasted(false)
, m_wantBroadcast(false) , m_wantBroadcast(false)
, m_localTimestamp(time(nullptr)) , m_localTimestamp(seconds_since_epoch())
{ {
uv_mutex_init_checked(&m_lock); uv_mutex_init_checked(&m_lock);
@@ -115,7 +115,7 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_broadcasted = b.m_broadcasted; m_broadcasted = b.m_broadcasted;
m_wantBroadcast = b.m_wantBroadcast; m_wantBroadcast = b.m_wantBroadcast;
m_localTimestamp = time(nullptr); m_localTimestamp = seconds_since_epoch();
if (lock_result == 0) { if (lock_result == 0) {
uv_mutex_unlock(&b.m_lock); uv_mutex_unlock(&b.m_lock);
@@ -157,8 +157,12 @@ void PoolBlock::serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, c
for (TxOutput& output : m_outputs) { for (TxOutput& output : m_outputs) {
writeVarint(output.m_reward, m_mainChainData); writeVarint(output.m_reward, m_mainChainData);
m_mainChainData.push_back(TXOUT_TO_KEY); m_mainChainData.push_back(output.m_txType);
m_mainChainData.insert(m_mainChainData.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE); m_mainChainData.insert(m_mainChainData.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
m_mainChainData.push_back(output.m_viewTag);
}
} }
m_mainChainOutputsBlobSize = static_cast<int>(m_mainChainData.size()) - m_mainChainOutputsOffset; m_mainChainOutputsBlobSize = static_cast<int>(m_mainChainData.size()) - m_mainChainOutputsOffset;
@@ -301,9 +305,19 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
uint64_t PoolBlock::get_payout(const Wallet& w) const uint64_t PoolBlock::get_payout(const Wallet& w) const
{ {
for (size_t i = 0, n = m_outputs.size(); i < n; ++i) { for (size_t i = 0, n = m_outputs.size(); i < n; ++i) {
const TxOutput& out = m_outputs[i];
hash eph_public_key; hash eph_public_key;
if ((w.get_eph_public_key(m_txkeySec, i, eph_public_key)) && (eph_public_key == m_outputs[i].m_ephPublicKey)) {
return m_outputs[i].m_reward; if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
if (w.get_eph_public_key_with_view_tag(m_txkeySec, i, eph_public_key, out.m_viewTag) && (eph_public_key == out.m_ephPublicKey)) {
return out.m_reward;
}
}
else {
uint8_t view_tag;
if (w.get_eph_public_key(m_txkeySec, i, eph_public_key, view_tag) && (eph_public_key == out.m_ephPublicKey)) {
return out.m_reward;
}
} }
} }
+10 -4
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -81,11 +81,13 @@ struct PoolBlock
struct TxOutput struct TxOutput
{ {
FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey() {} FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey(), m_txType(0), m_viewTag(0) {}
FORCEINLINE TxOutput(uint64_t r, const hash& k) : m_reward(r), m_ephPublicKey(k) {} FORCEINLINE TxOutput(uint64_t r, const hash& k, uint8_t tx_type, uint8_t view_tag) : m_reward(r), m_ephPublicKey(k), m_txType(tx_type), m_viewTag(view_tag) {}
uint64_t m_reward; uint64_t m_reward;
hash m_ephPublicKey; hash m_ephPublicKey;
uint8_t m_txType;
uint8_t m_viewTag;
}; };
std::vector<TxOutput> m_outputs; std::vector<TxOutput> m_outputs;
@@ -130,7 +132,7 @@ struct PoolBlock
bool m_broadcasted; bool m_broadcasted;
bool m_wantBroadcast; bool m_wantBroadcast;
time_t m_localTimestamp; uint64_t m_localTimestamp;
void serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash); void serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash);
void serialize_sidechain_data(); void serialize_sidechain_data();
@@ -139,6 +141,10 @@ struct PoolBlock
bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash); bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash);
uint64_t get_payout(const Wallet& w) const; uint64_t get_payout(const Wallet& w) const;
// Both tx types are allowed by Monero consensus during v15 because it needs to process pre-fork mempool transactions,
// but P2Pool can switch to using only TXOUT_TO_TAGGED_KEY for miner payouts starting from v15
FORCEINLINE uint8_t get_tx_type() const { return (m_majorVersion < HARDFORK_VIEW_TAGS_VERSION) ? TXOUT_TO_KEY : TXOUT_TO_TAGGED_KEY; }
}; };
} // namespace p2pool } // namespace p2pool
+14 -28
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -46,29 +46,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
#define READ_BYTE(x) do { if (!read_byte(x)) return __LINE__; } while (0) #define READ_BYTE(x) do { if (!read_byte(x)) return __LINE__; } while (0)
#define EXPECT_BYTE(value) do { uint8_t tmp; READ_BYTE(tmp); if (tmp != (value)) return __LINE__; } while (0) #define EXPECT_BYTE(value) do { uint8_t tmp; READ_BYTE(tmp); if (tmp != (value)) return __LINE__; } while (0)
auto read_varint = [&data, data_end](auto& b) -> bool #define READ_VARINT(x) do { data = readVarint(data, data_end, x); if (!data) return __LINE__; } while(0)
{
uint64_t result = 0;
int k = 0;
while (data < data_end) {
if (k >= static_cast<int>(sizeof(b)) * 8) {
return false;
}
const uint64_t cur_byte = *(data++);
result |= (cur_byte & 0x7F) << k;
k += 7;
if ((cur_byte & 0x80) == 0) {
b = result;
return true;
}
}
return false;
};
#define READ_VARINT(x) do { if (!read_varint(x)) return __LINE__; } while(0)
auto read_buf = [&data, data_end](void* buf, size_t size) -> bool auto read_buf = [&data, data_end](void* buf, size_t size) -> bool
{ {
@@ -121,7 +99,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
if (num_outputs > 0) { if (num_outputs > 0) {
// Outputs are in the buffer, just read them // Outputs are in the buffer, just read them
// Each output is at least 34 bytes, exit early if there's not enough data left // Each output is at least 34 bytes, exit early if there's not enough data left
// 1 byte for reward, 1 byte for TXOUT_TO_KEY, 32 bytes for eph_pub_key // 1 byte for reward, 1 byte for tx_type, 32 bytes for eph_pub_key
constexpr uint64_t MIN_OUTPUT_SIZE = 34; constexpr uint64_t MIN_OUTPUT_SIZE = 34;
if (num_outputs > std::numeric_limits<uint64_t>::max() / MIN_OUTPUT_SIZE) return __LINE__; if (num_outputs > std::numeric_limits<uint64_t>::max() / MIN_OUTPUT_SIZE) return __LINE__;
@@ -130,15 +108,23 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
m_outputs.clear(); m_outputs.clear();
m_outputs.reserve(num_outputs); m_outputs.reserve(num_outputs);
const uint8_t expected_tx_type = get_tx_type();
for (uint64_t i = 0; i < num_outputs; ++i) { for (uint64_t i = 0; i < num_outputs; ++i) {
TxOutput t; TxOutput t;
READ_VARINT(t.m_reward); READ_VARINT(t.m_reward);
total_reward += t.m_reward; total_reward += t.m_reward;
EXPECT_BYTE(TXOUT_TO_KEY); EXPECT_BYTE(expected_tx_type);
t.m_txType = expected_tx_type;
READ_BUF(t.m_ephPublicKey.h, HASH_SIZE); READ_BUF(t.m_ephPublicKey.h, HASH_SIZE);
if (expected_tx_type == TXOUT_TO_TAGGED_KEY) {
READ_BYTE(t.m_viewTag);
}
m_outputs.emplace_back(std::move(t)); m_outputs.emplace_back(std::move(t));
} }
@@ -187,7 +173,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
READ_VARINT(m_extraNonceSize); READ_VARINT(m_extraNonceSize);
// Sanity check // Sanity check
if ((m_extraNonceSize < EXTRA_NONCE_SIZE) || (m_extraNonceSize > EXTRA_NONCE_SIZE + 10)) return __LINE__; if ((m_extraNonceSize < EXTRA_NONCE_SIZE) || (m_extraNonceSize > EXTRA_NONCE_MAX_SIZE)) return __LINE__;
const int extra_nonce_offset = static_cast<int>((data - data_begin) + outputs_blob_size_diff); const int extra_nonce_offset = static_cast<int>((data - data_begin) + outputs_blob_size_diff);
READ_BUF(&m_extraNonce, EXTRA_NONCE_SIZE); READ_BUF(&m_extraNonce, EXTRA_NONCE_SIZE);
@@ -350,7 +336,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
m_broadcasted = false; m_broadcasted = false;
m_wantBroadcast = false; m_wantBroadcast = false;
m_localTimestamp = time(nullptr); m_localTimestamp = seconds_since_epoch();
return 0; return 0;
} }
+14 -2
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -19,9 +19,11 @@
#include "pow_hash.h" #include "pow_hash.h"
#include "p2pool.h" #include "p2pool.h"
#include "params.h" #include "params.h"
#ifdef WITH_RANDOMX
#include "randomx.h" #include "randomx.h"
#include "configuration.h" #include "configuration.h"
#include "virtual_machine.hpp" #include "virtual_machine.hpp"
#endif
#include "json_rpc_request.h" #include "json_rpc_request.h"
#include "json_parsers.h" #include "json_parsers.h"
#include <rapidjson/document.h> #include <rapidjson/document.h>
@@ -31,6 +33,7 @@ static constexpr char log_category_prefix[] = "RandomX_Hasher ";
namespace p2pool { namespace p2pool {
#ifdef WITH_RANDOMX
RandomX_Hasher::RandomX_Hasher(p2pool* pool) RandomX_Hasher::RandomX_Hasher(p2pool* pool)
: m_pool(pool) : m_pool(pool)
, m_cache{} , m_cache{}
@@ -38,6 +41,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
, m_seed{} , m_seed{}
, m_index(0) , m_index(0)
, m_seedCounter(0) , m_seedCounter(0)
, m_oldSeedCounter(0)
{ {
uint64_t memory_allocated = 0; uint64_t memory_allocated = 0;
@@ -220,6 +224,11 @@ void RandomX_Hasher::set_seed(const hash& seed)
numThreads /= 2; numThreads /= 2;
} }
// wait for set_old_seed() before initializing dataset
while (m_oldSeedCounter.load() == 0) {
std::this_thread::yield();
}
LOGINFO(1, log::LightCyan() << "running " << numThreads << " threads to update dataset"); LOGINFO(1, log::LightCyan() << "running " << numThreads << " threads to update dataset");
ReadLock lock2(m_cacheLock); ReadLock lock2(m_cacheLock);
@@ -271,7 +280,7 @@ void RandomX_Hasher::set_old_seed(const hash& seed)
{ {
// set_seed() must go first, wait for it // set_seed() must go first, wait for it
while (m_seedCounter.load() == 0) { while (m_seedCounter.load() == 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::yield();
} }
LOGINFO(1, "old seed " << log::LightBlue() << seed); LOGINFO(1, "old seed " << log::LightBlue() << seed);
@@ -279,6 +288,8 @@ void RandomX_Hasher::set_old_seed(const hash& seed)
{ {
WriteLock lock(m_cacheLock); WriteLock lock(m_cacheLock);
m_oldSeedCounter.fetch_add(1);
const uint32_t old_index = m_index ^ 1; const uint32_t old_index = m_index ^ 1;
m_seed[old_index] = seed; m_seed[old_index] = seed;
@@ -357,6 +368,7 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*
return false; return false;
} }
#endif
RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool) RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
: m_pool(pool) : m_pool(pool)
+4 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -43,6 +43,7 @@ public:
virtual bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) = 0; virtual bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) = 0;
}; };
#ifdef WITH_RANDOMX
class RandomX_Hasher : public RandomX_Hasher_Base class RandomX_Hasher : public RandomX_Hasher_Base
{ {
public: public:
@@ -89,7 +90,9 @@ private:
uint32_t m_index; uint32_t m_index;
std::atomic<uint32_t> m_seedCounter; std::atomic<uint32_t> m_seedCounter;
std::atomic<uint32_t> m_oldSeedCounter;
}; };
#endif
class RandomX_Hasher_RPC : public RandomX_Hasher_Base class RandomX_Hasher_RPC : public RandomX_Hasher_Base
{ {
+144 -72
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -21,10 +21,12 @@
#include "pool_block.h" #include "pool_block.h"
#include "wallet.h" #include "wallet.h"
#include "block_template.h" #include "block_template.h"
#ifdef WITH_RANDOMX
#include "randomx.h" #include "randomx.h"
#include "dataset.hpp" #include "dataset.hpp"
#include "configuration.h" #include "configuration.h"
#include "intrin_portable.h" #include "intrin_portable.h"
#endif
#include "keccak.h" #include "keccak.h"
#include "p2p_server.h" #include "p2p_server.h"
#include "stratum_server.h" #include "stratum_server.h"
@@ -58,7 +60,7 @@ static constexpr uint8_t mini_consensus_id[HASH_SIZE] = { 57,130,201,26,149,174,
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_networkType(type)
, m_chainTip(nullptr) , m_chainTip{ nullptr }
, m_poolName(pool_name ? pool_name : "default") , m_poolName(pool_name ? pool_name : "default")
, m_targetBlockTime(10) , m_targetBlockTime(10)
, m_minDifficulty(MIN_DIFFICULTY, 0) , m_minDifficulty(MIN_DIFFICULTY, 0)
@@ -108,6 +110,7 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
m_consensusId.assign(mini_consensus_id, mini_consensus_id + HASH_SIZE); m_consensusId.assign(mini_consensus_id, mini_consensus_id + HASH_SIZE);
} }
else { else {
#ifdef WITH_RANDOMX
const randomx_flags flags = randomx_get_flags(); const randomx_flags flags = randomx_get_flags();
randomx_cache* cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES); randomx_cache* cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
if (!cache) { if (!cache) {
@@ -142,6 +145,10 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
keccak(reinterpret_cast<uint8_t*>(scratchpad), static_cast<int>(scratchpad_size * sizeof(rx_vec_i128)), id.h, HASH_SIZE); 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
LOGERR(1, "Can't calculate consensus ID without RandomX library");
panic();
#endif
} }
s.m_pos = 0; s.m_pos = 0;
@@ -149,7 +156,8 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
// Hide most consensus ID bytes, we only want it on screen to show that we're on the right sidechain // Hide most consensus ID bytes, we only want it on screen to show that we're on the right sidechain
memset(buf + 8, '*', HASH_SIZE * 2 - 16); memset(buf + 8, '*', HASH_SIZE * 2 - 16);
LOGINFO(1, "consensus ID = " << log::LightCyan() << static_cast<char*>(buf)); m_consensusIdDisplayStr.assign(buf);
LOGINFO(1, "consensus ID = " << log::LightCyan() << m_consensusIdDisplayStr.c_str());
} }
SideChain::~SideChain() SideChain::~SideChain()
@@ -169,7 +177,9 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
block.m_txkeySec = txkeySec; block.m_txkeySec = txkeySec;
block.m_uncles.clear(); block.m_uncles.clear();
if (!m_chainTip) { const PoolBlock* tip = m_chainTip;
if (!tip) {
block.m_parent = {}; block.m_parent = {};
block.m_sidechainHeight = 0; block.m_sidechainHeight = 0;
block.m_difficulty = m_minDifficulty; block.m_difficulty = m_minDifficulty;
@@ -179,8 +189,8 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
return; return;
} }
block.m_parent = m_chainTip->m_sidechainId; block.m_parent = tip->m_sidechainId;
block.m_sidechainHeight = m_chainTip->m_sidechainHeight + 1; block.m_sidechainHeight = tip->m_sidechainHeight + 1;
// Collect uncles from 3 previous block heights // Collect uncles from 3 previous block heights
@@ -188,15 +198,15 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
std::vector<hash> mined_blocks; std::vector<hash> mined_blocks;
mined_blocks.reserve(UNCLE_BLOCK_DEPTH * 2 + 1); mined_blocks.reserve(UNCLE_BLOCK_DEPTH * 2 + 1);
PoolBlock* tmp = m_chainTip; const PoolBlock* tmp = tip;
for (uint64_t i = 0, n = std::min<uint64_t>(UNCLE_BLOCK_DEPTH, m_chainTip->m_sidechainHeight + 1); tmp && (i < n); ++i) { for (uint64_t i = 0, n = std::min<uint64_t>(UNCLE_BLOCK_DEPTH, tip->m_sidechainHeight + 1); tmp && (i < n); ++i) {
mined_blocks.push_back(tmp->m_sidechainId); mined_blocks.push_back(tmp->m_sidechainId);
mined_blocks.insert(mined_blocks.end(), tmp->m_uncles.begin(), tmp->m_uncles.end()); mined_blocks.insert(mined_blocks.end(), tmp->m_uncles.begin(), tmp->m_uncles.end());
tmp = get_parent(tmp); tmp = get_parent(tmp);
} }
for (uint64_t i = 0, n = std::min<uint64_t>(UNCLE_BLOCK_DEPTH, m_chainTip->m_sidechainHeight + 1); i < n; ++i) { for (uint64_t i = 0, n = std::min<uint64_t>(UNCLE_BLOCK_DEPTH, tip->m_sidechainHeight + 1); i < n; ++i) {
for (PoolBlock* uncle : m_blocksByHeight[m_chainTip->m_sidechainHeight - i]) { for (PoolBlock* uncle : m_blocksByHeight[tip->m_sidechainHeight - i]) {
// Only add verified and valid blocks // Only add verified and valid blocks
if (!uncle || !uncle->m_verified || uncle->m_invalid) { if (!uncle || !uncle->m_verified || uncle->m_invalid) {
continue; continue;
@@ -210,7 +220,7 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
// Only add it if it's on the same chain // Only add it if it's on the same chain
bool same_chain = false; bool same_chain = false;
do { do {
tmp = m_chainTip; tmp = tip;
while (tmp->m_sidechainHeight > uncle->m_sidechainHeight) { while (tmp->m_sidechainHeight > uncle->m_sidechainHeight) {
tmp = get_parent(tmp); tmp = get_parent(tmp);
if (!tmp) { if (!tmp) {
@@ -254,7 +264,7 @@ void SideChain::fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txk
} }
block.m_difficulty = m_curDifficulty; block.m_difficulty = m_curDifficulty;
block.m_cumulativeDifficulty = m_chainTip->m_cumulativeDifficulty + block.m_difficulty; block.m_cumulativeDifficulty = tip->m_cumulativeDifficulty + block.m_difficulty;
for (const hash& uncle_id : block.m_uncles) { for (const hash& uncle_id : block.m_uncles) {
auto it = m_blocksById.find(uncle_id); auto it = m_blocksById.find(uncle_id);
@@ -359,6 +369,7 @@ bool SideChain::block_seen(const PoolBlock& block)
{ {
// Check if it's some old block // Check if it's some old block
const PoolBlock* tip = m_chainTip; const PoolBlock* tip = m_chainTip;
if (tip && tip->m_sidechainHeight > block.m_sidechainHeight + m_chainWindowSize * 2 && if (tip && tip->m_sidechainHeight > block.m_sidechainHeight + m_chainWindowSize * 2 &&
block.m_cumulativeDifficulty < tip->m_cumulativeDifficulty) { block.m_cumulativeDifficulty < tip->m_cumulativeDifficulty) {
return true; return true;
@@ -398,7 +409,9 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
difficulty_type diff2 = block.m_difficulty; difficulty_type diff2 = block.m_difficulty;
diff2 += block.m_difficulty; diff2 += block.m_difficulty;
for (PoolBlock* tmp = m_chainTip; tmp && (tmp->m_sidechainHeight + m_chainWindowSize > m_chainTip->m_sidechainHeight); tmp = get_parent(tmp)) { const PoolBlock* tip = m_chainTip;
for (const PoolBlock* tmp = tip; tmp && (tmp->m_sidechainHeight + m_chainWindowSize > tip->m_sidechainHeight); tmp = get_parent(tmp)) {
if (diff2 >= tmp->m_difficulty) { if (diff2 >= tmp->m_difficulty) {
too_low_diff = false; too_low_diff = false;
break; break;
@@ -441,7 +454,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
const MinerData& miner_data = m_pool->miner_data(); MinerData miner_data = m_pool->miner_data();
if ((block.m_prevId == miner_data.prev_id) && miner_data.difficulty.check_pow(pow_hash)) { if ((block.m_prevId == miner_data.prev_id) && miner_data.difficulty.check_pow(pow_hash)) {
LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero network, submitting it"); LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero network, submitting it");
m_pool->submit_block_async(block.m_mainChainData); m_pool->submit_block_async(block.m_mainChainData);
@@ -506,11 +519,6 @@ void SideChain::add_block(const PoolBlock& block)
", verified = " << (block.m_verified ? 1 : 0) ", verified = " << (block.m_verified ? 1 : 0)
); );
// Save it for faster syncing on the next p2pool start
if (p2pServer()) {
p2pServer()->store_in_cache(block);
}
PoolBlock* new_block = new PoolBlock(block); PoolBlock* new_block = new PoolBlock(block);
MutexLock lock(m_sidechainLock); MutexLock lock(m_sidechainLock);
@@ -533,6 +541,11 @@ void SideChain::add_block(const PoolBlock& block)
if (new_block->m_verified) { if (new_block->m_verified) {
if (!new_block->m_invalid) { if (!new_block->m_invalid) {
update_chain_tip(new_block); update_chain_tip(new_block);
// Save it for faster syncing on the next p2pool start
if (P2PServer* server = p2pServer()) {
server->store_in_cache(*new_block);
}
} }
} }
else { else {
@@ -565,7 +578,7 @@ bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob)
{ {
MutexLock lock(m_sidechainLock); MutexLock lock(m_sidechainLock);
PoolBlock* block = nullptr; const PoolBlock* block = nullptr;
// Empty hash means we return current sidechain tip // Empty hash means we return current sidechain tip
if (id == hash()) { if (id == hash()) {
@@ -600,13 +613,17 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
PoolBlock* b = it->second; PoolBlock* b = it->second;
const size_t n = b->m_outputs.size(); const size_t n = b->m_outputs.size();
blob.reserve(n * 38 + 64); blob.reserve(n * 39 + 64);
writeVarint(n, blob); writeVarint(n, blob);
for (const PoolBlock::TxOutput& output : b->m_outputs) { for (const PoolBlock::TxOutput& output : b->m_outputs) {
writeVarint(output.m_reward, blob); writeVarint(output.m_reward, blob);
blob.emplace_back(TXOUT_TO_KEY); blob.emplace_back(output.m_txType);
blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE); blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
if (output.m_txType == TXOUT_TO_TAGGED_KEY) {
blob.emplace_back(output.m_viewTag);
}
} }
block->m_outputs = b->m_outputs; block->m_outputs = b->m_outputs;
@@ -619,25 +636,32 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
const size_t n = m_tmpShares.size(); const size_t n = m_tmpShares.size();
blob.reserve(n * 38 + 64); blob.reserve(n * 39 + 64);
writeVarint(n, blob); writeVarint(n, blob);
block->m_outputs.clear(); block->m_outputs.clear();
block->m_outputs.reserve(n); block->m_outputs.reserve(n);
const uint8_t tx_type = block->get_tx_type();
hash eph_public_key; hash eph_public_key;
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
writeVarint(m_tmpRewards[i], blob); writeVarint(m_tmpRewards[i], blob);
blob.emplace_back(TXOUT_TO_KEY); blob.emplace_back(tx_type);
if (!m_tmpShares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key)) { uint8_t view_tag;
if (!m_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);
block->m_outputs.emplace_back(m_tmpRewards[i], eph_public_key); if (tx_type == TXOUT_TO_TAGGED_KEY) {
blob.emplace_back(view_tag);
}
block->m_outputs.emplace_back(m_tmpRewards[i], eph_public_key, tx_type, view_tag);
} }
return true; return true;
@@ -653,12 +677,14 @@ void SideChain::print_status()
uint64_t rem; uint64_t rem;
uint64_t pool_hashrate = udiv128(m_curDifficulty.hi, m_curDifficulty.lo, m_targetBlockTime, &rem); uint64_t pool_hashrate = udiv128(m_curDifficulty.hi, m_curDifficulty.lo, m_targetBlockTime, &rem);
const difficulty_type& network_diff = m_pool->miner_data().difficulty; difficulty_type network_diff = m_pool->miner_data().difficulty;
uint64_t network_hashrate = udiv128(network_diff.hi, network_diff.lo, 120, &rem); uint64_t network_hashrate = udiv128(network_diff.hi, network_diff.lo, 120, &rem);
const PoolBlock* tip = m_chainTip;
uint64_t block_depth = 0; uint64_t block_depth = 0;
PoolBlock* cur = m_chainTip; const PoolBlock* cur = tip;
const uint64_t tip_height = m_chainTip ? m_chainTip->m_sidechainHeight : 0; const uint64_t tip_height = tip ? tip->m_sidechainHeight : 0;
uint32_t total_blocks_in_window = 0; uint32_t total_blocks_in_window = 0;
uint32_t total_uncles_in_window = 0; uint32_t total_uncles_in_window = 0;
@@ -707,7 +733,7 @@ void SideChain::print_status()
uint64_t your_reward = 0; uint64_t your_reward = 0;
uint64_t total_reward = 0; uint64_t total_reward = 0;
if (m_chainTip) { if (tip) {
std::sort(blocks_in_window.begin(), blocks_in_window.end()); std::sort(blocks_in_window.begin(), blocks_in_window.end());
for (uint64_t i = 0; (i < m_chainWindowSize) && (i <= tip_height); ++i) { for (uint64_t i = 0; (i < m_chainWindowSize) && (i <= tip_height); ++i) {
for (PoolBlock* block : m_blocksByHeight[tip_height - i]) { for (PoolBlock* block : m_blocksByHeight[tip_height - i]) {
@@ -722,14 +748,24 @@ void SideChain::print_status()
} }
Wallet w = m_pool->params().m_wallet; Wallet w = m_pool->params().m_wallet;
const std::vector<PoolBlock::TxOutput>& outs = m_chainTip->m_outputs;
hash eph_public_key; hash eph_public_key;
for (size_t i = 0, n = outs.size(); i < n; ++i) { for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
if (w.get_eph_public_key(m_chainTip->m_txkeySec, i, eph_public_key) && (outs[i].m_ephPublicKey == eph_public_key)) { const PoolBlock::TxOutput& out = tip->m_outputs[i];
your_reward = outs[i].m_reward; if (!your_reward) {
if (out.m_txType == TXOUT_TO_TAGGED_KEY) {
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
your_reward = out.m_reward;
}
}
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 += outs[i].m_reward; total_reward += out.m_reward;
} }
} }
@@ -738,43 +774,52 @@ void SideChain::print_status()
const uint64_t hashrate_est = total_reward ? udiv128(product[1], product[0], total_reward, &rem) : 0; const uint64_t hashrate_est = total_reward ? udiv128(product[1], product[0], total_reward, &rem) : 0;
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;
uint32_t our_blocks_in_window_total = std::accumulate(our_blocks_in_window.begin(), our_blocks_in_window.end(), decltype(our_blocks_in_window)::value_type(0)); const uint32_t our_blocks_in_window_total = std::accumulate(our_blocks_in_window.begin(), our_blocks_in_window.end(), 0U);
uint32_t our_uncles_in_window_total = std::accumulate(our_uncles_in_window.begin(), our_uncles_in_window.end(), decltype(our_uncles_in_window)::value_type(0)); 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;
our_blocks_in_window_chart.reserve(our_blocks_in_window.size()); if (our_blocks_in_window_total) {
for(const auto& p : our_blocks_in_window){ our_blocks_in_window_chart.reserve(our_blocks_in_window.size() + 32);
our_blocks_in_window_chart += (p > 0 ? (p > 9 ? "+" : std::to_string(p)) : "."); our_blocks_in_window_chart = "\nYour shares position = [";
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 += ']';
} }
std::string our_uncles_in_window_chart; std::string our_uncles_in_window_chart;
our_uncles_in_window_chart.reserve(our_uncles_in_window.size()); if (our_uncles_in_window_total) {
for(const auto& p : our_uncles_in_window){ our_uncles_in_window_chart.reserve(our_uncles_in_window.size() + 32);
our_uncles_in_window_chart += (p > 0 ? (p > 9 ? "+" : std::to_string(p)) : "."); our_uncles_in_window_chart = "\nYour uncles position = [";
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 += ']';
} }
LOGINFO(0, "status" << LOGINFO(0, "status" <<
"\nMain chain height = " << m_pool->block_template().height() << "\nMain chain height = " << m_pool->block_template().height() <<
"\nMain chain hashrate = " << log::Hashrate(network_hashrate) << "\nMain chain hashrate = " << log::Hashrate(network_hashrate) <<
"\nSide chain ID = " << (is_default() ? "default" : (is_mini() ? "mini" : m_consensusIdDisplayStr.c_str())) <<
"\nSide chain height = " << tip_height + 1 << "\nSide chain height = " << tip_height + 1 <<
"\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)" <<
"\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_total > 0 ? "\nYour shares position = " : "") << (our_blocks_in_window_total > 0 ? "[" + our_blocks_in_window_chart + "]" : "") << << our_blocks_in_window_chart << our_uncles_in_window_chart <<
(our_uncles_in_window_total > 0 ? "\nYour uncles position = " : "") << (our_uncles_in_window_total > 0 ? "[" + our_uncles_in_window_chart + "]" : "") <<
"\nBlock reward share = " << block_share << "% (" << log::XMRAmount(your_reward) << ')' "\nBlock reward share = " << block_share << "% (" << log::XMRAmount(your_reward) << ')'
); );
} }
difficulty_type SideChain::total_hashes() const difficulty_type SideChain::total_hashes() const
{ {
return m_chainTip ? m_chainTip->m_cumulativeDifficulty : difficulty_type(); const PoolBlock* tip = m_chainTip;
return tip ? tip->m_cumulativeDifficulty : difficulty_type();
} }
uint64_t SideChain::miner_count() uint64_t SideChain::miner_count()
{ {
const time_t cur_time = time(nullptr); const uint64_t cur_time = seconds_since_epoch();
MutexLock lock(m_sidechainLock); MutexLock lock(m_sidechainLock);
@@ -791,9 +836,10 @@ uint64_t SideChain::miner_count()
return m_seenWallets.size(); return m_seenWallets.size();
} }
time_t SideChain::last_updated() const uint64_t SideChain::last_updated() const
{ {
return m_chainTip ? m_chainTip->m_localTimestamp : 0; const PoolBlock* tip = m_chainTip;
return tip ? tip->m_localTimestamp : 0;
} }
bool SideChain::is_default() const bool SideChain::is_default() const
@@ -997,17 +1043,19 @@ void SideChain::verify_loop(PoolBlock* block)
", height " << block->m_sidechainHeight); ", height " << block->m_sidechainHeight);
} }
P2PServer* server = p2pServer();
// If it came through a broadcast, send it to our peers // If it came through a broadcast, send it to our peers
if (block->m_wantBroadcast && !block->m_broadcasted) { if (block->m_wantBroadcast && !block->m_broadcasted) {
block->m_broadcasted = true; block->m_broadcasted = true;
if (p2pServer() && (block->m_depth < UNCLE_BLOCK_DEPTH)) { if (server && (block->m_depth < UNCLE_BLOCK_DEPTH)) {
p2pServer()->broadcast(*block); server->broadcast(*block);
} }
} }
// Save it for faster syncing on the next p2pool start // Save it for faster syncing on the next p2pool start
if (p2pServer()) { if (server) {
p2pServer()->store_in_cache(*block); server->store_in_cache(*block);
} }
// Try to verify blocks on top of this one // Try to verify blocks on top of this one
@@ -1275,7 +1323,13 @@ void SideChain::verify(PoolBlock* block)
}); });
std::vector<uint64_t> rewards; std::vector<uint64_t> rewards;
split_reward(total_reward, shares, rewards); if (!split_reward(total_reward, shares, rewards)) {
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
", id = " << block->m_sidechainId <<
", mainchain height = " << block->m_txinGenHeight << ": split_reward failed");
block->m_invalid = true;
return;
}
if (rewards.size() != block->m_outputs.size()) { if (rewards.size() != block->m_outputs.size()) {
LOGWARN(3, "block at height = " << block->m_sidechainHeight << LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
@@ -1287,17 +1341,20 @@ void SideChain::verify(PoolBlock* block)
} }
for (size_t i = 0, n = rewards.size(); i < n; ++i) { for (size_t i = 0, n = rewards.size(); i < n; ++i) {
if (rewards[i] != block->m_outputs[i].m_reward) { const PoolBlock::TxOutput& out = block->m_outputs[i];
if (rewards[i] != out.m_reward) {
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 invalid reward at index " << i << ": got " << block->m_outputs[i].m_reward << ", expected " << rewards[i]); " has invalid reward at index " << i << ": got " << out.m_reward << ", expected " << rewards[i]);
block->m_invalid = true; block->m_invalid = true;
return; return;
} }
hash eph_public_key; hash eph_public_key;
if (!shares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key)) { uint8_t view_tag;
if (!shares[i].m_wallet->get_eph_public_key(block->m_txkeySec, i, eph_public_key, view_tag)) {
LOGWARN(3, "block at height = " << block->m_sidechainHeight << LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
", id = " << block->m_sidechainId << ", id = " << block->m_sidechainId <<
", mainchain height = " << block->m_txinGenHeight << ", mainchain height = " << block->m_txinGenHeight <<
@@ -1306,7 +1363,16 @@ void SideChain::verify(PoolBlock* block)
return; return;
} }
if (eph_public_key != block->m_outputs[i].m_ephPublicKey) { if ((out.m_txType == TXOUT_TO_TAGGED_KEY) && (out.m_viewTag != view_tag)) {
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
", id = " << block->m_sidechainId <<
", mainchain height = " << block->m_txinGenHeight <<
" has an incorrect view tag at index " << i);
block->m_invalid = true;
return;
}
if (eph_public_key != out.m_ephPublicKey) {
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 <<
@@ -1332,8 +1398,10 @@ void SideChain::update_chain_tip(PoolBlock* block)
return; return;
} }
const PoolBlock* tip = m_chainTip;
bool is_alternative; bool is_alternative;
if (is_longer_chain(m_chainTip, block, is_alternative)) { if (is_longer_chain(tip, block, is_alternative)) {
difficulty_type diff; difficulty_type diff;
if (get_difficulty(block, m_difficultyData, diff)) { if (get_difficulty(block, m_difficultyData, diff)) {
m_chainTip = block; m_chainTip = block;
@@ -1341,7 +1409,7 @@ void SideChain::update_chain_tip(PoolBlock* block)
LOGINFO(2, "new chain tip: next height = " << log::Gray() << block->m_sidechainHeight + 1 << log::NoColor() << LOGINFO(2, "new chain tip: next height = " << log::Gray() << block->m_sidechainHeight + 1 << log::NoColor() <<
", next difficulty = " << log::Gray() << m_curDifficulty << log::NoColor() << ", next difficulty = " << log::Gray() << m_curDifficulty << log::NoColor() <<
", main chain height = " << log::Gray() << m_chainTip->m_txinGenHeight); ", main chain height = " << log::Gray() << block->m_txinGenHeight);
block->m_wantBroadcast = true; block->m_wantBroadcast = true;
if (m_pool) { if (m_pool) {
@@ -1356,13 +1424,13 @@ void SideChain::update_chain_tip(PoolBlock* block)
prune_old_blocks(); prune_old_blocks();
} }
} }
else if (block->m_sidechainHeight > m_chainTip->m_sidechainHeight) { else if (block->m_sidechainHeight > tip->m_sidechainHeight) {
LOGINFO(4, "block " << block->m_sidechainId << LOGINFO(4, "block " << block->m_sidechainId <<
", height = " << block->m_sidechainHeight << ", height = " << block->m_sidechainHeight <<
" is not a longer chain than " << m_chainTip->m_sidechainId << " is not a longer chain than " << tip->m_sidechainId <<
", height " << m_chainTip->m_sidechainHeight); ", height " << tip->m_sidechainHeight);
} }
else if (block->m_sidechainHeight + UNCLE_BLOCK_DEPTH > m_chainTip->m_sidechainHeight) { else if (block->m_sidechainHeight + UNCLE_BLOCK_DEPTH > tip->m_sidechainHeight) {
LOGINFO(4, "possible uncle block: id = " << log::Gray() << block->m_sidechainId << log::NoColor() << LOGINFO(4, "possible uncle block: id = " << log::Gray() << block->m_sidechainId << log::NoColor() <<
", height = " << log::Gray() << block->m_sidechainHeight); ", height = " << log::Gray() << block->m_sidechainHeight);
m_pool->update_block_template_async(); m_pool->update_block_template_async();
@@ -1495,8 +1563,9 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
} }
// Final check: candidate chain must be built on top of recent mainchain blocks // Final check: candidate chain must be built on top of recent mainchain blocks
if (candidate_mainchain_height + 10 < m_pool->miner_data().height) { MinerData data = m_pool->miner_data();
LOGWARN(3, "received a longer alternative chain but it's stale: height " << candidate_mainchain_height << ", current height " << m_pool->miner_data().height); if (candidate_mainchain_height + 10 < data.height) {
LOGWARN(3, "received a longer alternative chain but it's stale: height " << candidate_mainchain_height << ", current height " << data.height);
return false; return false;
} }
@@ -1577,13 +1646,16 @@ void SideChain::prune_old_blocks()
const uint64_t prune_distance = m_chainWindowSize * 2 + 120 / m_targetBlockTime; const uint64_t prune_distance = m_chainWindowSize * 2 + 120 / m_targetBlockTime;
// Remove old blocks from alternative unconnected chains after long enough time // Remove old blocks from alternative unconnected chains after long enough time
const time_t prune_time = time(nullptr) - m_chainWindowSize * 4 * m_targetBlockTime; const uint64_t cur_time = seconds_since_epoch();
const uint64_t prune_delay = m_chainWindowSize * 4 * m_targetBlockTime;
if (m_chainTip->m_sidechainHeight < prune_distance) { const PoolBlock* tip = m_chainTip;
if (tip->m_sidechainHeight < prune_distance) {
return; return;
} }
const uint64_t h = m_chainTip->m_sidechainHeight - prune_distance; const uint64_t h = tip->m_sidechainHeight - prune_distance;
uint64_t num_blocks_pruned = 0; uint64_t num_blocks_pruned = 0;
@@ -1592,9 +1664,9 @@ void SideChain::prune_old_blocks()
std::vector<PoolBlock*>& v = it->second; std::vector<PoolBlock*>& v = it->second;
v.erase(std::remove_if(v.begin(), v.end(), v.erase(std::remove_if(v.begin(), v.end(),
[this, prune_distance, prune_time, &num_blocks_pruned, height](PoolBlock* block) [this, prune_distance, cur_time, prune_delay, &num_blocks_pruned, height](PoolBlock* block)
{ {
if ((block->m_depth >= prune_distance) || (block->m_localTimestamp <= prune_time)) { if ((block->m_depth >= prune_distance) || (cur_time >= block->m_localTimestamp + prune_delay)) {
auto it2 = m_blocksById.find(block->m_sidechainId); auto it2 = m_blocksById.find(block->m_sidechainId);
if (it2 != m_blocksById.end()) { if (it2 != m_blocksById.end()) {
m_blocksById.erase(it2); m_blocksById.erase(it2);
+5 -4
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -69,7 +69,7 @@ public:
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; }
uint64_t miner_count(); uint64_t miner_count();
time_t last_updated() const; uint64_t last_updated() const;
bool is_default() const; bool is_default() const;
bool is_mini() const; bool is_mini() const;
@@ -99,10 +99,10 @@ private:
bool check_config(); bool check_config();
mutable uv_mutex_t m_sidechainLock; mutable uv_mutex_t m_sidechainLock;
PoolBlock* m_chainTip; std::atomic<PoolBlock*> m_chainTip;
std::map<uint64_t, std::vector<PoolBlock*>> m_blocksByHeight; std::map<uint64_t, std::vector<PoolBlock*>> m_blocksByHeight;
unordered_map<hash, PoolBlock*> m_blocksById; unordered_map<hash, PoolBlock*> m_blocksById;
unordered_map<hash, time_t> m_seenWallets; unordered_map<hash, uint64_t> m_seenWallets;
std::vector<MinerShare> m_tmpShares; std::vector<MinerShare> m_tmpShares;
std::vector<uint64_t> m_tmpRewards; std::vector<uint64_t> m_tmpRewards;
@@ -119,6 +119,7 @@ private:
uint64_t m_unclePenalty; uint64_t m_unclePenalty;
std::vector<uint8_t> m_consensusId; std::vector<uint8_t> m_consensusId;
std::string m_consensusIdDisplayStr;
difficulty_type m_curDifficulty; difficulty_type m_curDifficulty;
+12 -10
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -39,8 +39,7 @@ StratumServer::StratumServer(p2pool* pool)
: TCPServer(StratumClient::allocate) : TCPServer(StratumClient::allocate)
, m_pool(pool) , m_pool(pool)
, m_extraNonce(0) , m_extraNonce(0)
, m_rd{} , m_rng(RandomDeviceSeed::instance)
, m_rng(m_rd())
, m_cumulativeHashes(0) , m_cumulativeHashes(0)
, m_cumulativeHashesAtLastShare(0) , m_cumulativeHashesAtLastShare(0)
, m_hashrateDataHead(0) , m_hashrateDataHead(0)
@@ -51,7 +50,10 @@ StratumServer::StratumServer(p2pool* pool)
, m_totalFoundShares(0) , m_totalFoundShares(0)
, m_apiLastUpdateTime(0) , m_apiLastUpdateTime(0)
{ {
m_hashrateData[0] = { time(nullptr), 0 }; // Diffuse the initial state in case it has low quality
m_rng.discard(10000);
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);
@@ -435,7 +437,7 @@ uint64_t StratumServer::get_random64()
void StratumServer::print_status() void StratumServer::print_status()
{ {
update_hashrate_data(0, time(nullptr)); update_hashrate_data(0, seconds_since_epoch());
print_stratum_status(); print_stratum_status();
} }
@@ -525,7 +527,7 @@ void StratumServer::on_blobs_ready()
size_t numClientsProcessed = 0; size_t numClientsProcessed = 0;
uint32_t extra_nonce = 0; uint32_t extra_nonce = 0;
const time_t cur_time = time(nullptr); const uint64_t cur_time = seconds_since_epoch();
{ {
MutexLock lock2(m_clientsListLock); MutexLock lock2(m_clientsListLock);
@@ -603,7 +605,7 @@ void StratumServer::on_blobs_ready()
LOGINFO(3, "sent new job to " << extra_nonce << '/' << numClientsProcessed << " clients"); LOGINFO(3, "sent new job to " << extra_nonce << '/' << numClientsProcessed << " clients");
} }
void StratumServer::update_hashrate_data(uint64_t hashes, time_t timestamp) void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp)
{ {
constexpr size_t N = array_size(&StratumServer::m_hashrateData); constexpr size_t N = array_size(&StratumServer::m_hashrateData);
@@ -705,7 +707,7 @@ void StratumServer::on_share_found(uv_work_t* req)
const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t)); const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t));
if (LIKELY(value < target)) { if (LIKELY(value < target)) {
const time_t timestamp = time(nullptr); const uint64_t timestamp = seconds_since_epoch();
server->update_hashrate_data(hashes, timestamp); server->update_hashrate_data(hashes, timestamp);
server->api_update_local_stats(timestamp); server->api_update_local_stats(timestamp);
share->m_result = SubmittedShare::Result::OK; share->m_result = SubmittedShare::Result::OK;
@@ -801,7 +803,7 @@ void StratumServer::StratumClient::reset()
bool StratumServer::StratumClient::on_connect() bool StratumServer::StratumClient::on_connect()
{ {
m_connectedTime = time(nullptr); m_connectedTime = seconds_since_epoch();
return true; return true;
} }
@@ -991,7 +993,7 @@ bool StratumServer::StratumClient::process_submit(rapidjson::Document& doc, uint
return static_cast<StratumServer*>(m_owner)->on_submit(this, id, job_id.GetString(), nonce.GetString(), result.GetString()); return static_cast<StratumServer*>(m_owner)->on_submit(this, id, job_id.GetString(), nonce.GetString(), result.GetString());
} }
void StratumServer::api_update_local_stats(time_t timestamp) void StratumServer::api_update_local_stats(uint64_t timestamp)
{ {
if (!m_pool->api() || !m_pool->params().m_localStats) { if (!m_pool->api() || !m_pool->params().m_localStats) {
return; return;
+6 -8
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
#include "tcp_server.h" #include "tcp_server.h"
#include <rapidjson/document.h> #include <rapidjson/document.h>
#include <random>
namespace p2pool { namespace p2pool {
@@ -53,7 +52,7 @@ public:
bool process_submit(rapidjson::Document& doc, uint32_t id); bool process_submit(rapidjson::Document& doc, uint32_t id);
uint32_t m_rpcId; uint32_t m_rpcId;
time_t m_connectedTime; uint64_t m_connectedTime;
uv_mutex_t m_jobsLock; uv_mutex_t m_jobsLock;
@@ -109,7 +108,6 @@ private:
std::atomic<uint32_t> m_extraNonce; std::atomic<uint32_t> m_extraNonce;
uv_mutex_t m_rngLock; uv_mutex_t m_rngLock;
std::random_device m_rd;
std::mt19937_64 m_rng; std::mt19937_64 m_rng;
struct SubmittedShare struct SubmittedShare
@@ -142,7 +140,7 @@ private:
struct HashrateData struct HashrateData
{ {
time_t m_timestamp; uint64_t m_timestamp;
uint64_t m_cumulativeHashes; uint64_t m_cumulativeHashes;
}; };
@@ -159,10 +157,10 @@ private:
double m_cumulativeFoundSharesDiff; double m_cumulativeFoundSharesDiff;
uint32_t m_totalFoundShares; uint32_t m_totalFoundShares;
time_t m_apiLastUpdateTime; uint64_t m_apiLastUpdateTime;
void update_hashrate_data(uint64_t hashes, time_t timestamp); void update_hashrate_data(uint64_t hashes, uint64_t timestamp);
void api_update_local_stats(time_t timestamp); void api_update_local_stats(uint64_t timestamp);
}; };
} // namespace p2pool } // namespace p2pool
+3 -5
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -53,7 +53,7 @@ public:
struct Client struct Client
{ {
Client(); Client();
virtual ~Client(); virtual ~Client() {}
virtual void reset(); virtual void reset();
virtual bool on_connect() = 0; virtual bool on_connect() = 0;
@@ -90,8 +90,6 @@ public:
uint32_t m_numRead; uint32_t m_numRead;
std::atomic<uint32_t> m_resetCounter{ 0 }; std::atomic<uint32_t> m_resetCounter{ 0 };
uv_mutex_t m_sendLock;
}; };
struct WriteBuf struct WriteBuf
@@ -152,7 +150,7 @@ protected:
int m_listenPort; int m_listenPort;
uv_loop_t m_loop; uv_loop_t m_loop;
volatile bool m_loopStopped; std::atomic<bool> m_loopStopped;
uv_mutex_t m_clientsListLock; uv_mutex_t m_clientsListLock;
std::vector<Client*> m_preallocatedClients; std::vector<Client*> m_preallocatedClients;
+6 -14
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -27,7 +27,7 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
, m_loopThread{} , m_loopThread{}
, m_finished(0) , m_finished(0)
, m_listenPort(-1) , m_listenPort(-1)
, m_loopStopped(false) , m_loopStopped{false}
, m_numConnections(0) , m_numConnections(0)
, m_numIncomingConnections(0) , m_numIncomingConnections(0)
{ {
@@ -532,8 +532,6 @@ bool TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::send_internal(Client* client, Sen
LOGERR(1, "sending data from another thread, this is not thread safe"); LOGERR(1, "sending data from another thread, this is not thread safe");
} }
MutexLock lock0(client->m_sendLock);
WriteBuf* buf = nullptr; WriteBuf* buf = nullptr;
{ {
@@ -620,9 +618,11 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_new_connection(uv_stream_t* se
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE> template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connection_close(uv_handle_t* handle) void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::on_connection_close(uv_handle_t* handle)
{ {
Client* client = static_cast<Client*>(handle->data); if (!server_event_loop_thread) {
MutexLock lock0(client->m_sendLock); LOGERR(1, "on_connection_close called from another thread, this is not thread safe");
}
Client* client = static_cast<Client*>(handle->data);
TCPServer* owner = client->m_owner; TCPServer* owner = client->m_owner;
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(client->m_addrString) << log::NoColor() << " disconnected"); LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(client->m_addrString) << log::NoColor() << " disconnected");
@@ -811,17 +811,9 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::Client()
{ {
Client::reset(); Client::reset();
uv_mutex_init_checked(&m_sendLock);
m_readBuf[0] = '\0'; m_readBuf[0] = '\0';
} }
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::~Client()
{
uv_mutex_destroy(&m_sendLock);
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE> template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::reset() void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::reset()
{ {
+5 -3
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -32,7 +32,7 @@ namespace p2pool {
#define STR2(X) STR(X) #define STR2(X) STR(X)
#define STR(X) #X #define STR(X) #X
const char* VERSION = "v1.8 (built" const char* VERSION = "v2.0 (built"
#if defined(__clang__) #if defined(__clang__)
" with clang/" __clang_version__ " with clang/" __clang_version__
#elif defined(__GNUC__) #elif defined(__GNUC__)
@@ -66,7 +66,7 @@ void make_thread_background()
SetThreadPriorityBoost(GetCurrentThread(), true); SetThreadPriorityBoost(GetCurrentThread(), true);
SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN); SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
#elif !defined(__APPLE__) #elif !defined(__APPLE__) && !defined(__FreeBSD__)
sched_param param; sched_param param;
param.sched_priority = 0; param.sched_priority = 0;
if (sched_setscheduler(0, SCHED_IDLE, &param) != 0) { if (sched_setscheduler(0, SCHED_IDLE, &param) != 0) {
@@ -382,4 +382,6 @@ bool resolve_host(std::string& host, bool& is_v6)
return true; return true;
} }
RandomDeviceSeed RandomDeviceSeed::instance;
} // namespace p2pool } // namespace p2pool
+59 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -109,6 +109,40 @@ FORCEINLINE void writeVarint(T value, std::vector<uint8_t>& out)
writeVarint(value, [&out](uint8_t b) { out.emplace_back(b); }); writeVarint(value, [&out](uint8_t b) { out.emplace_back(b); });
} }
template<typename T>
const uint8_t* readVarint(const uint8_t* data, const uint8_t* data_end, T& b)
{
uint64_t result = 0;
int k = 0;
while (data < data_end) {
if (k >= static_cast<int>(sizeof(T)) * 8) {
return nullptr;
}
const uint64_t cur_byte = *(data++);
result |= (cur_byte & 0x7F) << k;
k += 7;
if ((cur_byte & 0x80) == 0) {
b = result;
return data;
}
}
return nullptr;
}
template<typename T>
FORCEINLINE T read_unaligned(const T* p)
{
static_assert(std::is_integral<T>::value, "T must be an integer type");
T result;
memcpy(&result, p, sizeof(T));
return result;
}
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; }
@@ -145,6 +179,30 @@ using unordered_map = robin_hood::detail::Table<false, 80, Key, T, robin_hood::h
template <typename Key> template <typename Key>
using unordered_set = robin_hood::detail::Table<false, 80, Key, void, robin_hood::hash<Key>, std::equal_to<Key>>; using unordered_set = robin_hood::detail::Table<false, 80, Key, void, robin_hood::hash<Key>, std::equal_to<Key>>;
// Fills the whole initial MT19937-64 state with non-deterministic random numbers
struct RandomDeviceSeed
{
using result_type = std::random_device::result_type;
static_assert(sizeof(result_type) >= 4, "result_type must have at least 32 bits");
template<typename T>
static void generate(T begin, T end)
{
std::random_device rd;
for (T i = begin; i != end; ++i) {
*i = rd();
}
}
static RandomDeviceSeed instance;
};
FORCEINLINE uint64_t seconds_since_epoch()
{
using namespace std::chrono;
return duration_cast<seconds>(steady_clock::now().time_since_epoch()).count();
}
} // namespace p2pool } // namespace p2pool
namespace robin_hood { namespace robin_hood {
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+18 -7
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* Portions Copyright (c) 2012-2013 The Cryptonote developers * Portions Copyright (c) 2012-2013 The Cryptonote developers
* Portions Copyright (c) 2014-2021 The Monero Project * Portions Copyright (c) 2014-2021 The Monero Project
* Portions Copyright (c) 2021 XMRig <https://github.com/xmrig> * Portions Copyright (c) 2021 XMRig <https://github.com/xmrig>
@@ -84,10 +84,6 @@ Wallet::Wallet(const char* address) : m_prefix(0), m_checksum(0), m_type(Network
decode(address); decode(address);
} }
Wallet::~Wallet()
{
}
Wallet::Wallet(const Wallet& w) Wallet::Wallet(const Wallet& w)
{ {
operator=(w); operator=(w);
@@ -199,10 +195,25 @@ bool Wallet::assign(const hash& spend_pub_key, const hash& view_pub_key, Network
return true; return true;
} }
bool Wallet::get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key) const bool Wallet::get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag) const
{ {
hash derivation; hash derivation;
if (!generate_key_derivation(m_viewPublicKey, txkey_sec, derivation)) { if (!generate_key_derivation(m_viewPublicKey, txkey_sec, output_index, derivation, view_tag)) {
return false;
}
if (!derive_public_key(derivation, output_index, m_spendPublicKey, eph_public_key)) {
return false;
}
return true;
}
bool Wallet::get_eph_public_key_with_view_tag(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t expected_view_tag) const
{
hash derivation;
uint8_t view_tag;
if (!generate_key_derivation(m_viewPublicKey, txkey_sec, output_index, derivation, view_tag) || (view_tag != expected_view_tag)) {
return false; return false;
} }
+3 -3
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -25,7 +25,6 @@ class Wallet
{ {
public: public:
explicit Wallet(const char* address); explicit Wallet(const char* address);
~Wallet();
Wallet(const Wallet& w); Wallet(const Wallet& w);
Wallet& operator=(const Wallet& w); Wallet& operator=(const Wallet& w);
@@ -39,7 +38,8 @@ public:
FORCEINLINE const hash& spend_public_key() const { return m_spendPublicKey; } FORCEINLINE const hash& spend_public_key() const { return m_spendPublicKey; }
FORCEINLINE const hash& view_public_key() const { return m_viewPublicKey; } FORCEINLINE const hash& view_public_key() const { return m_viewPublicKey; }
bool get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key) const; bool get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag) const;
bool get_eph_public_key_with_view_tag(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t expected_view_tag) const;
FORCEINLINE bool operator<(const Wallet& w) const { return m_spendPublicKey < w.m_spendPublicKey; } FORCEINLINE bool operator<(const Wallet& w) const { return m_spendPublicKey < w.m_spendPublicKey; }
FORCEINLINE bool operator==(const Wallet& w) const { return m_spendPublicKey == w.m_spendPublicKey; } FORCEINLINE bool operator==(const Wallet& w) const { return m_spendPublicKey == w.m_spendPublicKey; }
+4 -5
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
#include "zmq_reader.h" #include "zmq_reader.h"
#include "json_parsers.h" #include "json_parsers.h"
#include <rapidjson/document.h> #include <rapidjson/document.h>
#include <random>
static constexpr char log_category_prefix[] = "ZMQReader "; static constexpr char log_category_prefix[] = "ZMQReader ";
@@ -160,10 +159,10 @@ bool ZMQReader::connect(const char* address)
m_subscriber.connect(address); m_subscriber.connect(address);
using namespace std::chrono; using namespace std::chrono;
system_clock::time_point start_time = system_clock::now(); steady_clock::time_point start_time = steady_clock::now();
while (!monitor.connected && monitor.check_event(-1)) { while (!monitor.connected && monitor.check_event(-1)) {
const system_clock::time_point cur_time = system_clock::now(); const steady_clock::time_point cur_time = steady_clock::now();
const int64_t elapsed_time = duration_cast<milliseconds>(cur_time - start_time).count(); const int64_t elapsed_time = duration_cast<milliseconds>(cur_time - start_time).count();
if (elapsed_time >= 3000) { if (elapsed_time >= 3000) {
LOGERR(1, "failed to connect to " << address); LOGERR(1, "failed to connect to " << address);
@@ -208,7 +207,7 @@ void ZMQReader::parse(char* data, size_t size)
return; return;
} }
m_tx.time_received = time(nullptr); m_tx.time_received = seconds_since_epoch();
for (SizeType i = 0, n = doc.Size(); i < n; ++i) { for (SizeType i = 0, n = doc.Size(); i < n; ++i) {
const auto& v = doc[i]; const auto& v = doc[i];
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool> * This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh> * Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
+2
View File
@@ -19,6 +19,7 @@ set(LIBS gtest)
add_subdirectory(../external/src/RandomX RandomX) add_subdirectory(../external/src/RandomX RandomX)
set(LIBS ${LIBS} randomx) set(LIBS ${LIBS} randomx)
add_definitions(-DWITH_RANDOMX)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU) if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(WARNING_FLAGS "") set(WARNING_FLAGS "")
@@ -73,6 +74,7 @@ set(SOURCES
src/keccak_tests.cpp src/keccak_tests.cpp
src/main.cpp src/main.cpp
src/pool_block_tests.cpp src/pool_block_tests.cpp
src/varint_tests.cpp
src/wallet_tests.cpp src/wallet_tests.cpp
../external/src/cryptonote/crypto-ops-data.c ../external/src/cryptonote/crypto-ops-data.c
../external/src/cryptonote/crypto-ops.c ../external/src/cryptonote/crypto-ops.c
+16 -1
View File
@@ -42,7 +42,8 @@ TEST(crypto, derivation)
if (result) { if (result) {
f >> expected_derivation; f >> expected_derivation;
} }
ASSERT_EQ(p2pool::generate_key_derivation(key1, key2, derivation), result); uint8_t view_tag;
ASSERT_EQ(p2pool::generate_key_derivation(key1, key2, 0, derivation, view_tag), result);
if (result) { if (result) {
ASSERT_EQ(derivation, expected_derivation); ASSERT_EQ(derivation, expected_derivation);
} }
@@ -61,6 +62,20 @@ TEST(crypto, derivation)
ASSERT_EQ(derived_key, expected_derived_key); ASSERT_EQ(derived_key, expected_derived_key);
} }
} }
else if (name == "derive_view_tag") {
hash derivation;
uint64_t output_index;
std::string result_str;
f >> derivation >> output_index >> result_str;
uint8_t view_tag;
p2pool::derive_view_tag(derivation, output_index, view_tag);
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf);
s << log::hex_buf(&view_tag, 1) << '\0';
ASSERT_EQ(buf, result_str);
}
} while (!f.eof()); } while (!f.eof());
} }
+70
View File
@@ -542,3 +542,73 @@ derive_public_key 5ea95a51ab11b80c7d09d0c8f8952b70c67e81d0fd421bbed43ab77c1b7b30
derive_public_key 2c312ef971def53361274c37a90bfde86f959d877a636ea641a9c976ee80c7e3 121 b611ebd2bcfefc81cb772e35e3dd0204575cb0da644f68d4f9828a2683861e6c false derive_public_key 2c312ef971def53361274c37a90bfde86f959d877a636ea641a9c976ee80c7e3 121 b611ebd2bcfefc81cb772e35e3dd0204575cb0da644f68d4f9828a2683861e6c false
derive_public_key 6fa161dd958022caf185faf873dd9adbc5578352cda505e84fff7cc99a8762a7 333934910 c2b56e207862958751d49643f23079009092c32bf82179a1295e3b85a385c1c3 false derive_public_key 6fa161dd958022caf185faf873dd9adbc5578352cda505e84fff7cc99a8762a7 333934910 c2b56e207862958751d49643f23079009092c32bf82179a1295e3b85a385c1c3 false
derive_public_key d85725562544e1984048391413a6112eb221bd217db3baaa9843bee331000e8e 21880446 f3148c3041e634d829e5df463ba3b1d64df282d620d63485e1f10024e003b939 false derive_public_key d85725562544e1984048391413a6112eb221bd217db3baaa9843bee331000e8e 21880446 f3148c3041e634d829e5df463ba3b1d64df282d620d63485e1f10024e003b939 false
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 0 76
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 1 d6
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 2 87
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 3 1b
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 12 d6
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 13 e9
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 14 12
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 15 26
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 127 2c
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 128 11
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 0 70
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 1 81
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 2 a0
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 3 ec
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 12 22
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 13 0a
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 14 87
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 15 76
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 127 4b
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 128 39
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 0 93
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 1 67
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 2 9d
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 3 2d
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 12 63
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 13 cf
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 14 ef
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 15 10
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 127 fc
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 128 73
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 0 90
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 1 5a
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 2 de
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 3 21
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 12 57
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 13 52
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 14 6f
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 15 eb
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 127 93
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 128 dc
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 0 c6
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 1 60
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 2 f0
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 3 71
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 12 0e
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 13 42
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 14 b2
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 15 61
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 127 c3
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 128 0a
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 0 4c
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 1 9b
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 2 64
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 3 ff
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 12 e3
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 13 24
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 14 ea
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 15 3b
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 127 47
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 128 5a
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 0 74
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 1 77
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 2 a9
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 3 44
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 12 75
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 13 05
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 14 ca
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 15 00
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 127 a6
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 128 0d
+4 -4
View File
@@ -254,7 +254,7 @@ TEST(difficulty_type, check_pow)
// Max hash value that passes this difficulty // Max hash value that passes this difficulty
memcpy(h.h, data, HASH_SIZE); memcpy(h.h, data, HASH_SIZE);
EXPECT_EQ(diff.check_pow(h), true); ASSERT_EQ(diff.check_pow(h), true);
// Add 1 to data (256-bit number) // Add 1 to data (256-bit number)
for (int j = 0; j <= 3; ++j) { for (int j = 0; j <= 3; ++j) {
@@ -267,7 +267,7 @@ TEST(difficulty_type, check_pow)
// Min hash value that fails this difficulty // Min hash value that fails this difficulty
memcpy(h.h, data, HASH_SIZE); memcpy(h.h, data, HASH_SIZE);
EXPECT_EQ(diff.check_pow(h), false); ASSERT_EQ(diff.check_pow(h), false);
} }
const uint64_t target = diff.target(); const uint64_t target = diff.target();
@@ -276,14 +276,14 @@ TEST(difficulty_type, check_pow)
for (int j = 0; j < 10000; ++j) { for (int j = 0; j < 10000; ++j) {
const uint64_t data[4] = { r(), r(), r(), r() % target }; const uint64_t data[4] = { r(), r(), r(), r() % target };
memcpy(h.h, data, HASH_SIZE); memcpy(h.h, data, HASH_SIZE);
EXPECT_EQ(diff.check_pow(h), true); ASSERT_EQ(diff.check_pow(h), true);
} }
// Random values that fail // Random values that fail
for (int j = 0; j < 10000; ++j) { for (int j = 0; j < 10000; ++j) {
const uint64_t data[4] = { r(), r(), r(), target + (r() % (std::numeric_limits<uint64_t>::max() - target + 1)) }; const uint64_t data[4] = { r(), r(), r(), target + (r() % (std::numeric_limits<uint64_t>::max() - target + 1)) };
memcpy(h.h, data, HASH_SIZE); memcpy(h.h, data, HASH_SIZE);
EXPECT_EQ(diff.check_pow(h), false); ASSERT_EQ(diff.check_pow(h), false);
} }
} }
} }
+85
View File
@@ -0,0 +1,85 @@
/*
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
* Copyright (c) 2021-2022 SChernykh <https://github.com/SChernykh>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include "util.h"
#include "gtest/gtest.h"
namespace p2pool {
TEST(varint, read_write)
{
std::vector<uint8_t> v;
v.reserve(16);
uint64_t check;
// 0...2^7 - 1
for (uint64_t value = 0; value < 0x80; ++value) {
v.clear();
writeVarint(value, v);
ASSERT_EQ(v.size(), 1);
ASSERT_EQ(v[0], value);
ASSERT_EQ(readVarint(v.data(), v.data() + v.size(), check), v.data() + v.size());
ASSERT_EQ(check, value);
}
// 2^7...2^14 - 1
for (uint64_t value = 0x80; value < 0x4000; ++value) {
v.clear();
writeVarint(value, v);
ASSERT_EQ(v.size(), 2);
ASSERT_EQ(v[0], (value & 0x7F) | 0x80);
ASSERT_EQ(v[1], value >> 7);
ASSERT_EQ(readVarint(v.data(), v.data() + v.size(), check), v.data() + v.size());
ASSERT_EQ(check, value);
}
// 2^14...2^21 - 1
for (uint64_t value = 0x4000; value < 0x200000; ++value) {
v.clear();
writeVarint(value, v);
ASSERT_EQ(v.size(), 3);
ASSERT_EQ(v[0], (value & 0x7F) | 0x80);
ASSERT_EQ(v[1], ((value >> 7) & 0x7F) | 0x80);
ASSERT_EQ(v[2], value >> 14);
ASSERT_EQ(readVarint(v.data(), v.data() + v.size(), check), v.data() + v.size());
ASSERT_EQ(check, value);
}
// 2^64 - 1
v.clear();
writeVarint(std::numeric_limits<uint64_t>::max(), v);
ASSERT_EQ(v.size(), 10);
for (int i = 0; i < 9; ++i) {
ASSERT_EQ(v[i], 0xFF);
}
ASSERT_EQ(v[9], 1);
ASSERT_EQ(readVarint(v.data(), v.data() + v.size(), check), v.data() + v.size());
ASSERT_EQ(check, std::numeric_limits<uint64_t>::max());
// Invalid value 1
uint8_t buf[16];
memset(buf, -1, sizeof(buf));
ASSERT_EQ(readVarint(buf, buf + sizeof(buf), check), nullptr);
// Invalid value 2
uint8_t buf2[1] = { 0x80 };
ASSERT_EQ(readVarint(buf2, buf2 + 1, check), nullptr);
}
}