2021-08-22 10:20:59 +00:00
/*
* This file is part of the Monero P2Pool < https : //github.com/SChernykh/p2pool>
2024-01-02 13:06:19 +00:00
* Copyright ( c ) 2021 - 2024 SChernykh < https : //github.com/SChernykh>
2021-08-22 10:20:59 +00:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , version 3.
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "common.h"
# include "stratum_server.h"
# include "block_template.h"
# include "p2pool.h"
2021-08-30 17:45:37 +00:00
# include "side_chain.h"
2021-08-22 10:20:59 +00:00
# include "params.h"
2021-10-01 23:09:42 +00:00
# include "p2pool_api.h"
2021-08-22 10:20:59 +00:00
2023-08-16 11:00:11 +00:00
LOG_CATEGORY ( StratumServer )
2021-08-22 10:20:59 +00:00
static constexpr int DEFAULT_BACKLOG = 128 ;
static constexpr uint64_t DEFAULT_BAN_TIME = 600 ;
2022-05-08 10:30:22 +00:00
static constexpr uint64_t MIN_DIFF = 1000 ;
static constexpr uint64_t AUTO_DIFF_TARGET_TIME = 30 ;
2021-08-22 10:20:59 +00:00
2021-08-30 14:51:23 +00:00
// Use short target format (4 bytes) for diff <= 4 million
2022-06-07 14:02:08 +00:00
static constexpr uint64_t TARGET_4_BYTES_LIMIT = std : : numeric_limits < uint64_t > : : max ( ) / 4000001 ;
2021-08-30 14:51:23 +00:00
2024-04-03 08:58:35 +00:00
static constexpr uint64_t AUTODIFF_START = std : : numeric_limits < uint64_t > : : max ( ) / 500001 ;
2022-08-22 09:07:11 +00:00
static constexpr int32_t BAD_SHARE_POINTS = - 5 ;
static constexpr int32_t GOOD_SHARE_POINTS = 1 ;
static constexpr int32_t BAN_THRESHOLD_POINTS = - 15 ;
2021-08-22 10:20:59 +00:00
namespace p2pool {
StratumServer : : StratumServer ( p2pool * pool )
2024-02-06 16:20:58 +00:00
: TCPServer ( DEFAULT_BACKLOG , StratumClient : : allocate , std : : string ( ) )
2021-08-22 10:20:59 +00:00
, m_pool ( pool )
2022-05-08 10:30:22 +00:00
, m_autoDiff ( pool - > params ( ) . m_autoDiff )
2022-03-17 15:14:29 +00:00
, m_rng ( RandomDeviceSeed : : instance )
2021-08-30 14:51:23 +00:00
, m_cumulativeHashes ( 0 )
2021-08-30 17:45:37 +00:00
, m_cumulativeHashesAtLastShare ( 0 )
2021-08-30 14:51:23 +00:00
, m_hashrateDataHead ( 0 )
, m_hashrateDataTail_15m ( 0 )
, m_hashrateDataTail_1h ( 0 )
, m_hashrateDataTail_24h ( 0 )
2021-08-30 18:49:51 +00:00
, m_cumulativeFoundSharesDiff ( 0.0 )
, m_totalFoundShares ( 0 )
2023-01-07 14:50:02 +00:00
, m_totalFailedShares ( 0 )
2021-10-04 08:28:56 +00:00
, m_apiLastUpdateTime ( 0 )
2021-08-22 10:20:59 +00:00
{
2023-04-19 09:36:12 +00:00
m_callbackBuf . resize ( STRATUM_BUF_SIZE ) ;
2022-03-17 15:14:29 +00:00
// Diffuse the initial state in case it has low quality
m_rng . discard ( 10000 ) ;
2022-03-23 10:30:38 +00:00
m_hashrateData [ 0 ] = { seconds_since_epoch ( ) , 0 } ;
2021-08-30 14:51:23 +00:00
2021-08-22 10:20:59 +00:00
uv_mutex_init_checked ( & m_blobsQueueLock ) ;
2023-09-04 17:33:31 +00:00
uv_mutex_init_checked ( & m_showWorkersLock ) ;
2021-08-22 10:20:59 +00:00
uv_mutex_init_checked ( & m_rngLock ) ;
2021-08-30 14:51:23 +00:00
uv_rwlock_init_checked ( & m_hashrateDataLock ) ;
2021-08-22 10:20:59 +00:00
2023-03-19 15:47:12 +00:00
m_extraNonce = get_random32 ( ) ;
2023-01-17 15:15:24 +00:00
2021-08-22 10:20:59 +00:00
m_submittedSharesPool . resize ( 10 ) ;
for ( size_t i = 0 ; i < m_submittedSharesPool . size ( ) ; + + i ) {
2023-03-06 13:30:13 +00:00
SubmittedShare * share = new SubmittedShare { } ;
ASAN_POISON_MEMORY_REGION ( share , sizeof ( SubmittedShare ) ) ;
m_submittedSharesPool [ i ] = share ;
2021-08-22 10:20:59 +00:00
}
2023-01-27 21:09:17 +00:00
uv_async_init_checked ( & m_loop , & m_blobsAsync , on_blobs_ready ) ;
2021-08-22 10:20:59 +00:00
m_blobsAsync . data = this ;
m_blobsQueue . reserve ( 2 ) ;
2021-08-26 21:27:05 +00:00
2023-01-27 21:09:17 +00:00
uv_async_init_checked ( & m_loop , & m_showWorkersAsync , on_show_workers ) ;
m_showWorkersAsync . data = this ;
2023-03-20 12:32:17 +00:00
const Params & params = pool - > params ( ) ;
start_listening ( params . m_stratumAddresses , params . m_upnp & & params . m_upnpStratum ) ;
2021-08-22 10:20:59 +00:00
}
StratumServer : : ~ StratumServer ( )
{
shutdown_tcp ( ) ;
2023-10-02 06:19:28 +00:00
{
MutexLock lock ( m_blobsQueueLock ) ;
for ( BlobsData * data : m_blobsQueue ) {
delete data ;
}
}
2021-08-22 10:20:59 +00:00
uv_mutex_destroy ( & m_blobsQueueLock ) ;
2023-09-04 17:33:31 +00:00
uv_mutex_destroy ( & m_showWorkersLock ) ;
2021-08-22 10:20:59 +00:00
uv_mutex_destroy ( & m_rngLock ) ;
2021-08-30 14:51:23 +00:00
uv_rwlock_destroy ( & m_hashrateDataLock ) ;
2021-08-22 10:20:59 +00:00
for ( SubmittedShare * share : m_submittedSharesPool ) {
2023-03-06 13:30:13 +00:00
ASAN_UNPOISON_MEMORY_REGION ( share , sizeof ( SubmittedShare ) ) ;
2021-08-22 10:20:59 +00:00
delete share ;
}
}
void StratumServer : : on_block ( const BlockTemplate & block )
{
2021-08-28 21:34:46 +00:00
LOGINFO ( 4 , " new block template at height " < < block . height ( ) ) ;
2021-08-22 10:20:59 +00:00
const uint32_t num_connections = m_numConnections ;
if ( num_connections = = 0 ) {
2021-08-28 21:34:46 +00:00
LOGINFO ( 4 , " no clients connected " ) ;
2023-04-24 10:02:35 +00:00
api_update_local_stats ( seconds_since_epoch ( ) ) ;
2021-08-22 10:20:59 +00:00
return ;
}
2022-10-11 09:30:02 +00:00
2023-03-19 15:47:12 +00:00
const uint32_t extra_nonce_start = get_random32 ( ) ;
2022-10-11 09:30:02 +00:00
m_extraNonce . exchange ( extra_nonce_start + num_connections ) ;
2021-08-22 10:20:59 +00:00
BlobsData * blobs_data = new BlobsData { } ;
2022-10-11 09:30:02 +00:00
blobs_data - > m_extraNonceStart = extra_nonce_start ;
2021-08-22 10:20:59 +00:00
difficulty_type difficulty ;
2023-11-19 18:43:36 +00:00
difficulty_type aux_diff ;
2021-08-22 10:20:59 +00:00
difficulty_type sidechain_difficulty ;
size_t nonce_offset ;
// More clients might connect between now and when we actually go through clients list - get_hashing_blobs() and async send take some time
// Even if they do, they'll be added to the beginning of the list and will get their block template in on_login()
// We'll iterate through the list backwards so when we get to the beginning and run out of extra_nonce values, it'll be only new clients left
blobs_data - > m_numClientsExpected = num_connections ;
2023-11-19 18:43:36 +00:00
blobs_data - > m_blobSize = block . get_hashing_blobs ( extra_nonce_start , num_connections , blobs_data - > m_blobs , blobs_data - > m_height , difficulty , aux_diff , sidechain_difficulty , blobs_data - > m_seedHash , nonce_offset , blobs_data - > m_templateId ) ;
2021-09-04 07:10:44 +00:00
// Integrity checks
if ( blobs_data - > m_blobSize < 76 ) {
LOGERR ( 1 , " internal error: get_hashing_blobs returned too small blobs ( " < < blobs_data - > m_blobSize < < " bytes) " ) ;
}
else if ( blobs_data - > m_blobs . size ( ) ! = blobs_data - > m_blobSize * num_connections ) {
LOGERR ( 1 , " internal error: get_hashing_blobs returned wrong amount of data " ) ;
}
else if ( num_connections > 1 ) {
std : : vector < uint64_t > blob_hashes ;
blob_hashes . reserve ( num_connections ) ;
const uint8_t * data = blobs_data - > m_blobs . data ( ) ;
const size_t size = blobs_data - > m_blobSize ;
// Get first 8 bytes of the Merkle root hash from each blob
for ( size_t i = 0 ; i < num_connections ; + + i ) {
2022-06-27 14:20:45 +00:00
blob_hashes . emplace_back ( read_unaligned ( reinterpret_cast < const uint64_t * > ( data + i * size + 43 ) ) ) ;
2021-09-04 07:10:44 +00:00
}
// Find duplicates
std : : sort ( blob_hashes . begin ( ) , blob_hashes . end ( ) ) ;
for ( uint32_t i = 1 ; i < num_connections ; + + i ) {
if ( blob_hashes [ i - 1 ] = = blob_hashes [ i ] ) {
LOGERR ( 1 , " internal error: get_hashing_blobs returned two identical blobs " ) ;
break ;
}
}
}
2021-08-22 10:20:59 +00:00
blobs_data - > m_target = std : : max ( difficulty . target ( ) , sidechain_difficulty . target ( ) ) ;
2023-11-19 18:43:36 +00:00
blobs_data - > m_target = std : : max ( blobs_data - > m_target , aux_diff . target ( ) ) ;
2021-08-22 10:20:59 +00:00
{
MutexLock lock ( m_blobsQueueLock ) ;
2021-08-23 21:08:46 +00:00
2023-09-04 17:33:31 +00:00
if ( uv_is_closing ( reinterpret_cast < uv_handle_t * > ( & m_blobsAsync ) ) ) {
delete blobs_data ;
return ;
}
2021-08-22 10:20:59 +00:00
2023-09-04 17:33:31 +00:00
m_blobsQueue . push_back ( blobs_data ) ;
2021-08-22 10:20:59 +00:00
2023-09-04 17:33:31 +00:00
const int err = uv_async_send ( & m_blobsAsync ) ;
if ( err ) {
LOGERR ( 1 , " uv_async_send failed, error " < < uv_err_name ( err ) ) ;
2021-08-22 10:20:59 +00:00
2023-09-04 17:33:31 +00:00
m_blobsQueue . pop_back ( ) ;
2021-08-22 10:20:59 +00:00
delete blobs_data ;
}
}
2023-04-24 10:02:35 +00:00
api_update_local_stats ( seconds_since_epoch ( ) ) ;
2021-08-22 10:20:59 +00:00
}
2022-05-07 09:00:29 +00:00
template < size_t N >
static bool get_custom_user ( const char * s , char ( & user ) [ N ] )
2021-10-15 21:02:21 +00:00
{
2022-05-07 09:00:29 +00:00
size_t len = 0 ;
2021-10-15 21:02:21 +00:00
// Find first of '+' or '.', drop non-printable characters
2022-05-07 09:00:29 +00:00
while ( s & & ( len < N - 1 ) ) {
2021-10-15 21:02:21 +00:00
const char c = * s ;
if ( ! c ) {
break ;
}
if ( ( c = = ' + ' ) | | ( c = = ' . ' ) ) {
break ;
}
2023-09-17 09:32:56 +00:00
// Limit to printable ASCII characters, also skip comma and JSON special characters
if ( c > = ' ' & & c < = ' ~ ' & & c ! = ' , ' & & c ! = ' " ' & & c ! = ' \\ ' ) {
2022-05-10 12:01:10 +00:00
user [ len + + ] = c ;
2021-10-15 21:02:21 +00:00
}
+ + s ;
}
2022-05-07 09:00:29 +00:00
user [ len ] = ' \0 ' ;
2021-10-15 21:02:21 +00:00
2022-05-07 09:00:29 +00:00
return ( len > 0 ) ;
2021-10-15 21:02:21 +00:00
}
2022-05-07 09:00:29 +00:00
static bool get_custom_diff ( const char * s , difficulty_type & diff )
2021-08-28 15:18:41 +00:00
{
const char * diff_str = nullptr ;
// Find last of '+' or '.'
while ( s ) {
const char c = * s ;
if ( ! c ) {
break ;
}
if ( ( c = = ' + ' ) | | ( c = = ' . ' ) ) {
diff_str = s ;
}
+ + s ;
}
if ( diff_str ) {
const uint64_t t = strtoull ( diff_str + 1 , nullptr , 10 ) ;
if ( t ) {
2022-05-08 10:30:22 +00:00
// Don't let clients set difficulty less than MIN_DIFF
diff = { std : : max < uint64_t > ( t + 1 , MIN_DIFF ) , 0 } ;
2021-08-28 15:18:41 +00:00
return true ;
}
}
return false ;
}
bool StratumServer : : on_login ( StratumClient * client , uint32_t id , const char * login )
2021-08-22 10:20:59 +00:00
{
2023-09-08 13:19:59 +00:00
if ( client - > m_rpcId ) {
LOGWARN ( 4 , " client " < < static_cast < char * > ( client - > m_addrString ) < < " tried to login, but it's already logged in " ) ;
return false ;
}
2021-08-22 10:20:59 +00:00
const uint32_t extra_nonce = m_extraNonce . fetch_add ( 1 ) ;
uint8_t hashing_blob [ 128 ] ;
2022-11-08 13:27:03 +00:00
uint64_t height , sidechain_height ;
2021-08-22 10:20:59 +00:00
difficulty_type difficulty ;
2023-11-19 18:43:36 +00:00
difficulty_type aux_diff ;
2021-08-22 10:20:59 +00:00
difficulty_type sidechain_difficulty ;
hash seed_hash ;
size_t nonce_offset ;
uint32_t template_id ;
2023-11-19 18:43:36 +00:00
const size_t blob_size = m_pool - > block_template ( ) . get_hashing_blob ( extra_nonce , hashing_blob , height , sidechain_height , difficulty , aux_diff , sidechain_difficulty , seed_hash , nonce_offset , template_id ) ;
2021-08-28 15:18:41 +00:00
uint64_t target = std : : max ( difficulty . target ( ) , sidechain_difficulty . target ( ) ) ;
2023-11-19 18:43:36 +00:00
target = std : : max ( target , aux_diff . target ( ) ) ;
2021-08-28 15:18:41 +00:00
if ( get_custom_diff ( login , client - > m_customDiff ) ) {
LOGINFO ( 5 , " client " < < log : : Gray ( ) < < static_cast < char * > ( client - > m_addrString ) < < " set custom difficulty " < < client - > m_customDiff ) ;
target = std : : max ( target , client - > m_customDiff . target ( ) ) ;
}
2022-06-26 15:06:21 +00:00
else if ( m_autoDiff ) {
// Limit autodiff to 4000000 for maximum compatibility
2024-04-03 08:58:35 +00:00
target = std : : max ( std : : max ( target , AUTODIFF_START ) , TARGET_4_BYTES_LIMIT ) ;
2022-06-26 15:06:21 +00:00
}
2021-08-22 10:20:59 +00:00
2021-10-15 21:02:21 +00:00
if ( get_custom_user ( login , client - > m_customUser ) ) {
2022-05-07 09:00:29 +00:00
const char * s = client - > m_customUser ;
LOGINFO ( 5 , " client " < < log : : Gray ( ) < < static_cast < char * > ( client - > m_addrString ) < < " set custom user " < < s ) ;
2021-10-15 21:02:21 +00:00
}
2021-08-22 10:20:59 +00:00
uint32_t job_id ;
{
2022-05-08 10:30:22 +00:00
job_id = + + client - > m_perConnectionJobId ;
2021-08-22 10:20:59 +00:00
2022-05-08 10:30:22 +00:00
StratumClient : : SavedJob & saved_job = client - > m_jobs [ job_id % StratumClient : : JOBS_SIZE ] ;
2021-08-22 10:20:59 +00:00
saved_job . job_id = job_id ;
saved_job . extra_nonce = extra_nonce ;
saved_job . template_id = template_id ;
saved_job . target = target ;
}
2023-01-27 21:09:17 +00:00
client - > m_lastJobTarget = target ;
2021-08-22 10:20:59 +00:00
const bool result = send ( client ,
2023-05-24 11:46:05 +00:00
[ client , id , & hashing_blob , job_id , blob_size , target , height , & seed_hash ] ( uint8_t * buf , size_t buf_size )
2021-08-22 10:20:59 +00:00
{
do {
2023-01-17 15:15:24 +00:00
client - > m_rpcId = static_cast < StratumServer * > ( client - > m_owner ) - > get_random32 ( ) ;
2021-08-22 10:20:59 +00:00
} while ( ! client - > m_rpcId ) ;
2023-07-07 09:00:19 +00:00
log : : hex_buf target_hex ( & target ) ;
2021-08-28 17:50:48 +00:00
2021-08-30 14:51:23 +00:00
if ( target > = TARGET_4_BYTES_LIMIT ) {
2021-08-28 17:50:48 +00:00
target_hex . m_data + = sizeof ( uint32_t ) ;
target_hex . m_size - = sizeof ( uint32_t ) ;
}
2022-05-04 13:53:01 +00:00
log : : Stream s ( buf , buf_size ) ;
2021-08-22 10:20:59 +00:00
s < < " { \" id \" : " < < id < < " , \" jsonrpc \" : \" 2.0 \" , \" result \" :{ \" id \" : \" " ;
s < < log : : Hex ( client - > m_rpcId ) < < " \" , \" job \" :{ \" blob \" : \" " ;
s < < log : : hex_buf ( hashing_blob , blob_size ) < < " \" , \" job_id \" : \" " ;
s < < log : : Hex ( job_id ) < < " \" , \" target \" : \" " ;
2021-08-28 17:50:48 +00:00
s < < target_hex < < " \" , \" algo \" : \" rx/0 \" , \" height \" : " ;
2021-08-22 10:20:59 +00:00
s < < height < < " , \" seed_hash \" : \" " ;
s < < seed_hash < < " \" }, \" extensions \" :[ \" algo \" ], \" status \" : \" OK \" }} \n " ;
return s . m_pos ;
} ) ;
return result ;
}
2021-08-28 15:18:41 +00:00
bool StratumServer : : on_submit ( StratumClient * client , uint32_t id , const char * job_id_str , const char * nonce_str , const char * result_str )
2021-08-22 10:20:59 +00:00
{
uint32_t job_id = 0 ;
for ( size_t i = 0 ; job_id_str [ i ] ; + + i ) {
uint32_t d ;
if ( ! from_hex ( job_id_str [ i ] , d ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( client - > m_addrString ) < < " invalid params ('job_id' is not a hex integer) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
job_id = ( job_id < < 4 ) + d ;
}
2022-05-08 10:30:22 +00:00
if ( ! job_id ) {
LOGWARN ( 4 , " client " < < static_cast < char * > ( client - > m_addrString ) < < " invalid params ('job_id' can't be 0) " ) ;
return false ;
}
2021-08-22 10:20:59 +00:00
uint32_t nonce = 0 ;
for ( int i = static_cast < int > ( sizeof ( uint32_t ) ) - 1 ; i > = 0 ; - - i ) {
uint32_t d [ 2 ] ;
if ( ! from_hex ( nonce_str [ i * 2 ] , d [ 0 ] ) | | ! from_hex ( nonce_str [ i * 2 + 1 ] , d [ 1 ] ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( client - > m_addrString ) < < " invalid params ('nonce' is not a hex integer) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
nonce = ( nonce < < 8 ) | ( d [ 0 ] < < 4 ) | d [ 1 ] ;
}
2021-08-28 15:18:41 +00:00
hash resultHash ;
for ( size_t i = 0 ; i < HASH_SIZE ; + + i ) {
uint32_t d [ 2 ] ;
if ( ! from_hex ( result_str [ i * 2 ] , d [ 0 ] ) | | ! from_hex ( result_str [ i * 2 + 1 ] , d [ 1 ] ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( client - > m_addrString ) < < " invalid params ('result' is not a hex value) " ) ;
2021-08-28 15:18:41 +00:00
return false ;
}
resultHash . h [ i ] = static_cast < uint8_t > ( ( d [ 0 ] < < 4 ) | d [ 1 ] ) ;
}
2021-08-22 10:20:59 +00:00
uint32_t template_id = 0 ;
uint32_t extra_nonce = 0 ;
2021-08-30 14:51:23 +00:00
uint64_t target = 0 ;
2021-08-22 10:20:59 +00:00
bool found = false ;
{
2022-05-08 10:30:22 +00:00
const StratumClient : : SavedJob & saved_job = client - > m_jobs [ job_id % StratumClient : : JOBS_SIZE ] ;
2021-08-22 10:20:59 +00:00
if ( saved_job . job_id = = job_id ) {
template_id = saved_job . template_id ;
extra_nonce = saved_job . extra_nonce ;
2021-08-30 14:51:23 +00:00
target = saved_job . target ;
2021-08-22 10:20:59 +00:00
found = true ;
}
}
if ( found ) {
2023-11-26 08:52:24 +00:00
const BlockTemplate & block = m_pool - > block_template ( ) ;
2022-11-08 13:27:03 +00:00
uint64_t height , sidechain_height ;
2023-11-19 18:43:36 +00:00
difficulty_type mainchain_diff , aux_diff , sidechain_diff ;
2021-10-13 16:57:21 +00:00
2023-11-19 18:43:36 +00:00
if ( ! block . get_difficulties ( template_id , height , sidechain_height , mainchain_diff , aux_diff , sidechain_diff ) ) {
2021-10-13 16:57:21 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( client - > m_addrString ) < < " got a stale share " ) ;
return send ( client ,
2023-05-24 11:46:05 +00:00
[ id ] ( uint8_t * buf , size_t buf_size )
2021-10-13 16:57:21 +00:00
{
2022-05-04 13:53:01 +00:00
log : : Stream s ( buf , buf_size ) ;
2021-10-13 16:57:21 +00:00
s < < " { \" id \" : " < < id < < " , \" jsonrpc \" : \" 2.0 \" , \" error \" :{ \" message \" : \" Stale share \" }} \n " ;
return s . m_pos ;
} ) ;
}
if ( mainchain_diff . check_pow ( resultHash ) ) {
2022-05-07 09:00:29 +00:00
const char * s = client - > m_customUser ;
2022-11-08 13:27:03 +00:00
LOGINFO ( 0 , log : : Green ( ) < < " client " < < static_cast < char * > ( client - > m_addrString ) < < ( * s ? " user " : " " ) < < s < < " found a mainchain block at height " < < height < < " , submitting it " ) ;
2021-10-13 16:57:21 +00:00
m_pool - > submit_block_async ( template_id , nonce , extra_nonce ) ;
}
2023-11-19 18:43:36 +00:00
if ( aux_diff . check_pow ( resultHash ) ) {
2023-11-19 20:43:18 +00:00
for ( const AuxChainData & aux_data : block . get_aux_chains ( template_id ) ) {
if ( aux_data . difficulty . check_pow ( resultHash ) ) {
2023-12-10 14:31:40 +00:00
const char * s = client - > m_customUser ;
LOGINFO ( 0 , log : : Green ( ) < < " client " < < static_cast < char * > ( client - > m_addrString ) < < ( * s ? " user " : " " ) < < s < < " found an aux block for chain_id " < < aux_data . unique_id < < " , diff " < < aux_data . difficulty < < " , submitting it " ) ;
2023-11-20 21:22:37 +00:00
m_pool - > submit_aux_block ( aux_data . unique_id , template_id , nonce , extra_nonce ) ;
2023-11-19 20:43:18 +00:00
}
}
2023-11-19 18:43:36 +00:00
}
2021-08-22 10:20:59 +00:00
SubmittedShare * share ;
2022-05-08 10:30:22 +00:00
if ( ! m_submittedSharesPool . empty ( ) ) {
share = m_submittedSharesPool . back ( ) ;
m_submittedSharesPool . pop_back ( ) ;
2023-03-06 13:30:13 +00:00
ASAN_UNPOISON_MEMORY_REGION ( share , sizeof ( SubmittedShare ) ) ;
2022-05-08 10:30:22 +00:00
}
else {
share = new SubmittedShare { } ;
}
2021-08-22 10:20:59 +00:00
2022-05-08 10:30:22 +00:00
if ( target > = TARGET_4_BYTES_LIMIT ) {
2022-06-26 15:07:19 +00:00
// "Low diff share" fix: adjust target to the same value as XMRig would use
target = std : : numeric_limits < uint64_t > : : max ( ) / ( std : : numeric_limits < uint32_t > : : max ( ) / ( target > > 32 ) ) ;
2021-08-22 10:20:59 +00:00
}
share - > m_req . data = share ;
share - > m_server = this ;
share - > m_client = client ;
2023-04-27 08:28:32 +00:00
share - > m_clientIPv6 = client - > m_isV6 ;
2021-10-04 13:51:28 +00:00
share - > m_clientAddr = client - > m_addr ;
2023-09-08 13:19:59 +00:00
memcpy ( share - > m_clientAddrString , client - > m_addrString , sizeof ( share - > m_clientAddrString ) ) ;
memcpy ( share - > m_clientCustomUser , client - > m_customUser , sizeof ( share - > m_clientCustomUser ) ) ;
2021-08-22 10:20:59 +00:00
share - > m_clientResetCounter = client - > m_resetCounter . load ( ) ;
share - > m_rpcId = client - > m_rpcId ;
share - > m_id = id ;
share - > m_templateId = template_id ;
share - > m_nonce = nonce ;
share - > m_extraNonce = extra_nonce ;
2021-08-30 14:51:23 +00:00
share - > m_target = target ;
2021-08-28 15:18:41 +00:00
share - > m_resultHash = resultHash ;
2021-10-13 16:57:21 +00:00
share - > m_sidechainDifficulty = sidechain_diff ;
2022-08-31 14:37:33 +00:00
share - > m_mainchainHeight = height ;
2022-11-08 13:27:03 +00:00
share - > m_sidechainHeight = sidechain_height ;
2022-08-31 14:37:33 +00:00
share - > m_effort = - 1.0 ;
2022-05-08 10:30:22 +00:00
share - > m_timestamp = seconds_since_epoch ( ) ;
uint64_t rem ;
share - > m_hashes = ( target > 1 ) ? udiv128 ( 1 , 0 , target , & rem ) : 1 ;
share - > m_highEnoughDifficulty = sidechain_diff . check_pow ( resultHash ) ;
2023-02-27 17:58:56 +00:00
share - > m_score = 0 ;
2022-05-08 10:30:22 +00:00
2022-12-22 10:48:50 +00:00
// Don't count shares that were found during sync
const SideChain & side_chain = m_pool - > side_chain ( ) ;
const PoolBlock * tip = side_chain . chainTip ( ) ;
if ( tip & & ( sidechain_height + side_chain . chain_window_size ( ) < tip - > m_sidechainHeight ) ) {
share - > m_highEnoughDifficulty = false ;
}
2022-05-08 10:30:22 +00:00
update_auto_diff ( client , share - > m_timestamp , share - > m_hashes ) ;
2021-08-22 10:20:59 +00:00
2021-10-21 17:09:36 +00:00
// If this share is below sidechain difficulty, process it in this thread because it'll be quick
2022-05-08 10:30:22 +00:00
if ( ! share - > m_highEnoughDifficulty ) {
2021-10-21 17:09:36 +00:00
on_share_found ( & share - > m_req ) ;
on_after_share_found ( & share - > m_req , 0 ) ;
return true ;
}
// Else switch to a worker thread to check PoW which can take a long time
2021-08-22 10:20:59 +00:00
const int err = uv_queue_work ( & m_loop , & share - > m_req , on_share_found , on_after_share_found ) ;
if ( err ) {
LOGERR ( 1 , " uv_queue_work failed, error " < < uv_err_name ( err ) ) ;
2021-10-21 17:09:36 +00:00
// If uv_queue_work failed, process this share here anyway
on_share_found ( & share - > m_req ) ;
on_after_share_found ( & share - > m_req , 0 ) ;
2021-08-22 10:20:59 +00:00
}
return true ;
}
2022-09-07 06:59:04 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( client - > m_addrString ) < < " got a share with invalid job id " < < job_id < < " (latest job sent has id " < < client - > m_perConnectionJobId < < ' ) ' ) ;
2021-08-22 10:20:59 +00:00
const bool result = send ( client ,
2023-05-24 11:46:05 +00:00
[ id ] ( uint8_t * buf , size_t buf_size )
2021-08-22 10:20:59 +00:00
{
2022-05-04 13:53:01 +00:00
log : : Stream s ( buf , buf_size ) ;
2021-08-22 10:20:59 +00:00
s < < " { \" id \" : " < < id < < " , \" jsonrpc \" : \" 2.0 \" , \" error \" :{ \" message \" : \" Invalid job id \" }} \n " ;
return s . m_pos ;
} ) ;
return result ;
}
2023-01-17 15:15:24 +00:00
uint32_t StratumServer : : get_random32 ( )
2021-08-22 10:20:59 +00:00
{
MutexLock lock ( m_rngLock ) ;
2023-01-17 15:15:24 +00:00
return static_cast < uint32_t > ( m_rng ( ) > > 32 ) ;
2021-08-22 10:20:59 +00:00
}
2021-08-30 15:50:09 +00:00
void StratumServer : : print_status ( )
{
2022-03-23 10:30:38 +00:00
update_hashrate_data ( 0 , seconds_since_epoch ( ) ) ;
2021-08-30 15:50:09 +00:00
print_stratum_status ( ) ;
}
2023-01-27 21:09:17 +00:00
void StratumServer : : show_workers_async ( )
{
2023-09-04 17:33:31 +00:00
MutexLock lock ( m_showWorkersLock ) ;
2023-01-27 21:09:17 +00:00
if ( ! uv_is_closing ( reinterpret_cast < uv_handle_t * > ( & m_showWorkersAsync ) ) ) {
uv_async_send ( & m_showWorkersAsync ) ;
}
}
2022-05-06 11:19:56 +00:00
void StratumServer : : show_workers ( )
{
2023-02-27 14:38:30 +00:00
check_event_loop_thread ( __func__ ) ;
2022-05-06 11:19:56 +00:00
const uint64_t cur_time = seconds_since_epoch ( ) ;
2022-05-15 16:13:36 +00:00
const difficulty_type pool_diff = m_pool - > side_chain ( ) . difficulty ( ) ;
2022-05-06 11:19:56 +00:00
2022-05-06 18:04:01 +00:00
int addr_len = 0 ;
for ( const StratumClient * c = static_cast < StratumClient * > ( m_connectedClientsList - > m_next ) ; c ! = m_connectedClientsList ; c = static_cast < StratumClient * > ( c - > m_next ) ) {
addr_len = std : : max ( addr_len , static_cast < int > ( strlen ( c - > m_addrString ) ) ) ;
}
size_t n = 0 ;
2022-05-08 10:30:22 +00:00
LOGINFO ( 0 , log : : pad_right ( " IP:port " , addr_len + 8 )
< < log : : pad_right ( " uptime " , 20 )
< < log : : pad_right ( " difficulty " , 20 )
< < log : : pad_right ( " hashrate " , 15 )
< < " name "
) ;
2022-05-06 18:04:01 +00:00
for ( const StratumClient * c = static_cast < StratumClient * > ( m_connectedClientsList - > m_next ) ; c ! = m_connectedClientsList ; c = static_cast < StratumClient * > ( c - > m_next ) ) {
2023-01-27 21:09:17 +00:00
difficulty_type diff = pool_diff ;
if ( c - > m_lastJobTarget > 1 ) {
uint64_t r ;
diff . lo = udiv128 ( 1 , 0 , c - > m_lastJobTarget , & r ) ;
diff . hi = 0 ;
if ( r ) {
+ + diff . lo ;
}
2022-05-15 16:13:36 +00:00
}
2022-05-06 18:04:01 +00:00
LOGINFO ( 0 , log : : pad_right ( static_cast < const char * > ( c - > m_addrString ) , addr_len + 8 )
< < log : : pad_right ( log : : Duration ( cur_time - c - > m_connectedTime ) , 20 )
2022-05-15 16:13:36 +00:00
< < log : : pad_right ( diff , 20 )
< < log : : pad_right ( log : : Hashrate ( c - > m_autoDiff . lo / AUTO_DIFF_TARGET_TIME , m_autoDiff & & ( c - > m_autoDiff ! = 0 ) ) , 15 )
2022-05-07 09:00:29 +00:00
< < ( c - > m_rpcId ? c - > m_customUser : " not logged in " )
2022-05-06 11:19:56 +00:00
) ;
2022-05-06 18:04:01 +00:00
+ + n ;
2022-05-06 11:19:56 +00:00
}
2022-05-06 18:04:01 +00:00
LOGINFO ( 0 , " Total: " < < n < < " workers " ) ;
2022-05-06 11:19:56 +00:00
}
2022-02-17 10:19:11 +00:00
void StratumServer : : reset_share_counters ( )
{
2022-12-22 19:12:37 +00:00
WriteLock lock ( m_hashrateDataLock ) ;
2023-02-10 16:55:26 +00:00
m_cumulativeHashesAtLastShare = m_cumulativeHashes ;
2022-02-17 10:19:11 +00:00
m_totalFoundShares = 0 ;
2023-01-07 14:50:02 +00:00
m_totalFailedShares = 0 ;
2022-02-17 10:19:11 +00:00
}
2023-08-16 11:00:11 +00:00
const char * StratumServer : : get_log_category ( ) const
{
return log_category_prefix ;
}
2021-08-30 14:51:23 +00:00
void StratumServer : : print_stratum_status ( ) const
{
uint64_t hashes_15m , hashes_1h , hashes_24h , total_hashes ;
int64_t dt_15m , dt_1h , dt_24h ;
2021-08-30 17:45:37 +00:00
uint64_t hashes_since_last_share ;
2022-12-22 20:00:59 +00:00
double average_effort ;
2023-01-07 14:50:02 +00:00
uint32_t shares_found , shares_failed ;
2021-08-30 17:45:37 +00:00
2021-08-30 14:51:23 +00:00
{
ReadLock lock ( m_hashrateDataLock ) ;
total_hashes = m_cumulativeHashes ;
2021-08-30 17:45:37 +00:00
hashes_since_last_share = m_cumulativeHashes - m_cumulativeHashesAtLastShare ;
2021-08-30 14:51:23 +00:00
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 ) ;
2022-12-22 19:12:37 +00:00
average_effort = 0.0 ;
2022-12-22 20:00:59 +00:00
const double diff = m_cumulativeFoundSharesDiff ;
2022-12-22 19:12:37 +00:00
if ( diff > 0.0 ) {
average_effort = static_cast < double > ( m_cumulativeHashesAtLastShare ) * 100.0 / diff ;
}
shares_found = m_totalFoundShares ;
2023-01-07 14:50:02 +00:00
shares_failed = m_totalFailedShares ;
2021-08-30 14:51:23 +00:00
}
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 ;
2023-01-07 14:50:02 +00:00
char shares_failed_buf [ 64 ] = { } ;
if ( shares_failed ) {
2023-07-02 13:33:51 +00:00
log : : Stream s ( shares_failed_buf ) ;
2023-01-07 14:50:02 +00:00
s < < log : : Yellow ( ) < < " \n Shares failed = " < < shares_failed < < log : : NoColor ( ) ;
}
2021-08-30 14:51:23 +00:00
LOGINFO ( 0 , " status " < <
" \n Hashrate (15m est) = " < < log : : Hashrate ( hashrate_15m ) < <
" \n Hashrate (1h est) = " < < log : : Hashrate ( hashrate_1h ) < <
" \n Hashrate (24h est) = " < < log : : Hashrate ( hashrate_24h ) < <
" \n Total hashes = " < < total_hashes < <
2023-01-18 23:04:35 +00:00
" \n Shares found = " < < shares_found < < static_cast < const char * > ( shares_failed_buf ) < <
2021-08-30 18:49:51 +00:00
" \n Average effort = " < < average_effort < < ' % ' < <
2021-08-30 17:45:37 +00:00
" \n Current effort = " < < static_cast < double > ( hashes_since_last_share ) * 100.0 / m_pool - > side_chain ( ) . difficulty ( ) . to_double ( ) < < ' % ' < <
2022-05-09 14:07:49 +00:00
" \n Connections = " < < m_numConnections . load ( ) < < " ( " < < m_numIncomingConnections . load ( ) < < " incoming) "
2021-08-30 14:51:23 +00:00
) ;
}
2022-05-08 10:30:22 +00:00
// Compresses 64-bit hashes value into 16-bit value (5 bits for shift, 11 bits for data)
namespace {
enum HashValue : uint64_t {
bits = 11 ,
mask = ( 1 < < bits ) - 1 ,
} ;
static constexpr FORCEINLINE uint64_t hash_uncompress ( uint64_t h )
{
return ( h & HashValue : : mask ) < < ( h > > HashValue : : bits ) ;
} ;
enum HashMaxValue : uint64_t {
value = hash_uncompress ( std : : numeric_limits < uint16_t > : : max ( ) )
} ;
2022-05-09 09:25:52 +00:00
static FORCEINLINE uint16_t hash_compress ( uint64_t h )
2022-05-08 10:30:22 +00:00
{
2022-05-09 09:25:52 +00:00
if ( h < = HashValue : : mask ) {
return static_cast < uint16_t > ( h ) ;
2022-05-08 10:30:22 +00:00
}
2022-05-09 09:25:52 +00:00
if ( h > = HashMaxValue : : value ) {
return std : : numeric_limits < uint16_t > : : max ( ) ;
2022-05-08 10:30:22 +00:00
}
2022-05-09 09:25:52 +00:00
const uint64_t shift = bsr ( h ) - ( HashValue : : bits - 1 ) ;
return static_cast < uint16_t > ( ( shift < < HashValue : : bits ) | ( h > > shift ) ) ;
2022-05-08 10:30:22 +00:00
}
}
void StratumServer : : update_auto_diff ( StratumClient * client , const uint64_t timestamp , const uint64_t hashes )
{
const uint16_t hashes_compressed = hash_compress ( hashes ) ;
client - > m_autoDiffWindowHashes + = hash_uncompress ( hashes_compressed ) ;
const uint32_t k = client - > m_autoDiffIndex + + ;
constexpr uint32_t N = StratumClient : : AUTO_DIFF_SIZE ;
StratumClient : : AutoDiffData & auto_diff_data = client - > m_autoDiffData [ k % N ] ;
if ( k > = N ) {
client - > m_autoDiffWindowHashes - = hash_uncompress ( auto_diff_data . m_hashes ) ;
}
const uint16_t t1 = auto_diff_data . m_timestamp ;
const uint16_t t2 = static_cast < uint16_t > ( timestamp ) ;
auto_diff_data . m_timestamp = t2 ;
auto_diff_data . m_hashes = hashes_compressed ;
if ( k > = N ) {
// Full window
2022-07-05 08:02:18 +00:00
const uint16_t dt = t2 - t1 ;
2022-05-08 10:30:22 +00:00
client - > m_autoDiff . lo = std : : max < uint64_t > ( ( client - > m_autoDiffWindowHashes * AUTO_DIFF_TARGET_TIME ) / ( dt ? dt : 1 ) , MIN_DIFF ) ;
client - > m_autoDiff . hi = 0 ;
}
else if ( k > = 10 ) {
// Partial window
const uint64_t h0 = hash_uncompress ( client - > m_autoDiffData [ 0 ] . m_hashes ) ;
2022-07-05 08:02:18 +00:00
const uint16_t dt = client - > m_autoDiffData [ k ] . m_timestamp - client - > m_autoDiffData [ 0 ] . m_timestamp ;
2022-05-08 10:30:22 +00:00
client - > m_autoDiff . lo = std : : max < uint64_t > ( ( ( client - > m_autoDiffWindowHashes - h0 ) * AUTO_DIFF_TARGET_TIME ) / ( dt ? dt : 1 ) , MIN_DIFF ) ;
client - > m_autoDiff . hi = 0 ;
}
else if ( k = = 0 ) {
// First share, fix auto diff to current difficulty until we have at least 10 shares
client - > m_autoDiff . lo = hashes ;
client - > m_autoDiff . hi = 0 ;
}
}
2021-08-22 10:20:59 +00:00
void StratumServer : : on_blobs_ready ( )
{
2023-02-27 14:38:30 +00:00
check_event_loop_thread ( __func__ ) ;
2021-08-22 10:20:59 +00:00
std : : vector < BlobsData * > blobs_queue ;
blobs_queue . reserve ( 2 ) ;
{
MutexLock lock ( m_blobsQueueLock ) ;
blobs_queue = m_blobsQueue ;
m_blobsQueue . clear ( ) ;
}
if ( blobs_queue . empty ( ) ) {
return ;
}
ON_SCOPE_LEAVE ( [ & blobs_queue ] ( )
{
for ( BlobsData * data : blobs_queue ) {
delete data ;
}
} ) ;
// Only send the latest blob
BlobsData * data = blobs_queue . back ( ) ;
2022-10-11 09:30:02 +00:00
const uint32_t extra_nonce_start = data - > m_extraNonceStart ;
2021-08-22 10:20:59 +00:00
size_t numClientsProcessed = 0 ;
2022-10-11 09:30:02 +00:00
uint32_t num_sent = 0 ;
2022-02-08 14:00:08 +00:00
2022-03-23 10:30:38 +00:00
const uint64_t cur_time = seconds_since_epoch ( ) ;
2021-08-22 10:20:59 +00:00
2023-02-27 14:38:30 +00:00
for ( StratumClient * client = static_cast < StratumClient * > ( m_connectedClientsList - > m_prev ) ; client ! = m_connectedClientsList ; client = static_cast < StratumClient * > ( client - > m_prev ) ) {
+ + numClientsProcessed ;
2021-08-22 10:20:59 +00:00
2023-02-27 14:38:30 +00:00
if ( ! client - > m_rpcId ) {
// Not logged in yet, on_login() will send the job to this client. Also close inactive connections.
if ( cur_time > = client - > m_connectedTime + 10 ) {
LOGWARN ( 4 , " client " < < static_cast < char * > ( client - > m_addrString ) < < " didn't send login data " ) ;
client - > ban ( DEFAULT_BAN_TIME ) ;
client - > close ( ) ;
2021-08-22 10:20:59 +00:00
}
2023-02-27 14:38:30 +00:00
continue ;
}
2021-08-22 10:20:59 +00:00
2023-02-27 14:38:30 +00:00
if ( num_sent > = data - > m_numClientsExpected ) {
// We don't have any more extra_nonce values available
continue ;
}
2021-08-22 10:20:59 +00:00
2023-02-27 14:38:30 +00:00
uint8_t * hashing_blob = data - > m_blobs . data ( ) + num_sent * data - > m_blobSize ;
2021-08-22 10:20:59 +00:00
2023-02-27 14:38:30 +00:00
uint64_t target = data - > m_target ;
if ( client - > m_customDiff . lo ) {
target = std : : max ( target , client - > m_customDiff . target ( ) ) ;
}
else if ( m_autoDiff ) {
// Limit autodiff to 4000000 for maximum compatibility
target = std : : max ( target , TARGET_4_BYTES_LIMIT ) ;
if ( client - > m_autoDiff . lo ) {
const uint32_t k = client - > m_autoDiffIndex ;
const uint16_t elapsed_time = static_cast < uint16_t > ( cur_time ) - client - > m_autoDiffData [ ( k - 1 ) % StratumClient : : AUTO_DIFF_SIZE ] . m_timestamp ;
if ( elapsed_time > AUTO_DIFF_TARGET_TIME * 5 ) {
// More than 500% effort, reduce the auto diff by 1/8 every time until the share is found
client - > m_autoDiff . lo = std : : max < uint64_t > ( client - > m_autoDiff . lo - client - > m_autoDiff . lo / 8 , MIN_DIFF ) ;
2022-05-08 10:30:22 +00:00
}
2023-02-27 14:38:30 +00:00
target = std : : max ( target , client - > m_autoDiff . target ( ) ) ;
}
else {
2024-04-03 08:58:35 +00:00
// Not enough shares from the client yet, start with 500k diff and cut diff in half every 16 seconds
target = std : : max ( target , AUTODIFF_START ) ;
2023-02-27 14:38:30 +00:00
const uint64_t num_halvings = ( cur_time - client - > m_connectedTime ) / 16 ;
constexpr uint64_t max_target = ( std : : numeric_limits < uint64_t > : : max ( ) / MIN_DIFF ) + 1 ;
for ( uint64_t i = 0 ; ( i < num_halvings ) & & ( target < max_target ) ; + + i ) {
target * = 2 ;
2022-05-08 10:30:22 +00:00
}
2023-02-27 14:38:30 +00:00
target = std : : min < uint64_t > ( target , max_target ) ;
2022-05-08 10:30:22 +00:00
}
2023-02-27 14:38:30 +00:00
}
2021-08-30 14:51:23 +00:00
2023-02-27 14:38:30 +00:00
uint32_t job_id ;
{
job_id = + + client - > m_perConnectionJobId ;
2021-08-28 15:18:41 +00:00
2023-02-27 14:38:30 +00:00
StratumClient : : SavedJob & saved_job = client - > m_jobs [ job_id % StratumClient : : JOBS_SIZE ] ;
saved_job . job_id = job_id ;
saved_job . extra_nonce = extra_nonce_start + num_sent ;
saved_job . template_id = data - > m_templateId ;
saved_job . target = target ;
}
client - > m_lastJobTarget = target ;
2021-08-28 17:50:48 +00:00
2023-02-27 14:38:30 +00:00
const bool result = send ( client ,
2023-05-24 11:46:05 +00:00
[ data , target , hashing_blob , job_id ] ( uint8_t * buf , size_t buf_size )
2023-02-27 14:38:30 +00:00
{
2023-07-07 09:00:19 +00:00
log : : hex_buf target_hex ( & target ) ;
2021-08-28 17:50:48 +00:00
2023-02-27 14:38:30 +00:00
if ( target > = TARGET_4_BYTES_LIMIT ) {
target_hex . m_data + = sizeof ( uint32_t ) ;
target_hex . m_size - = sizeof ( uint32_t ) ;
}
2021-08-22 10:20:59 +00:00
2023-02-27 14:38:30 +00:00
log : : Stream s ( buf , buf_size ) ;
s < < " { \" jsonrpc \" : \" 2.0 \" , \" method \" : \" job \" , \" params \" :{ \" blob \" : \" " ;
s < < log : : hex_buf ( hashing_blob , data - > m_blobSize ) < < " \" , \" job_id \" : \" " ;
s < < log : : Hex ( job_id ) < < " \" , \" target \" : \" " ;
s < < target_hex < < " \" , \" algo \" : \" rx/0 \" , \" height \" : " ;
s < < data - > m_height < < " , \" seed_hash \" : \" " ;
s < < data - > m_seedHash < < " \" }} \n " ;
return s . m_pos ;
} ) ;
if ( result ) {
+ + num_sent ;
2021-08-22 10:20:59 +00:00
}
2023-02-27 14:38:30 +00:00
else {
client - > close ( ) ;
2021-08-22 10:20:59 +00:00
}
}
2023-02-27 14:38:30 +00:00
const uint32_t num_connections = m_numConnections ;
if ( numClientsProcessed ! = num_connections ) {
LOGWARN ( 1 , " client list is broken, expected " < < num_connections < < " , got " < < numClientsProcessed < < " clients " ) ;
}
2022-10-11 09:30:02 +00:00
LOGINFO ( 3 , " sent new job to " < < num_sent < < ' / ' < < numClientsProcessed < < " clients " ) ;
2021-08-22 10:20:59 +00:00
}
2022-03-23 10:30:38 +00:00
void StratumServer : : update_hashrate_data ( uint64_t hashes , uint64_t timestamp )
2021-08-30 14:51:23 +00:00
{
2021-10-14 14:46:49 +00:00
constexpr size_t N = array_size ( & StratumServer : : m_hashrateData ) ;
2021-08-30 14:51:23 +00:00
WriteLock lock ( m_hashrateDataLock ) ;
m_cumulativeHashes + = hashes ;
HashrateData * data = m_hashrateData ;
HashrateData & head = data [ m_hashrateDataHead ] ;
if ( head . m_timestamp = = timestamp ) {
head . m_cumulativeHashes = m_cumulativeHashes ;
}
else {
2021-10-14 14:46:49 +00:00
m_hashrateDataHead = ( m_hashrateDataHead + 1 ) % N ;
2021-08-30 14:51:23 +00:00
data [ m_hashrateDataHead ] = { timestamp , m_cumulativeHashes } ;
}
while ( data [ m_hashrateDataTail_15m ] . m_timestamp + 15 * 60 < timestamp ) {
2021-10-14 14:46:49 +00:00
m_hashrateDataTail_15m = ( m_hashrateDataTail_15m + 1 ) % N ;
2021-08-30 14:51:23 +00:00
}
while ( data [ m_hashrateDataTail_1h ] . m_timestamp + 60 * 60 < timestamp ) {
2021-10-14 14:46:49 +00:00
m_hashrateDataTail_1h = ( m_hashrateDataTail_1h + 1 ) % N ;
2021-08-30 14:51:23 +00:00
}
while ( data [ m_hashrateDataTail_24h ] . m_timestamp + 60 * 60 * 24 < timestamp ) {
2021-10-14 14:46:49 +00:00
m_hashrateDataTail_24h = ( m_hashrateDataTail_24h + 1 ) % N ;
2021-08-30 14:51:23 +00:00
}
}
2021-08-22 10:20:59 +00:00
void StratumServer : : on_share_found ( uv_work_t * req )
{
SubmittedShare * share = reinterpret_cast < SubmittedShare * > ( req - > data ) ;
2023-01-25 10:36:52 +00:00
StratumServer * server = share - > m_server ;
2023-04-27 08:28:32 +00:00
if ( server - > is_banned ( share - > m_clientIPv6 , share - > m_clientAddr ) ) {
2023-01-25 10:36:52 +00:00
share - > m_highEnoughDifficulty = false ;
share - > m_result = SubmittedShare : : Result : : BANNED ;
return ;
}
2022-05-08 10:30:22 +00:00
if ( share - > m_highEnoughDifficulty ) {
2022-11-04 09:14:49 +00:00
BACKGROUND_JOB_START ( StratumServer : : on_share_found ) ;
2022-05-08 10:30:22 +00:00
}
2021-08-22 10:20:59 +00:00
p2pool * pool = server - > m_pool ;
2022-05-08 10:30:22 +00:00
const uint64_t target = share - > m_target ;
const uint64_t hashes = share - > m_hashes ;
2021-10-14 14:46:49 +00:00
2021-08-22 10:20:59 +00:00
if ( pool - > stopped ( ) ) {
LOGWARN ( 0 , " p2pool is shutting down, but a share was found. Trying to process it anyway! " ) ;
}
2022-05-08 10:30:22 +00:00
if ( share - > m_highEnoughDifficulty ) {
2021-10-13 16:57:21 +00:00
uint8_t blob [ 128 ] ;
uint64_t height ;
difficulty_type difficulty ;
2023-11-19 18:43:36 +00:00
difficulty_type aux_diff ;
2021-10-13 16:57:21 +00:00
difficulty_type sidechain_difficulty ;
hash seed_hash ;
size_t nonce_offset ;
2021-08-22 10:20:59 +00:00
2023-11-19 18:43:36 +00:00
const uint32_t blob_size = pool - > block_template ( ) . get_hashing_blob ( share - > m_templateId , share - > m_extraNonce , blob , height , difficulty , aux_diff , sidechain_difficulty , seed_hash , nonce_offset ) ;
2021-10-13 16:57:21 +00:00
if ( ! blob_size ) {
2023-09-08 13:19:59 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( share - > m_clientAddrString ) < < " got a stale share " ) ;
2021-10-13 16:57:21 +00:00
share - > m_result = SubmittedShare : : Result : : STALE ;
return ;
}
2021-08-22 10:20:59 +00:00
2021-08-28 15:18:41 +00:00
for ( uint32_t i = 0 , nonce = share - > m_nonce ; i < sizeof ( share - > m_nonce ) ; + + i ) {
blob [ nonce_offset + i ] = nonce & 255 ;
nonce > > = 8 ;
}
hash pow_hash ;
2023-03-28 08:14:47 +00:00
if ( ! pool - > calculate_hash ( blob , blob_size , height , seed_hash , pow_hash , false ) ) {
2023-09-08 13:19:59 +00:00
LOGWARN ( 3 , " client " < < static_cast < char * > ( share - > m_clientAddrString ) < < " couldn't check share PoW " ) ;
2021-08-28 15:18:41 +00:00
share - > m_result = SubmittedShare : : Result : : COULDNT_CHECK_POW ;
return ;
}
if ( pow_hash ! = share - > m_resultHash ) {
2023-09-08 13:19:59 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( share - > m_clientAddrString ) < < " submitted a share with invalid PoW " ) ;
2021-08-28 15:18:41 +00:00
share - > m_result = SubmittedShare : : Result : : INVALID_POW ;
2023-02-27 17:58:56 +00:00
share - > m_score = BAD_SHARE_POINTS ;
2023-03-28 08:14:47 +00:00
// 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 ) ;
}
2021-08-28 15:18:41 +00:00
return ;
}
2023-02-27 17:58:56 +00:00
share - > m_score = GOOD_SHARE_POINTS ;
2022-08-22 09:07:11 +00:00
2021-08-30 18:49:51 +00:00
const double diff = sidechain_difficulty . to_double ( ) ;
2022-12-22 19:12:37 +00:00
{
WriteLock lock ( server - > m_hashrateDataLock ) ;
2021-08-30 18:49:51 +00:00
2022-12-22 19:12:37 +00:00
const uint64_t n = server - > m_cumulativeHashes + hashes ;
share - > m_effort = static_cast < double > ( n - server - > m_cumulativeHashesAtLastShare ) * 100.0 / diff ;
server - > m_cumulativeHashesAtLastShare = n ;
server - > m_cumulativeFoundSharesDiff + = diff ;
+ + server - > m_totalFoundShares ;
}
2021-08-30 18:49:51 +00:00
2023-01-07 14:50:02 +00:00
if ( ! pool - > submit_sidechain_block ( share - > m_templateId , share - > m_nonce , share - > m_extraNonce ) ) {
WriteLock lock ( server - > m_hashrateDataLock ) ;
if ( server - > m_totalFoundShares > 0 ) {
- - server - > m_totalFoundShares ;
+ + server - > m_totalFailedShares ;
}
}
2021-08-22 10:20:59 +00:00
}
// Send the response to miner
2023-07-07 09:00:19 +00:00
const uint64_t value = share - > m_resultHash . u64 ( ) [ HASH_SIZE / sizeof ( uint64_t ) - 1 ] ;
2021-08-28 15:18:41 +00:00
2021-08-22 10:20:59 +00:00
if ( LIKELY ( value < target ) ) {
2022-05-08 10:30:22 +00:00
const uint64_t timestamp = share - > m_timestamp ;
2021-10-14 14:46:49 +00:00
server - > update_hashrate_data ( hashes , timestamp ) ;
2021-10-04 08:28:56 +00:00
server - > api_update_local_stats ( timestamp ) ;
2021-08-22 10:20:59 +00:00
share - > m_result = SubmittedShare : : Result : : OK ;
}
else {
2023-09-08 13:19:59 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( share - > m_clientAddrString ) < < " got a low diff share " ) ;
2021-08-22 10:20:59 +00:00
share - > m_result = SubmittedShare : : Result : : LOW_DIFF ;
2023-02-27 17:58:56 +00:00
share - > m_score = BAD_SHARE_POINTS ;
2021-08-22 10:20:59 +00:00
}
}
void StratumServer : : on_after_share_found ( uv_work_t * req , int /*status*/ )
{
SubmittedShare * share = reinterpret_cast < SubmittedShare * > ( req - > data ) ;
2022-08-31 14:37:33 +00:00
2022-05-08 10:30:22 +00:00
if ( share - > m_highEnoughDifficulty ) {
2023-09-08 13:19:59 +00:00
const char * s = share - > m_clientCustomUser ;
2023-03-02 12:19:25 +00:00
if ( share - > m_result = = SubmittedShare : : Result : : OK ) {
2023-09-08 13:19:59 +00:00
LOGINFO ( 0 , log : : Green ( ) < < " SHARE FOUND: mainchain height " < < share - > m_mainchainHeight < < " , sidechain height " < < share - > m_sidechainHeight < < " , diff " < < share - > m_sidechainDifficulty < < " , client " < < static_cast < char * > ( share - > m_clientAddrString ) < < ( * s ? " , user " : " " ) < < s < < " , effort " < < share - > m_effort < < ' % ' ) ;
2023-03-02 12:19:25 +00:00
}
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 " ;
2023-09-08 13:19:59 +00:00
LOGWARN ( 0 , " INVALID SHARE: mainchain height " < < share - > m_mainchainHeight < < " , sidechain height " < < share - > m_sidechainHeight < < " , diff " < < share - > m_sidechainDifficulty < < " , client " < < static_cast < char * > ( share - > m_clientAddrString ) < < ( * s ? " , user " : " " ) < < s < < " , reason: " < < reason ) ;
2023-03-02 12:19:25 +00:00
}
2022-11-04 09:14:49 +00:00
BACKGROUND_JOB_STOP ( StratumServer : : on_share_found ) ;
2022-05-08 10:30:22 +00:00
}
2021-08-22 10:20:59 +00:00
2023-08-14 14:10:44 +00:00
StratumServer * server = share - > m_server ;
ON_SCOPE_LEAVE ( [ share , server ] ( )
2023-03-06 13:30:13 +00:00
{
ASAN_POISON_MEMORY_REGION ( share , sizeof ( SubmittedShare ) ) ;
2023-08-14 14:10:44 +00:00
server - > m_submittedSharesPool . push_back ( share ) ;
2023-03-06 13:30:13 +00:00
} ) ;
2021-08-22 10:20:59 +00:00
2021-10-04 13:51:28 +00:00
const bool bad_share = ( share - > m_result = = SubmittedShare : : Result : : LOW_DIFF ) | | ( share - > m_result = = SubmittedShare : : Result : : INVALID_POW ) ;
2023-09-08 13:19:59 +00:00
StratumClient * client = share - > m_client ;
2023-09-29 09:55:25 +00:00
if ( client - > m_resetCounter . load ( ) = = share - > m_clientResetCounter ) {
2021-08-22 10:20:59 +00:00
const bool result = server - > send ( client ,
2023-05-24 11:46:05 +00:00
[ share ] ( uint8_t * buf , size_t buf_size )
2021-08-22 10:20:59 +00:00
{
2022-05-04 13:53:01 +00:00
log : : Stream s ( buf , buf_size ) ;
2021-08-22 10:20:59 +00:00
switch ( share - > m_result ) {
case SubmittedShare : : Result : : STALE :
s < < " { \" id \" : " < < share - > m_id < < " , \" jsonrpc \" : \" 2.0 \" , \" error \" :{ \" message \" : \" Stale share \" }} \n " ;
break ;
case SubmittedShare : : Result : : COULDNT_CHECK_POW :
s < < " { \" id \" : " < < share - > m_id < < " , \" jsonrpc \" : \" 2.0 \" , \" error \" :{ \" message \" : \" Couldn't check PoW \" }} \n " ;
break ;
case SubmittedShare : : Result : : LOW_DIFF :
s < < " { \" id \" : " < < share - > m_id < < " , \" jsonrpc \" : \" 2.0 \" , \" error \" :{ \" message \" : \" Low diff share \" }} \n " ;
break ;
2021-08-28 15:18:41 +00:00
case SubmittedShare : : Result : : INVALID_POW :
s < < " { \" id \" : " < < share - > m_id < < " , \" jsonrpc \" : \" 2.0 \" , \" error \" :{ \" message \" : \" Invalid PoW \" }} \n " ;
break ;
2023-01-25 10:36:52 +00:00
case SubmittedShare : : Result : : BANNED :
s < < " { \" id \" : " < < share - > m_id < < " , \" jsonrpc \" : \" 2.0 \" , \" error \" :{ \" message \" : \" Banned \" }} \n " ;
break ;
2021-08-22 10:20:59 +00:00
case SubmittedShare : : Result : : OK :
s < < " { \" id \" : " < < share - > m_id < < " , \" jsonrpc \" : \" 2.0 \" , \" error \" :null, \" result \" :{ \" status \" : \" OK \" }} \n " ;
break ;
}
return s . m_pos ;
} ) ;
2023-09-08 13:19:59 +00:00
client - > m_score + = share - > m_score ;
2022-08-22 09:07:11 +00:00
if ( bad_share & & ( client - > m_score < = BAN_THRESHOLD_POINTS ) ) {
2021-08-22 10:20:59 +00:00
client - > ban ( DEFAULT_BAN_TIME ) ;
client - > close ( ) ;
}
else if ( ! result ) {
client - > close ( ) ;
}
}
2021-10-04 13:51:28 +00:00
else if ( bad_share ) {
2023-04-27 08:28:32 +00:00
server - > ban ( share - > m_clientIPv6 , share - > m_clientAddr , DEFAULT_BAN_TIME ) ;
2021-10-04 13:51:28 +00:00
}
2021-08-22 10:20:59 +00:00
}
2022-10-07 14:02:08 +00:00
void StratumServer : : on_shutdown ( )
{
2023-09-04 17:33:31 +00:00
{
MutexLock lock ( m_blobsQueueLock ) ;
uv_close ( reinterpret_cast < uv_handle_t * > ( & m_blobsAsync ) , nullptr ) ;
}
{
MutexLock lock ( m_showWorkersLock ) ;
uv_close ( reinterpret_cast < uv_handle_t * > ( & m_showWorkersAsync ) , nullptr ) ;
}
2022-10-07 14:02:08 +00:00
}
2021-08-22 10:20:59 +00:00
StratumServer : : StratumClient : : StratumClient ( )
2023-04-19 09:36:12 +00:00
: Client ( m_stratumReadBuf , sizeof ( m_stratumReadBuf ) )
, m_rpcId ( 0 )
2022-05-07 10:29:45 +00:00
, m_perConnectionJobId ( 0 )
2022-02-08 14:00:08 +00:00
, m_connectedTime ( 0 )
2021-08-22 10:20:59 +00:00
, m_jobs { }
2022-05-08 10:30:22 +00:00
, m_autoDiffData { }
, m_autoDiffWindowHashes ( 0 )
, m_autoDiffIndex ( 0 )
2021-08-28 15:18:41 +00:00
, m_customDiff { }
2022-05-08 10:30:22 +00:00
, m_autoDiff { }
2022-05-07 09:00:29 +00:00
, m_customUser { }
2023-01-27 21:09:17 +00:00
, m_lastJobTarget ( 0 )
2022-08-22 09:07:11 +00:00
, m_score ( 0 )
2021-08-22 10:20:59 +00:00
{
2023-04-19 09:36:12 +00:00
m_stratumReadBuf [ 0 ] = ' \0 ' ;
2021-08-22 10:20:59 +00:00
}
void StratumServer : : StratumClient : : reset ( )
{
Client : : reset ( ) ;
m_rpcId = 0 ;
2022-05-07 10:29:45 +00:00
m_perConnectionJobId = 0 ;
2022-02-08 14:00:08 +00:00
m_connectedTime = 0 ;
2022-05-08 10:30:22 +00:00
for ( int i = 0 ; i < JOBS_SIZE ; + + i ) {
m_jobs [ i ] . job_id = 0 ;
}
m_autoDiffWindowHashes = 0 ;
m_autoDiffIndex = 0 ;
2021-08-28 15:18:41 +00:00
m_customDiff = { } ;
2022-05-08 10:30:22 +00:00
m_autoDiff = { } ;
m_customUser [ 0 ] = ' \0 ' ;
2022-08-22 09:07:11 +00:00
2023-01-27 21:09:17 +00:00
m_lastJobTarget = 0 ;
2022-08-22 09:07:11 +00:00
m_score = 0 ;
2021-08-22 10:20:59 +00:00
}
2022-02-08 14:00:08 +00:00
bool StratumServer : : StratumClient : : on_connect ( )
{
2022-03-23 10:30:38 +00:00
m_connectedTime = seconds_since_epoch ( ) ;
2022-02-08 14:00:08 +00:00
return true ;
}
2021-08-22 10:20:59 +00:00
bool StratumServer : : StratumClient : : on_read ( char * data , uint32_t size )
{
2023-04-19 09:36:12 +00:00
if ( ( data ! = m_readBuf + m_numRead ) | | ( data + size > m_readBuf + m_readBufSize ) ) {
2021-08-22 10:20:59 +00:00
LOGERR ( 1 , " client: invalid data pointer or size in on_read() " ) ;
ban ( DEFAULT_BAN_TIME ) ;
return false ;
}
m_numRead + = size ;
char * line_start = m_readBuf ;
for ( char * c = data ; c < m_readBuf + m_numRead ; + + c ) {
if ( * c = = ' \n ' ) {
* c = ' \0 ' ;
if ( ! process_request ( line_start , static_cast < uint32_t > ( c - line_start ) ) ) {
ban ( DEFAULT_BAN_TIME ) ;
return false ;
}
line_start = c + 1 ;
}
}
// Move the possible unfinished line to the beginning of m_readBuf to free up more space for reading
if ( line_start ! = m_readBuf ) {
m_numRead = static_cast < uint32_t > ( m_readBuf + m_numRead - line_start ) ;
if ( m_numRead > 0 ) {
memmove ( m_readBuf , line_start , m_numRead ) ;
}
}
return true ;
}
bool StratumServer : : StratumClient : : process_request ( char * data , uint32_t /*size*/ )
{
rapidjson : : Document doc ;
if ( doc . ParseInsitu ( data ) . HasParseError ( ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid JSON request (parse error) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
if ( ! doc . IsObject ( ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid JSON request (not an object) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
if ( ! doc . HasMember ( " id " ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid JSON request ('id' field not found) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
auto & id = doc [ " id " ] ;
if ( ! id . IsUint ( ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid JSON request ('id' field is not an integer) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
if ( ! doc . HasMember ( " method " ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid JSON request ('method' field not found) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
auto & method = doc [ " method " ] ;
if ( ! method . IsString ( ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid JSON request ('method' field is not a string) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
const char * s = method . GetString ( ) ;
if ( strcmp ( s , " login " ) = = 0 ) {
2021-08-28 15:18:41 +00:00
LOGINFO ( 6 , " incoming login from " < < log : : Gray ( ) < < static_cast < char * > ( m_addrString ) ) ;
2021-08-22 10:20:59 +00:00
return process_login ( doc , id . GetUint ( ) ) ;
}
else if ( strcmp ( s , " submit " ) = = 0 ) {
2021-08-28 15:18:41 +00:00
LOGINFO ( 6 , " incoming share from " < < log : : Gray ( ) < < static_cast < char * > ( m_addrString ) ) ;
2021-08-22 10:20:59 +00:00
return process_submit ( doc , id . GetUint ( ) ) ;
}
2021-08-31 18:57:01 +00:00
else if ( strcmp ( s , " keepalived " ) = = 0 ) {
LOGINFO ( 6 , " incoming keepalive from " < < log : : Gray ( ) < < static_cast < char * > ( m_addrString ) ) ;
return true ;
}
2021-08-22 10:20:59 +00:00
2022-06-01 12:25:58 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid JSON request (unknown method) " ) ;
return false ;
2021-08-22 10:20:59 +00:00
}
2021-08-28 15:18:41 +00:00
bool StratumServer : : StratumClient : : process_login ( rapidjson : : Document & doc , uint32_t id )
2021-08-22 10:20:59 +00:00
{
2021-08-28 15:18:41 +00:00
if ( ! doc . HasMember ( " params " ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid JSON login request ('params' field not found) " ) ;
2021-08-28 15:18:41 +00:00
return false ;
}
auto & params = doc [ " params " ] ;
if ( ! params . IsObject ( ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid JSON login request ('params' field is not an object) " ) ;
2021-08-28 15:18:41 +00:00
return false ;
}
if ( ! params . HasMember ( " login " ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid login params ('login' field not found) " ) ;
2021-08-28 15:18:41 +00:00
return false ;
}
auto & login = params [ " login " ] ;
if ( ! login . IsString ( ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid login params ('login' field is not a string) " ) ;
2021-08-28 15:18:41 +00:00
return false ;
}
return static_cast < StratumServer * > ( m_owner ) - > on_login ( this , id , login . GetString ( ) ) ;
2021-08-22 10:20:59 +00:00
}
bool StratumServer : : StratumClient : : process_submit ( rapidjson : : Document & doc , uint32_t id )
{
if ( ! doc . HasMember ( " params " ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid JSON submit request ('params' field not found) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
auto & params = doc [ " params " ] ;
if ( ! params . IsObject ( ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid JSON submit request ('params' field is not an object) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
if ( ! params . HasMember ( " id " ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid submit params ('id' field not found) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
auto & rpcId = params [ " id " ] ;
if ( ! rpcId . IsString ( ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid submit params ('id' field is not a string) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
if ( ! params . HasMember ( " job_id " ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid submit params ('job_id' field not found) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
auto & job_id = params [ " job_id " ] ;
if ( ! job_id . IsString ( ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid submit params ('job_id' field is not a string) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
if ( ! params . HasMember ( " nonce " ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid submit params ('nonce' field not found) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
auto & nonce = params [ " nonce " ] ;
if ( ! nonce . IsString ( ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid submit params ('nonce' field is not a string) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
if ( nonce . GetStringLength ( ) ! = sizeof ( uint32_t ) * 2 ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid submit params ('nonce' field has invalid length) " ) ;
2021-08-28 15:18:41 +00:00
return false ;
}
if ( ! params . HasMember ( " result " ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid submit params ('result' field not found) " ) ;
2021-08-28 15:18:41 +00:00
return false ;
}
auto & result = params [ " result " ] ;
if ( ! result . IsString ( ) ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid submit params ('result' field is not a string) " ) ;
2021-08-28 15:18:41 +00:00
return false ;
}
if ( result . GetStringLength ( ) ! = HASH_SIZE * 2 ) {
2021-09-05 17:16:23 +00:00
LOGWARN ( 4 , " client " < < static_cast < char * > ( m_addrString ) < < " invalid submit params ('result' field has invalid length) " ) ;
2021-08-22 10:20:59 +00:00
return false ;
}
2021-08-28 15:18:41 +00:00
return static_cast < StratumServer * > ( m_owner ) - > on_submit ( this , id , job_id . GetString ( ) , nonce . GetString ( ) , result . GetString ( ) ) ;
2021-08-22 10:20:59 +00:00
}
2022-03-23 10:30:38 +00:00
void StratumServer : : api_update_local_stats ( uint64_t timestamp )
2021-10-01 23:09:42 +00:00
{
2023-09-13 19:12:01 +00:00
if ( ! m_pool - > api ( ) | | ! m_pool - > params ( ) . m_localStats | | m_pool - > stopped ( ) ) {
2021-10-01 23:09:42 +00:00
return ;
}
2023-04-24 10:02:35 +00:00
// Rate limit to no more than once in 20 seconds.
2023-02-27 18:23:14 +00:00
uint64_t t = m_apiLastUpdateTime . load ( ) ;
2023-04-24 10:02:35 +00:00
if ( timestamp < t + 20 ) {
2021-10-01 23:09:42 +00:00
return ;
}
2023-02-27 18:23:14 +00:00
if ( ! m_apiLastUpdateTime . compare_exchange_strong ( t , timestamp ) ) {
return ;
}
2021-10-04 08:28:56 +00:00
2021-10-01 23:09:42 +00:00
uint64_t hashes_15m , hashes_1h , hashes_24h , total_hashes ;
int64_t dt_15m , dt_1h , dt_24h ;
uint64_t hashes_since_last_share ;
2022-12-22 20:00:59 +00:00
double average_effort ;
2023-01-07 14:50:02 +00:00
uint32_t shares_found , shares_failed ;
2021-10-01 23:09:42 +00:00
{
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 ) ;
2022-12-22 19:12:37 +00:00
average_effort = 0.0 ;
2022-12-22 20:00:59 +00:00
const double diff = m_cumulativeFoundSharesDiff ;
2022-12-22 19:12:37 +00:00
if ( diff > 0.0 ) {
average_effort = static_cast < double > ( m_cumulativeHashesAtLastShare ) * 100.0 / diff ;
}
shares_found = m_totalFoundShares ;
2023-01-07 14:50:02 +00:00
shares_failed = m_totalFailedShares ;
2021-10-01 23:09:42 +00:00
}
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 current_effort = static_cast < double > ( hashes_since_last_share ) * 100.0 / m_pool - > side_chain ( ) . difficulty ( ) . to_double ( ) ;
2023-03-27 09:04:02 +00:00
uint32_t connections = m_numConnections ;
uint32_t incoming_connections = m_numIncomingConnections ;
2021-10-01 23:09:42 +00:00
2023-04-24 09:45:02 +00:00
const double block_reward_share_percent = m_pool - > side_chain ( ) . get_reward_share ( m_pool - > params ( ) . m_wallet ) * 100.0 ;
2023-09-17 20:30:21 +00:00
CallOnLoop ( & m_loop , [ = ] ( ) {
m_pool - > api ( ) - > set ( p2pool_api : : Category : : LOCAL , " stratum " , [ = ] ( log : : Stream & s ) {
s < < " { \" hashrate_15m \" : " < < hashrate_15m
2021-10-01 23:09:42 +00:00
< < " , \" hashrate_1h \" : " < < hashrate_1h
< < " , \" hashrate_24h \" : " < < hashrate_24h
< < " , \" total_hashes \" : " < < total_hashes
< < " , \" shares_found \" : " < < shares_found
2023-01-07 14:50:02 +00:00
< < " , \" shares_failed \" : " < < shares_failed
2021-10-01 23:09:42 +00:00
< < " , \" average_effort \" : " < < average_effort
< < " , \" current_effort \" : " < < current_effort
< < " , \" connections \" : " < < connections
< < " , \" incoming_connections \" : " < < incoming_connections
2023-04-24 09:45:02 +00:00
< < " , \" block_reward_share_percent \" : " < < block_reward_share_percent
2023-09-17 18:09:53 +00:00
< < " , \" workers \" :[ " ;
2023-09-17 20:30:21 +00:00
const difficulty_type pool_diff = m_pool - > side_chain ( ) . difficulty ( ) ;
2023-09-17 18:09:53 +00:00
bool first = true ;
for ( const StratumClient * client = static_cast < StratumClient * > ( m_connectedClientsList - > m_next ) ; client ! = m_connectedClientsList ; client = static_cast < StratumClient * > ( client - > m_next ) ) {
if ( ! first ) {
s < < ' , ' ;
}
difficulty_type diff = pool_diff ;
if ( client - > m_lastJobTarget > 1 ) {
uint64_t r ;
diff . lo = udiv128 ( 1 , 0 , client - > m_lastJobTarget , & r ) ;
diff . hi = 0 ;
if ( r ) {
+ + diff . lo ;
}
}
s < < ' " ' < < static_cast < const char * > ( client - > m_addrString ) < < ' , '
2023-09-17 20:30:21 +00:00
< < ( timestamp - client - > m_connectedTime ) < < ' , '
< < diff < < ' , '
2023-09-17 18:09:53 +00:00
< < ( client - > m_autoDiff . lo / AUTO_DIFF_TARGET_TIME ) < < ' , '
2023-09-17 20:30:21 +00:00
< < ( client - > m_rpcId ? client - > m_customUser : " not logged in " )
2023-09-17 18:09:53 +00:00
< < ' " ' ;
first = false ;
}
s < < " ]} " ;
2021-10-01 23:09:42 +00:00
} ) ;
2023-09-17 20:30:21 +00:00
} ) ;
2021-10-01 23:09:42 +00:00
}
2021-08-22 10:20:59 +00:00
} // namespace p2pool