Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92827035e0 | |||
| 5f7b8c3371 | |||
| 03eddddc0e | |||
| 080fbaa26c | |||
| f6a285de87 | |||
| 55e4ea0277 | |||
| bb80fe12a0 | |||
| 5c67f0df20 | |||
| 3697c079fa | |||
| 2e6041dcf5 | |||
| 645de31fe3 | |||
| eef90da7ae | |||
| c0586a5089 | |||
| b30018bb38 | |||
| 6c45573976 | |||
| b6c1b1a6d7 | |||
| d41a441e98 | |||
| 86b6cf2d65 | |||
| cf11993cb1 | |||
| fe7ef30650 | |||
| bb4c7f0298 | |||
| a7db46d69b | |||
| ce81c2769c | |||
| d4a57370ef | |||
| 634039e551 | |||
| a83edceae3 | |||
| 19cc044cab | |||
| 79ad29e99a | |||
| f96e291681 | |||
| 6685a22118 | |||
| a84a0bd8dc | |||
| c2d8c806d2 | |||
| 387860aa4a | |||
| de7bdba95f | |||
| 6baf5247cb | |||
| 146d29b627 | |||
| c1a1249be1 | |||
| 8e7a797586 | |||
| 14a1918fda | |||
| 54e755efdd | |||
| 3df7430663 | |||
| c71d89c95a | |||
| 1b5860eec8 | |||
| 4741880044 | |||
| 02a0cb174a | |||
| e53fd7a062 |
@@ -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:
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
Submodule external/src/miniupnp added at e17579e4de
Generated
+6
-6
@@ -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": {
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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.
@@ -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.
@@ -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.
Reference in New Issue
Block a user