Compare commits

..

46 Commits

Author SHA1 Message Date
SChernykh 92827035e0 p2pool v3.2
C/C++ CI / build-ubuntu (map[c:gcc-11 cpp:g++-11 os:ubuntu-20.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu (map[c:gcc-12 cpp:g++-12 os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-static-libs (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (map[os:ubuntu-20.04]) (push) Has been cancelled
C/C++ CI / build-ubuntu-aarch64 (map[os:ubuntu-22.04]) (push) Has been cancelled
C/C++ CI / build-windows-msys2 (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:OFF upnp:OFF vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:OFF upnp:ON vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-windows-msbuild (map[msbuild:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64\ os:2019 rx:ON upnp:ON vs:Visual Studio 16 2019]) (push) Has been cancelled
C/C++ CI / build-macos (macos-11) (push) Has been cancelled
C/C++ CI / build-macos (macos-12) (push) Has been cancelled
C/C++ CI / build-freebsd (map[architecture:x86-64 host:ubuntu-22.04 name:freebsd version:12.4]) (push) Has been cancelled
C/C++ CI / build-openbsd (map[architecture:x86-64 host:ubuntu-22.04 name:openbsd version:7.2]) (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
cppcheck / cppcheck-ubuntu (push) Has been cancelled
cppcheck / cppcheck-windows (push) Has been cancelled
Microsoft C++ Code Analysis / Analyze (push) Has been cancelled
Sync test / sync-test-ubuntu (push) Has been cancelled
Sync test / sync-test-macos (push) Has been cancelled
Sync test / sync-test-windows (push) Has been cancelled
2023-03-31 18:30:34 +02:00
SChernykh 5f7b8c3371 Fixed cppcheck false positives 2023-03-31 13:35:23 +02:00
SChernykh 03eddddc0e Cache: save data from previous Monero block too
To improve caching of stale/outdated sidechain blocks.
2023-03-31 12:59:11 +02:00
SChernykh 080fbaa26c Util: adder PerfTimer 2023-03-30 19:09:15 +02:00
SChernykh f6a285de87 API: added PPLNS window size to pool stats 2023-03-28 13:53:52 +02:00
SChernykh 55e4ea0277 API: added sidechain diff and height to pool stats 2023-03-28 11:40:43 +02:00
SChernykh bb80fe12a0 Added a check for unstable hardware 2023-03-28 10:27:27 +02:00
SChernykh 5c67f0df20 P2PServer: truncate too long peers list in api 2023-03-27 12:15:33 +02:00
SChernykh 3697c079fa Fixed cppcheck error 2023-03-27 11:47:48 +02:00
SChernykh 2e6041dcf5 P2PServer: add local api data 2023-03-27 11:16:57 +02:00
SChernykh 645de31fe3 SideChain: stricter checks in get_difficulty 2023-03-24 13:26:30 +01:00
SChernykh eef90da7ae SideChain: stricter checks of alternative chains 2023-03-24 11:50:39 +01:00
SChernykh c0586a5089 Added --no-igd as an alias for --no-upnp
For compatibility with monerod command line parameters
2023-03-21 19:31:18 +01:00
SChernykh b30018bb38 SideChain::update_depths() - reduced log level 2023-03-21 16:22:11 +01:00
SChernykh 6c45573976 Faster block verification 2023-03-21 14:13:47 +01:00
SChernykh b6c1b1a6d7 P2PServer: log time it took to relay a block 2023-03-21 11:22:48 +01:00
SChernykh d41a441e98 UPnP: remove port mapping on shutdown 2023-03-21 10:23:17 +01:00
SChernykh 86b6cf2d65 UPnP: handle port mapping conflicts 2023-03-20 22:13:12 +01:00
SChernykh cf11993cb1 Update miniupnp 2023-03-20 15:56:21 +01:00
SChernykh fe7ef30650 Don't port forward Stratum port by default
Added a command line parameter to enable it
2023-03-20 14:04:19 +01:00
SChernykh bb4c7f0298 Run UPnP discovery in the background 2023-03-20 11:56:41 +01:00
SChernykh a7db46d69b Fixed command line help output 2023-03-20 10:04:43 +01:00
SChernykh ce81c2769c Added get_hashing_blobs test with full mempool 2023-03-20 09:46:09 +01:00
SChernykh d4a57370ef Disable cmd params depending on compile flags 2023-03-20 09:17:59 +01:00
SChernykh 634039e551 Fixed static build 2023-03-19 23:55:22 +01:00
SChernykh a83edceae3 Merge pull request #243 from hinto-janai/master
README: update gupax link
2023-03-19 23:04:11 +01:00
SChernykh 19cc044cab Added UPnP port forwarding 2023-03-19 23:02:09 +01:00
SChernykh 79ad29e99a Fixed pool_block.deserialize test 2023-03-19 20:54:09 +01:00
hinto.janai f96e291681 README: update gupax link 2023-03-19 15:05:02 -04:00
SChernykh 6685a22118 CI test-sync: removed unnecessary dependencies 2023-03-19 19:32:20 +01:00
SChernykh a84a0bd8dc Added miniupnp submodule 2023-03-19 19:13:29 +01:00
SChernykh c2d8c806d2 Removed fork code, only v2 is left 2023-03-19 17:19:25 +01:00
SChernykh 387860aa4a Post-fork test data 2023-03-19 11:38:43 +01:00
SChernykh de7bdba95f TCPServer: don't spam logs with 127.0.0.1 bans 2023-03-17 16:59:01 +01:00
SChernykh 6baf5247cb Fixed parallel_run logic on single CPU systems 2023-03-17 16:01:52 +01:00
SChernykh 146d29b627 Don't let user connect to a node without ZMQ 2023-03-10 15:02:07 +01:00
SChernykh c1a1249be1 Code cleanup 2023-03-09 10:59:52 +01:00
SChernykh 8e7a797586 Merge pull request #238 from twlee79/feature/namedpipe
Allow named pipe as stdin
2023-03-09 10:44:54 +01:00
SChernykh 14a1918fda Merge pull request #239 from hx507/master
Disable LTO on NixOS builds
2023-03-09 09:29:45 +01:00
git_unspecified 54e755efdd Fix nix build 2023-03-09 02:12:37 -06:00
T W Lee 3df7430663 allow named pipe as stdin, fixes after review 2023-03-09 14:38:20 +13:00
T W Lee c71d89c95a allow named pipe as stdin 2023-03-08 02:35:49 +13:00
SChernykh 1b5860eec8 Fixed cppcheck errors 2023-03-06 14:52:47 +01:00
SChernykh 4741880044 StratumServer: added address sanitizer checks 2023-03-06 14:30:13 +01:00
SChernykh 02a0cb174a StratumServer: show a warning for invalid shares 2023-03-02 13:19:25 +01:00
SChernykh e53fd7a062 Tests: fixed block_template test volatility
Hashes should stay the same from now on.
2023-03-01 09:20:09 +01:00
45 changed files with 839 additions and 334 deletions
+5 -6
View File
@@ -251,10 +251,9 @@ jobs:
strategy:
matrix:
config:
- {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 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\", rx: "OFF"}
#- {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"}
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\", rx: "ON", upnp: "ON"}
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\", rx: "OFF", upnp: "ON"}
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\", rx: "OFF", upnp: "OFF"}
steps:
- name: Checkout repository
@@ -269,7 +268,7 @@ jobs:
run: |
mkdir build
cd build
cmake .. -G "${{ matrix.config.vs }}" -DWITH_RANDOMX=${{ matrix.config.rx }}
cmake .. -G "${{ matrix.config.vs }}" -DWITH_RANDOMX=${{ matrix.config.rx }} -DWITH_UPNP=${{ matrix.config.upnp }}
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool.vcxproj
- name: Build tests
@@ -288,7 +287,7 @@ jobs:
- name: Archive binary
uses: actions/upload-artifact@v3
with:
name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}.exe
name: p2pool-msbuild-${{ matrix.config.os }}-randomx-${{ matrix.config.rx }}-upnp-${{ matrix.config.upnp }}.exe
path: build/Release/p2pool.exe
build-macos:
+1 -1
View File
@@ -54,7 +54,7 @@ jobs:
submodules: recursive
- name: Install dependencies
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake autoconf libtool automake libuv zmq libpgm curl
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake libuv zmq libpgm curl
- name: Build p2pool
run: |
+3
View File
@@ -22,3 +22,6 @@
[submodule "external/src/curl"]
path = external/src/curl
url = https://github.com/SChernykh/curl
[submodule "external/src/miniupnp"]
path = external/src/miniupnp
url = https://github.com/SChernykh/miniupnp
+15 -1
View File
@@ -5,6 +5,7 @@ option(STATIC_BINARY "Build static binary" OFF)
option(STATIC_LIBS "Link libuv and libzmq statically" OFF)
option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned off, p2pool will rely on monerod for verifying RandomX hashes" ON)
option(WITH_LTO "Use link-time compiler optimization (if linking fails for you, run cmake with -DWITH_LTO=OFF)" ON)
option(WITH_UPNP "Include UPnP support. If this is turned off, p2pool will not be able to configure port forwarding on UPnP-enabled routers." ON)
option(DEV_TEST_SYNC "[Developer only] Sync test, stop p2pool after sync is complete" OFF)
@@ -17,7 +18,13 @@ endif()
if (WITH_RANDOMX)
add_definitions(-DWITH_RANDOMX)
add_subdirectory(external/src/RandomX)
set(LIBS randomx)
set(LIBS ${LIBS} randomx)
endif()
if (WITH_UPNP)
add_definitions(-DWITH_UPNP)
add_subdirectory(external/src/miniupnp/miniupnpc)
set(LIBS ${LIBS} libminiupnpc-static)
endif()
if (DEV_TEST_SYNC)
@@ -117,6 +124,9 @@ if (WITH_RANDOMX)
endif()
include_directories(external/src/rapidjson/include)
include_directories(external/src/robin-hood-hashing/src/include)
if (WITH_UPNP)
include_directories(external/src/miniupnp/miniupnpc/include)
endif()
if (WIN32)
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
@@ -216,6 +226,10 @@ if (STATIC_BINARY OR STATIC_LIBS)
set(STATIC_LIBS randomx)
endif()
if (WITH_UPNP)
set(STATIC_LIBS ${STATIC_LIBS} libminiupnpc-static)
endif()
if (WIN32)
set(STATIC_LIBS ${STATIC_LIBS} ws2_32 iphlpapi userenv psapi wldap32)
if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
+1 -1
View File
@@ -104,7 +104,7 @@ Monero network upgrade happened on August 13th, 2022 (block 2,688,888). In order
### GUI for P2Pool
- [Gupax](https://github.com/hinto-janaiyo/gupax) project aims to provide an easy to use cross-platform GUI to configure and run P2Pool & [XMRig](https://github.com/xmrig/xmrig).
- [Gupax](https://github.com/hinto-janai/gupax) project aims to provide an easy to use cross-platform GUI to configure and run P2Pool & [XMRig](https://github.com/xmrig/xmrig).
### GNU/Linux
+5 -2
View File
@@ -24,8 +24,11 @@
--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum (WARNING: incompatible with Nicehash and MRR)
--rpc-login Specify username[:password] required for Monero RPC server
--socks5 Specify IP:port of a SOCKS5 proxy to use for outgoing connections
--no-dns disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too)
--p2p-external-port port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections
--no-dns Disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too)
--p2p-external-port Port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections
--no-upnp Disable UPnP port forwarding
--no-igd An alias for --no-upnp
--upnp-stratum Port forward Stratum port (it's not forwarded by default)
```
### Example command line
Vendored Submodule
+1
Submodule external/src/miniupnp added at e17579e4de
Generated
+6 -6
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1631368560,
"narHash": "sha256-JUx+2uf1C2dZ1n56hcy5gjPGh4UP0nQwIr+WbjaWaP4=",
"lastModified": 1678349056,
"narHash": "sha256-kcL7Ap6IfUQRmGFeE2F0SkzIbQaPvZiylIuL2dD6mL0=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "91333fe5c9212e1a0c587c8ce70b0571bf0b18bd",
"rev": "6a231a6eb788a345b265e1f26e04b96d8a402bf5",
"type": "github"
},
"original": {
@@ -23,11 +23,11 @@
},
"utils": {
"locked": {
"lastModified": 1629481132,
"narHash": "sha256-JHgasjPR0/J1J3DRm4KxM4zTyAj4IOJY8vIl75v/kPI=",
"lastModified": 1676283394,
"narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "997f7efcb746a9c140ce1f13c72263189225f482",
"rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
"type": "github"
},
"original": {
+2
View File
@@ -26,6 +26,8 @@
inherit (pkgs) libuv zeromq libsodium gss curl;
};
cmakeFlags = ["-DWITH_LTO=OFF"];
installPhase = ''
mkdir -p $out/bin
cp -r ./p2pool $out/bin/
+12 -21
View File
@@ -207,7 +207,7 @@ void BlockTemplate::shuffle_tx_order()
}
}
void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet)
void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const Wallet* miner_wallet)
{
if (data.major_version > HARDFORK_SUPPORTED_VERSION) {
LOGERR(1, "got hardfork version " << data.major_version << ", expected <= " << HARDFORK_SUPPORTED_VERSION);
@@ -272,14 +272,6 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
m_blockHeaderSize = m_blockHeader.size();
const int sidechain_version = m_poolBlockTemplate->get_sidechain_version();
if (sidechain_version <= 1) {
get_tx_keys(m_poolBlockTemplate->m_txkeyPub, m_poolBlockTemplate->m_txkeySec, miner_wallet->spend_public_key(), data.prev_id);
// Both values are the same before v2
m_poolBlockTemplate->m_txkeySecSeed = m_poolBlockTemplate->m_txkeySec;
}
m_poolBlockTemplate->m_minerWallet = *miner_wallet;
m_sidechain->fill_sidechain_data(*m_poolBlockTemplate, m_shares);
@@ -647,7 +639,11 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
// Layout: [software id, version, random number, sidechain extra_nonce]
uint32_t* sidechain_extra = m_poolBlockTemplate->m_sidechainExtraBuf;
sidechain_extra[0] = 0;
#ifdef P2POOL_SIDECHAIN_EXTRA_1
sidechain_extra[1] = P2POOL_SIDECHAIN_EXTRA_1;
#else
sidechain_extra[1] = (P2POOL_VERSION_MAJOR << 16) | P2POOL_VERSION_MINOR;
#endif
sidechain_extra[2] = static_cast<uint32_t>(m_rng() >> 32);
sidechain_extra[3] = 0;
@@ -665,7 +661,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
{
memset(m_sidechainHashKeccakState, 0, sizeof(m_sidechainHashKeccakState));
const size_t extra_nonce_offset = m_sidechainHashBlob.size() - HASH_SIZE - ((sidechain_version > 1) ? EXTRA_NONCE_SIZE : 0);
const size_t extra_nonce_offset = m_sidechainHashBlob.size() - HASH_SIZE - EXTRA_NONCE_SIZE;
if (extra_nonce_offset >= KeccakParams::HASH_DATA_AREA) {
// Sidechain data is big enough to cache keccak state up to extra_nonce
m_sidechainHashInputLength = (extra_nonce_offset / KeccakParams::HASH_DATA_AREA) * KeccakParams::HASH_DATA_AREA;
@@ -950,11 +946,10 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
hash BlockTemplate::calc_sidechain_hash(uint32_t sidechain_extra_nonce) const
{
// Calculate side-chain hash (all block template bytes + all side-chain bytes + consensus ID, replacing NONCE, EXTRA_NONCE and HASH itself with 0's)
const int v = m_poolBlockTemplate->get_sidechain_version();
const size_t size = m_sidechainHashBlob.size();
const size_t N = m_sidechainHashInputLength;
const size_t sidechain_extra_nonce_offset = size - HASH_SIZE - ((v > 1) ? EXTRA_NONCE_SIZE : 0);
const size_t sidechain_extra_nonce_offset = size - HASH_SIZE - EXTRA_NONCE_SIZE;
const uint8_t sidechain_extra_nonce_buf[EXTRA_NONCE_SIZE] = {
static_cast<uint8_t>(sidechain_extra_nonce >> 0),
static_cast<uint8_t>(sidechain_extra_nonce >> 8),
@@ -969,12 +964,10 @@ hash BlockTemplate::calc_sidechain_hash(uint32_t sidechain_extra_nonce) const
// Slow path: O(N)
if (!b || pool_block_debug()) {
keccak_custom([this, v, sidechain_extra_nonce_offset, &sidechain_extra_nonce_buf](int offset) -> uint8_t {
if (v > 1) {
const uint32_t k = static_cast<uint32_t>(offset - sidechain_extra_nonce_offset);
if (k < EXTRA_NONCE_SIZE) {
return sidechain_extra_nonce_buf[k];
}
keccak_custom([this, sidechain_extra_nonce_offset, &sidechain_extra_nonce_buf](int offset) -> uint8_t {
const uint32_t k = static_cast<uint32_t>(offset - sidechain_extra_nonce_offset);
if (k < EXTRA_NONCE_SIZE) {
return sidechain_extra_nonce_buf[k];
}
return m_sidechainHashBlob[offset];
}, static_cast<int>(size), result.h, HASH_SIZE);
@@ -985,9 +978,7 @@ hash BlockTemplate::calc_sidechain_hash(uint32_t sidechain_extra_nonce) const
const int inlen = static_cast<int>(size - N);
memcpy(buf, m_sidechainHashBlob.data() + N, size - N);
if (v > 1) {
memcpy(buf + sidechain_extra_nonce_offset - N, sidechain_extra_nonce_buf, EXTRA_NONCE_SIZE);
}
memcpy(buf + sidechain_extra_nonce_offset - N, sidechain_extra_nonce_buf, EXTRA_NONCE_SIZE);
uint64_t st[25];
memcpy(st, m_sidechainHashKeccakState, sizeof(st));
+1 -1
View File
@@ -39,7 +39,7 @@ public:
BlockTemplate(const BlockTemplate& b);
BlockTemplate& operator=(const BlockTemplate& b);
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet);
void update(const MinerData& data, const Mempool& mempool, const Wallet* miner_wallet);
uint64_t last_updated() const { return m_lastUpdated.load(); }
bool get_difficulties(const uint32_t template_id, uint64_t& height, uint64_t& sidechain_height, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
+30 -8
View File
@@ -36,12 +36,16 @@ ConsoleCommands::ConsoleCommands(p2pool* pool)
, m_loop{}
, m_shutdownAsync{}
, m_tty{}
, m_stdin_pipe{}
, m_stdin_handle(nullptr)
, m_loopThread{}
, m_readBuf{}
, m_readBufInUse(false)
{
if (uv_guess_handle(0) != UV_TTY) {
LOGERR(1, "tty is not available");
const uv_handle_type stdin_type = uv_guess_handle(0);
LOGINFO(3, "uv_guess_handle returned " << static_cast<int>(stdin_type));
if (stdin_type != UV_TTY && stdin_type != UV_NAMED_PIPE) {
LOGERR(1, "tty or named pipe is not available");
throw std::exception();
}
@@ -58,14 +62,32 @@ ConsoleCommands::ConsoleCommands(p2pool* pool)
}
m_shutdownAsync.data = this;
err = uv_tty_init(&m_loop, &m_tty, 0, 1);
if (err) {
LOGERR(1, "uv_tty_init failed, error " << uv_err_name(err));
throw std::exception();
if (stdin_type == UV_TTY) {
LOGINFO(3, "processing stdin as UV_TTY");
err = uv_tty_init(&m_loop, &m_tty, 0, 1);
if (err) {
LOGERR(1, "uv_tty_init failed, error " << uv_err_name(err));
throw std::exception();
}
m_stdin_handle = reinterpret_cast<uv_stream_t*>(&m_tty);
}
m_tty.data = this;
else {
LOGINFO(3, "processing stdin as UV_NAMED_PIPE");
err = uv_pipe_init(&m_loop, &m_stdin_pipe, 0);
if (err) {
LOGERR(1, "uv_pipe_init failed, error " << uv_err_name(err));
throw std::exception();
}
m_stdin_handle = reinterpret_cast<uv_stream_t*>(&m_stdin_pipe);
err = uv_pipe_open(&m_stdin_pipe, 0);
if (err) {
LOGERR(1, "uv_pipe_open failed, error " << uv_err_name(err));
throw std::exception();
}
}
m_stdin_handle->data = this;
err = uv_read_start(reinterpret_cast<uv_stream_t*>(&m_tty), allocCallback, stdinReadCallback);
err = uv_read_start(m_stdin_handle, allocCallback, stdinReadCallback);
if (err) {
LOGERR(1, "uv_read_start failed, error " << uv_err_name(err));
throw std::exception();
+3 -1
View File
@@ -35,6 +35,8 @@ private:
uv_loop_t m_loop;
uv_async_t m_shutdownAsync;
uv_tty_t m_tty;
uv_pipe_t m_stdin_pipe;
uv_stream_t* m_stdin_handle;
uv_thread_t m_loopThread;
char m_readBuf[64];
@@ -48,7 +50,7 @@ private:
{
ConsoleCommands* pThis = reinterpret_cast<ConsoleCommands*>(async->data);
uv_close(reinterpret_cast<uv_handle_t*>(&pThis->m_shutdownAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pThis->m_tty), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(pThis->m_stdin_handle), nullptr);
}
static void allocCallback(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
+57 -11
View File
@@ -214,10 +214,11 @@ public:
derive_view_tag(derivation, output_index, view_tag);
const uint64_t t = seconds_since_epoch();
{
WriteLock lock(derivations_lock);
DerivationEntry& entry = derivations->emplace(index, DerivationEntry{ derivation, { 0xFFFFFFFFUL, 0xFFFFFFFFUL }, {} }).first->second;
DerivationEntry& entry = derivations->emplace(index, DerivationEntry{ derivation, { 0xFFFFFFFFUL, 0xFFFFFFFFUL }, {}, t }).first->second;
entry.add_view_tag(static_cast<uint32_t>(output_index << 8) | view_tag);
}
@@ -235,7 +236,7 @@ public:
ReadLock lock(public_keys_lock);
auto it = public_keys->find(index);
if (it != public_keys->end()) {
derived_key = it->second;
derived_key = it->second.m_key;
return true;
}
}
@@ -258,9 +259,10 @@ public:
ge_p1p1_to_p2(&point5, &point4);
ge_tobytes(derived_key.h, &point5);
const uint64_t t = seconds_since_epoch();
{
WriteLock lock(public_keys_lock);
public_keys->emplace(index, derived_key);
public_keys->emplace(index, PublicKeyEntry{ derived_key, t });
}
return true;
@@ -276,8 +278,8 @@ public:
ReadLock lock(tx_keys_lock);
auto it = tx_keys->find(index);
if (it != tx_keys->end()) {
pub = it->second.first;
sec = it->second.second;
pub = it->second.m_pub;
sec = it->second.m_sec;
return;
}
}
@@ -292,14 +294,42 @@ public:
generate_keys_deterministic(pub, sec, entropy, sizeof(entropy));
const uint64_t t = seconds_since_epoch();
{
WriteLock lock(tx_keys_lock);
tx_keys->emplace(index, std::pair<hash, hash>(pub, sec));
tx_keys->emplace(index, TxKeyEntry{ pub, sec, t });
}
}
void clear()
void clear(uint64_t timestamp)
{
if (timestamp) {
auto clean_old = [timestamp](auto* table) {
for (auto it = table->begin(); it != table->end();) {
if (it->second.m_timestamp < timestamp) {
it = table->erase(it);
}
else {
++it;
}
}
};
{
WriteLock lock(derivations_lock);
clean_old(derivations);
}
{
WriteLock lock(public_keys_lock);
clean_old(public_keys);
}
{
WriteLock lock(tx_keys_lock);
clean_old(tx_keys);
}
return;
}
{
WriteLock lock(derivations_lock);
delete derivations;
@@ -326,6 +356,7 @@ private:
hash m_derivation;
uint32_t m_viewTags1[2] = { 0xFFFFFFFFUL, 0xFFFFFFFFUL };
std::vector<uint32_t> m_viewTags2;
uint64_t m_timestamp;
FORCEINLINE bool find_view_tag(size_t output_index, uint8_t& view_tag) const
{
@@ -373,9 +404,24 @@ private:
}
};
struct PublicKeyEntry
{
hash m_key;
// cppcheck-suppress unusedStructMember
uint64_t m_timestamp;
};
struct TxKeyEntry
{
hash m_pub;
hash m_sec;
// cppcheck-suppress unusedStructMember
uint64_t m_timestamp;
};
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, DerivationEntry> DerivationsMap;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> PublicKeysMap;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> TxKeysMap;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, PublicKeyEntry> PublicKeysMap;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, TxKeyEntry> TxKeysMap;
uv_rwlock_t derivations_lock;
DerivationsMap* derivations;
@@ -435,10 +481,10 @@ void destroy_crypto_cache()
}
}
void clear_crypto_cache()
void clear_crypto_cache(uint64_t timestamp)
{
if (cache) {
cache->clear();
cache->clear(timestamp);
}
}
+1 -1
View File
@@ -29,6 +29,6 @@ void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_
void init_crypto_cache();
void destroy_crypto_cache();
void clear_crypto_cache();
void clear_crypto_cache(uint64_t timestamp = 0);
} // namespace p2pool
+9 -2
View File
@@ -41,7 +41,9 @@ void p2pool_usage()
"--stratum-api An alias for --local-api\n"
"--no-cache Disable p2pool.cache\n"
"--no-color Disable colors in console output\n"
#ifdef WITH_RANDOMX
"--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes\n"
#endif
"--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 450)\n"
"--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 450)\n"
"--start-mining N Start built-in miner using N threads (any value between 1 and 64)\n"
@@ -49,8 +51,13 @@ void p2pool_usage()
"--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum (WARNING: incompatible with Nicehash and MRR)\n"
"--rpc-login Specify username[:password] required for Monero RPC server\n"
"--socks5 Specify IP:port of a SOCKS5 proxy to use for outgoing connections\n"
"--no-dns disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too)\n"
"--p2p-external-port port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections\n"
"--no-dns Disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too)\n"
"--p2p-external-port Port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections\n"
#ifdef WITH_UPNP
"--no-upnp Disable UPnP port forwarding\n"
"--no-igd An alias for --no-upnp\n"
"--upnp-stratum Port forward Stratum port (it's not forwarded by default)\n"
#endif
"--help Show this help message\n\n"
"Example command line:\n\n"
"%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",
+1 -1
View File
@@ -106,7 +106,7 @@ void Miner::on_block(const BlockTemplate& block)
Job& j = m_job[next_index];
hash seed;
const uint32_t extra_nonce = PoolBlock::signal_v2_readiness(static_cast<uint32_t>(m_rng() >> 32));
const uint32_t extra_nonce = static_cast<uint32_t>(m_rng() >> 32);
j.m_blobSize = block.get_hashing_blob(extra_nonce, j.m_blob, j.m_height, j.m_sidechainHeight, j.m_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
const uint64_t next_full_nonce = (static_cast<uint64_t>(extra_nonce) << 32) | std::numeric_limits<uint32_t>::max();
+82 -22
View File
@@ -26,6 +26,7 @@
#include "json_rpc_request.h"
#include "json_parsers.h"
#include "block_template.h"
#include "p2pool_api.h"
#include <rapidjson/document.h>
#include <fstream>
#include <numeric>
@@ -134,7 +135,7 @@ P2PServer::P2PServer(p2pool* pool)
}
load_peer_list();
start_listening(params.m_p2pAddresses);
start_listening(params.m_p2pAddresses, params.m_upnp);
}
P2PServer::~P2PServer()
@@ -741,7 +742,10 @@ void P2PServer::broadcast(const PoolBlock& block, const PoolBlock* parent)
return;
}
Broadcast* data = new Broadcast();
Broadcast* data = new Broadcast{};
data->id = block.m_sidechainId;
data->received_timestamp = block.m_receivedTimestamp;
int outputs_offset, outputs_blob_size;
const std::vector<uint8_t> mainchain_data = block.serialize_mainchain_data(nullptr, nullptr, &outputs_offset, &outputs_blob_size);
@@ -857,13 +861,6 @@ void P2PServer::on_broadcast()
return;
}
ON_SCOPE_LEAVE([&broadcast_queue]()
{
for (const Broadcast* data : broadcast_queue) {
delete data;
}
});
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if (!client->is_good()) {
continue;
@@ -936,6 +933,12 @@ void P2PServer::on_broadcast()
}
}
}
for (const Broadcast* data : broadcast_queue) {
const double t = static_cast<double>(microseconds_since_epoch() - data->received_timestamp) * 1e-3;
LOGINFO(5, "Block " << data->id << " took " << t << " ms to process and relay to other peers");
delete data;
}
}
uint64_t P2PServer::get_random64()
@@ -989,13 +992,13 @@ void P2PServer::show_peers() const
LOGINFO(0, "Total: " << n << " peers");
}
int P2PServer::listen_port() const
int P2PServer::external_listen_port() const
{
const Params& params = m_pool->params();
return params.m_p2pExternalPort ? params.m_p2pExternalPort : m_listenPort;
}
int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size, bool compact)
int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size, bool compact, uint64_t received_timestamp)
{
int result;
@@ -1010,6 +1013,7 @@ int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size, bool compact
m_lookForMissingBlocks = true;
}
m_block->m_receivedTimestamp = received_timestamp;
return result;
}
@@ -1029,6 +1033,7 @@ void P2PServer::on_timer()
update_peer_connections();
check_zmq();
check_block_template();
api_update_local_stats();
}
void P2PServer::flush_cache()
@@ -1153,6 +1158,12 @@ void P2PServer::check_zmq()
return;
}
if (!m_pool->zmq_running()) {
LOGERR(1, "ZMQ is not running, restarting it");
m_pool->restart_zmq();
return;
}
const uint64_t cur_time = seconds_since_epoch();
const uint64_t last_active = m_pool->zmq_last_active();
@@ -1211,6 +1222,57 @@ void P2PServer::on_shutdown()
uv_close(reinterpret_cast<uv_handle_t*>(&m_showPeersAsync), nullptr);
}
void P2PServer::api_update_local_stats()
{
if (!m_pool->api() || !m_pool->params().m_localStats || ((m_timerCounter % 30) != 5)) {
return;
}
m_pool->api()->set(p2pool_api::Category::LOCAL, "p2p",
[this](log::Stream& s)
{
const uint64_t cur_time = seconds_since_epoch();
s << "{\"connections\":" << m_numConnections.load()
<< ",\"incoming_connections\":" << m_numIncomingConnections.load()
<< ",\"peer_list_size\":" << m_peerList.size()
<< ",\"peers\":[";
bool first = true;
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
if (client->m_listenPort >= 0) {
char buf[32] = {};
log::Stream s1(buf);
if (client->m_SoftwareVersion) {
s1 << client->software_name() << " v" << (client->m_SoftwareVersion >> 16) << '.' << (client->m_SoftwareVersion & 0xFFFF);
}
if (!first) {
s << ',';
}
s << '"'
<< (client->m_isIncoming ? "I," : "O,")
<< (cur_time - client->m_connectedTime) << ','
<< client->m_pingTime << ','
<< static_cast<const char*>(buf) << ','
<< client->m_broadcastMaxHeight << ','
<< static_cast<char*>(client->m_addrString)
<< '"';
if (s.m_pos + 128 >= s.m_bufSize) {
break;
}
first = false;
}
}
s << "],\"uptime\":" << cur_time - m_pool->start_time() << '}';
});
}
P2PServer::P2PClient::~P2PClient()
{
}
@@ -1488,11 +1550,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
m_pingTime = std::max<int64_t>(duration_cast<milliseconds>(high_resolution_clock::now() - m_lastPeerListRequestTime).count(), 0);
--m_peerListPendingRequests;
if (!on_peer_list_response(buf + 1)) {
ban(DEFAULT_BAN_TIME);
server->remove_peer_from_list(this);
return false;
}
on_peer_list_response(buf + 1);
}
}
break;
@@ -1842,7 +1900,7 @@ void P2PServer::P2PClient::on_after_handshake(uint8_t* &p)
LOGINFO(5, "sending LISTEN_PORT to " << static_cast<char*>(m_addrString));
*(p++) = static_cast<uint8_t>(MessageId::LISTEN_PORT);
const int32_t port = m_owner->listen_port();
const int32_t port = m_owner->external_listen_port();
memcpy(p, &port, sizeof(port));
p += sizeof(port);
@@ -1922,11 +1980,13 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size,
return true;
}
const uint64_t received_timestamp = microseconds_since_epoch();
P2PServer* server = static_cast<P2PServer*>(m_owner);
MutexLock lock(server->m_blockLock);
const int result = server->deserialize_block(buf, size, false);
const int result = server->deserialize_block(buf, size, false, received_timestamp);
if (result != 0) {
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
return false;
@@ -1967,11 +2027,13 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size,
return false;
}
const uint64_t received_timestamp = microseconds_since_epoch();
P2PServer* server = static_cast<P2PServer*>(m_owner);
MutexLock lock(server->m_blockLock);
const int result = server->deserialize_block(buf, size, compact);
const int result = server->deserialize_block(buf, size, compact, received_timestamp);
if (result != 0) {
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
return false;
@@ -2120,7 +2182,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
});
}
bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf)
void P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf)
{
P2PServer* server = static_cast<P2PServer*>(m_owner);
const uint64_t cur_time = seconds_since_epoch();
@@ -2181,8 +2243,6 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf)
server->m_peerList.emplace_back(Peer{ is_v6, ip, port, 0, cur_time });
}
}
return true;
}
bool P2PServer::P2PClient::handle_incoming_block_async(const PoolBlock* block, uint64_t max_time_delta)
+8 -3
View File
@@ -104,7 +104,7 @@ public:
bool on_block_response(const uint8_t* buf, uint32_t size, const hash& expected_id);
bool on_block_broadcast(const uint8_t* buf, uint32_t size, bool compact);
bool on_peer_list_request(const uint8_t* buf);
bool on_peer_list_response(const uint8_t* buf);
void on_peer_list_response(const uint8_t* buf);
bool handle_incoming_block_async(const PoolBlock* block, uint64_t max_time_delta = 0);
void handle_incoming_block(p2pool* pool, PoolBlock& block, const uint32_t reset_counter, const raw_ip& addr, std::vector<hash>& missing_blocks);
@@ -155,7 +155,7 @@ public:
void show_peers_async();
size_t peer_list_size() const { MutexLock lock(m_peerListLock); return m_peerList.size(); }
int listen_port() const override;
int external_listen_port() const override;
uint32_t max_outgoing_peers() const { return m_maxOutgoingPeers; }
uint32_t max_incoming_peers() const { return m_maxIncomingPeers; }
@@ -163,7 +163,7 @@ public:
void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 450U); }
void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 450U); }
int deserialize_block(const uint8_t* buf, uint32_t size, bool compact);
int deserialize_block(const uint8_t* buf, uint32_t size, bool compact, uint64_t received_timestamp);
const PoolBlock* get_block() const { return m_block; }
private:
@@ -226,6 +226,9 @@ private:
struct Broadcast
{
hash id;
uint64_t received_timestamp;
std::vector<uint8_t> blob;
std::vector<uint8_t> pruned_blob;
std::vector<uint8_t> compact_blob;
@@ -256,6 +259,8 @@ private:
void show_peers() const;
void on_shutdown() override;
void api_update_local_stats();
};
} // namespace p2pool
+66 -36
View File
@@ -52,42 +52,51 @@ namespace p2pool {
p2pool::p2pool(int argc, char* argv[])
: m_stopped(false)
, m_params(new Params(argc, argv))
, m_updateSeed(true)
, m_submitBlockData{}
, m_zmqLastActive(0)
, m_startTime(seconds_since_epoch())
, m_lastMinerDataReceived(0)
{
LOGINFO(1, log::LightCyan() << VERSION);
if (!m_params->m_wallet.valid()) {
Params* p = new Params(argc, argv);
m_params = p;
#ifdef WITH_UPNP
if (p->m_upnp) {
init_upnp();
}
#endif
if (!p->m_wallet.valid()) {
LOGERR(1, "Invalid wallet address. Try \"p2pool --help\".");
throw std::exception();
}
m_hostStr = m_params->m_host;
m_hostStr = p->m_host;
if (m_params->m_socks5Proxy.empty()) {
if (m_params->m_dns) {
if (p->m_socks5Proxy.empty()) {
if (p->m_dns) {
bool is_v6;
if (!resolve_host(m_params->m_host, is_v6)) {
LOGERR(1, "resolve_host failed for " << m_params->m_host);
if (!resolve_host(p->m_host, is_v6)) {
LOGERR(1, "resolve_host failed for " << p->m_host);
throw std::exception();
}
}
else if (m_params->m_host.find_first_not_of("0123456789.:") != std::string::npos) {
LOGERR(1, "Can't resolve hostname " << m_params->m_host << " with DNS disabled");
else if (p->m_host.find_first_not_of("0123456789.:") != std::string::npos) {
LOGERR(1, "Can't resolve hostname " << p->m_host << " with DNS disabled");
throw std::exception();
}
}
{
const bool changed = (m_params->m_host != m_hostStr);
const std::string rpc_port = ':' + std::to_string(m_params->m_rpcPort);
const std::string zmq_port = ":ZMQ:" + std::to_string(m_params->m_zmqPort);
const bool changed = (p->m_host != m_hostStr);
const std::string rpc_port = ':' + std::to_string(p->m_rpcPort);
const std::string zmq_port = ":ZMQ:" + std::to_string(p->m_zmqPort);
m_hostStr += rpc_port + zmq_port;
if (changed) {
m_hostStr += " (" + m_params->m_host + ')';
m_hostStr += " (" + p->m_host + ')';
}
}
@@ -95,12 +104,12 @@ p2pool::p2pool(int argc, char* argv[])
generate_keys(pub, sec);
uint8_t view_tag;
if (!m_params->m_wallet.get_eph_public_key(sec, 0, eph_public_key, view_tag)) {
if (!p->m_wallet.get_eph_public_key(sec, 0, eph_public_key, view_tag)) {
LOGERR(1, "Invalid wallet address: get_eph_public_key failed");
throw std::exception();
}
const NetworkType type = m_params->m_wallet.type();
const NetworkType type = p->m_wallet.type();
if (type == NetworkType::Testnet) {
LOGWARN(1, "Mining to a testnet wallet address");
@@ -145,27 +154,27 @@ p2pool::p2pool(int argc, char* argv[])
#endif
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 = p->m_apiPath.empty() ? nullptr : new p2pool_api(p->m_apiPath, p->m_localStats);
if (m_params->m_localStats && !m_api) {
if (p->m_localStats && !m_api) {
LOGERR(1, "--local-api and --stratum-api command line parameters can't be used without --data-api");
throw std::exception();
}
m_sideChain = new SideChain(this, type, m_params->m_mini ? "mini" : nullptr);
m_sideChain = new SideChain(this, type, p->m_mini ? "mini" : nullptr);
if (m_params->m_p2pAddresses.empty()) {
if (p->m_p2pAddresses.empty()) {
const int p2p_port = m_sideChain->is_mini() ? DEFAULT_P2P_PORT_MINI : DEFAULT_P2P_PORT;
char buf[log::Stream::BUF_SIZE + 1];
char buf[48] = {};
log::Stream s(buf);
s << "[::]:" << p2p_port << ",0.0.0.0:" << p2p_port << '\0';
s << "[::]:" << p2p_port << ",0.0.0.0:" << p2p_port;
m_params->m_p2pAddresses = buf;
p->m_p2pAddresses = buf;
}
#ifdef WITH_RANDOMX
if (m_params->m_disableRandomX) {
if (p->m_disableRandomX) {
m_hasher = new RandomX_Hasher_RPC(this);
}
else {
@@ -189,6 +198,12 @@ p2pool::p2pool(int argc, char* argv[])
p2pool::~p2pool()
{
#ifdef WITH_UPNP
if (m_params->m_upnp) {
destroy_upnp();
}
#endif
uv_rwlock_destroy(&m_mainchainLock);
uv_rwlock_destroy(&m_minerDataLock);
uv_mutex_destroy(&m_foundBlocksLock);
@@ -205,9 +220,9 @@ p2pool::~p2pool()
delete m_params;
}
bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result)
bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result, bool force_light_mode)
{
return m_hasher->calculate(data, size, height, seed, result);
return m_hasher->calculate(data, size, height, seed, result, force_light_mode);
}
uint64_t p2pool::get_seed_height(uint64_t height)
@@ -321,9 +336,12 @@ void p2pool::handle_miner_data(MinerData& data)
// Tx secret keys from all miners change every block, so cache can be cleared here
if (m_sideChain->precalcFinished()) {
clear_crypto_cache();
// Clear all cache entries older than the previous miner data
clear_crypto_cache(m_lastMinerDataReceived);
}
m_lastMinerDataReceived = seconds_since_epoch();
if (!is_main_thread()) {
update_block_template_async();
}
@@ -735,14 +753,6 @@ void p2pool::download_block_headers(uint64_t current_height)
if (parse_block_headers_range(data, size) == current_height - start_height) {
update_median_timestamp();
if (m_serversStarted.exchange(1) == 0) {
try {
m_ZMQReader = new ZMQReader(m_params->m_host, m_params->m_zmqPort, m_params->m_socks5Proxy, this);
}
catch (const std::exception& e) {
LOGERR(1, "Couldn't start ZMQ reader: exception " << e.what());
PANIC_STOP();
}
m_stratumServer = new StratumServer(this);
m_p2pServer = new P2PServer(this);
#ifdef WITH_RANDOMX
@@ -750,6 +760,13 @@ void p2pool::download_block_headers(uint64_t current_height)
start_mining(m_params->m_minerThreads);
}
#endif
try {
m_ZMQReader = new ZMQReader(m_params->m_host, m_params->m_zmqPort, m_params->m_socks5Proxy, this);
}
catch (const std::exception& e) {
LOGERR(1, "Couldn't start ZMQ reader: exception " << e.what());
PANIC_STOP();
}
api_update_network_stats();
get_miner_data();
}
@@ -1238,8 +1255,13 @@ void p2pool::api_update_pool_stats()
return;
}
const PoolBlock* tip = m_sideChain->chainTip();
const uint64_t bottom_height = m_sideChain->bottom_height(tip);
const uint64_t pplns_window_size = (tip && bottom_height) ? (tip->m_sidechainHeight - bottom_height + 1U) : m_sideChain->chain_window_size();
uint64_t t;
const difficulty_type& diff = m_sideChain->difficulty();
const difficulty_type diff = m_sideChain->difficulty();
const uint64_t height = tip ? tip->m_sidechainHeight : 0;
const uint64_t hashrate = udiv128(diff.hi, diff.lo, m_sideChain->block_time(), &t);
const uint64_t miners = std::max<uint64_t>(m_sideChain->miner_count(), m_p2pServer ? m_p2pServer->peer_list_size() : 0U);
const difficulty_type total_hashes = m_sideChain->total_hashes();
@@ -1261,7 +1283,7 @@ void p2pool::api_update_pool_stats()
}
m_api->set(p2pool_api::Category::POOL, "stats",
[hashrate, miners, &total_hashes, last_block_found_time, last_block_found_height, total_blocks_found, &pplns_weight](log::Stream& s)
[hashrate, miners, &total_hashes, last_block_found_time, last_block_found_height, total_blocks_found, &pplns_weight, pplns_window_size, diff, height](log::Stream& s)
{
s << "{\"pool_list\":[\"pplns\"],\"pool_statistics\":{\"hashRate\":" << hashrate
<< ",\"miners\":" << miners
@@ -1270,6 +1292,9 @@ void p2pool::api_update_pool_stats()
<< ",\"lastBlockFound\":" << last_block_found_height
<< ",\"totalBlocksFound\":" << total_blocks_found
<< ",\"pplnsWeight\":" << pplns_weight
<< ",\"pplnsWindowSize\":" << pplns_window_size
<< ",\"sidechainDifficulty\":" << diff
<< ",\"sidechainHeight\":" << height
<< "}}";
});
@@ -1535,6 +1560,11 @@ void p2pool::stop()
}
}
bool p2pool::zmq_running() const
{
return m_ZMQReader && m_ZMQReader->is_running();
}
void p2pool::restart_zmq()
{
// If p2pool is stopped, m_restartZMQAsync is most likely already closed
+5 -2
View File
@@ -60,7 +60,7 @@ public:
p2pool_api* api() const { return m_api; }
RandomX_Hasher_Base* hasher() const { return m_hasher; }
bool calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result);
bool calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result, bool force_light_mode);
static uint64_t get_seed_height(uint64_t height);
bool get_seed(uint64_t height, hash& seed) const;
@@ -95,6 +95,7 @@ public:
void stop_mining();
#endif
bool zmq_running() const;
uint64_t zmq_last_active() const { return m_zmqLastActive; }
uint64_t start_time() const { return m_startTime; }
void restart_zmq();
@@ -113,7 +114,7 @@ private:
std::atomic<bool> m_stopped;
std::string m_hostStr;
Params* m_params;
const Params* m_params;
p2pool_api* m_api;
SideChain* m_sideChain;
@@ -210,6 +211,8 @@ private:
hash m_getMinerDataHash;
bool m_getMinerDataPending = false;
std::atomic<uint64_t> m_lastMinerDataReceived;
};
} // namespace p2pool
+16 -2
View File
@@ -100,10 +100,12 @@ Params::Params(int argc, char* argv[])
ok = true;
}
#ifdef WITH_RANDOMX
if (strcmp(argv[i], "--no-randomx") == 0) {
m_disableRandomX = true;
ok = true;
}
#endif
if ((!strcmp(argv[i], "--out-peers") || !strcmp(argv[i], "--outpeers")) && (i + 1 < argc)) {
m_maxOutgoingPeers = std::min(std::max(strtoul(argv[++i], nullptr, 10), 10UL), 450UL);
@@ -151,6 +153,18 @@ Params::Params(int argc, char* argv[])
ok = true;
}
#ifdef WITH_UPNP
if ((strcmp(argv[i], "--no-upnp") == 0) || (strcmp(argv[i], "--no-igd") == 0)) {
m_upnp = false;
ok = true;
}
if (strcmp(argv[i], "--upnp-stratum") == 0) {
m_upnpStratum = true;
ok = true;
}
#endif
if (!ok) {
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
p2pool_usage();
@@ -161,9 +175,9 @@ Params::Params(int argc, char* argv[])
if (m_stratumAddresses.empty()) {
const int stratum_port = DEFAULT_STRATUM_PORT;
char buf[log::Stream::BUF_SIZE + 1];
char buf[48] = {};
log::Stream s(buf);
s << "[::]:" << stratum_port << ",0.0.0.0:" << stratum_port << '\0';
s << "[::]:" << stratum_port << ",0.0.0.0:" << stratum_port;
m_stratumAddresses = buf;
}
+7
View File
@@ -53,6 +53,13 @@ struct Params
std::string m_socks5Proxy;
bool m_dns = true;
uint32_t m_p2pExternalPort = 0;
#ifdef WITH_UPNP
bool m_upnp = true;
bool m_upnpStratum = false;
#else
bool m_upnp = false;
bool m_upnpStratum = false;
#endif
};
} // namespace p2pool
+8 -34
View File
@@ -53,6 +53,7 @@ PoolBlock::PoolBlock()
, m_wantBroadcast(false)
, m_precalculated(false)
, m_localTimestamp(seconds_since_epoch())
, m_receivedTimestamp(0)
{
}
@@ -102,6 +103,7 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_precalculated = b.m_precalculated;
m_localTimestamp = seconds_since_epoch();
m_receivedTimestamp = b.m_receivedTimestamp;
return *this;
}
@@ -220,18 +222,9 @@ std::vector<uint8_t> PoolBlock::serialize_sidechain_data() const
const hash& spend = m_minerWallet.spend_public_key();
const hash& view = m_minerWallet.view_public_key();
const int sidechain_version = get_sidechain_version();
data.insert(data.end(), spend.h, spend.h + HASH_SIZE);
data.insert(data.end(), view.h, view.h + HASH_SIZE);
if (sidechain_version > 1) {
data.insert(data.end(), m_txkeySecSeed.h, m_txkeySecSeed.h + HASH_SIZE);
}
else {
data.insert(data.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE);
}
data.insert(data.end(), m_txkeySecSeed.h, m_txkeySecSeed.h + HASH_SIZE);
data.insert(data.end(), m_parent.h, m_parent.h + HASH_SIZE);
writeVarint(m_uncles.size(), data);
@@ -248,10 +241,8 @@ std::vector<uint8_t> PoolBlock::serialize_sidechain_data() const
writeVarint(m_cumulativeDifficulty.lo, data);
writeVarint(m_cumulativeDifficulty.hi, data);
if (sidechain_version > 1) {
const uint8_t* p = reinterpret_cast<const uint8_t*>(m_sidechainExtraBuf);
data.insert(data.end(), p, p + sizeof(m_sidechainExtraBuf));
}
const uint8_t* p = reinterpret_cast<const uint8_t*>(m_sidechainExtraBuf);
data.insert(data.end(), p, p + sizeof(m_sidechainExtraBuf));
#if POOL_BLOCK_DEBUG
if (!m_sideChainDataDebug.empty() && (data != m_sideChainDataDebug)) {
@@ -277,9 +268,10 @@ void PoolBlock::reset_offchain_data()
m_precalculated = false;
m_localTimestamp = seconds_since_epoch();
m_receivedTimestamp = 0;
}
bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash)
bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash, bool force_light_mode)
{
alignas(8) uint8_t hashes[HASH_SIZE * 3];
@@ -357,7 +349,7 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
writeVarint(count, [&blob, &blob_size](uint8_t b) { blob[blob_size++] = b; });
return hasher->calculate(blob, blob_size, height, seed_hash, pow_hash);
return hasher->calculate(blob, blob_size, height, seed_hash, pow_hash, force_light_mode);
}
uint64_t PoolBlock::get_payout(const Wallet& w) const
@@ -386,24 +378,6 @@ uint64_t PoolBlock::get_payout(const Wallet& w) const
return 0;
}
static constexpr uint64_t VERSION2_MAINNET_TIMESTAMP = 1679173200U; // 2023-03-18 21:00 UTC
static constexpr uint64_t VERSION2_TESTNET_TIMESTAMP = 1674507600U; // 2023-01-23 21:00 UTC
uint32_t PoolBlock::signal_v2_readiness(uint32_t extra_nonce)
{
const uint64_t ts = (SideChain::network_type() == NetworkType::Mainnet) ? VERSION2_MAINNET_TIMESTAMP : VERSION2_TESTNET_TIMESTAMP;
if (time(nullptr) < static_cast<int64_t>(ts)) {
return (extra_nonce & 0x007FFFFFUL) | 0xFF000000UL;
}
return extra_nonce;
}
int PoolBlock::get_sidechain_version() const
{
const uint64_t ts = (SideChain::network_type() == NetworkType::Mainnet) ? VERSION2_MAINNET_TIMESTAMP : VERSION2_TESTNET_TIMESTAMP;
return (m_timestamp >= ts) ? 2 : 1;
}
hash PoolBlock::calculate_tx_key_seed() const
{
const char domain[] = "tx_key_seed";
+2 -7
View File
@@ -133,6 +133,7 @@ struct PoolBlock
bool m_precalculated;
uint64_t m_localTimestamp;
uint64_t m_receivedTimestamp;
std::vector<uint8_t> serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr, const uint32_t* nonce = nullptr, const uint32_t* extra_nonce = nullptr) const;
std::vector<uint8_t> serialize_sidechain_data() const;
@@ -140,7 +141,7 @@ struct PoolBlock
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact);
void reset_offchain_data();
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, bool force_light_mode = false);
uint64_t get_payout(const Wallet& w) const;
@@ -148,12 +149,6 @@ struct PoolBlock
// 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; }
// Signal hardfork readiness (only before the v2 hardfork)
// TODO: remove this code after hardfork
static uint32_t signal_v2_readiness(uint32_t extra_nonce);
int get_sidechain_version() const;
typedef std::array<uint8_t, HASH_SIZE + NONCE_SIZE + EXTRA_NONCE_SIZE> full_id;
FORCEINLINE full_id get_full_id() const
+5 -24
View File
@@ -260,27 +260,10 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
READ_BUF(m_txkeySecSeed.h, HASH_SIZE);
const int sidechain_version = get_sidechain_version();
if (sidechain_version > 1) {
hash pub;
get_tx_keys(pub, m_txkeySec, m_txkeySecSeed, m_prevId);
if (pub != m_txkeyPub) {
return __LINE__;
}
}
else {
// Both values are the same before v2
m_txkeySec = m_txkeySecSeed;
// Enforce deterministic tx keys starting from v15
if (m_majorVersion >= HARDFORK_VIEW_TAGS_VERSION) {
hash pub, sec;
get_tx_keys(pub, sec, spend_pub_key, m_prevId);
if ((pub != m_txkeyPub) || (sec != m_txkeySec)) {
return __LINE__;
}
}
hash pub;
get_tx_keys(pub, m_txkeySec, m_txkeySecSeed, m_prevId);
if (pub != m_txkeyPub) {
return __LINE__;
}
if (!check_keys(m_txkeyPub, m_txkeySec)) {
@@ -329,9 +312,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
READ_VARINT(m_cumulativeDifficulty.lo);
READ_VARINT(m_cumulativeDifficulty.hi);
if (sidechain_version > 1) {
READ_BUF(m_sidechainExtraBuf, sizeof(m_sidechainExtraBuf));
}
READ_BUF(m_sidechainExtraBuf, sizeof(m_sidechainExtraBuf));
#undef READ_BYTE
#undef EXPECT_BYTE
+4 -4
View File
@@ -322,10 +322,10 @@ void RandomX_Hasher::sync_wait()
ReadLock lock2(m_cacheLock);
}
bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*/, const hash& seed, hash& result)
bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*/, const hash& seed, hash& result, bool force_light_mode)
{
// First try to use the dataset if it's ready
if (uv_rwlock_tryrdlock(&m_datasetLock) == 0) {
if (!force_light_mode && (uv_rwlock_tryrdlock(&m_datasetLock) == 0)) {
ON_SCOPE_LEAVE([this]() { uv_rwlock_rdunlock(&m_datasetLock); });
if (m_stopped.load()) {
@@ -340,7 +340,7 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*
}
}
// If dataset is not ready, use the cache and wait if necessary
// If dataset is not ready, or force_light_mode = true, use the cache and wait if necessary
ReadLock lock(m_cacheLock);
if (m_stopped.load()) {
@@ -429,7 +429,7 @@ void RandomX_Hasher_RPC::loop(void* data)
LOGINFO(1, "event loop stopped");
}
bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t height, const hash& /*seed*/, hash& h)
bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t height, const hash& /*seed*/, hash& h, bool /*force_light_mode*/)
{
MutexLock lock(m_requestMutex);
+3 -3
View File
@@ -40,7 +40,7 @@ public:
virtual uint32_t seed_counter() const { return 0; }
virtual void sync_wait() {}
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, bool force_light_mode) = 0;
};
#ifdef WITH_RANDOMX
@@ -60,7 +60,7 @@ public:
uint32_t seed_counter() const override { return m_seedCounter.load(); }
void sync_wait() override;
bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) override;
bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result, bool force_light_mode) override;
private:
@@ -100,7 +100,7 @@ public:
explicit RandomX_Hasher_RPC(p2pool* pool);
~RandomX_Hasher_RPC();
bool calculate(const void* data_ptr, size_t size, uint64_t height, const hash& seed, hash& h) override;
bool calculate(const void* data_ptr, size_t size, uint64_t height, const hash& seed, hash& h, bool force_light_mode) override;
private:
static void loop(void* data);
+110 -42
View File
@@ -217,7 +217,6 @@ SideChain::~SideChain()
void SideChain::fill_sidechain_data(PoolBlock& block, std::vector<MinerShare>& shares) const
{
const int sidechain_version = block.get_sidechain_version();
block.m_uncles.clear();
ReadLock lock(m_sidechainLock);
@@ -229,20 +228,15 @@ void SideChain::fill_sidechain_data(PoolBlock& block, std::vector<MinerShare>& s
block.m_sidechainHeight = 0;
block.m_difficulty = m_minDifficulty;
block.m_cumulativeDifficulty = m_minDifficulty;
if (sidechain_version > 1) {
block.m_txkeySecSeed = m_consensusHash;
get_tx_keys(block.m_txkeyPub, block.m_txkeySec, block.m_txkeySecSeed, block.m_prevId);
}
block.m_txkeySecSeed = m_consensusHash;
get_tx_keys(block.m_txkeyPub, block.m_txkeySec, block.m_txkeySecSeed, block.m_prevId);
get_shares(&block, shares);
return;
}
if (sidechain_version > 1) {
block.m_txkeySecSeed = (block.m_prevId == tip->m_prevId) ? tip->m_txkeySecSeed : tip->calculate_tx_key_seed();
get_tx_keys(block.m_txkeyPub, block.m_txkeySec, block.m_txkeySecSeed, block.m_prevId);
}
block.m_txkeySecSeed = (block.m_prevId == tip->m_prevId) ? tip->m_txkeySecSeed : tip->calculate_tx_key_seed();
get_tx_keys(block.m_txkeyPub, block.m_txkeySec, block.m_txkeySecSeed, block.m_prevId);
block.m_parent = tip->m_sidechainId;
block.m_sidechainHeight = tip->m_sidechainHeight + 1;
@@ -368,8 +362,7 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares
// Dynamic PPLNS window starting from v2
// Limit PPLNS weight to 2x of the Monero difficulty (max 2 blocks per PPLNS window on average)
const int sidechain_version = tip->get_sidechain_version();
const difficulty_type max_pplns_weight = (sidechain_version > 1) ? (mainchain_diff * 2) : diff_max;
const difficulty_type max_pplns_weight = mainchain_diff * 2;
difficulty_type pplns_weight;
unordered_set<MinerShare> shares_set;
@@ -454,7 +447,7 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares
const uint64_t n = shares.size();
// Shuffle shares
if ((sidechain_version > 1) && (n > 1)) {
if (n > 1) {
hash h;
keccak(tip->m_txkeySecSeed.h, HASH_SIZE, h.h);
@@ -577,7 +570,20 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
}
if (!block.m_difficulty.check_pow(pow_hash)) {
LOGWARN(3, "add_external_block mined by " << block.m_minerWallet << ": not enough PoW for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight);
LOGWARN(3,
"add_external_block mined by " << block.m_minerWallet <<
": not enough PoW for height = " << block.m_sidechainHeight <<
", id = " << block.m_sidechainId <<
", nonce = " << block.m_nonce <<
", mainchain height = " << block.m_txinGenHeight
);
// Calculate the same hash second time to check if it's an unstable hardware that caused this
hash pow_hash2;
if (block.get_pow_hash(m_pool->hasher(), block.m_txinGenHeight, seed, pow_hash2, true) && (pow_hash2 != pow_hash)) {
LOGERR(0, "UNSTABLE HARDWARE DETECTED: Calculated the same hash twice, got different results: " << pow_hash << " != " << pow_hash2 << " (sidechain id = " << block.m_sidechainId << ')');
}
return false;
}
@@ -863,12 +869,12 @@ void SideChain::print_status(bool obtain_sidechain_lock) const
const PoolBlock* tip = m_chainTip;
std::vector<MinerShare> shares;
uint64_t bottom_height = 0;
uint64_t bh = 0;
if (tip) {
get_shares(tip, shares, &bottom_height, true);
get_shares(tip, shares, &bh, true);
}
const uint64_t window_size = (tip && bottom_height) ? (tip->m_sidechainHeight - bottom_height + 1U) : m_chainWindowSize;
const uint64_t window_size = (tip && bh) ? (tip->m_sidechainHeight - bh + 1U) : m_chainWindowSize;
uint64_t block_depth = 0;
const PoolBlock* cur = tip;
@@ -1107,6 +1113,22 @@ bool SideChain::is_mini() const
return (memcmp(m_consensusId.data(), mini_consensus_id, HASH_SIZE) == 0);
}
uint64_t SideChain::bottom_height(const PoolBlock* tip) const
{
if (!tip) {
return 0;
}
uint64_t bottom_height;
std::vector<MinerShare> shares;
if (!get_shares(tip, shares, &bottom_height, true)) {
return 0;
}
return bottom_height;
}
bool SideChain::split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards)
{
const size_t num_shares = shares.size();
@@ -1208,7 +1230,11 @@ bool SideChain::get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>
std::nth_element(tmpTimestamps.begin(), tmpTimestamps.begin() + index2, tmpTimestamps.end());
const uint64_t timestamp2 = oldest_timestamp + tmpTimestamps[index2];
const uint64_t delta_t = (timestamp2 > timestamp1) ? (timestamp2 - timestamp1) : 1;
// Make a reasonable assumption that each block has higher timestamp, so delta_t can't be less than delta_index
// Because if it is, someone is trying to mess with timestamps
// In reality, delta_t ~ delta_index*10 (sidechain block time)
const uint64_t delta_index = (index2 > index1) ? (index2 - index1) : 1U;
const uint64_t delta_t = (timestamp2 > timestamp1 + delta_index) ? (timestamp2 - timestamp1) : delta_index;
difficulty_type diff1{ std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max() };
difficulty_type diff2{ 0, 0 };
@@ -1323,15 +1349,13 @@ void SideChain::verify_loop(PoolBlock* block)
void SideChain::verify(PoolBlock* block)
{
const int sidechain_version = block->get_sidechain_version();
// Genesis block
if (block->m_sidechainHeight == 0) {
if (!block->m_parent.empty() ||
!block->m_uncles.empty() ||
(block->m_difficulty != m_minDifficulty) ||
(block->m_cumulativeDifficulty != m_minDifficulty) ||
((sidechain_version > 1) && (block->m_txkeySecSeed != m_consensusHash)))
(block->m_txkeySecSeed != m_consensusHash))
{
block->m_invalid = true;
}
@@ -1377,15 +1401,13 @@ void SideChain::verify(PoolBlock* block)
return;
}
if (sidechain_version > 1) {
// Check m_txkeySecSeed
const hash h = (block->m_prevId == parent->m_prevId) ? parent->m_txkeySecSeed : parent->calculate_tx_key_seed();
if (block->m_txkeySecSeed != h) {
LOGWARN(3, "block " << block->m_sidechainId << " has invalid tx key seed: expected " << h << ", got " << block->m_txkeySecSeed);
block->m_verified = true;
block->m_invalid = true;
return;
}
// Check m_txkeySecSeed
const hash h = (block->m_prevId == parent->m_prevId) ? parent->m_txkeySecSeed : parent->calculate_tx_key_seed();
if (block->m_txkeySecSeed != h) {
LOGWARN(3, "block " << block->m_sidechainId << " has invalid tx key seed: expected " << h << ", got " << block->m_txkeySecSeed);
block->m_verified = true;
block->m_invalid = true;
return;
}
const uint64_t expectedHeight = parent->m_sidechainHeight + 1;
@@ -1543,7 +1565,11 @@ void SideChain::verify(PoolBlock* block)
}
difficulty_type diff;
if (!get_difficulty(parent, m_difficultyData, diff)) {
if (parent == m_chainTip) {
LOGINFO(6, "block " << block->m_sidechainId << " is built on top of the current chain tip, using current difficulty for verification");
diff = difficulty();
}
else if (!get_difficulty(parent, m_difficultyData, diff)) {
block->m_invalid = true;
return;
}
@@ -1724,7 +1750,7 @@ PoolBlock* SideChain::get_parent(const PoolBlock* block) const
return nullptr;
}
bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative)
bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative) const
{
is_alternative = false;
@@ -1783,11 +1809,32 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
uint64_t candidate_mainchain_height = 0;
uint64_t candidate_mainchain_min_height = 0;
hash mainchain_prev_id;
unordered_set<hash> current_chain_monero_blocks, candidate_chain_monero_blocks;
{
const uint64_t k = m_chainWindowSize * m_targetBlockTime * 2 / MONERO_BLOCK_TIME;
current_chain_monero_blocks.reserve(k);
candidate_chain_monero_blocks.reserve(k);
}
for (uint64_t i = 0; (i < m_chainWindowSize) && (old_chain || new_chain); ++i) {
if (old_chain) {
block_total_diff += old_chain->m_difficulty;
for (const hash& uncle : old_chain->m_uncles) {
auto it = m_blocksById.find(uncle);
if (it != m_blocksById.end()) {
block_total_diff += it->second->m_difficulty;
}
}
ChainMain data;
const hash& h = old_chain->m_prevId;
if ((current_chain_monero_blocks.count(h) == 0) && m_pool->chainmain_get_by_hash(h, data)) {
current_chain_monero_blocks.insert(h);
}
old_chain = get_parent(old_chain);
}
@@ -1795,9 +1842,18 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
candidate_mainchain_min_height = candidate_mainchain_min_height ? std::min(candidate_mainchain_min_height, new_chain->m_txinGenHeight) : new_chain->m_txinGenHeight;
candidate_total_diff += new_chain->m_difficulty;
for (const hash& uncle : new_chain->m_uncles) {
auto it = m_blocksById.find(uncle);
if (it != m_blocksById.end()) {
candidate_total_diff += it->second->m_difficulty;
}
}
ChainMain data;
if ((new_chain->m_prevId != mainchain_prev_id) && m_pool->chainmain_get_by_hash(new_chain->m_prevId, data)) {
mainchain_prev_id = new_chain->m_prevId;
const hash& h = new_chain->m_prevId;
if ((candidate_chain_monero_blocks.count(h) == 0) && m_pool->chainmain_get_by_hash(h, data)) {
candidate_chain_monero_blocks.insert(h);
candidate_mainchain_height = std::max(candidate_mainchain_height, data.height);
}
@@ -1809,7 +1865,7 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
return false;
}
// Final check: candidate chain must be built on top of recent mainchain blocks
// Candidate chain must be built on top of recent mainchain blocks
MinerData data = m_pool->miner_data();
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);
@@ -1822,6 +1878,12 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
return false;
}
// Candidate chain must have been mined on top of at least half as many known Monero blocks, compared to the current chain
if (candidate_chain_monero_blocks.size() * 2 < current_chain_monero_blocks.size()) {
LOGWARN(3, "received a longer alternative chain but it wasn't mined on current Monero blockchain: only " << candidate_chain_monero_blocks.size() << '/' << current_chain_monero_blocks.size() << " blocks found");
return false;
}
LOGINFO(3, "received a longer alternative chain: height " <<
log::Gray() << block->m_sidechainHeight << log::NoColor() << " -> " <<
log::Gray() << candidate->m_sidechainHeight << log::NoColor() << ", cumulative difficulty " <<
@@ -1834,18 +1896,22 @@ bool SideChain::is_longer_chain(const PoolBlock* block, const PoolBlock* candida
void SideChain::update_depths(PoolBlock* block)
{
for (size_t i = 1; i <= UNCLE_BLOCK_DEPTH; ++i) {
for (PoolBlock* child : m_blocksByHeight[block->m_sidechainHeight + i]) {
auto it = m_blocksByHeight.find(block->m_sidechainHeight + i);
if (it == m_blocksByHeight.end()) {
continue;
}
for (PoolBlock* child : it->second) {
if (child->m_parent == block->m_sidechainId) {
if (i != 1) {
LOGERR(1, "m_blocksByHeight is inconsistent with child->m_parent. Fix the code!");
LOGWARN(3, "Block " << block->m_sidechainId << ": m_sidechainHeight is inconsistent with child's m_sidechainHeight.");
return;
}
else {
block->m_depth = std::max(block->m_depth, child->m_depth + 1);
}
}
auto it = std::find(child->m_uncles.begin(), child->m_uncles.end(), block->m_sidechainId);
if (it != child->m_uncles.end()) {
if (std::find(child->m_uncles.begin(), child->m_uncles.end(), block->m_sidechainId) != child->m_uncles.end()) {
block->m_depth = std::max(block->m_depth, child->m_depth + i);
}
}
@@ -1865,7 +1931,8 @@ void SideChain::update_depths(PoolBlock* block)
auto it = m_blocksById.find(block->m_parent);
if (it != m_blocksById.end()) {
if (it->second->m_sidechainHeight + 1 != block->m_sidechainHeight) {
LOGERR(1, "m_sidechainHeight is inconsistent with block->m_parent. Fix the code!");
LOGWARN(3, "Block " << block->m_sidechainId << ": m_sidechainHeight is inconsistent with parent's m_sidechainHeight.");
return;
}
if (it->second->m_depth < block->m_depth + 1) {
@@ -1881,7 +1948,8 @@ void SideChain::update_depths(PoolBlock* block)
}
if ((it->second->m_sidechainHeight >= block->m_sidechainHeight) || (it->second->m_sidechainHeight + UNCLE_BLOCK_DEPTH < block->m_sidechainHeight)) {
LOGERR(1, "m_sidechainHeight is inconsistent with block->m_uncles. Fix the code!");
LOGWARN(3, "Block " << block->m_sidechainId << ": m_sidechainHeight is inconsistent with uncle's m_sidechainHeight.");
return;
}
const uint64_t d = block->m_sidechainHeight - it->second->m_sidechainHeight;
+2 -1
View File
@@ -75,6 +75,7 @@ public:
uint64_t last_updated() const;
bool is_default() const;
bool is_mini() const;
uint64_t bottom_height(const PoolBlock* tip) const;
const PoolBlock* chainTip() const { return m_chainTip; }
bool precalcFinished() const { return m_precalcFinished.load(); }
@@ -99,7 +100,7 @@ private:
PoolBlock* get_parent(const PoolBlock* block) const;
// Checks if "candidate" has longer (higher difficulty) chain than "block"
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative);
bool is_longer_chain(const PoolBlock* block, const PoolBlock* candidate, bool& is_alternative) const;
void update_depths(PoolBlock* block);
void prune_old_blocks();
+42 -10
View File
@@ -66,11 +66,13 @@ StratumServer::StratumServer(p2pool* pool)
uv_mutex_init_checked(&m_rngLock);
uv_rwlock_init_checked(&m_hashrateDataLock);
m_extraNonce = PoolBlock::signal_v2_readiness(get_random32());
m_extraNonce = get_random32();
m_submittedSharesPool.resize(10);
for (size_t i = 0; i < m_submittedSharesPool.size(); ++i) {
m_submittedSharesPool[i] = new SubmittedShare{};
SubmittedShare* share = new SubmittedShare{};
ASAN_POISON_MEMORY_REGION(share, sizeof(SubmittedShare));
m_submittedSharesPool[i] = share;
}
uv_async_init_checked(&m_loop, &m_blobsAsync, on_blobs_ready);
@@ -80,7 +82,8 @@ StratumServer::StratumServer(p2pool* pool)
uv_async_init_checked(&m_loop, &m_showWorkersAsync, on_show_workers);
m_showWorkersAsync.data = this;
start_listening(pool->params().m_stratumAddresses);
const Params& params = pool->params();
start_listening(params.m_stratumAddresses, params.m_upnp && params.m_upnpStratum);
}
StratumServer::~StratumServer()
@@ -92,6 +95,7 @@ StratumServer::~StratumServer()
uv_rwlock_destroy(&m_hashrateDataLock);
for (SubmittedShare* share : m_submittedSharesPool) {
ASAN_UNPOISON_MEMORY_REGION(share, sizeof(SubmittedShare));
delete share;
}
}
@@ -106,7 +110,7 @@ void StratumServer::on_block(const BlockTemplate& block)
return;
}
const uint32_t extra_nonce_start = PoolBlock::signal_v2_readiness(get_random32());
const uint32_t extra_nonce_start = get_random32();
m_extraNonce.exchange(extra_nonce_start + num_connections);
BlobsData* blobs_data = new BlobsData{};
@@ -389,6 +393,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
if (!m_submittedSharesPool.empty()) {
share = m_submittedSharesPool.back();
m_submittedSharesPool.pop_back();
ASAN_UNPOISON_MEMORY_REGION(share, sizeof(SubmittedShare));
}
else {
share = new SubmittedShare{};
@@ -876,7 +881,7 @@ void StratumServer::on_share_found(uv_work_t* req)
}
hash pow_hash;
if (!pool->calculate_hash(blob, blob_size, height, seed_hash, pow_hash)) {
if (!pool->calculate_hash(blob, blob_size, height, seed_hash, pow_hash, false)) {
LOGWARN(3, "client " << static_cast<char*>(client->m_addrString) << " couldn't check share PoW");
share->m_result = SubmittedShare::Result::COULDNT_CHECK_POW;
return;
@@ -886,6 +891,13 @@ void StratumServer::on_share_found(uv_work_t* req)
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " submitted a share with invalid PoW");
share->m_result = SubmittedShare::Result::INVALID_POW;
share->m_score = BAD_SHARE_POINTS;
// Calculate the same hash second time to check if it's an unstable hardware that caused this
hash pow_hash2;
if (pool->calculate_hash(blob, blob_size, height, seed_hash, pow_hash2, true) && (pow_hash2 != pow_hash)) {
LOGERR(0, "UNSTABLE HARDWARE DETECTED: Calculated the same hash twice, got different results: " << pow_hash << " != " << pow_hash2);
}
return;
}
@@ -937,11 +949,31 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
if (share->m_highEnoughDifficulty) {
const char* s = client->m_customUser;
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << share->m_mainchainHeight << ", sidechain height " << share->m_sidechainHeight << ", diff " << share->m_sidechainDifficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << ", effort " << share->m_effort << '%');
if (share->m_result == SubmittedShare::Result::OK) {
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << share->m_mainchainHeight << ", sidechain height " << share->m_sidechainHeight << ", diff " << share->m_sidechainDifficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? ", user " : "") << s << ", effort " << share->m_effort << '%');
}
else {
static const char* reason_list[] = {
"stale share",
"couldn't check PoW",
"low difficulty",
"invalid PoW",
"worker banned",
};
static_assert(array_size(reason_list) == static_cast<size_t>(SubmittedShare::Result::OK), "Update reason_list to match SubmittedShare::Result enum");
const size_t k = static_cast<size_t>(share->m_result);
const char* reason = (k < array_size(reason_list)) ? reason_list[k] : "unknown";
LOGWARN(0, log::Green() << "INVALID SHARE: mainchain height " << share->m_mainchainHeight << ", sidechain height " << share->m_sidechainHeight << ", diff " << share->m_sidechainDifficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? ", user " : "") << s << ", reason: " << reason);
}
BACKGROUND_JOB_STOP(StratumServer::on_share_found);
}
ON_SCOPE_LEAVE([share]() { share->m_server->m_submittedSharesPool.push_back(share); });
ON_SCOPE_LEAVE([share]()
{
ASAN_POISON_MEMORY_REGION(share, sizeof(SubmittedShare));
share->m_server->m_submittedSharesPool.push_back(share);
});
StratumServer* server = share->m_server;
@@ -1281,10 +1313,10 @@ void StratumServer::api_update_local_stats(uint64_t timestamp)
double current_effort = static_cast<double>(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double();
int connections = m_numConnections;
int incoming_connections = m_numIncomingConnections;
uint32_t connections = m_numConnections;
uint32_t incoming_connections = m_numIncomingConnections;
m_pool->api()->set(p2pool_api::Category::LOCAL, "stats",
m_pool->api()->set(p2pool_api::Category::LOCAL, "stratum",
[hashrate_15m, hashrate_1h, hashrate_24h, total_hashes, shares_found, shares_failed, average_effort, current_effort, connections, incoming_connections](log::Stream& s)
{
s << "{\"hashrate_15m\":" << hashrate_15m
+6 -2
View File
@@ -42,7 +42,7 @@ public:
uv_loop_t* get_loop() { return &m_loop; }
virtual int listen_port() const { return m_listenPort; }
virtual int external_listen_port() const { return m_listenPort; }
bool connect_to_peer(bool is_v6, const raw_ip& ip, int port);
virtual void on_connect_failed(bool /*is_v6*/, const raw_ip& /*ip*/, int /*port*/) {}
@@ -158,7 +158,11 @@ private:
uv_thread_t m_loopThread;
protected:
void start_listening(const std::string& listen_addresses);
void start_listening(const std::string& listen_addresses, bool upnp);
#ifdef WITH_UPNP
int m_portMapping;
#endif
std::string m_socks5Proxy;
bool m_socks5ProxyV6;
+23 -1
View File
@@ -25,6 +25,9 @@ template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback allocate_new_client)
: m_allocateNewClient(allocate_new_client)
, m_loopThread{}
#ifdef WITH_UPNP
, m_portMapping(0)
#endif
, m_socks5ProxyV6(false)
, m_socks5ProxyIP{}
, m_socks5ProxyPort(-1)
@@ -128,7 +131,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::parse_address_list(const std::str
}
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string& listen_addresses)
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string& listen_addresses, bool upnp)
{
if (listen_addresses.empty()) {
LOGERR(1, "listen address not set");
@@ -206,6 +209,14 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::start_listening(const std::string
LOGINFO(1, "listening on " << log::Gray() << address);
});
#ifdef WITH_UPNP
if (upnp) {
m_portMapping = add_portmapping(external_listen_port(), m_listenPort);
}
#else
(void)upnp;
#endif
const int err = uv_thread_create(&m_loopThread, loop, this);
if (err) {
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
@@ -420,6 +431,13 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::shutdown_tcp()
}
uv_async_send(&m_shutdownAsync);
#ifdef WITH_UPNP
if (m_portMapping) {
remove_portmapping(m_portMapping);
}
#endif
uv_thread_join(&m_loopThread);
uv_mutex_destroy(&m_bansLock);
@@ -1164,6 +1182,10 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::close()
template<size_t READ_BUF_SIZE, size_t WRITE_BUF_SIZE>
void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::Client::ban(uint64_t seconds)
{
if (m_addr.is_localhost()) {
return;
}
if (m_owner) {
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " banned for " << seconds << " seconds");
m_owner->ban(m_addr, seconds);
+154
View File
@@ -25,6 +25,11 @@
#include <sched.h>
#endif
#ifdef WITH_UPNP
#include "miniupnpc.h"
#include "upnpcommands.h"
#endif
static constexpr char log_category_prefix[] = "Util ";
namespace p2pool {
@@ -560,4 +565,153 @@ UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create)
return data;
}
#ifdef WITH_UPNP
static struct UPnP_Discover
{
uv_mutex_t lock;
int error;
UPNPDev* devlist;
} upnp_discover;
void init_upnp()
{
uv_mutex_init_checked(&upnp_discover.lock);
uv_work_t* req = new uv_work_t{};
const int err = uv_queue_work(uv_default_loop_checked(), req,
[](uv_work_t* /*req*/)
{
BACKGROUND_JOB_START(init_upnp);
LOGINFO(1, "UPnP: Started scanning for UPnP IGD devices");
{
MutexLock lock(upnp_discover.lock);
upnp_discover.devlist = upnpDiscover(1000, nullptr, nullptr, UPNP_LOCAL_PORT_ANY, 0, 2, &upnp_discover.error);
}
LOGINFO(1, "UPnP: Finished scanning for UPnP IGD devices");
},
[](uv_work_t* req, int /*status*/)
{
delete req;
BACKGROUND_JOB_STOP(init_upnp);
}
);
if (err) {
LOGERR(0, "init_upnp: uv_queue_work failed, error " << uv_err_name(err));
delete req;
}
}
void destroy_upnp()
{
{
MutexLock lock(upnp_discover.lock);
freeUPNPDevlist(upnp_discover.devlist);
upnp_discover.devlist = nullptr;
}
uv_mutex_destroy(&upnp_discover.lock);
}
int add_portmapping(int external_port, int internal_port)
{
LOGINFO(1, "UPnP: trying to map WAN:" << external_port << " to LAN:" << internal_port);
MutexLock lock(upnp_discover.lock);
if (!upnp_discover.devlist) {
LOGWARN(1, "upnpDiscover: no UPnP IGD devices found, error " << upnp_discover.error);
return 0;
}
UPNPUrls urls;
IGDdatas data;
char local_addr[64] = {};
int result = UPNP_GetValidIGD(upnp_discover.devlist, &urls, &data, local_addr, sizeof(local_addr));
if (result != 1) {
LOGWARN(1, "UPNP_GetValidIGD returned " << result << ", no valid UPnP IGD devices found");
return 0;
}
LOGINFO(1, "UPnP: LAN IP address " << log::Gray() << static_cast<const char*>(local_addr));
char ext_addr[64] = {};
result = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, ext_addr);
if ((result != UPNPCOMMAND_SUCCESS) || !ext_addr[0]) {
LOGWARN(1, "UPNP_GetExternalIPAddress: failed to query external IP address, error " << result);
}
else {
LOGINFO(1, "UPnP: WAN IP address " << log::Gray() << static_cast<const char*>(ext_addr));
}
const std::string eport = std::to_string(external_port);
const std::string iport = std::to_string(internal_port);
result = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, eport.c_str(), iport.c_str(), local_addr, "P2Pool", "TCP", nullptr, nullptr);
// ConflictInMappingEntry: try to delete the old record and then add the new one again
if (result == 718) {
LOGWARN(1, "UPNP_AddPortMapping failed: ConflictInMappingEntry");
result = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, eport.c_str(), "TCP", nullptr);
if (result) {
LOGWARN(1, "UPNP_DeletePortMapping returned error " << result);
return 0;
}
else {
LOGINFO(1, "UPnP: Deleted mapping for external port " << external_port);
result = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, eport.c_str(), iport.c_str(), local_addr, "P2Pool", "TCP", nullptr, nullptr);
}
}
if (result) {
LOGWARN(1, "UPNP_AddPortMapping returned error " << result);
return 0;
}
LOGINFO(1, "UPnP: Mapped " << log::Gray() << static_cast<const char*>(ext_addr) << ':' << external_port << log::NoColor() << " to " << log::Gray() << static_cast<const char*>(local_addr) << ':' << internal_port);
return external_port;
}
void remove_portmapping(int external_port)
{
LOGINFO(1, "UPnP: trying to delete mapping for external port " << external_port);
MutexLock lock(upnp_discover.lock);
if (!upnp_discover.devlist) {
LOGWARN(1, "upnpDiscover: no UPnP IGD devices found, error " << upnp_discover.error);
return;
}
UPNPUrls urls;
IGDdatas data;
char local_addr[64] = {};
int result = UPNP_GetValidIGD(upnp_discover.devlist, &urls, &data, local_addr, sizeof(local_addr));
if (result != 1) {
LOGWARN(1, "UPNP_GetValidIGD returned " << result << ", no valid UPnP IGD devices found");
return;
}
const std::string eport = std::to_string(external_port);
result = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, eport.c_str(), "TCP", nullptr);
if (result) {
LOGWARN(1, "UPNP_DeletePortMapping returned error " << result);
}
else {
LOGINFO(1, "UPnP: Deleted mapping for external port " << external_port);
}
}
#endif
NOINLINE PerfTimer::~PerfTimer()
{
using namespace std::chrono;
const duration<double, std::milli> dt = high_resolution_clock::now() - m_start;
LOGINFO(m_level, m_name << " took " << dt.count() << " ms");
}
} // namespace p2pool
+30 -1
View File
@@ -35,7 +35,7 @@
namespace p2pool {
#define P2POOL_VERSION_MAJOR 3
#define P2POOL_VERSION_MINOR 1
#define P2POOL_VERSION_MINOR 2
extern const char* VERSION;
@@ -227,6 +227,12 @@ FORCEINLINE uint64_t seconds_since_epoch()
return duration_cast<seconds>(steady_clock::now().time_since_epoch()).count();
}
FORCEINLINE uint64_t microseconds_since_epoch()
{
using namespace std::chrono;
return duration_cast<microseconds>(steady_clock::now().time_since_epoch()).count();
}
uint64_t bsr_reference(uint64_t x);
#ifdef HAVE_BUILTIN_CLZLL
@@ -246,6 +252,29 @@ FORCEINLINE uint64_t bsr(uint64_t x)
bool str_to_ip(bool is_v6, const char* ip, raw_ip& result);
bool is_localhost(const std::string& host);
#ifdef WITH_UPNP
void init_upnp();
void destroy_upnp();
int add_portmapping(int external_port, int internal_port);
void remove_portmapping(int external_port);
#endif
struct PerfTimer
{
FORCEINLINE PerfTimer(int level, const char* name) : m_level(level), m_name(name), m_start(std::chrono::high_resolution_clock::now()) {}
~PerfTimer();
int m_level;
const char* m_name;
std::chrono::time_point<std::chrono::high_resolution_clock> m_start;
};
#ifdef P2POOL_LOG_DISABLE
#define PERFLOG(level, name)
#else
#define PERFLOG(level, name) PerfTimer CONCAT(perf_timer_, __LINE__)(level, name)
#endif
} // namespace p2pool
void memory_tracking_start();
+8 -7
View File
@@ -179,16 +179,17 @@ bool CallOnLoop(uv_loop_t* loop, T&& callback)
template<typename T>
void parallel_run(uv_loop_t* loop, T&& callback, bool wait = false)
{
uint32_t THREAD_COUNT = std::thread::hardware_concurrency();
const uint32_t THREAD_COUNT = std::thread::hardware_concurrency();
if (THREAD_COUNT > 0) {
--THREAD_COUNT;
// Don't start other threads on single CPU systems
if (THREAD_COUNT <= 1) {
callback();
return;
}
// "THREAD_COUNT - 1" because current thread is already running
// No more than 8 threads because our UV worker thread pool has 8 threads
if (THREAD_COUNT > 8) {
THREAD_COUNT = 8;
}
const uint32_t THREADS_TO_START = std::min<uint32_t>(THREAD_COUNT - 1, 8);
struct Callback
{
@@ -206,7 +207,7 @@ void parallel_run(uv_loop_t* loop, T&& callback, bool wait = false)
std::shared_ptr<Callback> cb;
};
for (size_t i = 0; i < THREAD_COUNT; ++i) {
for (size_t i = 0; i < THREADS_TO_START; ++i) {
Work* w = new Work{ {}, cb };
w->req.data = w;
+36 -35
View File
@@ -58,10 +58,32 @@ ZMQReader::ZMQReader(const std::string& address, uint32_t zmq_port, const std::s
throw zmq::error_t(EFSM);
}
m_subscriber.set(zmq::sockopt::connect_timeout, 1000);
if (!m_proxy.empty()) {
m_subscriber.set(zmq::sockopt::socks_proxy, zmq::const_buffer(m_proxy.c_str(), m_proxy.length()));
}
std::string addr = "tcp://" + m_address + ':' + std::to_string(m_zmqPort);
if (!connect(addr)) {
throw zmq::error_t(EFSM);
}
m_subscriber.set(zmq::sockopt::socks_proxy, zmq::const_buffer());
addr = "tcp://127.0.0.1:" + std::to_string(m_publisherPort);
if (!connect(addr)) {
throw zmq::error_t(EFSM);
}
m_subscriber.set(zmq::sockopt::subscribe, "json-full-chain_main");
m_subscriber.set(zmq::sockopt::subscribe, "json-full-miner_data");
m_subscriber.set(zmq::sockopt::subscribe, "json-minimal-txpool_add");
const int err = uv_thread_create(&m_worker, run_wrapper, this);
if (err) {
LOGERR(1, "failed to start ZMQ thread, error " << uv_err_name(err));
throw zmq::error_t(EFSM);
throw zmq::error_t(EMTHREAD);
}
}
@@ -69,7 +91,7 @@ ZMQReader::~ZMQReader()
{
LOGINFO(1, "stopping");
m_finished.exchange(1);
m_finished.exchange(true);
try {
const char msg[] = "json-minimal-txpool_add:[]";
@@ -89,28 +111,12 @@ void ZMQReader::run_wrapper(void* arg)
void ZMQReader::run()
{
m_threadRunning = true;
ON_SCOPE_LEAVE([this]() { m_threadRunning = false; });
zmq_msg_t message = {};
try {
if (!m_proxy.empty()) {
m_subscriber.set(zmq::sockopt::socks_proxy, zmq::const_buffer(m_proxy.c_str(), m_proxy.length()));
}
std::string addr = "tcp://" + m_address + ':' + std::to_string(m_zmqPort);
if (!connect(addr)) {
return;
}
m_subscriber.set(zmq::sockopt::socks_proxy, zmq::const_buffer());
addr = "tcp://127.0.0.1:" + std::to_string(m_publisherPort);
if (!connect(addr)) {
return;
}
m_subscriber.set(zmq::sockopt::subscribe, "json-full-chain_main");
m_subscriber.set(zmq::sockopt::subscribe, "json-full-miner_data");
m_subscriber.set(zmq::sockopt::subscribe, "json-minimal-txpool_add");
zmq_msg_t message;
int rc = zmq_msg_init(&message);
if (rc != 0) {
throw zmq::error_t(errno);
@@ -130,12 +136,12 @@ void ZMQReader::run()
parse(reinterpret_cast<char*>(zmq_msg_data(&message)), zmq_msg_size(&message));
} while (true);
zmq_msg_close(&message);
}
catch (const std::exception& e) {
LOGERR(1, "exception " << e.what());
}
zmq_msg_close(&message);
}
bool ZMQReader::connect(const std::string& address)
@@ -163,21 +169,16 @@ bool ZMQReader::connect(const std::string& address)
s << "inproc://p2pool-connect-mon-" << id << '\0';
++id;
using namespace std::chrono;
const auto start_time = steady_clock::now();
monitor.init(m_subscriber, buf);
m_subscriber.connect(address);
using namespace std::chrono;
steady_clock::time_point start_time = steady_clock::now();
while (!monitor.connected && monitor.check_event(-1)) {
const steady_clock::time_point cur_time = steady_clock::now();
const int64_t elapsed_time = duration_cast<milliseconds>(cur_time - start_time).count();
if (elapsed_time >= 3000) {
if (duration_cast<milliseconds>(steady_clock::now() - start_time).count() >= 1000) {
LOGERR(1, "failed to connect to " << address);
if (m_finished.load()) {
return false;
}
start_time = cur_time;
return false;
}
}
+4 -1
View File
@@ -27,6 +27,8 @@ public:
ZMQReader(const std::string& address, uint32_t zmq_port, const std::string& proxy, MinerCallbackHandler* handler);
~ZMQReader();
bool is_running() const { return m_threadRunning.load(); }
private:
static void run_wrapper(void* arg);
void run();
@@ -44,7 +46,8 @@ private:
zmq::socket_t m_publisher{ m_context, ZMQ_PUB };
zmq::socket_t m_subscriber{ m_context, ZMQ_SUB };
uint16_t m_publisherPort = 37891;
std::atomic<int> m_finished{ 0 };
std::atomic<bool> m_finished{ false };
std::atomic<bool> m_threadRunning{ false };
TxMempoolData m_tx;
MinerData m_minerData;
+2 -1
View File
@@ -17,6 +17,7 @@ set(LIBS ${LIBS} randomx)
add_definitions(-DWITH_RANDOMX)
add_definitions(-DP2POOL_UNIT_TESTS)
add_definitions(-DP2POOL_SIDECHAIN_EXTRA_1=1)
include(cmake/flags.cmake)
@@ -172,6 +173,6 @@ endif()
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} debug ${CURL_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} optimized ${CURL_LIBRARY} ${LIBS})
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/crypto_tests.txt" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/mainnet_test2_block.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/block.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/sidechain_dump.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/sidechain_dump_mini.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
Binary file not shown.
+16 -2
View File
@@ -60,7 +60,7 @@ TEST(block_template, update)
tpl.update(data, mempool, &wallet);
const PoolBlock* b = tpl.pool_block_template();
ASSERT_EQ(b->m_sidechainId, H("5b6d5ac9e8cc8b973cedbf6114584b79f03f9d7d9d413a8f7e6fc398aac1a26c"));
ASSERT_EQ(b->m_sidechainId, H("0355a4eeb586ab4d30f3ba89f0ebf7ac1c34a55aebf1bee50af04ff893330280"));
std::vector<uint8_t> blobs;
uint64_t height;
@@ -79,7 +79,7 @@ TEST(block_template, update)
hash blobs_hash;
keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h);
ASSERT_EQ(blobs_hash, H("a500d8d337db35f8c3e8e70dea78122ea3cfb87874245bbc4e5f48892e8d1cb1"));
ASSERT_EQ(blobs_hash, H("70ee1661794c9a55f006432b57b0fc37922e0ccf8f1e4294b43890f8a6f44c62"));
// Test 2: mempool with high fee and low fee transactions, it must choose high fee transactions
for (uint64_t i = 0; i < 512; ++i) {
@@ -92,10 +92,24 @@ TEST(block_template, update)
tpl.update(data, mempool, &wallet);
ASSERT_EQ(b->m_transactions.size(), 203);
for (size_t i = 1; i < b->m_transactions.size(); ++i) {
ASSERT_GE(*reinterpret_cast<const uint64_t*>(b->m_transactions[i].h), 256);
}
tpl.get_hashing_blobs(0, 10000, blobs, height, diff, sidechain_diff, seed_hash, nonce_offset, template_id);
ASSERT_EQ(height, 2762973);
ASSERT_EQ(diff, 300346053753ULL);
ASSERT_EQ(sidechain_diff, sidechain.difficulty());
ASSERT_EQ(seed_hash, data.seed_hash);
ASSERT_EQ(nonce_offset, 39);
ASSERT_EQ(template_id, 2);
keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h);
ASSERT_EQ(blobs_hash, H("c74d295a9cb7e808030284e2169a6f05b685a11c6c577a774d5eb8fad175d5cd"));
destroy_crypto_cache();
}
Binary file not shown.
+47 -31
View File
@@ -30,9 +30,9 @@ TEST(pool_block, deserialize)
init_crypto_cache();
PoolBlock b;
SideChain sidechain(nullptr, NetworkType::Mainnet, "mainnet test 2");
SideChain sidechain(nullptr, NetworkType::Mainnet, "default");
constexpr uint64_t expeted_consensus_id[HASH_SIZE / sizeof(uint64_t)] = {
constexpr uint64_t expected_consensus_id[HASH_SIZE / sizeof(uint64_t)] = {
0x92680bb5e77eaf22ull,
0x27446c2c6bda99e3ull,
0x008e04a9d40451b2ull,
@@ -41,9 +41,9 @@ TEST(pool_block, deserialize)
const std::vector<uint8_t>& consensus_id = sidechain.consensus_id();
ASSERT_EQ(consensus_id.size(), HASH_SIZE);
ASSERT_EQ(memcmp(consensus_id.data(), expeted_consensus_id, HASH_SIZE), 0);
ASSERT_EQ(memcmp(consensus_id.data(), expected_consensus_id, HASH_SIZE), 0);
std::ifstream f("mainnet_test2_block.dat", std::ios::binary | std::ios::ate);
std::ifstream f("block.dat", std::ios::binary | std::ios::ate);
ASSERT_EQ(f.good() && f.is_open(), true);
std::vector<uint8_t> buf(f.tellg());
@@ -57,26 +57,26 @@ TEST(pool_block, deserialize)
int outputs_offset, outputs_blob_size;
const std::vector<uint8_t> mainchain_data = b.serialize_mainchain_data(&header_size, &miner_tx_size, &outputs_offset, &outputs_blob_size);
ASSERT_EQ(mainchain_data.size(), 5607);
ASSERT_EQ(mainchain_data.size(), 1757);
ASSERT_EQ(header_size, 43);
ASSERT_EQ(miner_tx_size, 506);
ASSERT_EQ(miner_tx_size, 1457);
ASSERT_EQ(outputs_offset, 54);
ASSERT_EQ(outputs_blob_size, 420);
ASSERT_EQ(outputs_blob_size, 1371);
ASSERT_EQ(b.m_majorVersion, 14);
ASSERT_EQ(b.m_minorVersion, 14);
ASSERT_EQ(b.m_timestamp, 1630934403);
ASSERT_EQ(b.m_nonce, 2432795907);
ASSERT_EQ(b.m_txinGenHeight, 2443466);
ASSERT_EQ(b.m_outputs.size(), 11);
ASSERT_EQ(b.m_majorVersion, 16);
ASSERT_EQ(b.m_minorVersion, 16);
ASSERT_EQ(b.m_timestamp, 1679221824);
ASSERT_EQ(b.m_nonce, 1247);
ASSERT_EQ(b.m_txinGenHeight, 2845298);
ASSERT_EQ(b.m_outputs.size(), 35);
ASSERT_EQ(b.m_extraNonceSize, 4);
ASSERT_EQ(b.m_extraNonce, 28);
ASSERT_EQ(b.m_transactions.size(), 159);
ASSERT_EQ(b.m_extraNonce, 1482827308);
ASSERT_EQ(b.m_transactions.size(), 9);
ASSERT_EQ(b.m_uncles.size(), 0);
ASSERT_EQ(b.m_sidechainHeight, 53450);
ASSERT_EQ(b.m_difficulty.lo, 319296691);
ASSERT_EQ(b.m_sidechainHeight, 4674483);
ASSERT_EQ(b.m_difficulty.lo, 1854596983);
ASSERT_EQ(b.m_difficulty.hi, 0);
ASSERT_EQ(b.m_cumulativeDifficulty.lo, 12544665764606ull);
ASSERT_EQ(b.m_cumulativeDifficulty.lo, 7172845253120126ull);
ASSERT_EQ(b.m_cumulativeDifficulty.hi, 0);
ASSERT_EQ(b.m_depth, 0);
ASSERT_EQ(b.m_verified, false);
@@ -84,23 +84,36 @@ TEST(pool_block, deserialize)
ASSERT_EQ(b.m_broadcasted, false);
ASSERT_EQ(b.m_wantBroadcast, false);
RandomX_Hasher hasher(nullptr);
hash seed;
class RandomX_Hasher_Test : public RandomX_Hasher_Base
{
std::stringstream s;
s << "c293f04b0f97cf76008c6ce8b0dbd2ba5be6b734de0aec9d1b758a12d7ec6451";
s >> seed;
}
public:
bool calculate(const void* data, size_t size, uint64_t, const hash&, hash& result, bool force_light_mode) override
{
if (size == 76) {
char buf[76 * 2 + 1];
{
log::Stream s(buf);
s << log::hex_buf(reinterpret_cast<const uint8_t*>(data), size);
buf[76 * 2] = '\0';
}
const char ref[] = "1010c0c8dba006b78e04571806733a74ef1014f404484d3358bfca889a75bb0fe9aff64a41c92bdf040000ecf0a11f83c6eced7d7cdfbdcd5a193f64d334b2c5491a9c595b4527e531ae7209";
if (memcmp(buf, ref, sizeof(buf)) == 0) {
std::stringstream s;
s << "aa7a3c4a2d67cb6a728e244288219bf038024f3b511b0da197a19ec601000000";
s >> result;
return true;
}
}
return false;
}
} hasher;
hasher.set_seed(seed);
hash pow_hash;
hash seed, pow_hash;
ASSERT_EQ(b.get_pow_hash(&hasher, 0, seed, pow_hash), true);
std::stringstream s;
s << pow_hash;
ASSERT_EQ(s.str(), "f76d731c61c9c9b6c3f46be2e60c9478930b49b4455feecd41ecb9420d000000");
ASSERT_EQ(s.str(), "aa7a3c4a2d67cb6a728e244288219bf038024f3b511b0da197a19ec601000000");
ASSERT_EQ(b.m_difficulty.check_pow(pow_hash), true);
@@ -118,8 +131,8 @@ TEST(pool_block, verify)
uint64_t m_txinGenHeight;
uint64_t m_sidechainHeight;
} tests[2] = {
{ "default", "sidechain_dump.dat", 2483901, 522805 },
{ "mini", "sidechain_dump_mini.dat", 2696040, 2424349 },
{ "default", "sidechain_dump.dat", 2845288, 4674368 },
{ "mini", "sidechain_dump_mini.dat", 2845293, 4129185 },
};
for (const STest& t : tests)
@@ -127,6 +140,9 @@ TEST(pool_block, verify)
PoolBlock b;
SideChain sidechain(nullptr, NetworkType::Mainnet, t.m_poolName);
// Difficulty of block 2844672
sidechain.m_testMainChainDiff = difficulty_type(321967641416ULL, 0ULL);
std::ifstream f(t.m_fileName, std::ios::binary | std::ios::ate);
ASSERT_EQ(f.good() && f.is_open(), true);
Binary file not shown.
Binary file not shown.