Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f17c23528a | |||
| a06f4dcdcd | |||
| 3bc03e4801 | |||
| 816a29c5ab | |||
| 285560e120 | |||
| 39ba5c4131 | |||
| 9b86f8e81f | |||
| 9e90e988fa | |||
| 39b2167c98 | |||
| f3024d3556 | |||
| 61ac90f1d5 | |||
| 60a8538e0c | |||
| 77a3a5857c | |||
| af2a8eeb08 | |||
| 54d0ab3658 | |||
| c9faa92098 | |||
| 52e5008bf4 | |||
| a0a31b0f3b | |||
| 7742d163f7 |
@@ -96,7 +96,13 @@ jobs:
|
||||
|
||||
build-windows-msbuild:
|
||||
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-${{ matrix.config.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {vs: Visual Studio 16 2019, os: 2019, msbuild: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin\\amd64\\"}
|
||||
- {vs: Visual Studio 17 2022, os: 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Preview\\Msbuild\\Current\\Bin\\amd64\\"}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -107,23 +113,20 @@ jobs:
|
||||
- name: Setup cmake
|
||||
uses: lukka/get-cmake@latest
|
||||
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1.0.3
|
||||
|
||||
- name: Build p2pool
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Visual Studio 16 2019"
|
||||
msbuild /m /p:Configuration=Release p2pool.vcxproj
|
||||
cmake .. -G "${{ matrix.config.vs }}"
|
||||
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool.vcxproj
|
||||
|
||||
- name: Build tests
|
||||
run: |
|
||||
cd tests
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Visual Studio 16 2019"
|
||||
msbuild /m /p:Configuration=Release p2pool_tests.vcxproj
|
||||
cmake .. -G "${{ matrix.config.vs }}"
|
||||
& "${{ matrix.config.msbuild }}msbuild" /m /p:Configuration=Release p2pool_tests.vcxproj
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
@@ -133,7 +136,7 @@ jobs:
|
||||
- name: Archive binary
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: p2pool-msbuild.exe
|
||||
name: p2pool-msbuild-${{ matrix.config.os }}.exe
|
||||
path: build/Release/p2pool.exe
|
||||
|
||||
build-macos:
|
||||
|
||||
+25
-6
@@ -66,7 +66,7 @@ BlockTemplate::BlockTemplate(p2pool* pool)
|
||||
m_mempoolTxsOrder.reserve(1024);
|
||||
m_shares.reserve(m_pool->side_chain().chain_window_size() * 2);
|
||||
|
||||
for (size_t i = 0; i < array_size(m_oldTemplates); ++i) {
|
||||
for (size_t i = 0; i < array_size(&BlockTemplate::m_oldTemplates); ++i) {
|
||||
m_oldTemplates[i] = new BlockTemplate(*this);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ BlockTemplate::BlockTemplate(p2pool* pool)
|
||||
|
||||
BlockTemplate::~BlockTemplate()
|
||||
{
|
||||
for (size_t i = 0; i < array_size(m_oldTemplates); ++i) {
|
||||
for (size_t i = 0; i < array_size(&BlockTemplate::m_oldTemplates); ++i) {
|
||||
delete m_oldTemplates[i];
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
WriteLock lock(m_lock);
|
||||
|
||||
if (m_templateId > 0) {
|
||||
*m_oldTemplates[m_templateId % array_size(m_oldTemplates)] = *this;
|
||||
*m_oldTemplates[m_templateId % array_size(&BlockTemplate::m_oldTemplates)] = *this;
|
||||
}
|
||||
|
||||
++m_templateId;
|
||||
@@ -838,6 +838,25 @@ void BlockTemplate::calc_merkle_tree_main_branch()
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockTemplate::get_difficulties(const uint32_t template_id, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const
|
||||
{
|
||||
ReadLock lock(m_lock);
|
||||
|
||||
if (template_id == m_templateId) {
|
||||
mainchain_difficulty = m_difficulty;
|
||||
sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
|
||||
return true;
|
||||
}
|
||||
|
||||
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
|
||||
|
||||
if (old && (template_id == old->m_templateId)) {
|
||||
return old->get_difficulties(template_id, mainchain_difficulty, sidechain_difficulty);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t BlockTemplate::get_hashing_blob(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const
|
||||
{
|
||||
ReadLock lock(m_lock);
|
||||
@@ -852,7 +871,7 @@ uint32_t BlockTemplate::get_hashing_blob(const uint32_t template_id, uint32_t ex
|
||||
return get_hashing_blob_nolock(extra_nonce, blob);
|
||||
}
|
||||
|
||||
const BlockTemplate* old = m_oldTemplates[template_id % array_size(m_oldTemplates)];
|
||||
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
|
||||
|
||||
if (old && (template_id == old->m_templateId)) {
|
||||
return old->get_hashing_blob(template_id, extra_nonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset);
|
||||
@@ -953,7 +972,7 @@ std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id
|
||||
ReadLock lock(m_lock);
|
||||
|
||||
if (template_id != m_templateId) {
|
||||
const BlockTemplate* old = m_oldTemplates[template_id % array_size(m_oldTemplates)];
|
||||
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
|
||||
if (old && (template_id == old->m_templateId)) {
|
||||
return old->get_block_template_blob(template_id, nonce_offset, extra_nonce_offset);
|
||||
}
|
||||
@@ -1016,7 +1035,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
|
||||
return;
|
||||
}
|
||||
|
||||
BlockTemplate* old = m_oldTemplates[template_id % array_size(m_oldTemplates)];
|
||||
BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
|
||||
|
||||
if (old && (template_id == old->m_templateId)) {
|
||||
old->submit_sidechain_block(template_id, nonce, extra_nonce);
|
||||
|
||||
@@ -40,6 +40,7 @@ public:
|
||||
|
||||
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet);
|
||||
|
||||
bool get_difficulties(const uint32_t template_id, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
|
||||
uint32_t get_hashing_blob(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const;
|
||||
|
||||
uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
|
||||
|
||||
@@ -371,6 +371,7 @@ template<> struct log::Stream::Entry<XMRAmount>
|
||||
|
||||
template<> struct log::Stream::Entry<NetworkType>
|
||||
{
|
||||
// cppcheck-suppress constParameter
|
||||
static NOINLINE void put(const NetworkType& value, Stream* wrapper)
|
||||
{
|
||||
switch (value) {
|
||||
|
||||
+4
-1
@@ -33,13 +33,16 @@ static void usage()
|
||||
"--p2p Comma-separated list of IP:port for p2p server to listen on\n"
|
||||
"--addpeers Comma-separated list of IP:port of other p2pool nodes to connect to\n"
|
||||
"--light-mode Don't allocate RandomX dataset, saves 2GB of RAM\n"
|
||||
"--loglevel Verbosity of the log, integer number between 0 and 5\n"
|
||||
"--loglevel Verbosity of the log, integer number between 0 and %d\n"
|
||||
"--config Name of the p2pool config file\n"
|
||||
"--data-api Path to the p2pool JSON data (use it in tandem with an external web-server)\n"
|
||||
"--stratum-api Enable /local/ path in api path for Stratum Server statistics\n"
|
||||
"--no-cache Disable p2pool.cache\n"
|
||||
"--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",
|
||||
p2pool::VERSION,
|
||||
p2pool::log::MAX_GLOBAL_LOG_LEVEL,
|
||||
#ifdef _WIN32
|
||||
"p2pool.exe"
|
||||
#else
|
||||
|
||||
+124
-45
@@ -42,13 +42,14 @@ namespace p2pool {
|
||||
P2PServer::P2PServer(p2pool* pool)
|
||||
: TCPServer(P2PClient::allocate)
|
||||
, m_pool(pool)
|
||||
, m_cache(new BlockCache())
|
||||
, m_cache(pool->params().m_blockCache ? new BlockCache() : nullptr)
|
||||
, m_cacheLoaded(false)
|
||||
, m_initialPeerList(pool->params().m_p2pPeerList)
|
||||
, m_rd{}
|
||||
, m_rng(m_rd())
|
||||
, m_block(new PoolBlock())
|
||||
, m_timer{}
|
||||
, m_timerCounter(0)
|
||||
, m_peerId(m_rng())
|
||||
, m_peerListLastSaved(0)
|
||||
{
|
||||
@@ -166,12 +167,6 @@ void P2PServer::on_connect_failed(bool is_v6, const raw_ip& ip, int port)
|
||||
|
||||
void P2PServer::update_peer_connections()
|
||||
{
|
||||
std::vector<Peer> peer_list;
|
||||
{
|
||||
MutexLock lock(m_peerListLock);
|
||||
peer_list = m_peerList;
|
||||
}
|
||||
|
||||
const time_t cur_time = time(nullptr);
|
||||
const time_t last_updated = m_pool->side_chain().last_updated();
|
||||
|
||||
@@ -180,13 +175,14 @@ void P2PServer::update_peer_connections()
|
||||
MutexLock lock(m_clientsListLock);
|
||||
connected_clients.reserve(m_numConnections);
|
||||
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
connected_clients.emplace_back(client->m_addr);
|
||||
bool disconnected = false;
|
||||
|
||||
const int timeout = client->m_handshakeComplete ? 300 : 10;
|
||||
if (cur_time >= client->m_lastAlive + timeout) {
|
||||
const uint64_t idle_time = static_cast<uint64_t>(cur_time - client->m_lastAlive);
|
||||
LOGWARN(5, "peer " << static_cast<char*>(client->m_addrString) << " has been idle for " << idle_time << " seconds, disconnecting");
|
||||
client->close();
|
||||
disconnected = true;
|
||||
}
|
||||
|
||||
if (client->m_handshakeComplete && client->m_lastBroadcastTimestamp) {
|
||||
@@ -200,11 +196,35 @@ void P2PServer::update_peer_connections()
|
||||
client->ban(DEFAULT_BAN_TIME);
|
||||
remove_peer_from_list(client);
|
||||
client->close();
|
||||
disconnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!disconnected) {
|
||||
connected_clients.emplace_back(client->m_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Peer> peer_list;
|
||||
{
|
||||
MutexLock lock(m_peerListLock);
|
||||
|
||||
if ((m_timerCounter % 30) == 1) {
|
||||
// Update last seen time for currently connected peers
|
||||
for (Peer& p : m_peerList) {
|
||||
if (std::find_if(connected_clients.begin(), connected_clients.end(), [&p](const raw_ip& addr) { return p.m_addr == addr; }) != connected_clients.end()) {
|
||||
p.m_lastSeen = cur_time;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all peers that weren't seen for more than 1 hour
|
||||
m_peerList.erase(std::remove_if(m_peerList.begin(), m_peerList.end(), [cur_time](const Peer& p) { return p.m_lastSeen + 3600 < cur_time; }), m_peerList.end());
|
||||
}
|
||||
|
||||
peer_list = m_peerList;
|
||||
}
|
||||
|
||||
// Try to have at least 8 outgoing connections
|
||||
for (uint32_t i = m_numConnections - m_numIncomingConnections; (i < 8) && !peer_list.empty();) {
|
||||
const uint64_t k = get_random64() % peer_list.size();
|
||||
@@ -227,6 +247,10 @@ void P2PServer::update_peer_connections()
|
||||
}
|
||||
peer_list.pop_back();
|
||||
}
|
||||
|
||||
if ((m_numConnections == 0) && ((m_timerCounter % 30) == 0)) {
|
||||
LOGERR(1, "no connections to other p2pool nodes, check your monerod/p2pool/network/firewall setup!!!");
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::update_peer_list()
|
||||
@@ -244,13 +268,17 @@ void P2PServer::update_peer_list()
|
||||
// Send peer list requests at random intervals (60-120 seconds)
|
||||
client->m_nextPeerListRequest = cur_time + 60 + (get_random64() % 61);
|
||||
|
||||
send(client,
|
||||
const bool result = send(client,
|
||||
[](void* buf)
|
||||
{
|
||||
LOGINFO(5, "sending PEER_LIST_REQUEST");
|
||||
*reinterpret_cast<uint8_t*>(buf) = static_cast<uint8_t>(MessageId::PEER_LIST_REQUEST);
|
||||
return 1;
|
||||
});
|
||||
|
||||
if (result) {
|
||||
++client->m_peerListPendingRequests;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -424,8 +452,6 @@ void P2PServer::load_peer_list()
|
||||
}
|
||||
p.m_isV6 = true;
|
||||
memcpy(p.m_addr.data, &addr6.sin6_addr, sizeof(in6_addr));
|
||||
p.m_port = port;
|
||||
p.m_numFailedConnections = 0;
|
||||
}
|
||||
else {
|
||||
sockaddr_in addr4;
|
||||
@@ -439,8 +465,6 @@ void P2PServer::load_peer_list()
|
||||
p.m_addr.data[10] = 0xFF;
|
||||
p.m_addr.data[11] = 0xFF;
|
||||
memcpy(p.m_addr.data + 12, &addr4.sin_addr, sizeof(in_addr));
|
||||
p.m_port = port;
|
||||
p.m_numFailedConnections = 0;
|
||||
}
|
||||
|
||||
bool already_added = false;
|
||||
@@ -451,6 +475,10 @@ void P2PServer::load_peer_list()
|
||||
}
|
||||
}
|
||||
|
||||
p.m_port = port;
|
||||
p.m_numFailedConnections = 0;
|
||||
p.m_lastSeen = time(nullptr);
|
||||
|
||||
if (!already_added && !is_banned(p.m_addr)) {
|
||||
m_peerList.push_back(p);
|
||||
}
|
||||
@@ -461,18 +489,21 @@ void P2PServer::load_peer_list()
|
||||
|
||||
void P2PServer::update_peer_in_list(bool is_v6, const raw_ip& ip, int port)
|
||||
{
|
||||
const time_t cur_time = time(nullptr);
|
||||
|
||||
MutexLock lock(m_peerListLock);
|
||||
|
||||
for (Peer& p : m_peerList) {
|
||||
if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) {
|
||||
p.m_port = port;
|
||||
p.m_numFailedConnections = 0;
|
||||
p.m_lastSeen = cur_time;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_banned(ip)) {
|
||||
m_peerList.emplace_back(Peer{ is_v6, ip, port, 0 });
|
||||
m_peerList.emplace_back(Peer{ is_v6, ip, port, 0, cur_time });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,18 +640,19 @@ void P2PServer::on_broadcast()
|
||||
uint8_t* p = p0;
|
||||
|
||||
bool send_pruned = true;
|
||||
{
|
||||
ReadLock lock(client->m_broadcastedHashesLock);
|
||||
for (const hash& id : data->ancestor_hashes) {
|
||||
if (client->m_broadcastedHashes.find(id) == client->m_broadcastedHashes.end()) {
|
||||
send_pruned = false;
|
||||
break;
|
||||
}
|
||||
|
||||
const hash* a = client->m_broadcastedHashes;
|
||||
const hash* b = client->m_broadcastedHashes + array_size(&P2PClient::m_broadcastedHashes);
|
||||
|
||||
for (const hash& id : data->ancestor_hashes) {
|
||||
if (std::find(a, b, id) == b) {
|
||||
send_pruned = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (send_pruned) {
|
||||
LOGINFO(5, "sending BLOCK_BROADCAST (pruned) to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
LOGINFO(6, "sending BLOCK_BROADCAST (pruned) to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
|
||||
|
||||
*reinterpret_cast<uint32_t*>(p) = static_cast<uint32_t>(data->pruned_blob.size());
|
||||
@@ -662,6 +694,8 @@ void P2PServer::print_status()
|
||||
|
||||
void P2PServer::on_timer()
|
||||
{
|
||||
++m_timerCounter;
|
||||
|
||||
if (!m_initialPeerList.empty()) {
|
||||
connect_to_peers(m_initialPeerList);
|
||||
m_initialPeerList.clear();
|
||||
@@ -672,6 +706,7 @@ void P2PServer::on_timer()
|
||||
update_peer_list();
|
||||
save_peer_list_async();
|
||||
update_peer_connections();
|
||||
check_zmq();
|
||||
}
|
||||
|
||||
void P2PServer::flush_cache()
|
||||
@@ -772,6 +807,21 @@ void P2PServer::download_missing_blocks()
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::check_zmq()
|
||||
{
|
||||
if ((m_timerCounter % 30) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const time_t cur_time = time(nullptr);
|
||||
const time_t last_active = m_pool->zmq_last_active();
|
||||
|
||||
if (cur_time >= last_active + 300) {
|
||||
const uint64_t dt = static_cast<uint64_t>(cur_time - last_active);
|
||||
LOGERR(1, "no ZMQ messages received from monerod in the last " << dt << " seconds, check your monerod/p2pool/network/firewall setup!!!");
|
||||
}
|
||||
}
|
||||
|
||||
P2PServer::P2PClient::P2PClient()
|
||||
: m_peerId(0)
|
||||
, m_expectedMessage(MessageId::HANDSHAKE_CHALLENGE)
|
||||
@@ -781,16 +831,16 @@ P2PServer::P2PClient::P2PClient()
|
||||
, m_handshakeInvalid(false)
|
||||
, m_listenPort(-1)
|
||||
, m_nextPeerListRequest(0)
|
||||
, m_peerListPendingRequests(0)
|
||||
, m_lastAlive(0)
|
||||
, m_lastBroadcastTimestamp(0)
|
||||
, m_lastBlockrequestTimestamp(0)
|
||||
, m_broadcastedHashes{}
|
||||
{
|
||||
uv_rwlock_init_checked(&m_broadcastedHashesLock);
|
||||
}
|
||||
|
||||
P2PServer::P2PClient::~P2PClient()
|
||||
{
|
||||
uv_rwlock_destroy(&m_broadcastedHashesLock);
|
||||
}
|
||||
|
||||
void P2PServer::P2PClient::reset()
|
||||
@@ -805,12 +855,15 @@ void P2PServer::P2PClient::reset()
|
||||
m_handshakeInvalid = false;
|
||||
m_listenPort = -1;
|
||||
m_nextPeerListRequest = 0;
|
||||
m_peerListPendingRequests = 0;
|
||||
m_lastAlive = 0;
|
||||
m_lastBroadcastTimestamp = 0;
|
||||
m_lastBlockrequestTimestamp = 0;
|
||||
|
||||
WriteLock lock(m_broadcastedHashesLock);
|
||||
m_broadcastedHashes.clear();
|
||||
for (hash& h : m_broadcastedHashes) {
|
||||
h = {};
|
||||
}
|
||||
m_broadcastedHashesIndex = 0;
|
||||
}
|
||||
|
||||
bool P2PServer::P2PClient::on_connect()
|
||||
@@ -866,6 +919,13 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
switch (id)
|
||||
{
|
||||
case MessageId::HANDSHAKE_CHALLENGE:
|
||||
if (m_handshakeComplete) {
|
||||
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected HANDSHAKE_CHALLENGE");
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent HANDSHAKE_CHALLENGE");
|
||||
|
||||
if (bytes_left >= 1 + CHALLENGE_SIZE + sizeof(uint64_t)) {
|
||||
@@ -880,6 +940,13 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
break;
|
||||
|
||||
case MessageId::HANDSHAKE_SOLUTION:
|
||||
if (m_handshakeComplete) {
|
||||
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected HANDSHAKE_SOLUTION");
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent HANDSHAKE_SOLUTION");
|
||||
|
||||
if (bytes_left >= 1 + HASH_SIZE + CHALLENGE_SIZE) {
|
||||
@@ -935,7 +1002,7 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
break;
|
||||
|
||||
case MessageId::BLOCK_BROADCAST:
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_BROADCAST");
|
||||
LOGINFO(6, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_BROADCAST");
|
||||
|
||||
if (bytes_left >= 1 + sizeof(uint32_t)) {
|
||||
const uint32_t block_size = *reinterpret_cast<uint32_t*>(buf + 1);
|
||||
@@ -964,6 +1031,13 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
break;
|
||||
|
||||
case MessageId::PEER_LIST_RESPONSE:
|
||||
if (m_peerListPendingRequests <= 0) {
|
||||
LOGWARN(4, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent an unexpected PEER_LIST_RESPONSE");
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent PEER_LIST_RESPONSE");
|
||||
|
||||
if (bytes_left >= 2) {
|
||||
@@ -977,6 +1051,8 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
|
||||
if (bytes_left >= 2u + num_peers * 19) {
|
||||
bytes_read = 2u + num_peers * 19;
|
||||
|
||||
--m_peerListPendingRequests;
|
||||
if (!on_peer_list_response(buf + 1)) {
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
@@ -1413,10 +1489,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
WriteLock lock2(m_broadcastedHashesLock);
|
||||
m_broadcastedHashes.insert(server->m_block->m_sidechainId);
|
||||
}
|
||||
m_broadcastedHashes[m_broadcastedHashesIndex.fetch_add(1) % array_size(&P2PClient::m_broadcastedHashes)] = server->m_block->m_sidechainId;
|
||||
|
||||
const MinerData& miner_data = server->m_pool->miner_data();
|
||||
|
||||
@@ -1444,8 +1517,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
}
|
||||
else if (peer_height > our_height) {
|
||||
if (peer_height >= our_height + 2) {
|
||||
const int level = (peer_height >= our_height + 3) ? 3 : 4;
|
||||
LOGWARN(level , "peer " << static_cast<char*>(m_addrString) << " is ahead on mainchain (height " << peer_height << ", your height " << our_height << "). Is your monerod stuck or lagging?");
|
||||
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " is ahead on mainchain (height " << peer_height << ", your height " << our_height << "). Is your monerod stuck or lagging?");
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1470,23 +1542,28 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||
MutexLock lock(server->m_clientsListLock);
|
||||
|
||||
// Send every 4th peer on average, selected at random
|
||||
const uint32_t n = server->m_numConnections;
|
||||
const uint32_t peers_to_send_target = std::min<uint32_t>(PEER_LIST_RESPONSE_MAX_PEERS, std::max<uint32_t>(1, n / 4));
|
||||
const uint32_t peers_to_send_target = std::min<uint32_t>(PEER_LIST_RESPONSE_MAX_PEERS, std::max<uint32_t>(1, server->m_numConnections / 4));
|
||||
uint32_t n = 0;
|
||||
|
||||
for (P2PClient* client = static_cast<P2PClient*>(server->m_connectedClientsList->m_next); client != server->m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
if (client->m_listenPort < 0) {
|
||||
if ((client->m_listenPort < 0) || (client->m_addr == m_addr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t hi;
|
||||
umul128(server->get_random64(), n, &hi);
|
||||
const Peer p{ client->m_isV6, client->m_addr, client->m_listenPort, 0, 0 };
|
||||
++n;
|
||||
|
||||
if ((hi < peers_to_send_target) && (client->m_addr != m_addr)) {
|
||||
peers[num_selected_peers++] = { client->m_isV6, client->m_addr, client->m_listenPort, 0 };
|
||||
// Use https://en.wikipedia.org/wiki/Reservoir_sampling algorithm
|
||||
if (num_selected_peers < peers_to_send_target) {
|
||||
peers[num_selected_peers++] = p;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (num_selected_peers >= PEER_LIST_RESPONSE_MAX_PEERS) {
|
||||
break;
|
||||
}
|
||||
uint64_t k;
|
||||
umul128(server->get_random64(), n, &k);
|
||||
|
||||
if (k < peers_to_send_target) {
|
||||
peers[k] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1520,6 +1597,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||
bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
||||
{
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
const time_t cur_time = time(nullptr);
|
||||
|
||||
MutexLock lock(server->m_peerListLock);
|
||||
|
||||
@@ -1536,15 +1614,16 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
||||
buf += 2;
|
||||
|
||||
bool already_added = false;
|
||||
for (const Peer& p : server->m_peerList) {
|
||||
for (Peer& p : server->m_peerList) {
|
||||
if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) {
|
||||
already_added = true;
|
||||
p.m_lastSeen = cur_time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!already_added && !server->is_banned(ip)) {
|
||||
server->m_peerList.emplace_back(Peer{ is_v6, ip, port, 0 });
|
||||
server->m_peerList.emplace_back(Peer{ is_v6, ip, port, 0, cur_time });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -110,12 +110,13 @@ public:
|
||||
bool m_handshakeInvalid;
|
||||
int m_listenPort;
|
||||
time_t m_nextPeerListRequest;
|
||||
int m_peerListPendingRequests;
|
||||
time_t m_lastAlive;
|
||||
time_t m_lastBroadcastTimestamp;
|
||||
time_t m_lastBlockrequestTimestamp;
|
||||
|
||||
uv_rwlock_t m_broadcastedHashesLock;
|
||||
std::set<hash> m_broadcastedHashes;
|
||||
hash m_broadcastedHashes[8];
|
||||
std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 };
|
||||
};
|
||||
|
||||
void broadcast(const PoolBlock& block);
|
||||
@@ -139,6 +140,7 @@ private:
|
||||
|
||||
void flush_cache();
|
||||
void download_missing_blocks();
|
||||
void check_zmq();
|
||||
void update_peer_connections();
|
||||
void update_peer_list();
|
||||
void save_peer_list_async();
|
||||
@@ -156,6 +158,7 @@ private:
|
||||
PoolBlock* m_block;
|
||||
|
||||
uv_timer_t m_timer;
|
||||
uint32_t m_timerCounter;
|
||||
|
||||
uint64_t m_peerId;
|
||||
|
||||
@@ -167,6 +170,7 @@ private:
|
||||
raw_ip m_addr;
|
||||
int m_port;
|
||||
uint32_t m_numFailedConnections;
|
||||
time_t m_lastSeen;
|
||||
};
|
||||
|
||||
std::vector<Peer> m_peerList;
|
||||
|
||||
+7
-1
@@ -105,7 +105,7 @@ p2pool::p2pool(int argc, char* argv[])
|
||||
uv_mutex_init_checked(&m_foundBlocksLock);
|
||||
uv_mutex_init_checked(&m_submitBlockDataLock);
|
||||
|
||||
m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath);
|
||||
m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath, m_params->m_localStats);
|
||||
|
||||
m_sideChain = new SideChain(this, type);
|
||||
m_hasher = new RandomX_Hasher(this);
|
||||
@@ -173,6 +173,8 @@ void p2pool::handle_tx(TxMempoolData& tx)
|
||||
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
||||
m_blockTemplate->update(m_minerData, *m_mempool, &m_params->m_wallet);
|
||||
#endif
|
||||
|
||||
m_zmqLastActive = time(nullptr);
|
||||
}
|
||||
|
||||
void p2pool::handle_miner_data(MinerData& data)
|
||||
@@ -226,6 +228,8 @@ void p2pool::handle_miner_data(MinerData& data)
|
||||
else {
|
||||
update_block_template();
|
||||
}
|
||||
|
||||
m_zmqLastActive = time(nullptr);
|
||||
}
|
||||
|
||||
const char* BLOCK_FOUND = "\n\
|
||||
@@ -288,6 +292,8 @@ void p2pool::handle_chain_main(ChainMain& data, const char* extra)
|
||||
}
|
||||
|
||||
api_update_network_stats();
|
||||
|
||||
m_zmqLastActive = time(nullptr);
|
||||
}
|
||||
|
||||
void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
|
||||
|
||||
@@ -49,6 +49,8 @@ public:
|
||||
SideChain& side_chain() { return *m_sideChain; }
|
||||
const MinerData& miner_data() const { return m_minerData; }
|
||||
|
||||
p2pool_api* api() const { return m_api; }
|
||||
|
||||
RandomX_Hasher* hasher() const { return m_hasher; }
|
||||
bool calculate_hash(const void* data, size_t size, const hash& seed, hash& result);
|
||||
static uint64_t get_seed_height(uint64_t height);
|
||||
@@ -76,6 +78,8 @@ public:
|
||||
|
||||
bool get_difficulty_at_height(uint64_t height, difficulty_type& diff);
|
||||
|
||||
time_t zmq_last_active() const { return m_zmqLastActive; }
|
||||
|
||||
private:
|
||||
p2pool(const p2pool&) = delete;
|
||||
p2pool(p2pool&&) = delete;
|
||||
@@ -165,6 +169,8 @@ private:
|
||||
uv_async_t m_submitBlockAsync;
|
||||
uv_async_t m_blockTemplateAsync;
|
||||
uv_async_t m_stopAsync;
|
||||
|
||||
time_t m_zmqLastActive = 0;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+7
-1
@@ -28,7 +28,7 @@ static constexpr char log_category_prefix[] = "P2Pool API ";
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
p2pool_api::p2pool_api(const std::string& api_path) : m_apiPath(api_path)
|
||||
p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats): m_apiPath(api_path)
|
||||
{
|
||||
if (m_apiPath.empty()) {
|
||||
LOGERR(1, "api path is empty");
|
||||
@@ -60,9 +60,14 @@ p2pool_api::p2pool_api(const std::string& api_path) : m_apiPath(api_path)
|
||||
|
||||
m_networkPath = m_apiPath + "network/";
|
||||
m_poolPath = m_apiPath + "pool/";
|
||||
m_localPath = m_apiPath + "local/";
|
||||
|
||||
create_dir(m_networkPath);
|
||||
create_dir(m_poolPath);
|
||||
|
||||
if (local_stats) {
|
||||
create_dir(m_localPath);
|
||||
}
|
||||
}
|
||||
|
||||
p2pool_api::~p2pool_api()
|
||||
@@ -109,6 +114,7 @@ void p2pool_api::dump_to_file_async_internal(const Category& category, const cha
|
||||
case Category::GLOBAL: path = m_apiPath + filename; break;
|
||||
case Category::NETWORK: path = m_networkPath + filename; break;
|
||||
case Category::POOL: path = m_poolPath + filename; break;
|
||||
case Category::LOCAL: path = m_localPath + filename; break;
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
+3
-1
@@ -25,13 +25,14 @@ namespace p2pool {
|
||||
class p2pool_api
|
||||
{
|
||||
public:
|
||||
explicit p2pool_api(const std::string& api_path);
|
||||
p2pool_api(const std::string& api_path, const bool local_stats);
|
||||
~p2pool_api();
|
||||
|
||||
enum class Category {
|
||||
GLOBAL,
|
||||
NETWORK,
|
||||
POOL,
|
||||
LOCAL,
|
||||
};
|
||||
|
||||
void on_stop();
|
||||
@@ -80,6 +81,7 @@ private:
|
||||
std::string m_apiPath;
|
||||
std::string m_networkPath;
|
||||
std::string m_poolPath;
|
||||
std::string m_localPath;
|
||||
|
||||
uv_mutex_t m_dumpDataLock;
|
||||
std::unordered_map<std::string, std::vector<char>> m_dumpData;
|
||||
|
||||
@@ -69,6 +69,14 @@ Params::Params(int argc, char* argv[])
|
||||
if ((strcmp(argv[i], "--data-api") == 0) && (i + 1 < argc)) {
|
||||
m_apiPath = argv[++i];
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--stratum-api") == 0) {
|
||||
m_localStats = true;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--no-cache") == 0) {
|
||||
m_blockCache = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_stratumAddresses.empty()) {
|
||||
|
||||
@@ -37,6 +37,8 @@ struct Params
|
||||
std::string m_p2pPeerList;
|
||||
std::string m_config;
|
||||
std::string m_apiPath;
|
||||
bool m_localStats = false;
|
||||
bool m_blockCache = true;
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+4
-4
@@ -54,7 +54,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
|
||||
|
||||
const randomx_flags flags = randomx_get_flags();
|
||||
|
||||
for (size_t i = 0; i < array_size(m_cache); ++i) {
|
||||
for (size_t i = 0; i < array_size(&RandomX_Hasher::m_cache); ++i) {
|
||||
m_cache[i] = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
|
||||
if (!m_cache[i]) {
|
||||
LOGWARN(1, "couldn't allocate RandomX cache using large pages");
|
||||
@@ -70,7 +70,7 @@ RandomX_Hasher::RandomX_Hasher(p2pool* pool)
|
||||
uv_rwlock_init_checked(&m_datasetLock);
|
||||
uv_rwlock_init_checked(&m_cacheLock);
|
||||
|
||||
for (size_t i = 0; i < array_size(m_vm); ++i) {
|
||||
for (size_t i = 0; i < array_size(&RandomX_Hasher::m_vm); ++i) {
|
||||
uv_mutex_init_checked(&m_vm[i].mutex);
|
||||
m_vm[i].vm = nullptr;
|
||||
}
|
||||
@@ -91,7 +91,7 @@ RandomX_Hasher::~RandomX_Hasher()
|
||||
uv_rwlock_destroy(&m_datasetLock);
|
||||
uv_rwlock_destroy(&m_cacheLock);
|
||||
|
||||
for (size_t i = 0; i < array_size(m_vm); ++i) {
|
||||
for (size_t i = 0; i < array_size(&RandomX_Hasher::m_vm); ++i) {
|
||||
{
|
||||
MutexLock lock(m_vm[i].mutex);
|
||||
if (m_vm[i].vm) {
|
||||
@@ -105,7 +105,7 @@ RandomX_Hasher::~RandomX_Hasher()
|
||||
randomx_release_dataset(m_dataset);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < array_size(m_cache); ++i) {
|
||||
for (size_t i = 0; i < array_size(&RandomX_Hasher::m_cache); ++i) {
|
||||
if (m_cache[i]) {
|
||||
randomx_release_cache(m_cache[i]);
|
||||
}
|
||||
|
||||
+10
-2
@@ -355,6 +355,12 @@ bool SideChain::block_seen(const PoolBlock& block)
|
||||
return !m_seenBlocks.insert(block.m_sidechainId).second;
|
||||
}
|
||||
|
||||
void SideChain::unsee_block(const PoolBlock& block)
|
||||
{
|
||||
MutexLock lock(m_sidechainLock);
|
||||
m_seenBlocks.erase(block.m_sidechainId);
|
||||
}
|
||||
|
||||
extern const char* BLOCK_FOUND;
|
||||
|
||||
bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks)
|
||||
@@ -407,13 +413,15 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
hash seed;
|
||||
if (!m_pool->get_seed(block.m_txinGenHeight, seed)) {
|
||||
LOGWARN(3, "add_external_block: couldn't get seed hash for mainchain height " << block.m_txinGenHeight);
|
||||
unsee_block(block);
|
||||
return false;
|
||||
}
|
||||
|
||||
hash pow_hash;
|
||||
if (!block.get_pow_hash(m_pool->hasher(), seed, pow_hash)) {
|
||||
LOGWARN(3, "add_external_block: couldn't get PoW hash for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight);
|
||||
return false;
|
||||
LOGWARN(3, "add_external_block: couldn't get PoW hash for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight << ". Ignoring it.");
|
||||
unsee_block(block);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if it has the correct parent and difficulty to go right to monerod for checking
|
||||
|
||||
@@ -47,6 +47,7 @@ public:
|
||||
void fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares);
|
||||
|
||||
bool block_seen(const PoolBlock& block);
|
||||
void unsee_block(const PoolBlock& block);
|
||||
bool add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks);
|
||||
void add_block(const PoolBlock& block);
|
||||
void get_missing_blocks(std::vector<hash>& missing_blocks);
|
||||
|
||||
+132
-44
@@ -21,6 +21,7 @@
|
||||
#include "p2pool.h"
|
||||
#include "side_chain.h"
|
||||
#include "params.h"
|
||||
#include "p2pool_api.h"
|
||||
|
||||
static constexpr char log_category_prefix[] = "StratumServer ";
|
||||
|
||||
@@ -48,6 +49,7 @@ StratumServer::StratumServer(p2pool* pool)
|
||||
, m_hashrateDataTail_24h(0)
|
||||
, m_cumulativeFoundSharesDiff(0.0)
|
||||
, m_totalFoundShares(0)
|
||||
, m_apiLastUpdateTime(0)
|
||||
{
|
||||
m_hashrateData[0] = { time(nullptr), 0 };
|
||||
|
||||
@@ -229,7 +231,7 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
|
||||
|
||||
job_id = client->m_perConnectionJobId++;
|
||||
|
||||
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(client->m_jobs)];
|
||||
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
|
||||
saved_job.job_id = job_id;
|
||||
saved_job.extra_nonce = extra_nonce;
|
||||
saved_job.template_id = template_id;
|
||||
@@ -307,7 +309,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
{
|
||||
MutexLock lock(client->m_jobsLock);
|
||||
|
||||
const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(client->m_jobs)];
|
||||
const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
|
||||
if (saved_job.job_id == job_id) {
|
||||
template_id = saved_job.template_id;
|
||||
extra_nonce = saved_job.extra_nonce;
|
||||
@@ -317,6 +319,26 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
}
|
||||
|
||||
if (found) {
|
||||
BlockTemplate& block = m_pool->block_template();
|
||||
difficulty_type mainchain_diff, sidechain_diff;
|
||||
|
||||
if (!block.get_difficulties(template_id, mainchain_diff, sidechain_diff)) {
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
|
||||
return send(client,
|
||||
[id](void* buf)
|
||||
{
|
||||
log::Stream s(reinterpret_cast<char*>(buf));
|
||||
s << "{\"id\":" << id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Stale share\"}}\n";
|
||||
return s.m_pos;
|
||||
});
|
||||
}
|
||||
|
||||
if (mainchain_diff.check_pow(resultHash)) {
|
||||
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << " found a mainchain block, submitting it");
|
||||
m_pool->submit_block_async(template_id, nonce, extra_nonce);
|
||||
block.update_tx_keys();
|
||||
}
|
||||
|
||||
SubmittedShare* share;
|
||||
|
||||
{
|
||||
@@ -334,6 +356,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
share->m_req.data = share;
|
||||
share->m_server = this;
|
||||
share->m_client = client;
|
||||
share->m_clientAddr = client->m_addr;
|
||||
share->m_clientResetCounter = client->m_resetCounter.load();
|
||||
share->m_rpcId = client->m_rpcId;
|
||||
share->m_id = id;
|
||||
@@ -342,6 +365,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
share->m_extraNonce = extra_nonce;
|
||||
share->m_target = target;
|
||||
share->m_resultHash = resultHash;
|
||||
share->m_sidechainDifficulty = sidechain_diff;
|
||||
|
||||
const int err = uv_queue_work(&m_loop, &share->m_req, on_share_found, on_after_share_found);
|
||||
if (err) {
|
||||
@@ -375,7 +399,7 @@ uint64_t StratumServer::get_random64()
|
||||
|
||||
void StratumServer::print_status()
|
||||
{
|
||||
update_hashrate_data(0);
|
||||
update_hashrate_data(0, time(nullptr));
|
||||
print_stratum_status();
|
||||
}
|
||||
|
||||
@@ -485,7 +509,7 @@ void StratumServer::on_blobs_ready()
|
||||
|
||||
job_id = client->m_perConnectionJobId++;
|
||||
|
||||
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(client->m_jobs)];
|
||||
StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)];
|
||||
saved_job.job_id = job_id;
|
||||
saved_job.extra_nonce = extra_nonce;
|
||||
saved_job.template_id = data->m_templateId;
|
||||
@@ -528,12 +552,9 @@ void StratumServer::on_blobs_ready()
|
||||
LOGINFO(3, "sent new job to " << extra_nonce << '/' << numClientsProcessed << " clients");
|
||||
}
|
||||
|
||||
void StratumServer::update_hashrate_data(uint64_t target)
|
||||
void StratumServer::update_hashrate_data(uint64_t hashes, time_t timestamp)
|
||||
{
|
||||
const time_t timestamp = time(nullptr);
|
||||
|
||||
uint64_t rem;
|
||||
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
|
||||
constexpr size_t N = array_size(&StratumServer::m_hashrateData);
|
||||
|
||||
WriteLock lock(m_hashrateDataLock);
|
||||
|
||||
@@ -545,20 +566,20 @@ void StratumServer::update_hashrate_data(uint64_t target)
|
||||
head.m_cumulativeHashes = m_cumulativeHashes;
|
||||
}
|
||||
else {
|
||||
m_hashrateDataHead = (m_hashrateDataHead + 1) % array_size(m_hashrateData);
|
||||
m_hashrateDataHead = (m_hashrateDataHead + 1) % N;
|
||||
data[m_hashrateDataHead] = { timestamp, m_cumulativeHashes };
|
||||
}
|
||||
|
||||
while (data[m_hashrateDataTail_15m].m_timestamp + 15 * 60 < timestamp) {
|
||||
m_hashrateDataTail_15m = (m_hashrateDataTail_15m + 1) % array_size(m_hashrateData);
|
||||
m_hashrateDataTail_15m = (m_hashrateDataTail_15m + 1) % N;
|
||||
}
|
||||
|
||||
while (data[m_hashrateDataTail_1h].m_timestamp + 60 * 60 < timestamp) {
|
||||
m_hashrateDataTail_1h = (m_hashrateDataTail_1h + 1) % array_size(m_hashrateData);
|
||||
m_hashrateDataTail_1h = (m_hashrateDataTail_1h + 1) % N;
|
||||
}
|
||||
|
||||
while (data[m_hashrateDataTail_24h].m_timestamp + 60 * 60 * 24 < timestamp) {
|
||||
m_hashrateDataTail_24h = (m_hashrateDataTail_24h + 1) % array_size(m_hashrateData);
|
||||
m_hashrateDataTail_24h = (m_hashrateDataTail_24h + 1) % N;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,35 +591,34 @@ void StratumServer::on_share_found(uv_work_t* req)
|
||||
StratumClient* client = share->m_client;
|
||||
StratumServer* server = share->m_server;
|
||||
p2pool* pool = server->m_pool;
|
||||
BlockTemplate& block = pool->block_template();
|
||||
|
||||
uint64_t target = share->m_target;
|
||||
if (target >= TARGET_4_BYTES_LIMIT) {
|
||||
target = (target >> 32) << 32;
|
||||
}
|
||||
|
||||
uint64_t rem;
|
||||
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
|
||||
|
||||
if (pool->stopped()) {
|
||||
LOGWARN(0, "p2pool is shutting down, but a share was found. Trying to process it anyway!");
|
||||
}
|
||||
|
||||
uint8_t blob[128];
|
||||
uint64_t height;
|
||||
difficulty_type difficulty;
|
||||
difficulty_type sidechain_difficulty;
|
||||
hash seed_hash;
|
||||
size_t nonce_offset;
|
||||
if (share->m_sidechainDifficulty.check_pow(share->m_resultHash)) {
|
||||
uint8_t blob[128];
|
||||
uint64_t height;
|
||||
difficulty_type difficulty;
|
||||
difficulty_type sidechain_difficulty;
|
||||
hash seed_hash;
|
||||
size_t nonce_offset;
|
||||
|
||||
const uint32_t blob_size = block.get_hashing_blob(share->m_templateId, share->m_extraNonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset);
|
||||
if (!blob_size) {
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
|
||||
share->m_result = SubmittedShare::Result::STALE;
|
||||
return;
|
||||
}
|
||||
const uint32_t blob_size = pool->block_template().get_hashing_blob(share->m_templateId, share->m_extraNonce, blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset);
|
||||
if (!blob_size) {
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
|
||||
share->m_result = SubmittedShare::Result::STALE;
|
||||
return;
|
||||
}
|
||||
|
||||
const bool mainchain_solution = difficulty.check_pow(share->m_resultHash);
|
||||
const bool sidechain_solution = sidechain_difficulty.check_pow(share->m_resultHash);
|
||||
|
||||
if (mainchain_solution || sidechain_solution) {
|
||||
for (uint32_t i = 0, nonce = share->m_nonce; i < sizeof(share->m_nonce); ++i) {
|
||||
blob[nonce_offset + i] = nonce & 255;
|
||||
nonce >>= 8;
|
||||
@@ -617,9 +637,6 @@ void StratumServer::on_share_found(uv_work_t* req)
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t rem;
|
||||
const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0;
|
||||
|
||||
const uint64_t n = server->m_cumulativeHashes + hashes;
|
||||
const double diff = sidechain_difficulty.to_double();
|
||||
const double effort = static_cast<double>(n - server->m_cumulativeHashesAtLastShare) * 100.0 / diff;
|
||||
@@ -629,22 +646,16 @@ void StratumServer::on_share_found(uv_work_t* req)
|
||||
++server->m_totalFoundShares;
|
||||
|
||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << height << ", diff " << sidechain_difficulty << ", effort " << effort << '%');
|
||||
|
||||
if (mainchain_solution) {
|
||||
pool->submit_block_async(share->m_templateId, share->m_nonce, share->m_extraNonce);
|
||||
block.update_tx_keys();
|
||||
}
|
||||
|
||||
if (sidechain_solution) {
|
||||
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
|
||||
}
|
||||
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
|
||||
}
|
||||
|
||||
// Send the response to miner
|
||||
const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t));
|
||||
|
||||
if (LIKELY(value < target)) {
|
||||
server->update_hashrate_data(target);
|
||||
const time_t timestamp = time(nullptr);
|
||||
server->update_hashrate_data(hashes, timestamp);
|
||||
server->api_update_local_stats(timestamp);
|
||||
share->m_result = SubmittedShare::Result::OK;
|
||||
}
|
||||
else {
|
||||
@@ -670,6 +681,8 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
StratumClient* client = share->m_client;
|
||||
StratumServer* server = share->m_server;
|
||||
|
||||
const bool bad_share = (share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW);
|
||||
|
||||
if ((client->m_resetCounter.load() == share->m_clientResetCounter) && (client->m_rpcId == share->m_rpcId)) {
|
||||
const bool result = server->send(client,
|
||||
[share](void* buf)
|
||||
@@ -695,7 +708,7 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
return s.m_pos;
|
||||
});
|
||||
|
||||
if ((share->m_result == SubmittedShare::Result::LOW_DIFF) || (share->m_result == SubmittedShare::Result::INVALID_POW)) {
|
||||
if (bad_share) {
|
||||
client->ban(DEFAULT_BAN_TIME);
|
||||
client->close();
|
||||
}
|
||||
@@ -703,6 +716,9 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
client->close();
|
||||
}
|
||||
}
|
||||
else if (bad_share) {
|
||||
server->ban(share->m_clientAddr, DEFAULT_BAN_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
StratumServer::StratumClient::StratumClient()
|
||||
@@ -914,4 +930,76 @@ bool StratumServer::StratumClient::process_submit(rapidjson::Document& doc, uint
|
||||
return static_cast<StratumServer*>(m_owner)->on_submit(this, id, job_id.GetString(), nonce.GetString(), result.GetString());
|
||||
}
|
||||
|
||||
void StratumServer::api_update_local_stats(time_t timestamp)
|
||||
{
|
||||
if (!m_pool->api() || !m_pool->params().m_localStats) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rate limit to no more than once in 60 seconds.
|
||||
if (timestamp < m_apiLastUpdateTime + 60) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_apiLastUpdateTime = timestamp;
|
||||
|
||||
uint64_t hashes_15m, hashes_1h, hashes_24h, total_hashes;
|
||||
int64_t dt_15m, dt_1h, dt_24h;
|
||||
|
||||
uint64_t hashes_since_last_share;
|
||||
|
||||
{
|
||||
ReadLock lock(m_hashrateDataLock);
|
||||
|
||||
total_hashes = m_cumulativeHashes;
|
||||
hashes_since_last_share = m_cumulativeHashes - m_cumulativeHashesAtLastShare;
|
||||
|
||||
const HashrateData* data = m_hashrateData;
|
||||
const HashrateData& head = data[m_hashrateDataHead];
|
||||
const HashrateData& tail_15m = data[m_hashrateDataTail_15m];
|
||||
const HashrateData& tail_1h = data[m_hashrateDataTail_1h];
|
||||
const HashrateData& tail_24h = data[m_hashrateDataTail_24h];
|
||||
|
||||
hashes_15m = head.m_cumulativeHashes - tail_15m.m_cumulativeHashes;
|
||||
dt_15m = static_cast<int64_t>(head.m_timestamp - tail_15m.m_timestamp);
|
||||
|
||||
hashes_1h = head.m_cumulativeHashes - tail_1h.m_cumulativeHashes;
|
||||
dt_1h = static_cast<int64_t>(head.m_timestamp - tail_1h.m_timestamp);
|
||||
|
||||
hashes_24h = head.m_cumulativeHashes - tail_24h.m_cumulativeHashes;
|
||||
dt_24h = static_cast<int64_t>(head.m_timestamp - tail_24h.m_timestamp);
|
||||
}
|
||||
|
||||
const uint64_t hashrate_15m = (dt_15m > 0) ? (hashes_15m / dt_15m) : 0;
|
||||
const uint64_t hashrate_1h = (dt_1h > 0) ? (hashes_1h / dt_1h ) : 0;
|
||||
const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0;
|
||||
|
||||
double average_effort = 0.0;
|
||||
if (m_cumulativeFoundSharesDiff > 0.0) {
|
||||
average_effort = static_cast<double>(m_cumulativeHashesAtLastShare) * 100.0 / m_cumulativeFoundSharesDiff;
|
||||
}
|
||||
|
||||
int shares_found = m_totalFoundShares;
|
||||
|
||||
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;
|
||||
|
||||
m_pool->api()->set(p2pool_api::Category::LOCAL, "stats",
|
||||
[hashrate_15m, hashrate_1h, hashrate_24h, total_hashes, shares_found, average_effort, current_effort, connections, incoming_connections](log::Stream& s)
|
||||
{
|
||||
s << "{\"hashrate_15m\":" << hashrate_15m
|
||||
<< ",\"hashrate_1h\":" << hashrate_1h
|
||||
<< ",\"hashrate_24h\":" << hashrate_24h
|
||||
<< ",\"total_hashes\":" << total_hashes
|
||||
<< ",\"shares_found\":" << shares_found
|
||||
<< ",\"average_effort\":" << average_effort
|
||||
<< ",\"current_effort\":" << current_effort
|
||||
<< ",\"connections\":" << connections
|
||||
<< ",\"incoming_connections\":" << incoming_connections
|
||||
<< "}";
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
@@ -112,6 +112,7 @@ private:
|
||||
uv_work_t m_req;
|
||||
StratumServer* m_server;
|
||||
StratumClient* m_client;
|
||||
raw_ip m_clientAddr;
|
||||
uint32_t m_clientResetCounter;
|
||||
uint32_t m_rpcId;
|
||||
uint32_t m_id;
|
||||
@@ -120,6 +121,7 @@ private:
|
||||
uint32_t m_extraNonce;
|
||||
uint64_t m_target;
|
||||
hash m_resultHash;
|
||||
difficulty_type m_sidechainDifficulty;
|
||||
|
||||
enum class Result {
|
||||
STALE,
|
||||
@@ -152,7 +154,10 @@ private:
|
||||
double m_cumulativeFoundSharesDiff;
|
||||
uint32_t m_totalFoundShares;
|
||||
|
||||
void update_hashrate_data(uint64_t target);
|
||||
time_t m_apiLastUpdateTime;
|
||||
|
||||
void update_hashrate_data(uint64_t hashes, time_t timestamp);
|
||||
void api_update_local_stats(time_t timestamp);
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ void TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::parse_address_list(const std::str
|
||||
}
|
||||
|
||||
const int port = atoi(address.substr(k2 + 1).c_str());
|
||||
if ((port > 0) && (port < 655356)) {
|
||||
if ((port > 0) && (port < 65536)) {
|
||||
callback(is_v6, address, ip, port);
|
||||
}
|
||||
else {
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ namespace p2pool {
|
||||
#define STR2(X) STR(X)
|
||||
#define STR(X) #X
|
||||
|
||||
const char* VERSION = "v1.1 (built"
|
||||
const char* VERSION = "v1.2 (built"
|
||||
#if defined(__clang__)
|
||||
" with clang/" __clang_version__
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
@@ -95,6 +95,7 @@ FORCEINLINE void writeVarint(T value, std::vector<uint8_t>& out)
|
||||
}
|
||||
|
||||
template<typename T, size_t N> FORCEINLINE constexpr size_t array_size(T(&)[N]) { return N; }
|
||||
template<typename T, typename U, size_t N> FORCEINLINE constexpr size_t array_size(T(U::*)[N]) { return N; }
|
||||
|
||||
[[noreturn]] void panic();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user