2021-08-22 10:20:59 +00:00
/*
* This file is part of the Monero P2Pool < https : //github.com/SChernykh/p2pool>
* Copyright ( c ) 2021 SChernykh < https : //github.com/SChernykh>
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , version 3.
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "common.h"
# include "p2pool.h"
# include "zmq_reader.h"
# include "mempool.h"
# include "json_rpc_request.h"
# include "rapidjson/document.h"
# include "json_parsers.h"
# include "pow_hash.h"
# include "block_template.h"
# include "side_chain.h"
# include "stratum_server.h"
# include "p2p_server.h"
# include "params.h"
# include "console_commands.h"
2021-08-31 16:26:28 +00:00
# include "crypto.h"
2021-09-01 11:49:58 +00:00
# include "p2pool_api.h"
2021-08-22 10:20:59 +00:00
# include <thread>
2021-09-01 15:21:27 +00:00
# include <fstream>
2021-08-22 10:20:59 +00:00
2021-09-01 15:21:27 +00:00
constexpr char log_category_prefix [ ] = " P2Pool " ;
2021-08-22 10:20:59 +00:00
constexpr int BLOCK_HEADERS_REQUIRED = 720 ;
constexpr uint64_t SEEDHASH_EPOCH_BLOCKS = 2048 ;
constexpr uint64_t SEEDHASH_EPOCH_LAG = 64 ;
2021-09-01 15:21:27 +00:00
constexpr char FOUND_BLOCKS_FILE [ ] = " p2pool.blocks " ;
2021-08-22 10:20:59 +00:00
namespace p2pool {
p2pool : : p2pool ( int argc , char * argv [ ] )
: m_stopped ( false )
, m_params ( new Params ( argc , argv ) )
2021-08-25 10:17:14 +00:00
, m_updateSeed ( true )
2021-08-25 10:45:14 +00:00
, m_submitBlockData { }
2021-10-26 15:55:47 +00:00
, m_zmqLastActive ( 0 )
, m_startTime ( time ( nullptr ) )
2021-08-22 10:20:59 +00:00
{
2021-10-02 15:06:48 +00:00
LOGINFO ( 1 , log : : LightCyan ( ) < < VERSION ) ;
2021-08-22 10:20:59 +00:00
if ( ! m_params - > m_wallet . valid ( ) ) {
LOGERR ( 1 , " Invalid wallet address. Try \" p2pool --help \" . " ) ;
2021-08-31 16:26:28 +00:00
panic ( ) ;
}
2021-09-08 18:25:39 +00:00
bool is_v6 ;
if ( ! resolve_host ( m_params - > m_host , is_v6 ) ) {
LOGERR ( 1 , " resolve_host failed for " < < m_params - > m_host ) ;
panic ( ) ;
}
2021-08-31 16:26:28 +00:00
hash pub , sec , eph_public_key ;
generate_keys ( pub , sec ) ;
if ( ! m_params - > m_wallet . get_eph_public_key ( sec , 0 , eph_public_key ) ) {
LOGERR ( 1 , " Invalid wallet address: get_eph_public_key failed " ) ;
2021-08-22 10:20:59 +00:00
panic ( ) ;
}
2021-08-27 09:25:25 +00:00
const NetworkType type = m_params - > m_wallet . type ( ) ;
2021-08-22 10:20:59 +00:00
2021-08-27 09:25:25 +00:00
if ( type = = NetworkType : : Testnet ) {
2021-08-22 10:20:59 +00:00
LOGWARN ( 1 , " Mining to a testnet wallet address " ) ;
}
2021-08-27 09:25:25 +00:00
else if ( type = = NetworkType : : Stagenet ) {
2021-08-22 10:20:59 +00:00
LOGWARN ( 1 , " Mining to a stagenet wallet address " ) ;
}
2021-08-25 10:45:14 +00:00
int err = uv_async_init ( uv_default_loop_checked ( ) , & m_submitBlockAsync , on_submit_block ) ;
if ( err ) {
LOGERR ( 1 , " uv_async_init failed, error " < < uv_err_name ( err ) ) ;
panic ( ) ;
}
m_submitBlockAsync . data = this ;
err = uv_async_init ( uv_default_loop_checked ( ) , & m_blockTemplateAsync , on_update_block_template ) ;
2021-08-25 10:17:14 +00:00
if ( err ) {
LOGERR ( 1 , " uv_async_init failed, error " < < uv_err_name ( err ) ) ;
panic ( ) ;
}
m_blockTemplateAsync . data = this ;
err = uv_async_init ( uv_default_loop_checked ( ) , & m_stopAsync , on_stop ) ;
if ( err ) {
LOGERR ( 1 , " uv_async_init failed, error " < < uv_err_name ( err ) ) ;
panic ( ) ;
}
m_stopAsync . data = this ;
2021-08-22 10:20:59 +00:00
uv_rwlock_init_checked ( & m_mainchainLock ) ;
2021-09-01 15:21:27 +00:00
uv_mutex_init_checked ( & m_foundBlocksLock ) ;
2021-08-25 20:07:42 +00:00
uv_mutex_init_checked ( & m_submitBlockDataLock ) ;
2021-08-22 10:20:59 +00:00
2021-10-01 23:09:42 +00:00
m_api = m_params - > m_apiPath . empty ( ) ? nullptr : new p2pool_api ( m_params - > m_apiPath , m_params - > m_localStats ) ;
2021-08-22 10:20:59 +00:00
2021-08-27 09:25:25 +00:00
m_sideChain = new SideChain ( this , type ) ;
2021-11-20 10:51:22 +00:00
if ( m_params - > m_disableRandomX ) {
m_hasher = new RandomX_Hasher_RPC ( this ) ;
}
else {
m_hasher = new RandomX_Hasher ( this ) ;
}
2021-08-22 10:20:59 +00:00
m_blockTemplate = new BlockTemplate ( this ) ;
2021-08-23 09:44:26 +00:00
m_mempool = new Mempool ( ) ;
2021-08-22 10:20:59 +00:00
m_consoleCommands = new ConsoleCommands ( this ) ;
}
p2pool : : ~ p2pool ( )
{
uv_rwlock_destroy ( & m_mainchainLock ) ;
2021-09-01 15:21:27 +00:00
uv_mutex_destroy ( & m_foundBlocksLock ) ;
2021-08-25 20:07:42 +00:00
uv_mutex_destroy ( & m_submitBlockDataLock ) ;
2021-08-22 10:20:59 +00:00
2021-09-01 11:49:58 +00:00
delete m_api ;
2021-08-22 10:20:59 +00:00
delete m_sideChain ;
delete m_hasher ;
delete m_blockTemplate ;
delete m_mempool ;
delete m_params ;
delete m_consoleCommands ;
}
2021-11-20 10:51:22 +00:00
bool p2pool : : calculate_hash ( const void * data , size_t size , uint64_t height , const hash & seed , hash & result )
2021-08-22 10:20:59 +00:00
{
2021-11-20 10:51:22 +00:00
return m_hasher - > calculate ( data , size , height , seed , result ) ;
2021-08-22 10:20:59 +00:00
}
uint64_t p2pool : : get_seed_height ( uint64_t height )
{
if ( LIKELY ( height > SEEDHASH_EPOCH_LAG ) ) {
return ( height - SEEDHASH_EPOCH_LAG - 1 ) & ~ ( SEEDHASH_EPOCH_BLOCKS - 1 ) ;
}
return 0 ;
}
bool p2pool : : get_seed ( uint64_t height , hash & seed ) const
{
ReadLock lock ( m_mainchainLock ) ;
auto it = m_mainchainByHeight . find ( get_seed_height ( height ) ) ;
if ( it = = m_mainchainByHeight . end ( ) ) {
return false ;
}
seed = it - > second . id ;
return true ;
}
void p2pool : : handle_tx ( TxMempoolData & tx )
{
if ( ! tx . weight | | ! tx . fee ) {
LOGWARN ( 1 , " invalid transaction: tx id = " < < tx . id < < " , size = " < < tx . blob_size < < " , weight = " < < tx . weight < < " , fee = " < < static_cast < double > ( tx . fee ) / 1e6 < < " um " ) ;
return ;
}
m_mempool - > add ( tx ) ;
LOGINFO ( 5 ,
" new tx id = " < < log : : LightBlue ( ) < < tx . id < < log : : NoColor ( ) < <
" , size = " < < log : : Gray ( ) < < tx . blob_size < < log : : NoColor ( ) < <
" , weight = " < < log : : Gray ( ) < < tx . weight < < log : : NoColor ( ) < <
" , fee = " < < log : : Gray ( ) < < static_cast < double > ( tx . fee ) / 1e6 < < " um " ) ;
# if TEST_MEMPOOL_PICKING_ALGORITHM
m_blockTemplate - > update ( m_minerData , * m_mempool , & m_params - > m_wallet ) ;
# endif
2021-10-09 09:01:26 +00:00
m_zmqLastActive = time ( nullptr ) ;
2021-08-22 10:20:59 +00:00
}
void p2pool : : handle_miner_data ( MinerData & data )
{
# if TEST_MEMPOOL_PICKING_ALGORITHM
if ( m_mempool - > m_transactions . size ( ) < data . tx_backlog . size ( ) ) {
m_mempool - > swap ( data . tx_backlog ) ;
}
# else
m_mempool - > swap ( data . tx_backlog ) ;
# endif
{
WriteLock lock ( m_mainchainLock ) ;
2021-09-03 20:28:54 +00:00
m_mainchainByHeight [ data . height ] . difficulty = data . difficulty ;
2021-09-01 11:49:58 +00:00
2021-08-22 10:20:59 +00:00
ChainMain & c = m_mainchainByHeight [ data . height - 1 ] ;
c . height = data . height - 1 ;
c . id = data . prev_id ;
2021-09-01 08:43:10 +00:00
// timestamp and reward is unknown here
2021-08-22 10:20:59 +00:00
c . timestamp = 0 ;
2021-09-01 08:43:10 +00:00
c . reward = 0 ;
2021-08-22 10:20:59 +00:00
m_mainchainByHash [ c . id ] = c ;
2021-10-29 09:14:28 +00:00
cleanup_mainchain_data ( data . height ) ;
2021-08-22 10:20:59 +00:00
}
data . tx_backlog . clear ( ) ;
2021-08-31 07:58:57 +00:00
data . time_received = std : : chrono : : system_clock : : now ( ) ;
2021-08-22 10:20:59 +00:00
m_minerData = data ;
2021-08-25 10:17:14 +00:00
m_updateSeed = true ;
2021-08-22 10:20:59 +00:00
update_median_timestamp ( ) ;
LOGINFO ( 2 ,
" new miner data \n --------------------------------------------------------------------------------------------------------------- " < <
" \n major_version = " < < data . major_version < <
" \n height = " < < data . height < <
" \n prev_id = " < < log : : LightBlue ( ) < < data . prev_id < < log : : NoColor ( ) < <
" \n seed_hash = " < < log : : LightBlue ( ) < < data . seed_hash < < log : : NoColor ( ) < <
" \n difficulty = " < < data . difficulty < <
" \n median_weight = " < < data . median_weight < <
" \n already_generated_coins = " < < data . already_generated_coins < <
" \n transactions = " < < m_mempool - > m_transactions . size ( ) < <
" \n --------------------------------------------------------------------------------------------------------------- "
) ;
2021-10-31 13:19:49 +00:00
if ( ! is_main_thread ( ) ) {
2021-08-25 10:17:14 +00:00
update_block_template_async ( ) ;
}
else {
update_block_template ( ) ;
}
2021-10-09 09:01:26 +00:00
m_zmqLastActive = time ( nullptr ) ;
2021-10-31 11:20:29 +00:00
if ( m_serversStarted . load ( ) ) {
std : : vector < uint64_t > missing_heights ;
{
WriteLock lock ( m_mainchainLock ) ;
for ( uint64_t h = data . height ; h & & ( h + BLOCK_HEADERS_REQUIRED > data . height ) ; - - h ) {
if ( m_mainchainByHeight . find ( h ) = = m_mainchainByHeight . end ( ) ) {
LOGWARN ( 3 , " Mainchain data for height " < < h < < " is missing, requesting it from monerod again " ) ;
missing_heights . push_back ( h ) ;
}
}
}
for ( uint64_t h : missing_heights ) {
char buf [ log : : Stream : : BUF_SIZE + 1 ] ;
log : : Stream s ( buf ) ;
s < < " { \" jsonrpc \" : \" 2.0 \" , \" id \" : \" 0 \" , \" method \" : \" get_block_header_by_height \" , \" params \" :{ \" height \" : " < < h < < " }} \0 " ;
JSONRPCRequest : : call ( m_params - > m_host . c_str ( ) , m_params - > m_rpcPort , buf ,
[ this , h ] ( const char * data , size_t size )
{
ChainMain block ;
if ( ! parse_block_header ( data , size , block ) ) {
LOGERR ( 1 , " couldn't download block header for height " < < h ) ;
}
} ,
[ h ] ( const char * data , size_t size )
{
if ( size > 0 ) {
LOGERR ( 1 , " couldn't download block header for height " < < h < < " , error " < < log : : const_buf ( data , size ) ) ;
}
} ) ;
}
}
2021-08-22 10:20:59 +00:00
}
2021-09-06 21:33:52 +00:00
const char * BLOCK_FOUND = " \n \
2021-08-22 10:20:59 +00:00
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ n \
| # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # | \ n \
| # # # # # # # # # # # # # # # # # # # | \ n \
| # # # # # # # # # # # # # # # # # # | \ n \
| # # # # # # # # # # # # # # # # # # # # # # # # # # # | \ n \
| # # # # # # # # # # # # # # # # # # | \ n \
| # # # # # # # # # # # # # # # # # # # | \ n \
| # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # | \ n \
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " ;
void p2pool : : handle_chain_main ( ChainMain & data , const char * extra )
{
{
WriteLock lock ( m_mainchainLock ) ;
ChainMain & c = m_mainchainByHeight [ data . height ] ;
c . height = data . height ;
c . timestamp = data . timestamp ;
2021-09-01 08:43:10 +00:00
c . reward = data . reward ;
2021-08-22 10:20:59 +00:00
// data.id not filled in here, but c.id should be available. Copy it to data.id for logging
data . id = c . id ;
m_mainchainByHash [ c . id ] = c ;
}
update_median_timestamp ( ) ;
hash sidechain_id ;
if ( extra ) {
const size_t n = strlen ( extra ) ;
if ( n > = HASH_SIZE * 2 ) {
const char * s = extra + n - HASH_SIZE * 2 ;
for ( size_t i = 0 ; i < HASH_SIZE ; + + i ) {
uint8_t d [ 2 ] ;
if ( ! from_hex ( s [ i * 2 ] , d [ 0 ] ) | | ! from_hex ( s [ i * 2 + 1 ] , d [ 1 ] ) ) {
sidechain_id = { } ;
break ;
}
sidechain_id . h [ i ] = ( d [ 0 ] < < 4 ) | d [ 1 ] ;
}
}
}
LOGINFO ( 2 , " new main chain block: height = " < < log : : Gray ( ) < < data . height < < log : : NoColor ( ) < <
" , id = " < < log : : LightBlue ( ) < < data . id < < log : : NoColor ( ) < <
2021-09-01 08:43:10 +00:00
" , timestamp = " < < log : : Gray ( ) < < data . timestamp < < log : : NoColor ( ) < <
" , reward = " < < log : : Gray ( ) < < log : : XMRAmount ( data . reward ) ) ;
2021-08-22 10:20:59 +00:00
2021-09-06 21:33:52 +00:00
if ( ! sidechain_id . empty ( ) ) {
if ( side_chain ( ) . has_block ( sidechain_id ) ) {
LOGINFO ( 0 , log : : LightGreen ( ) < < " BLOCK FOUND: main chain block at height " < < data . height < < " was mined by this p2pool " < < BLOCK_FOUND ) ;
api_update_block_found ( & data ) ;
}
else {
side_chain ( ) . watch_mainchain_block ( data , sidechain_id ) ;
}
2021-08-22 10:20:59 +00:00
}
2021-09-01 11:49:58 +00:00
api_update_network_stats ( ) ;
2021-10-09 09:01:26 +00:00
m_zmqLastActive = time ( nullptr ) ;
2021-08-22 10:20:59 +00:00
}
2021-08-25 10:45:14 +00:00
void p2pool : : submit_block_async ( uint32_t template_id , uint32_t nonce , uint32_t extra_nonce )
{
2021-08-25 20:07:42 +00:00
{
MutexLock lock ( m_submitBlockDataLock ) ;
m_submitBlockData . template_id = template_id ;
m_submitBlockData . nonce = nonce ;
m_submitBlockData . extra_nonce = extra_nonce ;
m_submitBlockData . blob . clear ( ) ;
}
const int err = uv_async_send ( & m_submitBlockAsync ) ;
if ( err ) {
LOGERR ( 1 , " uv_async_send failed, error " < < uv_err_name ( err ) ) ;
}
}
void p2pool : : submit_block_async ( const std : : vector < uint8_t > & blob )
{
{
MutexLock lock ( m_submitBlockDataLock ) ;
m_submitBlockData . template_id = 0 ;
m_submitBlockData . nonce = 0 ;
m_submitBlockData . extra_nonce = 0 ;
m_submitBlockData . blob = blob ;
}
2021-08-25 10:45:14 +00:00
const int err = uv_async_send ( & m_submitBlockAsync ) ;
if ( err ) {
LOGERR ( 1 , " uv_async_send failed, error " < < uv_err_name ( err ) ) ;
}
}
2021-08-27 08:13:33 +00:00
void p2pool : : on_stop ( uv_async_t * async )
{
p2pool * pool = reinterpret_cast < p2pool * > ( async - > data ) ;
2021-09-01 11:49:58 +00:00
if ( pool - > m_api ) {
pool - > m_api - > on_stop ( ) ;
}
2021-08-27 08:13:33 +00:00
uv_close ( reinterpret_cast < uv_handle_t * > ( & pool - > m_submitBlockAsync ) , nullptr ) ;
uv_close ( reinterpret_cast < uv_handle_t * > ( & pool - > m_blockTemplateAsync ) , nullptr ) ;
uv_close ( reinterpret_cast < uv_handle_t * > ( & pool - > m_stopAsync ) , nullptr ) ;
uv_stop ( uv_default_loop ( ) ) ;
}
2021-08-25 10:45:14 +00:00
void p2pool : : submit_block ( ) const
2021-08-22 10:20:59 +00:00
{
2021-08-25 20:07:42 +00:00
SubmitBlockData submit_data ;
{
MutexLock lock ( m_submitBlockDataLock ) ;
submit_data = m_submitBlockData ;
}
2021-08-25 10:45:14 +00:00
2021-08-22 10:20:59 +00:00
const uint64_t height = m_blockTemplate - > height ( ) ;
const difficulty_type diff = m_blockTemplate - > difficulty ( ) ;
2021-08-25 20:07:42 +00:00
size_t nonce_offset = 0 ;
size_t extra_nonce_offset = 0 ;
2021-09-10 14:18:16 +00:00
bool is_external = false ;
2021-08-25 20:07:42 +00:00
if ( submit_data . blob . empty ( ) ) {
LOGINFO ( 0 , " submit_block: height = " < < height < < " , template id = " < < submit_data . template_id < < " , nonce = " < < submit_data . nonce < < " , extra_nonce = " < < submit_data . extra_nonce ) ;
2021-08-22 10:20:59 +00:00
2021-08-25 20:07:42 +00:00
submit_data . blob = m_blockTemplate - > get_block_template_blob ( submit_data . template_id , nonce_offset , extra_nonce_offset ) ;
if ( submit_data . blob . empty ( ) ) {
LOGERR ( 0 , " submit_block: couldn't find block template with id " < < submit_data . template_id ) ;
return ;
}
}
else {
LOGINFO ( 0 , " submit_block: height = " < < height < < " , external blob ( " < < submit_data . blob . size ( ) < < " bytes) " ) ;
2021-09-10 14:18:16 +00:00
is_external = true ;
2021-08-22 10:20:59 +00:00
}
std : : string request ;
2021-08-25 20:07:42 +00:00
request . reserve ( submit_data . blob . size ( ) * 2 + 128 ) ;
2021-08-22 10:20:59 +00:00
request = " { \" jsonrpc \" : \" 2.0 \" , \" id \" : \" 0 \" , \" method \" : \" submit_block \" , \" params \" :[ \" " ;
2021-09-26 14:50:24 +00:00
const uint32_t template_id = submit_data . template_id ;
const uint32_t nonce = submit_data . nonce ;
const uint32_t extra_nonce = submit_data . extra_nonce ;
2021-08-25 20:07:42 +00:00
for ( size_t i = 0 ; i < submit_data . blob . size ( ) ; + + i ) {
2021-08-22 10:20:59 +00:00
char buf [ 16 ] ;
2021-08-25 20:07:42 +00:00
if ( nonce_offset & & nonce_offset < = i & & i < nonce_offset + sizeof ( submit_data . nonce ) ) {
snprintf ( buf , sizeof ( buf ) , " %02x " , submit_data . nonce & 255 ) ;
submit_data . nonce > > = 8 ;
2021-08-22 10:20:59 +00:00
}
2021-08-25 20:07:42 +00:00
else if ( extra_nonce_offset & & extra_nonce_offset < = i & & i < extra_nonce_offset + sizeof ( submit_data . extra_nonce ) ) {
snprintf ( buf , sizeof ( buf ) , " %02x " , submit_data . extra_nonce & 255 ) ;
submit_data . extra_nonce > > = 8 ;
2021-08-22 10:20:59 +00:00
}
else {
2021-08-25 20:07:42 +00:00
snprintf ( buf , sizeof ( buf ) , " %02x " , submit_data . blob [ i ] ) ;
2021-08-22 10:20:59 +00:00
}
request . append ( buf ) ;
}
request . append ( " \" ]} " ) ;
2021-09-08 18:25:39 +00:00
JSONRPCRequest : : call ( m_params - > m_host . c_str ( ) , m_params - > m_rpcPort , request . c_str ( ) ,
2021-09-10 14:18:16 +00:00
[ height , diff , template_id , nonce , extra_nonce , is_external ] ( const char * data , size_t size )
2021-08-22 10:20:59 +00:00
{
rapidjson : : Document doc ;
2021-08-29 06:34:26 +00:00
if ( doc . Parse < rapidjson : : kParseCommentsFlag | rapidjson : : kParseTrailingCommasFlag > ( data , size ) . HasParseError ( ) | | ! doc . IsObject ( ) ) {
2021-08-22 10:20:59 +00:00
LOGERR ( 0 , " submit_block: invalid JSON response from daemon " ) ;
return ;
}
if ( doc . HasMember ( " error " ) ) {
auto & err = doc [ " error " ] ;
if ( ! err . IsObject ( ) ) {
LOGERR ( 0 , " submit_block: invalid JSON reponse from daemon: 'error' is not an object " ) ;
return ;
}
const char * error_msg = nullptr ;
auto it = doc . FindMember ( " message " ) ;
if ( it ! = doc . MemberEnd ( ) & & it - > value . IsString ( ) ) {
error_msg = it - > value . GetString ( ) ;
}
2021-09-10 14:18:16 +00:00
if ( is_external ) {
2021-09-18 08:03:06 +00:00
LOGWARN ( 3 , " submit_block (external blob): daemon returned error: " < < ( error_msg ? error_msg : " unknown error " ) ) ;
2021-09-10 14:18:16 +00:00
}
else {
LOGERR ( 0 , " submit_block: daemon returned error: ' " < < ( error_msg ? error_msg : " unknown error " ) < < " ', template id = " < < template_id < < " , nonce = " < < nonce < < " , extra_nonce = " < < extra_nonce ) ;
}
2021-08-22 10:20:59 +00:00
return ;
}
auto it = doc . FindMember ( " result " ) ;
if ( it ! = doc . MemberEnd ( ) & & it - > value . IsObject ( ) ) {
auto & result = it - > value ;
auto it2 = result . FindMember ( " status " ) ;
if ( it2 ! = result . MemberEnd ( ) & & it2 - > value . IsString ( ) & & ( strcmp ( it2 - > value . GetString ( ) , " OK " ) = = 0 ) ) {
LOGINFO ( 0 , log : : LightGreen ( ) < < " submit_block: BLOCK ACCEPTED at height " < < height < < " and difficulty = " < < diff ) ;
return ;
}
}
LOGWARN ( 0 , " submit_block: daemon sent unrecognizable reply: " < < log : : const_buf ( data , size ) ) ;
2021-09-10 14:18:16 +00:00
} ,
[ is_external ] ( const char * data , size_t size )
{
if ( size > 0 ) {
if ( is_external ) {
2021-09-18 08:03:06 +00:00
LOGWARN ( 3 , " submit_block (external blob): RPC request failed, error " < < log : : const_buf ( data , size ) ) ;
2021-09-10 14:18:16 +00:00
}
else {
LOGERR ( 0 , " submit_block (external blob): RPC request failed, error " < < log : : const_buf ( data , size ) ) ;
}
}
2021-08-22 10:20:59 +00:00
} ) ;
}
void p2pool : : submit_sidechain_block ( uint32_t template_id , uint32_t nonce , uint32_t extra_nonce )
{
2021-08-25 20:07:42 +00:00
LOGINFO ( 3 , " submit_sidechain_block: template id = " < < template_id < < " , nonce = " < < nonce < < " , extra_nonce = " < < extra_nonce ) ;
2021-08-22 10:20:59 +00:00
m_blockTemplate - > submit_sidechain_block ( template_id , nonce , extra_nonce ) ;
}
void p2pool : : update_block_template_async ( )
{
2021-08-25 10:17:14 +00:00
const int err = uv_async_send ( & m_blockTemplateAsync ) ;
2021-08-22 10:20:59 +00:00
if ( err ) {
2021-08-25 10:17:14 +00:00
LOGERR ( 1 , " uv_async_send failed, error " < < uv_err_name ( err ) ) ;
}
}
void p2pool : : update_block_template ( )
{
if ( m_updateSeed ) {
m_hasher - > set_seed_async ( m_minerData . seed_hash ) ;
m_updateSeed = false ;
2021-08-22 10:20:59 +00:00
}
2021-08-25 10:17:14 +00:00
m_blockTemplate - > update ( m_minerData , * m_mempool , & m_params - > m_wallet ) ;
stratum_on_block ( ) ;
2021-09-01 14:26:56 +00:00
api_update_pool_stats ( ) ;
2021-08-22 10:20:59 +00:00
}
void p2pool : : download_block_headers ( uint64_t current_height )
{
const uint64_t seed_height = get_seed_height ( current_height ) ;
const uint64_t prev_seed_height = ( seed_height > SEEDHASH_EPOCH_BLOCKS ) ? ( seed_height - SEEDHASH_EPOCH_BLOCKS ) : 0 ;
char buf [ log : : Stream : : BUF_SIZE + 1 ] ;
log : : Stream s ( buf ) ;
// First download 2 RandomX seeds
const uint64_t seed_heights [ 2 ] = { prev_seed_height , seed_height } ;
for ( uint64_t height : seed_heights ) {
s . m_pos = 0 ;
s < < " { \" jsonrpc \" : \" 2.0 \" , \" id \" : \" 0 \" , \" method \" : \" get_block_header_by_height \" , \" params \" :{ \" height \" : " < < height < < " }} \0 " ;
2021-09-08 18:25:39 +00:00
JSONRPCRequest : : call ( m_params - > m_host . c_str ( ) , m_params - > m_rpcPort , buf ,
2021-08-22 10:20:59 +00:00
[ this , prev_seed_height , height ] ( const char * data , size_t size )
{
ChainMain block ;
if ( parse_block_header ( data , size , block ) ) {
if ( height = = prev_seed_height ) {
2021-08-25 16:31:42 +00:00
// Do it synchronously to make sure stratum and p2p don't start before it's finished
m_hasher - > set_old_seed ( block . id ) ;
2021-08-22 10:20:59 +00:00
}
}
else {
LOGERR ( 1 , " fatal error: couldn't download block header for height " < < height ) ;
panic ( ) ;
}
2021-09-10 14:18:16 +00:00
} ,
[ height ] ( const char * data , size_t size )
{
if ( size > 0 ) {
LOGERR ( 1 , " fatal error: couldn't download block header for height " < < height < < " , error " < < log : : const_buf ( data , size ) ) ;
panic ( ) ;
}
2021-08-22 10:20:59 +00:00
} ) ;
}
s . m_pos = 0 ;
s < < " { \" jsonrpc \" : \" 2.0 \" , \" id \" : \" 0 \" , \" method \" : \" get_block_headers_range \" , \" params \" :{ \" start_height \" : " < < current_height - BLOCK_HEADERS_REQUIRED < < " , \" end_height \" : " < < current_height - 1 < < " }} \0 " ;
2021-09-08 18:25:39 +00:00
JSONRPCRequest : : call ( m_params - > m_host . c_str ( ) , m_params - > m_rpcPort , buf ,
2021-08-22 10:20:59 +00:00
[ this , current_height ] ( const char * data , size_t size )
{
if ( parse_block_headers_range ( data , size ) = = BLOCK_HEADERS_REQUIRED ) {
update_median_timestamp ( ) ;
if ( m_serversStarted . exchange ( 1 ) = = 0 ) {
2021-10-28 18:05:40 +00:00
m_ZMQReader = new ZMQReader ( m_params - > m_host . c_str ( ) , m_params - > m_zmqPort , this ) ;
2021-08-22 10:20:59 +00:00
m_stratumServer = new StratumServer ( this ) ;
m_p2pServer = new P2PServer ( this ) ;
2021-09-01 11:49:58 +00:00
api_update_network_stats ( ) ;
2021-08-22 10:20:59 +00:00
}
}
else {
LOGERR ( 1 , " fatal error: couldn't download block headers for heights " < < current_height - BLOCK_HEADERS_REQUIRED < < " - " < < current_height - 1 ) ;
panic ( ) ;
}
2021-09-10 14:18:16 +00:00
} ,
[ current_height ] ( const char * data , size_t size )
{
if ( size > 0 ) {
LOGERR ( 1 , " fatal error: couldn't download block headers for heights " < < current_height - BLOCK_HEADERS_REQUIRED < < " - " < < current_height - 1 < < " , error " < < log : : const_buf ( data , size ) ) ;
panic ( ) ;
}
2021-08-22 10:20:59 +00:00
} ) ;
}
bool p2pool : : chainmain_get_by_hash ( const hash & id , ChainMain & data ) const
{
ReadLock lock ( m_mainchainLock ) ;
auto it = m_mainchainByHash . find ( id ) ;
if ( it = = m_mainchainByHash . end ( ) ) {
return false ;
}
data = it - > second ;
return true ;
}
bool p2pool : : get_timestamps ( uint64_t ( & timestamps ) [ TIMESTAMP_WINDOW ] ) const
{
ReadLock lock ( m_mainchainLock ) ;
if ( m_mainchainByHeight . size ( ) < = TIMESTAMP_WINDOW ) {
return false ;
}
auto it = m_mainchainByHeight . end ( ) ;
for ( int i = 0 ; ( i < TIMESTAMP_WINDOW ) & & ( it ! = m_mainchainByHeight . begin ( ) ) ; + + i ) {
- - it ;
timestamps [ i ] = it - > second . timestamp ;
}
return true ;
}
void p2pool : : update_median_timestamp ( )
{
uint64_t timestamps [ TIMESTAMP_WINDOW ] ;
if ( ! get_timestamps ( timestamps ) )
{
m_minerData . median_timestamp = 0 ;
return ;
}
std : : sort ( timestamps , timestamps + TIMESTAMP_WINDOW ) ;
// Shift it +1 block compared to Monero's code because we don't have the latest block yet when we receive new miner data
m_minerData . median_timestamp = ( timestamps [ TIMESTAMP_WINDOW / 2 ] + timestamps [ TIMESTAMP_WINDOW / 2 + 1 ] ) / 2 ;
LOGINFO ( 4 , " median timestamp updated to " < < log : : Gray ( ) < < m_minerData . median_timestamp ) ;
}
void p2pool : : stratum_on_block ( )
{
#if 0
uint8_t hashing_blob [ 128 ] ;
uint64_t height ;
difficulty_type difficulty ;
difficulty_type sidechain_difficulty ;
hash seed_hash ;
size_t nonce_offset ;
uint32_t template_id ;
m_blockTemplate - > get_hashing_blob ( 0 , hashing_blob , height , difficulty , sidechain_difficulty , seed_hash , nonce_offset , template_id ) ;
submit_block ( template_id , 0 , 0 ) ;
# else
if ( m_stratumServer ) {
m_stratumServer - > on_block ( * m_blockTemplate ) ;
}
# endif
}
2021-08-27 09:25:25 +00:00
void p2pool : : get_info ( )
{
2021-09-08 18:25:39 +00:00
JSONRPCRequest : : call ( m_params - > m_host . c_str ( ) , m_params - > m_rpcPort , " { \" jsonrpc \" : \" 2.0 \" , \" id \" : \" 0 \" , \" method \" : \" get_info \" } " ,
2021-08-27 09:25:25 +00:00
[ this ] ( const char * data , size_t size )
{
parse_get_info_rpc ( data , size ) ;
2021-09-10 14:18:16 +00:00
} ,
[ this ] ( const char * data , size_t size )
{
if ( size > 0 ) {
LOGWARN ( 1 , " get_info RPC request failed: error " < < log : : const_buf ( data , size ) < < " , trying again in 1 second " ) ;
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1000 ) ) ;
get_info ( ) ;
}
2021-08-27 09:25:25 +00:00
} ) ;
2021-09-10 14:18:16 +00:00
}
2021-09-01 15:21:27 +00:00
2021-09-10 14:18:16 +00:00
void p2pool : : load_found_blocks ( )
{
if ( ! m_api ) {
return ;
}
2021-09-01 15:21:27 +00:00
2021-09-10 14:18:16 +00:00
std : : ifstream f ( FOUND_BLOCKS_FILE ) ;
if ( ! f . is_open ( ) ) {
return ;
}
2021-09-03 16:04:54 +00:00
2021-09-10 14:18:16 +00:00
while ( ! f . eof ( ) ) {
time_t timestamp ;
f > > timestamp ;
if ( f . eof ( ) ) break ;
2021-09-05 20:28:57 +00:00
2021-09-10 14:18:16 +00:00
uint64_t height ;
f > > height ;
if ( f . eof ( ) ) break ;
2021-09-01 15:21:27 +00:00
2021-09-10 14:18:16 +00:00
hash id ;
f > > id ;
if ( f . eof ( ) ) break ;
2021-09-03 16:04:54 +00:00
2021-09-10 14:18:16 +00:00
difficulty_type block_difficulty ;
f > > block_difficulty ;
if ( f . eof ( ) ) break ;
difficulty_type cumulative_difficulty ;
f > > cumulative_difficulty ;
m_foundBlocks . emplace_back ( timestamp , height , id , block_difficulty , cumulative_difficulty ) ;
2021-09-01 15:21:27 +00:00
}
2021-09-10 14:18:16 +00:00
api_update_block_found ( nullptr ) ;
2021-08-27 09:25:25 +00:00
}
void p2pool : : parse_get_info_rpc ( const char * data , size_t size )
{
rapidjson : : Document doc ;
2021-08-29 06:34:26 +00:00
doc . Parse < rapidjson : : kParseCommentsFlag | rapidjson : : kParseTrailingCommasFlag > ( data , size ) ;
2021-08-27 09:25:25 +00:00
if ( ! doc . IsObject ( ) | | ! doc . HasMember ( " result " ) ) {
LOGWARN ( 1 , " get_info RPC response is invalid ( \" result \" not found), trying again in 1 second " ) ;
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1000 ) ) ;
get_info ( ) ;
return ;
}
const auto & result = doc [ " result " ] ;
struct {
2021-10-28 16:47:28 +00:00
bool busy_syncing , synchronized , mainnet , testnet , stagenet ;
2021-08-27 09:25:25 +00:00
} info ;
2021-10-28 16:47:28 +00:00
if ( ! PARSE ( result , info , busy_syncing ) | |
! PARSE ( result , info , synchronized ) | |
! PARSE ( result , info , mainnet ) | |
! PARSE ( result , info , testnet ) | |
! PARSE ( result , info , stagenet ) ) {
2021-08-27 09:25:25 +00:00
LOGWARN ( 1 , " get_info RPC response is invalid, trying again in 1 second " ) ;
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1000 ) ) ;
get_info ( ) ;
return ;
}
2021-10-28 16:47:28 +00:00
if ( info . busy_syncing | | ! info . synchronized ) {
LOGINFO ( 1 , " monerod is " < < ( info . busy_syncing ? " busy syncing " : " not synchronized " ) < < " , trying again in 1 second " ) ;
2021-08-27 09:25:25 +00:00
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1000 ) ) ;
get_info ( ) ;
return ;
}
NetworkType monero_network = NetworkType : : Invalid ;
if ( info . mainnet ) monero_network = NetworkType : : Mainnet ;
if ( info . testnet ) monero_network = NetworkType : : Testnet ;
if ( info . stagenet ) monero_network = NetworkType : : Stagenet ;
const NetworkType sidechain_network = m_sideChain - > network_type ( ) ;
if ( monero_network ! = sidechain_network ) {
LOGERR ( 1 , " monerod is on " < < monero_network < < " , but you're mining to a " < < sidechain_network < < " sidechain " ) ;
panic ( ) ;
}
2021-09-13 16:27:47 +00:00
get_version ( ) ;
}
void p2pool : : get_version ( )
{
JSONRPCRequest : : call ( m_params - > m_host . c_str ( ) , m_params - > m_rpcPort , " { \" jsonrpc \" : \" 2.0 \" , \" id \" : \" 0 \" , \" method \" : \" get_version \" } " ,
[ this ] ( const char * data , size_t size )
{
parse_get_version_rpc ( data , size ) ;
} ,
[ this ] ( const char * data , size_t size )
{
if ( size > 0 ) {
LOGWARN ( 1 , " get_version RPC request failed: error " < < log : : const_buf ( data , size ) < < " , trying again in 1 second " ) ;
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1000 ) ) ;
get_version ( ) ;
}
} ) ;
}
void p2pool : : parse_get_version_rpc ( const char * data , size_t size )
{
rapidjson : : Document doc ;
doc . Parse < rapidjson : : kParseCommentsFlag | rapidjson : : kParseTrailingCommasFlag > ( data , size ) ;
if ( ! doc . IsObject ( ) | | ! doc . HasMember ( " result " ) ) {
LOGWARN ( 1 , " get_version RPC response is invalid ( \" result \" not found), trying again in 1 second " ) ;
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1000 ) ) ;
get_version ( ) ;
return ;
}
const auto & result = doc [ " result " ] ;
std : : string status ;
uint64_t version ;
if ( ! parseValue ( result , " status " , status ) | | ! parseValue ( result , " version " , version ) ) {
LOGWARN ( 1 , " get_version RPC response is invalid, trying again in 1 second " ) ;
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1000 ) ) ;
get_version ( ) ;
return ;
}
if ( status ! = " OK " ) {
LOGWARN ( 1 , " get_version RPC failed, trying again in 1 second " ) ;
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1000 ) ) ;
get_version ( ) ;
return ;
}
2021-11-20 10:51:22 +00:00
const uint64_t required = m_params - > m_disableRandomX ? 0x30009 : 0x30008 ;
if ( version < required ) {
2021-09-13 16:27:47 +00:00
const uint64_t version_hi = version > > 16 ;
const uint64_t version_lo = version & 65535 ;
2021-11-20 10:51:22 +00:00
const uint64_t required_version_hi = required > > 16 ;
const uint64_t required_version_lo = required & 65535 ;
2021-12-07 15:25:23 +00:00
LOGERR ( 1 , " monerod RPC v " < < version_hi < < ' . ' < < version_lo < < " is incompatible, update to RPC >= v " < < required_version_hi < < ' . ' < < required_version_lo < < " (Monero v0.17.3.0 or newer) " ) ;
2021-09-13 16:27:47 +00:00
panic ( ) ;
}
2021-08-27 09:25:25 +00:00
get_miner_data ( ) ;
}
2021-08-22 10:20:59 +00:00
void p2pool : : get_miner_data ( )
{
2021-09-08 18:25:39 +00:00
JSONRPCRequest : : call ( m_params - > m_host . c_str ( ) , m_params - > m_rpcPort , " { \" jsonrpc \" : \" 2.0 \" , \" id \" : \" 0 \" , \" method \" : \" get_miner_data \" } " ,
2021-08-22 10:20:59 +00:00
[ this ] ( const char * data , size_t size )
{
parse_get_miner_data_rpc ( data , size ) ;
2021-09-10 14:18:16 +00:00
} ,
[ this ] ( const char * data , size_t size )
{
if ( size > 0 ) {
LOGWARN ( 1 , " get_miner_data RPC request failed: error " < < log : : const_buf ( data , size ) < < " , trying again in 1 second " ) ;
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1000 ) ) ;
get_miner_data ( ) ;
}
2021-08-22 10:20:59 +00:00
} ) ;
}
void p2pool : : parse_get_miner_data_rpc ( const char * data , size_t size )
{
rapidjson : : Document doc ;
2021-08-29 06:34:26 +00:00
doc . Parse < rapidjson : : kParseCommentsFlag | rapidjson : : kParseTrailingCommasFlag > ( data , size ) ;
2021-08-22 10:20:59 +00:00
if ( ! doc . IsObject ( ) | | ! doc . HasMember ( " result " ) ) {
LOGWARN ( 1 , " get_miner_data RPC response is invalid, skipping it " ) ;
return ;
}
MinerData minerData ;
const auto & result = doc [ " result " ] ;
if ( ! PARSE ( result , minerData , major_version ) | |
! PARSE ( result , minerData , height ) | |
! PARSE ( result , minerData , prev_id ) | |
! PARSE ( result , minerData , seed_hash ) | |
! PARSE ( result , minerData , median_weight ) | |
! PARSE ( result , minerData , already_generated_coins ) | |
! PARSE ( result , minerData , difficulty ) ) {
LOGWARN ( 1 , " get_miner_data RPC response failed to parse, skipping it " ) ;
return ;
}
auto it = result . FindMember ( " tx_backlog " ) ;
if ( ( it ! = result . MemberEnd ( ) ) & & it - > value . IsArray ( ) ) {
const auto & tx_backlog = it - > value . GetArray ( ) ;
for ( rapidjson : : SizeType i = 0 , n = tx_backlog . Size ( ) ; i < n ; + + i ) {
const auto & tx = tx_backlog [ i ] ;
if ( ! tx . IsObject ( ) ) {
continue ;
}
TxMempoolData tx_data ;
if ( PARSE ( tx , tx_data , id ) & & PARSE ( tx , tx_data , weight ) & & PARSE ( tx , tx_data , fee ) ) {
tx_data . blob_size = 0 ;
minerData . tx_backlog . emplace_back ( std : : move ( tx_data ) ) ;
}
}
}
handle_miner_data ( minerData ) ;
download_block_headers ( minerData . height ) ;
}
2021-09-01 11:49:58 +00:00
bool p2pool : : parse_block_header ( const char * data , size_t size , ChainMain & c )
2021-08-22 10:20:59 +00:00
{
rapidjson : : Document doc ;
2021-08-29 06:34:26 +00:00
if ( doc . Parse < rapidjson : : kParseCommentsFlag | rapidjson : : kParseTrailingCommasFlag > ( data , size ) . HasParseError ( ) | | ! doc . IsObject ( ) ) {
2021-08-22 10:20:59 +00:00
LOGERR ( 1 , " parse_block_header: invalid JSON response from daemon " ) ;
return false ;
}
auto it = doc . FindMember ( " result " ) ;
if ( it = = doc . MemberEnd ( ) | | ! it - > value . IsObject ( ) ) {
LOGERR ( 1 , " parse_block_header: invalid JSON response from daemon : 'result' is not found or not an object " ) ;
return false ;
}
auto it2 = it - > value . FindMember ( " block_header " ) ;
if ( it2 = = it - > value . MemberEnd ( ) | | ! it2 - > value . IsObject ( ) ) {
LOGERR ( 1 , " parse_block_header: invalid JSON response from daemon: 'block_header' is not found or not an object " ) ;
return false ;
}
2021-09-01 11:49:58 +00:00
const auto & v = it2 - > value ;
2021-09-03 20:28:54 +00:00
if ( ! parseValue ( v , " difficulty " , c . difficulty . lo ) | | ! parseValue ( v , " difficulty_top64 " , c . difficulty . hi ) ) {
LOGERR ( 1 , " parse_block_header: invalid JSON response from daemon: failed to parse difficulty " ) ;
return false ;
}
if ( ! PARSE ( v , c , height ) | | ! PARSE ( v , c , timestamp ) | | ! PARSE ( v , c , reward ) | | ! parseValue ( v , " hash " , c . id ) ) {
2021-08-22 10:20:59 +00:00
LOGERR ( 1 , " parse_block_header: invalid JSON response from daemon: failed to parse 'block_header' " ) ;
return false ;
}
{
WriteLock lock ( m_mainchainLock ) ;
2021-09-01 11:49:58 +00:00
m_mainchainByHeight [ c . height ] = c ;
m_mainchainByHash [ c . id ] = c ;
2021-08-22 10:20:59 +00:00
}
2021-09-01 11:49:58 +00:00
LOGINFO ( 4 , " parsed block header for height " < < c . height ) ;
2021-08-22 10:20:59 +00:00
return true ;
}
uint32_t p2pool : : parse_block_headers_range ( const char * data , size_t size )
{
rapidjson : : Document doc ;
2021-08-29 06:34:26 +00:00
if ( doc . Parse < rapidjson : : kParseCommentsFlag | rapidjson : : kParseTrailingCommasFlag > ( data , size ) . HasParseError ( ) | | ! doc . IsObject ( ) ) {
2021-08-22 10:20:59 +00:00
LOGERR ( 1 , " parse_block_headers_range: invalid JSON response from daemon " ) ;
return 0 ;
}
auto it = doc . FindMember ( " result " ) ;
if ( it = = doc . MemberEnd ( ) | | ! it - > value . IsObject ( ) ) {
LOGERR ( 1 , " parse_block_headers_range: invalid JSON response from daemon: 'result' is not found or not an object " ) ;
return 0 ;
}
auto it2 = it - > value . FindMember ( " headers " ) ;
if ( it2 = = it - > value . MemberEnd ( ) | | ! it2 - > value . IsArray ( ) ) {
LOGERR ( 1 , " parse_block_headers_range: invalid JSON response from daemon: 'headers' is not found or not an array " ) ;
return 0 ;
}
uint32_t num_headers_parsed = 0 ;
WriteLock lock ( m_mainchainLock ) ;
auto headers = it2 - > value . GetArray ( ) ;
uint64_t min_height = std : : numeric_limits < uint64_t > : : max ( ) ;
uint64_t max_height = 0 ;
for ( auto i = headers . begin ( ) ; i ! = headers . end ( ) ; + + i ) {
if ( ! i - > IsObject ( ) ) {
continue ;
}
ChainMain c ;
2021-09-03 20:28:54 +00:00
if ( ! parseValue ( * i , " difficulty " , c . difficulty . lo ) | | ! parseValue ( * i , " difficulty_top64 " , c . difficulty . hi ) ) {
continue ;
}
if ( PARSE ( * i , c , height ) & & PARSE ( * i , c , timestamp ) & & PARSE ( * i , c , reward ) & & parseValue ( * i , " hash " , c . id ) ) {
2021-08-22 10:20:59 +00:00
min_height = std : : min ( min_height , c . height ) ;
max_height = std : : max ( max_height , c . height ) ;
m_mainchainByHeight [ c . height ] = c ;
m_mainchainByHash [ c . id ] = c ;
+ + num_headers_parsed ;
}
}
LOGINFO ( 4 , " parsed " < < num_headers_parsed < < " block headers for heights " < < min_height < < " - " < < max_height ) ;
return num_headers_parsed ;
}
2021-09-01 11:49:58 +00:00
void p2pool : : api_update_network_stats ( )
{
if ( ! m_api ) {
return ;
}
ChainMain mainnet_tip ;
{
ReadLock lock ( m_mainchainLock ) ;
mainnet_tip = m_mainchainByHash [ m_minerData . prev_id ] ;
}
m_api - > set ( p2pool_api : : Category : : NETWORK , " stats " ,
2021-09-05 09:50:56 +00:00
[ mainnet_tip ] ( log : : Stream & s )
2021-09-01 11:49:58 +00:00
{
s < < " { \" difficulty \" : " < < mainnet_tip . difficulty
< < " , \" hash \" : \" " < < mainnet_tip . id
< < " \" , \" height \" : " < < mainnet_tip . height
< < " , \" reward \" : " < < mainnet_tip . reward
< < " , \" timestamp \" : " < < mainnet_tip . timestamp < < " } " ;
} ) ;
2021-09-08 07:57:31 +00:00
api_update_stats_mod ( ) ;
2021-09-01 14:26:56 +00:00
}
void p2pool : : api_update_pool_stats ( )
{
if ( ! m_api ) {
return ;
}
uint64_t t ;
2021-09-02 10:29:50 +00:00
const difficulty_type & diff = m_sideChain - > difficulty ( ) ;
2021-09-01 14:26:56 +00:00
const uint64_t hashrate = udiv128 ( diff . hi , diff . lo , m_sideChain - > block_time ( ) , & t ) ;
2021-10-31 11:41:13 +00:00
const uint64_t miners = std : : max < uint64_t > ( m_sideChain - > miner_count ( ) , m_p2pServer ? m_p2pServer - > peer_list_size ( ) : 0U ) ;
2021-09-01 14:26:56 +00:00
const difficulty_type total_hashes = m_sideChain - > total_hashes ( ) ;
2021-09-01 15:21:27 +00:00
time_t last_block_found_time = 0 ;
uint64_t last_block_found_height = 0 ;
2021-09-02 07:02:24 +00:00
uint64_t total_blocks_found = 0 ;
2021-09-01 15:21:27 +00:00
{
MutexLock lock ( m_foundBlocksLock ) ;
if ( ! m_foundBlocks . empty ( ) ) {
2021-09-02 07:02:24 +00:00
total_blocks_found = m_foundBlocks . size ( ) ;
2021-09-03 16:04:54 +00:00
last_block_found_time = m_foundBlocks . back ( ) . timestamp ;
last_block_found_height = m_foundBlocks . back ( ) . height ;
2021-09-01 15:21:27 +00:00
}
}
2021-09-01 14:26:56 +00:00
m_api - > set ( p2pool_api : : Category : : POOL , " stats " ,
2021-09-02 07:02:24 +00:00
[ hashrate , miners , & total_hashes , last_block_found_time , last_block_found_height , total_blocks_found ] ( log : : Stream & s )
2021-09-01 14:26:56 +00:00
{
s < < " { \" pool_list \" :[ \" pplns \" ], \" pool_statistics \" :{ \" hashRate \" : " < < hashrate
< < " , \" miners \" : " < < miners
< < " , \" totalHashes \" : " < < total_hashes
2021-09-01 15:21:27 +00:00
< < " , \" lastBlockFoundTime \" : " < < last_block_found_time
2021-09-02 07:02:24 +00:00
< < " , \" lastBlockFound \" : " < < last_block_found_height
< < " , \" totalBlocksFound \" : " < < total_blocks_found
< < " }} " ;
2021-09-01 15:21:27 +00:00
} ) ;
2021-09-08 07:57:31 +00:00
api_update_stats_mod ( ) ;
}
void p2pool : : api_update_stats_mod ( )
{
if ( ! m_api ) {
return ;
}
ChainMain mainnet_tip ;
{
ReadLock lock ( m_mainchainLock ) ;
mainnet_tip = m_mainchainByHash [ m_minerData . prev_id ] ;
}
time_t last_block_found_time = 0 ;
uint64_t last_block_found_height = 0 ;
hash last_block_found_hash ;
difficulty_type last_block_total_hashes ;
{
MutexLock lock ( m_foundBlocksLock ) ;
if ( ! m_foundBlocks . empty ( ) ) {
last_block_found_time = m_foundBlocks . back ( ) . timestamp ;
last_block_found_height = m_foundBlocks . back ( ) . height ;
last_block_found_hash = m_foundBlocks . back ( ) . id ;
last_block_total_hashes = m_foundBlocks . back ( ) . total_hashes ;
}
}
char last_block_found_buf [ log : : Stream : : BUF_SIZE + 1 ] ;
log : : Stream s ( last_block_found_buf ) ;
s < < last_block_found_hash < < ' \0 ' ;
memcpy ( last_block_found_buf + 4 , " ... " , 4 ) ;
2021-10-31 11:41:13 +00:00
const uint64_t miners = std : : max < uint64_t > ( m_sideChain - > miner_count ( ) , m_p2pServer ? m_p2pServer - > peer_list_size ( ) : 0U ) ;
2021-09-08 07:57:31 +00:00
uint64_t t ;
const difficulty_type & diff = m_sideChain - > difficulty ( ) ;
const uint64_t hashrate = udiv128 ( diff . hi , diff . lo , m_sideChain - > block_time ( ) , & t ) ;
const difficulty_type total_hashes = m_sideChain - > total_hashes ( ) ;
if ( total_hashes < last_block_total_hashes ) {
return ;
}
const uint64_t round_hashes = total_hashes . lo - last_block_total_hashes . lo ;
2021-09-18 08:03:06 +00:00
const int stratum_port = DEFAULT_STRATUM_PORT ;
2021-09-08 07:57:31 +00:00
m_api - > set ( p2pool_api : : Category : : GLOBAL , " stats_mod " ,
2021-09-18 08:03:06 +00:00
[ & mainnet_tip , last_block_found_time , & last_block_found_buf , last_block_found_height , miners , hashrate , round_hashes , stratum_port ] ( log : : Stream & s )
2021-09-08 07:57:31 +00:00
{
2021-09-18 08:03:06 +00:00
s < < " { \" config \" :{ \" ports \" :[{ \" port \" : " < < stratum_port < < " , \" tls \" :false}], \" fee \" :0, \" minPaymentThreshold \" :400000000}, \" network \" :{ \" height \" : "
2021-09-08 07:57:31 +00:00
< < mainnet_tip . height < < " }, \" pool \" :{ \" stats \" :{ \" lastBlockFound \" : \" "
< < last_block_found_time < < " 000 \" }, \" blocks \" :[ \" "
< < static_cast < char * > ( last_block_found_buf ) < < static_cast < char * > ( last_block_found_buf ) + HASH_SIZE * 2 - 4 < < ' : '
< < last_block_found_time < < " \" , \" "
< < last_block_found_height < < " \" ], \" miners \" : "
< < miners < < " , \" hashrate \" : "
< < hashrate < < " , \" roundHashes \" : "
< < round_hashes < < " }} " ;
} ) ;
2021-09-01 15:21:27 +00:00
}
2021-10-29 09:14:28 +00:00
void p2pool : : cleanup_mainchain_data ( uint64_t height )
{
// Expects m_mainchainLock to be already locked here
// Deletes everything older than 720 blocks, except for the 3 latest RandomX seed heights
2021-10-31 11:20:29 +00:00
constexpr uint64_t PRUNE_DISTANCE = BLOCK_HEADERS_REQUIRED ;
2021-10-29 09:14:28 +00:00
const uint64_t seed_height = get_seed_height ( height ) ;
const std : : array < uint64_t , 3 > seed_heights { seed_height , seed_height - SEEDHASH_EPOCH_BLOCKS , seed_height - SEEDHASH_EPOCH_BLOCKS * 2 } ;
for ( auto it = m_mainchainByHeight . begin ( ) ; it ! = m_mainchainByHeight . end ( ) ; ) {
const uint64_t h = it - > first ;
if ( h + PRUNE_DISTANCE > = height ) {
break ;
}
if ( std : : find ( seed_heights . begin ( ) , seed_heights . end ( ) , h ) = = seed_heights . end ( ) ) {
m_mainchainByHash . erase ( it - > second . id ) ;
it = m_mainchainByHeight . erase ( it ) ;
}
else {
+ + it ;
}
}
}
2021-09-01 15:21:27 +00:00
void p2pool : : api_update_block_found ( const ChainMain * data )
{
2021-09-27 11:28:23 +00:00
clear_crypto_cache ( ) ;
2021-09-01 15:21:27 +00:00
if ( ! m_api ) {
return ;
}
const time_t cur_time = time ( nullptr ) ;
2021-09-03 16:04:54 +00:00
const difficulty_type total_hashes = m_sideChain - > total_hashes ( ) ;
difficulty_type diff ;
2021-09-01 15:21:27 +00:00
2021-09-13 08:14:53 +00:00
if ( data & & get_difficulty_at_height ( data - > height , diff ) ) {
2021-09-01 15:21:27 +00:00
std : : ofstream f ( FOUND_BLOCKS_FILE , std : : ios : : app ) ;
if ( f . is_open ( ) ) {
2021-09-05 21:05:36 +00:00
f < < cur_time < < ' ' < < data - > height < < ' ' < < data - > id < < ' ' < < diff < < ' ' < < total_hashes < < ' \n ' ;
2021-09-01 15:21:27 +00:00
}
}
2021-09-03 16:04:54 +00:00
std : : vector < FoundBlock > found_blocks ;
2021-09-01 15:21:27 +00:00
{
MutexLock lock ( m_foundBlocksLock ) ;
if ( data ) {
2021-09-05 20:28:57 +00:00
m_foundBlocks . emplace_back ( cur_time , data - > height , data - > id , diff , total_hashes ) ;
2021-09-01 15:21:27 +00:00
}
2021-09-10 06:18:38 +00:00
found_blocks . assign ( m_foundBlocks . end ( ) - std : : min < size_t > ( m_foundBlocks . size ( ) , 51 ) , m_foundBlocks . end ( ) ) ;
2021-09-01 15:21:27 +00:00
}
m_api - > set ( p2pool_api : : Category : : POOL , " blocks " ,
[ & found_blocks ] ( log : : Stream & s )
{
s < < ' [ ' ;
bool first = true ;
for ( auto i = found_blocks . rbegin ( ) ; i ! = found_blocks . rend ( ) ; + + i ) {
if ( ! first ) {
s < < ' , ' ;
}
2021-09-03 16:04:54 +00:00
s < < " { \" height \" : " < < i - > height < < ' , '
2021-09-05 20:28:57 +00:00
< < " \" hash \" : \" " < < i - > id < < " \" , "
2021-09-03 16:04:54 +00:00
< < " \" difficulty \" : " < < i - > block_diff < < ' , '
< < " \" totalHashes \" : " < < i - > total_hashes < < ' , '
< < " \" ts \" : " < < i - > timestamp < < ' } ' ;
2021-09-01 15:21:27 +00:00
first = false ;
}
s < < ' ] ' ;
2021-09-01 14:26:56 +00:00
} ) ;
2021-09-08 07:57:31 +00:00
api_update_stats_mod ( ) ;
2021-09-01 11:49:58 +00:00
}
2021-09-13 08:14:53 +00:00
bool p2pool : : get_difficulty_at_height ( uint64_t height , difficulty_type & diff )
{
ReadLock lock ( m_mainchainLock ) ;
auto it = m_mainchainByHeight . find ( height ) ;
if ( it = = m_mainchainByHeight . end ( ) ) {
return false ;
}
diff = it - > second . difficulty ;
return true ;
}
2021-08-22 10:20:59 +00:00
static void on_signal ( uv_signal_t * handle , int signum )
{
2021-08-25 10:28:15 +00:00
p2pool * pool = reinterpret_cast < p2pool * > ( handle - > data ) ;
2021-08-22 10:20:59 +00:00
switch ( signum ) {
case SIGHUP :
LOGINFO ( 1 , " caught SIGHUP " ) ;
break ;
case SIGINT :
LOGINFO ( 1 , " caught SIGINT " ) ;
break ;
case SIGTERM :
LOGINFO ( 1 , " caught SIGTERM " ) ;
break ;
# ifdef SIGBREAK
case SIGBREAK :
LOGINFO ( 1 , " caught SIGBREAK " ) ;
break ;
# endif
# ifdef SIGUSR1
case SIGUSR1 :
log : : reopen ( ) ;
return ;
# endif
default :
LOGINFO ( 1 , " caught signal " < < signum ) ;
}
LOGINFO ( 1 , " stopping " ) ;
uv_signal_stop ( handle ) ;
2021-08-25 10:28:15 +00:00
pool - > stop ( ) ;
2021-08-22 10:20:59 +00:00
}
static bool init_uv_threadpool ( )
{
static uv_work_t dummy ;
2021-08-25 10:17:14 +00:00
return ( uv_queue_work ( uv_default_loop_checked ( ) , & dummy , [ ] ( uv_work_t * ) { } , nullptr ) = = 0 ) ;
2021-08-22 10:20:59 +00:00
}
2021-08-25 10:28:15 +00:00
static bool init_signals ( p2pool * pool )
2021-08-22 10:20:59 +00:00
{
constexpr int signal_names [ ] = {
SIGHUP ,
SIGINT ,
SIGTERM ,
# ifdef SIGBREAK
SIGBREAK ,
# endif
# ifdef SIGUSR1
SIGUSR1 ,
# endif
} ;
static uv_signal_t signals [ array_size ( signal_names ) ] ;
for ( size_t i = 0 ; i < array_size ( signal_names ) ; + + i ) {
2021-08-25 10:17:14 +00:00
uv_signal_init ( uv_default_loop_checked ( ) , & signals [ i ] ) ;
2021-08-25 10:28:15 +00:00
signals [ i ] . data = pool ;
2021-08-22 10:20:59 +00:00
const int rc = uv_signal_start ( & signals [ i ] , on_signal , signal_names [ i ] ) ;
if ( rc ! = 0 ) {
LOGERR ( 1 , " failed to initialize signal, error " < < rc ) ;
return false ;
}
}
return true ;
}
2021-08-23 21:25:35 +00:00
void p2pool : : stop ( )
{
2021-08-25 10:17:14 +00:00
uv_async_send ( & m_stopAsync ) ;
2021-08-23 21:25:35 +00:00
}
2021-08-22 10:20:59 +00:00
int p2pool : : run ( )
{
if ( ! m_params - > ok ( ) ) {
LOGERR ( 1 , " Invalid or missing command line. Try \" p2pool --help \" . " ) ;
return 1 ;
}
if ( ! init_uv_threadpool ( ) ) {
LOGERR ( 1 , " failed to start UV thread pool " ) ;
return 1 ;
}
2021-08-25 10:28:15 +00:00
if ( ! init_signals ( this ) ) {
2021-08-22 10:20:59 +00:00
LOGERR ( 1 , " failed to initialize signal handlers " ) ;
return 1 ;
}
2021-09-06 07:02:35 +00:00
try {
2021-08-27 09:25:25 +00:00
get_info ( ) ;
2021-09-10 14:18:16 +00:00
load_found_blocks ( ) ;
2021-08-25 10:17:14 +00:00
const int rc = uv_run ( uv_default_loop_checked ( ) , UV_RUN_DEFAULT ) ;
2021-08-22 10:20:59 +00:00
LOGINFO ( 1 , " uv_run exited, result = " < < rc ) ;
2021-10-28 18:05:40 +00:00
delete m_ZMQReader ;
2021-08-22 10:20:59 +00:00
}
2021-09-06 07:02:35 +00:00
catch ( const std : : exception & e ) {
2021-09-06 13:49:39 +00:00
const char * s = e . what ( ) ;
LOGERR ( 1 , " exception " < < s ) ;
2021-09-06 07:02:35 +00:00
panic ( ) ;
}
2021-08-22 10:20:59 +00:00
m_stopped = true ;
2021-08-24 09:42:41 +00:00
bkg_jobs_tracker . wait ( ) ;
2021-08-23 20:50:36 +00:00
2021-08-22 10:20:59 +00:00
delete m_stratumServer ;
delete m_p2pServer ;
LOGINFO ( 1 , " stopped " ) ;
return 0 ;
}
} // namespace p2pool