2021-08-22 10:20:59 +00:00
/*
* This file is part of the Monero P2Pool < https : //github.com/SChernykh/p2pool>
2023-01-04 12:07:55 +00:00
* Copyright ( c ) 2021 - 2023 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 "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"
2022-03-15 15:56:37 +00:00
# ifdef WITH_RANDOMX
2022-01-22 22:09:29 +00:00
# include "miner.h"
2022-03-15 15:56:37 +00:00
# endif
2021-08-22 10:20:59 +00:00
# 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"
2022-01-29 16:00:12 +00:00
# include "pool_block.h"
2022-05-11 14:10:10 +00:00
# include "keccak.h"
2021-08-22 10:20:59 +00:00
# include <thread>
2021-09-01 15:21:27 +00:00
# include <fstream>
2023-01-13 15:01:35 +00:00
# include <numeric>
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 )
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 )
2022-03-23 10:30:38 +00:00
, m_startTime ( seconds_since_epoch ( ) )
2023-03-31 10:52:17 +00:00
, m_lastMinerDataReceived ( 0 )
2021-08-22 10:20:59 +00:00
{
2021-10-02 15:06:48 +00:00
LOGINFO ( 1 , log : : LightCyan ( ) < < VERSION ) ;
2023-03-20 12:32:17 +00:00
Params * p = new Params ( argc , argv ) ;
m_params = p ;
2023-03-20 09:56:18 +00:00
# ifdef WITH_UPNP
2023-03-20 12:32:17 +00:00
if ( p - > m_upnp ) {
2023-03-20 09:56:18 +00:00
init_upnp ( ) ;
}
# endif
2023-03-20 12:32:17 +00:00
if ( ! p - > m_wallet . valid ( ) ) {
2021-08-22 10:20:59 +00:00
LOGERR ( 1 , " Invalid wallet address. Try \" p2pool --help \" . " ) ;
2022-05-23 07:37:11 +00:00
throw std : : exception ( ) ;
2021-08-31 16:26:28 +00:00
}
2023-03-20 12:32:17 +00:00
m_hostStr = p - > m_host ;
2023-01-27 16:42:00 +00:00
2023-03-20 12:32:17 +00:00
if ( p - > m_socks5Proxy . empty ( ) ) {
if ( p - > m_dns ) {
2022-09-04 16:10:14 +00:00
bool is_v6 ;
2023-03-20 12:32:17 +00:00
if ( ! resolve_host ( p - > m_host , is_v6 ) ) {
LOGERR ( 1 , " resolve_host failed for " < < p - > m_host ) ;
2022-09-04 16:10:14 +00:00
throw std : : exception ( ) ;
}
}
2023-03-20 12:32:17 +00:00
else if ( p - > m_host . find_first_not_of ( " 0123456789.: " ) ! = std : : string : : npos ) {
LOGERR ( 1 , " Can't resolve hostname " < < p - > m_host < < " with DNS disabled " ) ;
2022-09-04 16:10:14 +00:00
throw std : : exception ( ) ;
}
2021-09-08 18:25:39 +00:00
}
2023-01-27 16:42:00 +00:00
{
2023-03-20 12:32:17 +00:00
const bool changed = ( p - > m_host ! = m_hostStr ) ;
const std : : string rpc_port = ' : ' + std : : to_string ( p - > m_rpcPort ) ;
const std : : string zmq_port = " :ZMQ: " + std : : to_string ( p - > m_zmqPort ) ;
2023-01-27 16:42:00 +00:00
m_hostStr + = rpc_port + zmq_port ;
if ( changed ) {
2023-03-20 12:32:17 +00:00
m_hostStr + = " ( " + p - > m_host + ' ) ' ;
2023-01-27 16:42:00 +00:00
}
}
2021-08-31 16:26:28 +00:00
hash pub , sec , eph_public_key ;
generate_keys ( pub , sec ) ;
2022-04-01 14:52:23 +00:00
uint8_t view_tag ;
2023-03-20 12:32:17 +00:00
if ( ! p - > m_wallet . get_eph_public_key ( sec , 0 , eph_public_key , view_tag ) ) {
2021-08-31 16:26:28 +00:00
LOGERR ( 1 , " Invalid wallet address: get_eph_public_key failed " ) ;
2022-05-23 07:37:11 +00:00
throw std : : exception ( ) ;
2021-08-22 10:20:59 +00:00
}
2023-03-20 12:32:17 +00:00
const NetworkType type = p - > 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 " ) ;
}
2022-06-07 14:02:08 +00:00
int err = uv_async_init ( uv_default_loop_checked ( ) , & m_submitBlockAsync , on_submit_block ) ;
2021-08-25 10:45:14 +00:00
if ( err ) {
LOGERR ( 1 , " uv_async_init failed, error " < < uv_err_name ( err ) ) ;
2022-05-23 07:37:11 +00:00
throw std : : exception ( ) ;
2021-08-25 10:45:14 +00:00
}
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 ) ) ;
2022-05-23 07:37:11 +00:00
throw std : : exception ( ) ;
2021-08-25 10:17:14 +00:00
}
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 ) ) ;
2022-05-23 07:37:11 +00:00
throw std : : exception ( ) ;
2021-08-25 10:17:14 +00:00
}
m_stopAsync . data = this ;
2022-05-11 09:44:42 +00:00
err = uv_async_init ( uv_default_loop_checked ( ) , & m_restartZMQAsync , on_restart_zmq ) ;
if ( err ) {
LOGERR ( 1 , " uv_async_init failed, error " < < uv_err_name ( err ) ) ;
2022-05-23 07:37:11 +00:00
throw std : : exception ( ) ;
2022-05-11 09:44:42 +00:00
}
m_restartZMQAsync . data = this ;
2021-08-22 10:20:59 +00:00
uv_rwlock_init_checked ( & m_mainchainLock ) ;
2022-04-08 21:14:08 +00:00
uv_rwlock_init_checked ( & m_minerDataLock ) ;
2021-09-01 15:21:27 +00:00
uv_mutex_init_checked ( & m_foundBlocksLock ) ;
2022-04-09 16:08:37 +00:00
# ifdef WITH_RANDOMX
uv_mutex_init_checked ( & m_minerLock ) ;
# endif
2021-08-25 20:07:42 +00:00
uv_mutex_init_checked ( & m_submitBlockDataLock ) ;
2021-08-22 10:20:59 +00:00
2023-03-20 12:32:17 +00:00
m_api = p - > m_apiPath . empty ( ) ? nullptr : new p2pool_api ( p - > m_apiPath , p - > m_localStats ) ;
2021-08-22 10:20:59 +00:00
2023-03-20 12:32:17 +00:00
if ( p - > m_localStats & & ! m_api ) {
2022-02-18 15:21:28 +00:00
LOGERR ( 1 , " --local-api and --stratum-api command line parameters can't be used without --data-api " ) ;
2022-05-23 07:37:11 +00:00
throw std : : exception ( ) ;
2022-02-18 15:21:28 +00:00
}
2023-03-20 12:32:17 +00:00
m_sideChain = new SideChain ( this , type , p - > m_mini ? " mini " : nullptr ) ;
2021-11-20 10:51:22 +00:00
2023-03-20 12:32:17 +00:00
if ( p - > m_p2pAddresses . empty ( ) ) {
2021-12-30 10:10:18 +00:00
const int p2p_port = m_sideChain - > is_mini ( ) ? DEFAULT_P2P_PORT_MINI : DEFAULT_P2P_PORT ;
2023-03-20 12:32:17 +00:00
char buf [ 48 ] = { } ;
2021-12-30 10:10:18 +00:00
log : : Stream s ( buf ) ;
2023-03-20 12:32:17 +00:00
s < < " [::]: " < < p2p_port < < " ,0.0.0.0: " < < p2p_port ;
2021-12-30 10:10:18 +00:00
2023-03-20 12:32:17 +00:00
p - > m_p2pAddresses = buf ;
2021-12-30 10:10:18 +00:00
}
2022-03-15 15:56:37 +00:00
# ifdef WITH_RANDOMX
2023-03-20 12:32:17 +00:00
if ( p - > m_disableRandomX ) {
2021-11-20 10:51:22 +00:00
m_hasher = new RandomX_Hasher_RPC ( this ) ;
}
else {
m_hasher = new RandomX_Hasher ( this ) ;
}
2022-03-15 15:56:37 +00:00
# else
m_hasher = new RandomX_Hasher_RPC ( this ) ;
# endif
2021-11-20 10:51:22 +00:00
2022-11-24 20:38:15 +00:00
m_blockTemplate = new BlockTemplate ( m_sideChain , m_hasher ) ;
2021-08-23 09:44:26 +00:00
m_mempool = new Mempool ( ) ;
2022-07-07 06:32:37 +00:00
try {
m_consoleCommands = new ConsoleCommands ( this ) ;
}
catch ( . . . ) {
LOGERR ( 1 , " Couldn't start console commands handler " ) ;
m_consoleCommands = nullptr ;
}
2021-08-22 10:20:59 +00:00
}
p2pool : : ~ p2pool ( )
{
2023-03-20 09:56:18 +00:00
# ifdef WITH_UPNP
if ( m_params - > m_upnp ) {
destroy_upnp ( ) ;
}
# endif
2021-08-22 10:20:59 +00:00
uv_rwlock_destroy ( & m_mainchainLock ) ;
2022-04-08 21:14:08 +00:00
uv_rwlock_destroy ( & m_minerDataLock ) ;
2021-09-01 15:21:27 +00:00
uv_mutex_destroy ( & m_foundBlocksLock ) ;
2022-04-09 16:08:37 +00:00
# ifdef WITH_RANDOMX
uv_mutex_destroy ( & m_minerLock ) ;
# endif
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 ;
}
2023-03-28 08:14:47 +00:00
bool p2pool : : calculate_hash ( const void * data , size_t size , uint64_t height , const hash & seed , hash & result , bool force_light_mode )
2021-08-22 10:20:59 +00:00
{
2023-03-28 08:14:47 +00:00
return m_hasher - > calculate ( data , size , height , seed , result , force_light_mode ) ;
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 ;
}
2022-04-09 16:08:37 +00:00
# ifdef WITH_RANDOMX
void p2pool : : print_miner_status ( )
{
MutexLock lock ( m_minerLock ) ;
if ( m_miner ) {
m_miner - > print_status ( ) ;
}
}
# endif
2021-08-22 10:20:59 +00:00
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 " ) ;
2023-01-02 13:32:13 +00:00
if ( tx . fee > = HIGH_FEE_VALUE ) {
LOGINFO ( 4 , " high fee tx received: " < < log : : LightBlue ( ) < < tx . id < < log : : NoColor ( ) < < " , " < < log : : XMRAmount ( tx . fee ) < < " - updating block template " ) ;
update_block_template_async ( ) ;
}
2021-08-22 10:20:59 +00:00
# if TEST_MEMPOOL_PICKING_ALGORITHM
2022-04-08 21:14:08 +00:00
m_blockTemplate - > update ( miner_data ( ) , * m_mempool , & m_params - > m_wallet ) ;
2021-08-22 10:20:59 +00:00
# endif
2021-10-09 09:01:26 +00:00
2022-03-23 10:30:38 +00:00
m_zmqLastActive = seconds_since_epoch ( ) ;
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 ( ) ;
2022-03-23 14:49:24 +00:00
data . time_received = std : : chrono : : high_resolution_clock : : now ( ) ;
2022-04-08 21:14:08 +00:00
{
WriteLock lock ( m_minerDataLock ) ;
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 --------------------------------------------------------------------------------------------------------------- "
) ;
2022-10-04 12:35:38 +00:00
// Tx secret keys from all miners change every block, so cache can be cleared here
2022-10-04 15:51:40 +00:00
if ( m_sideChain - > precalcFinished ( ) ) {
2023-03-31 10:52:17 +00:00
// Clear all cache entries older than the previous miner data
clear_crypto_cache ( m_lastMinerDataReceived ) ;
2022-10-04 15:51:40 +00:00
}
2022-10-04 12:35:38 +00:00
2023-03-31 10:52:17 +00:00
m_lastMinerDataReceived = seconds_since_epoch ( ) ;
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
2022-03-23 10:30:38 +00:00
m_zmqLastActive = seconds_since_epoch ( ) ;
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 ) {
2022-05-12 06:49:34 +00:00
auto it = m_mainchainByHeight . find ( h ) ;
if ( ( it = = m_mainchainByHeight . end ( ) ) | | it - > second . difficulty . empty ( ) ) {
2021-10-31 11:20:29 +00:00
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 ) {
2022-06-04 11:16:05 +00:00
char buf [ log : : Stream : : BUF_SIZE + 1 ] = { } ;
2021-10-31 11:20:29 +00:00
log : : Stream s ( buf ) ;
s < < " { \" jsonrpc \" : \" 2.0 \" , \" id \" : \" 0 \" , \" method \" : \" get_block_header_by_height \" , \" params \" :{ \" height \" : " < < h < < " }} \0 " ;
2022-08-31 14:37:33 +00:00
JSONRPCRequest : : call ( m_params - > m_host , m_params - > m_rpcPort , buf , m_params - > m_rpcLogin , m_params - > m_socks5Proxy ,
2021-10-31 11:20:29 +00:00
[ 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 ( ) ) {
2022-01-29 16:00:12 +00:00
PoolBlock * block = side_chain ( ) . find_block ( sidechain_id ) ;
if ( block ) {
2021-09-06 21:33:52 +00:00
LOGINFO ( 0 , log : : LightGreen ( ) < < " BLOCK FOUND: main chain block at height " < < data . height < < " was mined by this p2pool " < < BLOCK_FOUND ) ;
2022-12-18 10:06:38 +00:00
const Wallet & w = params ( ) . m_wallet ;
const uint64_t payout = block - > get_payout ( w ) ;
2022-01-29 16:00:12 +00:00
if ( payout ) {
2022-12-18 10:06:38 +00:00
LOGINFO ( 0 , log : : LightCyan ( ) < < " Your wallet " < < log : : LightGreen ( ) < < w < < log : : LightCyan ( ) < < " got a payout of " < < log : : LightGreen ( ) < < log : : XMRAmount ( payout ) < < log : : LightCyan ( ) < < " in block " < < log : : LightGreen ( ) < < data . height ) ;
}
else {
LOGINFO ( 0 , log : : LightCyan ( ) < < " Your wallet " < < log : : LightYellow ( ) < < w < < log : : LightCyan ( ) < < " didn't get a payout in block " < < log : : LightYellow ( ) < < data . height < < log : : LightCyan ( ) < < " because you had no shares in PPLNS window " ) ;
2022-01-29 16:00:12 +00:00
}
2022-12-18 10:06:38 +00:00
2022-10-09 16:17:01 +00:00
api_update_block_found ( & data , block ) ;
2021-09-06 21:33:52 +00:00
}
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
2022-03-23 10:30:38 +00:00
m_zmqLastActive = seconds_since_epoch ( ) ;
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 ( ) ;
}
2022-06-03 16:28:10 +00:00
// If p2pool is stopped, m_submitBlockAsync is most likely already closed
if ( m_stopped ) {
LOGWARN ( 0 , " p2pool is shutting down, but a block was found. Trying to submit it anyway! " ) ;
submit_block ( ) ;
return ;
}
2021-08-25 20:07:42 +00:00
const int err = uv_async_send ( & m_submitBlockAsync ) ;
if ( err ) {
LOGERR ( 1 , " uv_async_send failed, error " < < uv_err_name ( err ) ) ;
}
}
2022-09-30 12:37:32 +00:00
void p2pool : : submit_block_async ( std : : vector < uint8_t > & & blob )
2021-08-25 20:07:42 +00:00
{
{
MutexLock lock ( m_submitBlockDataLock ) ;
m_submitBlockData . template_id = 0 ;
m_submitBlockData . nonce = 0 ;
m_submitBlockData . extra_nonce = 0 ;
2022-09-30 12:37:32 +00:00
m_submitBlockData . blob = std : : move ( blob ) ;
2021-08-25 20:07:42 +00:00
}
2021-08-25 10:45:14 +00:00
2022-06-03 16:28:10 +00:00
// If p2pool is stopped, m_submitBlockAsync is most likely already closed
if ( m_stopped ) {
LOGWARN ( 0 , " p2pool is shutting down, but a block was found. Trying to submit it anyway! " ) ;
submit_block ( ) ;
return ;
}
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 ) ) ;
}
}
2022-08-31 14:37:33 +00:00
bool init_signals ( p2pool * pool , bool init ) ;
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
2022-09-08 14:16:00 +00:00
delete pool - > m_consoleCommands ;
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 ) ;
2022-05-11 09:44:42 +00:00
uv_close ( reinterpret_cast < uv_handle_t * > ( & pool - > m_restartZMQAsync ) , nullptr ) ;
2022-06-04 11:16:05 +00:00
2022-08-31 14:37:33 +00:00
init_signals ( pool , false ) ;
2022-06-04 11:16:05 +00:00
uv_loop_t * loop = uv_default_loop_checked ( ) ;
delete GetLoopUserData ( loop , false ) ;
2022-08-31 14:37:33 +00:00
loop - > data = nullptr ;
2021-08-27 08:13:33 +00:00
}
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 ;
2023-01-09 21:27:06 +00:00
size_t sidechain_id_offset = 0 ;
hash sidechain_id ;
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 ( ) ) {
2023-01-09 21:27:06 +00:00
submit_data . blob = m_blockTemplate - > get_block_template_blob ( submit_data . template_id , submit_data . extra_nonce , nonce_offset , extra_nonce_offset , sidechain_id_offset , sidechain_id ) ;
LOGINFO ( 0 , log : : LightGreen ( ) < < " submit_block: height = " < < height
< < " , template id = " < < submit_data . template_id
< < " , nonce = " < < submit_data . nonce
< < " , extra_nonce = " < < submit_data . extra_nonce
< < " , id = " < < sidechain_id ) ;
2021-08-22 10:20:59 +00:00
2021-08-25 20:07:42 +00:00
if ( submit_data . blob . empty ( ) ) {
LOGERR ( 0 , " submit_block: couldn't find block template with id " < < submit_data . template_id ) ;
return ;
}
}
else {
2023-01-09 21:27:06 +00:00
LOGINFO ( 0 , log : : LightGreen ( ) < < " 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 ) {
2023-01-09 21:27:06 +00:00
uint8_t b ;
2021-08-25 20:07:42 +00:00
if ( nonce_offset & & nonce_offset < = i & & i < nonce_offset + sizeof ( submit_data . nonce ) ) {
2023-01-09 21:27:06 +00:00
b = submit_data . nonce & 255 ;
2021-08-25 20:07:42 +00:00
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 ) ) {
2023-01-09 21:27:06 +00:00
b = submit_data . extra_nonce & 255 ;
2021-08-25 20:07:42 +00:00
submit_data . extra_nonce > > = 8 ;
2021-08-22 10:20:59 +00:00
}
2023-01-09 21:27:06 +00:00
else if ( sidechain_id_offset & & sidechain_id_offset < = i & & i < sidechain_id_offset + HASH_SIZE ) {
b = sidechain_id . h [ i - sidechain_id_offset ] ;
}
2021-08-22 10:20:59 +00:00
else {
2023-01-09 21:27:06 +00:00
b = submit_data . blob [ i ] ;
2021-08-22 10:20:59 +00:00
}
2023-01-09 21:27:06 +00:00
request . append ( 1 , " 0123456789abcdef " [ b > > 4 ] ) ;
request . append ( 1 , " 0123456789abcdef " [ b & 15 ] ) ;
2021-08-22 10:20:59 +00:00
}
request . append ( " \" ]} " ) ;
2022-08-31 14:37:33 +00:00
JSONRPCRequest : : call ( m_params - > m_host , m_params - > m_rpcPort , request , m_params - > m_rpcLogin , m_params - > m_socks5Proxy ,
2023-01-24 07:19:13 +00:00
[ height , diff , template_id , nonce , extra_nonce , sidechain_id , 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 ;
2022-12-25 15:23:57 +00:00
auto it = err . FindMember ( " message " ) ;
if ( it ! = err . MemberEnd ( ) & & it - > value . IsString ( ) ) {
2021-08-22 10:20:59 +00:00
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 {
2023-01-09 21:27:06 +00:00
LOGERR ( 0 , " submit_block: daemon returned error: ' " < < ( error_msg ? error_msg : " unknown error " ) < < " ', template id = " < < template_id < < " , nonce = " < < nonce < < " , extra_nonce = " < < extra_nonce < < " , id = " < < sidechain_id ) ;
2021-09-10 14:18:16 +00:00
}
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 {
2022-06-09 07:20:24 +00:00
LOGERR ( 0 , " submit_block: RPC request failed, error " < < log : : const_buf ( data , size ) ) ;
2021-09-10 14:18:16 +00:00
}
}
2021-08-22 10:20:59 +00:00
} ) ;
}
2023-01-07 14:50:02 +00:00
bool p2pool : : submit_sidechain_block ( uint32_t template_id , uint32_t nonce , uint32_t extra_nonce )
2021-08-22 10:20:59 +00:00
{
2021-08-25 20:07:42 +00:00
LOGINFO ( 3 , " submit_sidechain_block: template id = " < < template_id < < " , nonce = " < < nonce < < " , extra_nonce = " < < extra_nonce ) ;
2023-01-07 14:50:02 +00:00
return m_blockTemplate - > submit_sidechain_block ( template_id , nonce , extra_nonce ) ;
2021-08-22 10:20:59 +00:00
}
2022-07-14 11:14:20 +00:00
void p2pool : : update_block_template_async ( bool is_alternative_block )
2021-08-22 10:20:59 +00:00
{
2022-06-03 16:28:10 +00:00
// If p2pool is stopped, m_blockTemplateAsync is most likely already closed
if ( m_stopped ) {
return ;
}
2022-12-22 20:53:04 +00:00
if ( is_alternative_block ) {
m_isAlternativeBlock = true ;
}
2022-07-14 11:14:20 +00:00
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 ( )
{
2022-04-08 21:14:08 +00:00
MinerData data = miner_data ( ) ;
2023-02-27 18:23:14 +00:00
if ( m_updateSeed . exchange ( false ) ) {
2022-04-08 21:14:08 +00:00
m_hasher - > set_seed_async ( data . seed_hash ) ;
2021-08-22 10:20:59 +00:00
}
2022-04-08 21:14:08 +00:00
m_blockTemplate - > update ( data , * m_mempool , & m_params - > m_wallet ) ;
2021-08-25 10:17:14 +00:00
stratum_on_block ( ) ;
2021-09-01 14:26:56 +00:00
api_update_pool_stats ( ) ;
2022-07-14 11:14:20 +00:00
# ifdef WITH_RANDOMX
if ( m_isAlternativeBlock . exchange ( false ) ) {
MutexLock lock ( m_minerLock ) ;
if ( m_miner ) {
m_miner - > reset_share_counters ( ) ;
}
}
# endif
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 ;
2022-06-04 11:16:05 +00:00
char buf [ log : : Stream : : BUF_SIZE + 1 ] = { } ;
2021-08-22 10:20:59 +00:00
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 " ;
2022-08-31 14:37:33 +00:00
JSONRPCRequest : : call ( m_params - > m_host , m_params - > m_rpcPort , buf , m_params - > m_rpcLogin , m_params - > m_socks5Proxy ,
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 {
2022-11-02 10:30:49 +00:00
LOGERR ( 1 , " fatal error: couldn't download block header for seed height " < < height ) ;
2023-01-14 11:19:25 +00:00
PANIC_STOP ( ) ;
2021-08-22 10:20:59 +00:00
}
2021-09-10 14:18:16 +00:00
} ,
[ height ] ( const char * data , size_t size )
{
if ( size > 0 ) {
2022-11-02 10:30:49 +00:00
LOGERR ( 1 , " fatal error: couldn't download block header for seed height " < < height < < " , error " < < log : : const_buf ( data , size ) ) ;
2023-01-14 11:19:25 +00:00
PANIC_STOP ( ) ;
2021-09-10 14:18:16 +00:00
}
2021-08-22 10:20:59 +00:00
} ) ;
}
2022-04-01 14:52:23 +00:00
const uint64_t start_height = ( current_height > BLOCK_HEADERS_REQUIRED ) ? ( current_height - BLOCK_HEADERS_REQUIRED ) : 0 ;
2021-08-22 10:20:59 +00:00
s . m_pos = 0 ;
2022-04-01 14:52:23 +00:00
s < < " { \" jsonrpc \" : \" 2.0 \" , \" id \" : \" 0 \" , \" method \" : \" get_block_headers_range \" , \" params \" :{ \" start_height \" : " < < start_height < < " , \" end_height \" : " < < current_height - 1 < < " }} \0 " ;
2021-08-22 10:20:59 +00:00
2022-08-31 14:37:33 +00:00
JSONRPCRequest : : call ( m_params - > m_host , m_params - > m_rpcPort , buf , m_params - > m_rpcLogin , m_params - > m_socks5Proxy ,
2022-04-01 14:52:23 +00:00
[ this , start_height , current_height ] ( const char * data , size_t size )
2021-08-22 10:20:59 +00:00
{
2022-04-01 14:52:23 +00:00
if ( parse_block_headers_range ( data , size ) = = current_height - start_height ) {
2021-08-22 10:20:59 +00:00
update_median_timestamp ( ) ;
if ( m_serversStarted . exchange ( 1 ) = = 0 ) {
m_stratumServer = new StratumServer ( this ) ;
m_p2pServer = new P2PServer ( this ) ;
2022-03-15 15:56:37 +00:00
# ifdef WITH_RANDOMX
2022-01-22 22:09:29 +00:00
if ( m_params - > m_minerThreads ) {
start_mining ( m_params - > m_minerThreads ) ;
}
2022-03-15 15:56:37 +00:00
# endif
2023-03-10 13:46:53 +00:00
try {
m_ZMQReader = new ZMQReader ( m_params - > m_host , m_params - > m_zmqPort , m_params - > m_socks5Proxy , this ) ;
}
catch ( const std : : exception & e ) {
LOGERR ( 1 , " Couldn't start ZMQ reader: exception " < < e . what ( ) ) ;
PANIC_STOP ( ) ;
}
2021-09-01 11:49:58 +00:00
api_update_network_stats ( ) ;
2022-12-18 10:46:18 +00:00
get_miner_data ( ) ;
2021-08-22 10:20:59 +00:00
}
}
else {
2022-11-02 10:30:49 +00:00
LOGERR ( 1 , " Couldn't download block headers for heights " < < start_height < < " - " < < current_height - 1 ) ;
download_block_headers ( current_height ) ;
2021-08-22 10:20:59 +00:00
}
2021-09-10 14:18:16 +00:00
} ,
2022-11-02 10:30:49 +00:00
[ this , start_height , current_height ] ( const char * data , size_t size )
2021-09-10 14:18:16 +00:00
{
if ( size > 0 ) {
2022-11-02 10:30:49 +00:00
LOGERR ( 1 , " Couldn't download block headers for heights " < < start_height < < " - " < < current_height - 1 < < " , error " < < log : : const_buf ( data , size ) ) ;
download_block_headers ( current_height ) ;
2021-09-10 14:18:16 +00:00
}
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 ) )
{
2022-04-08 21:14:08 +00:00
WriteLock lock ( m_minerDataLock ) ;
2021-08-22 10:20:59 +00:00
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
2022-04-08 21:14:08 +00:00
const uint64_t ts = ( timestamps [ TIMESTAMP_WINDOW / 2 ] + timestamps [ TIMESTAMP_WINDOW / 2 + 1 ] ) / 2 ;
LOGINFO ( 4 , " median timestamp updated to " < < log : : Gray ( ) < < ts ) ;
WriteLock lock ( m_minerDataLock ) ;
m_minerData . median_timestamp = ts ;
2021-08-22 10:20:59 +00:00
}
void p2pool : : stratum_on_block ( )
{
2022-03-15 15:56:37 +00:00
# ifdef WITH_RANDOMX
2022-04-09 16:08:37 +00:00
{
MutexLock lock ( m_minerLock ) ;
if ( m_miner ) {
m_miner - > on_block ( * m_blockTemplate ) ;
}
2022-01-22 22:09:29 +00:00
}
2022-03-15 15:56:37 +00:00
# endif
2022-04-09 16:08:37 +00:00
2021-08-22 10:20:59 +00:00
if ( m_stratumServer ) {
m_stratumServer - > on_block ( * m_blockTemplate ) ;
}
}
2021-08-27 09:25:25 +00:00
void p2pool : : get_info ( )
{
2022-08-31 14:37:33 +00:00
JSONRPCRequest : : call ( m_params - > m_host , m_params - > m_rpcPort , " { \" jsonrpc \" : \" 2.0 \" , \" id \" : \" 0 \" , \" method \" : \" get_info \" } " , m_params - > m_rpcLogin , m_params - > m_socks5Proxy ,
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 " ) ;
2022-07-05 12:29:41 +00:00
if ( ! m_stopped ) {
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1000 ) ) ;
get_info ( ) ;
}
2021-09-10 14:18:16 +00:00
}
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
2022-09-13 16:26:01 +00:00
while ( f . good ( ) ) {
2021-09-10 14:18:16 +00:00
time_t timestamp ;
f > > timestamp ;
2022-09-13 16:26:01 +00:00
if ( ! f . good ( ) ) break ;
2021-09-05 20:28:57 +00:00
2021-09-10 14:18:16 +00:00
uint64_t height ;
f > > height ;
2022-09-13 16:26:01 +00:00
if ( ! f . good ( ) ) break ;
2021-09-01 15:21:27 +00:00
2021-09-10 14:18:16 +00:00
hash id ;
f > > id ;
2022-09-13 16:26:01 +00:00
if ( ! f . good ( ) ) break ;
2021-09-03 16:04:54 +00:00
2021-09-10 14:18:16 +00:00
difficulty_type block_difficulty ;
f > > block_difficulty ;
2022-09-13 16:26:01 +00:00
if ( ! f . good ( ) ) break ;
2021-09-10 14:18:16 +00:00
difficulty_type cumulative_difficulty ;
f > > cumulative_difficulty ;
2022-09-13 16:26:01 +00:00
if ( ! f . good ( ) & & ! f . eof ( ) ) break ;
2021-09-10 14:18:16 +00:00
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
2022-10-09 16:17:01 +00:00
api_update_block_found ( nullptr , nullptr ) ;
2021-08-27 09:25:25 +00:00
}
void p2pool : : parse_get_info_rpc ( const char * data , size_t size )
{
2022-07-05 12:29:41 +00:00
if ( m_stopped ) {
return ;
}
2021-08-27 09:25:25 +00:00
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 " ) ;
2023-01-14 11:19:25 +00:00
PANIC_STOP ( ) ;
2021-08-27 09:25:25 +00:00
}
2021-09-13 16:27:47 +00:00
get_version ( ) ;
}
void p2pool : : get_version ( )
{
2022-08-31 14:37:33 +00:00
JSONRPCRequest : : call ( m_params - > m_host , m_params - > m_rpcPort , " { \" jsonrpc \" : \" 2.0 \" , \" id \" : \" 0 \" , \" method \" : \" get_version \" } " , m_params - > m_rpcLogin , m_params - > m_socks5Proxy ,
2021-09-13 16:27:47 +00:00
[ 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 " ) ;
2022-07-05 12:29:41 +00:00
if ( ! m_stopped ) {
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1000 ) ) ;
get_version ( ) ;
}
2021-09-13 16:27:47 +00:00
}
} ) ;
}
void p2pool : : parse_get_version_rpc ( const char * data , size_t size )
{
2022-07-05 12:29:41 +00:00
if ( m_stopped ) {
return ;
}
2021-09-13 16:27:47 +00:00
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 ;
}
2022-09-14 12:45:10 +00:00
constexpr uint64_t major = 3 ;
constexpr uint64_t minor = 10 ;
constexpr uint64_t required = ( major < < 16 ) | minor ;
2021-11-20 10:51:22 +00:00
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 ;
2022-09-14 12:45:10 +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.18.0.0 or newer) " ) ;
2023-01-14 11:19:25 +00:00
PANIC_STOP ( ) ;
2021-09-13 16:27:47 +00:00
}
2021-08-27 09:25:25 +00:00
get_miner_data ( ) ;
}
2021-08-22 10:20:59 +00:00
void p2pool : : get_miner_data ( )
{
2022-12-18 10:46:18 +00:00
if ( m_getMinerDataPending ) {
return ;
}
2022-05-11 22:18:02 +00:00
m_getMinerDataPending = true ;
2022-08-31 14:37:33 +00:00
JSONRPCRequest : : call ( m_params - > m_host , m_params - > m_rpcPort , " { \" jsonrpc \" : \" 2.0 \" , \" id \" : \" 0 \" , \" method \" : \" get_miner_data \" } " , m_params - > m_rpcLogin , m_params - > m_socks5Proxy ,
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 " ) ;
2022-07-05 12:29:41 +00:00
if ( ! m_stopped ) {
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1000 ) ) ;
get_miner_data ( ) ;
}
2021-09-10 14:18:16 +00:00
}
2022-05-11 22:18:02 +00:00
else {
m_getMinerDataPending = false ;
}
2021-08-22 10:20:59 +00:00
} ) ;
}
void p2pool : : parse_get_miner_data_rpc ( const char * data , size_t size )
{
2022-07-05 12:29:41 +00:00
if ( m_stopped ) {
return ;
}
2022-05-11 14:10:10 +00:00
hash h ;
2023-01-08 11:56:26 +00:00
keccak ( reinterpret_cast < const uint8_t * > ( data ) , static_cast < int > ( size ) , h . h ) ;
2022-05-11 14:10:10 +00:00
if ( h = = m_getMinerDataHash ) {
LOGWARN ( 4 , " Received a duplicate get_miner_data RPC response, ignoring it " ) ;
return ;
}
m_getMinerDataHash = h ;
2021-08-22 10:20:59 +00:00
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 ) ;
2022-12-18 10:46:18 +00:00
if ( m_serversStarted . load ( ) = = 0 ) {
download_block_headers ( minerData . height ) ;
}
2021-08-22 10:20:59 +00:00
}
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 ;
}
2022-04-08 21:14:08 +00:00
hash prev_id ;
{
ReadLock lock ( m_minerDataLock ) ;
prev_id = m_minerData . prev_id ;
}
2021-09-01 11:49:58 +00:00
ChainMain mainnet_tip ;
{
ReadLock lock ( m_mainchainLock ) ;
2022-04-08 21:14:08 +00:00
mainnet_tip = m_mainchainByHash [ prev_id ] ;
2021-09-01 11:49:58 +00:00
}
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 ;
}
2023-03-28 11:28:34 +00:00
const PoolBlock * tip = m_sideChain - > chainTip ( ) ;
const uint64_t bottom_height = m_sideChain - > bottom_height ( tip ) ;
const uint64_t pplns_window_size = ( tip & & bottom_height ) ? ( tip - > m_sidechainHeight - bottom_height + 1U ) : m_sideChain - > chain_window_size ( ) ;
2021-09-01 14:26:56 +00:00
uint64_t t ;
2023-03-28 09:40:43 +00:00
const difficulty_type diff = m_sideChain - > difficulty ( ) ;
2023-03-28 11:28:34 +00:00
const uint64_t height = tip ? tip - > m_sidechainHeight : 0 ;
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 ( ) ;
2023-01-13 15:01:35 +00:00
const auto & s = m_blockTemplate - > shares ( ) ;
const difficulty_type pplns_weight = std : : accumulate ( s . begin ( ) , s . end ( ) , difficulty_type ( ) , [ ] ( const auto & a , const auto & b ) { return a + b . m_weight ; } ) ;
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 " ,
2023-03-28 11:28:34 +00:00
[ hashrate , miners , & total_hashes , last_block_found_time , last_block_found_height , total_blocks_found , & pplns_weight , pplns_window_size , diff , height ] ( log : : Stream & s )
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
2023-01-13 15:01:35 +00:00
< < " , \" pplnsWeight \" : " < < pplns_weight
2023-03-28 11:28:34 +00:00
< < " , \" pplnsWindowSize \" : " < < pplns_window_size
2023-03-28 09:40:43 +00:00
< < " , \" sidechainDifficulty \" : " < < diff
< < " , \" sidechainHeight \" : " < < height
2021-09-02 07:02:24 +00:00
< < " }} " ;
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 ;
}
2022-04-08 21:14:08 +00:00
hash prev_id ;
{
ReadLock lock ( m_minerDataLock ) ;
prev_id = m_minerData . prev_id ;
}
2021-09-08 07:57:31 +00:00
ChainMain mainnet_tip ;
{
ReadLock lock ( m_mainchainLock ) ;
2022-04-08 21:14:08 +00:00
mainnet_tip = m_mainchainByHash [ prev_id ] ;
2021-09-08 07:57:31 +00:00
}
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 ( ) ) {
2022-04-07 17:11:20 +00:00
const FoundBlock & b = m_foundBlocks . back ( ) ;
last_block_found_time = b . timestamp ;
last_block_found_height = b . height ;
last_block_found_hash = b . id ;
last_block_total_hashes = b . total_hashes ;
2021-09-08 07:57:31 +00:00
}
}
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
{
2022-02-20 16:08:27 +00:00
s < < " { \" config \" :{ \" ports \" :[{ \" port \" : " < < stratum_port < < " , \" tls \" :false}], \" fee \" :0, \" minPaymentThreshold \" :300000000}, \" 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 ;
}
}
}
2022-10-09 16:17:01 +00:00
void p2pool : : api_update_block_found ( const ChainMain * data , const PoolBlock * block )
2021-09-01 15:21:27 +00:00
{
if ( ! m_api ) {
return ;
}
const time_t cur_time = time ( nullptr ) ;
2022-10-09 16:17:01 +00:00
const difficulty_type total_hashes = block ? block - > m_cumulativeDifficulty : m_sideChain - > total_hashes ( ) ;
2021-09-03 16:04:54 +00:00
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 ' ;
2022-09-13 16:26:01 +00:00
f . flush ( ) ;
f . close ( ) ;
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 ;
}
2022-03-15 15:56:37 +00:00
# ifdef WITH_RANDOMX
2022-01-22 22:09:29 +00:00
void p2pool : : start_mining ( uint32_t threads )
{
stop_mining ( ) ;
2022-04-09 16:08:37 +00:00
MutexLock lock ( m_minerLock ) ;
2022-01-22 22:09:29 +00:00
m_miner = new Miner ( this , threads ) ;
}
void p2pool : : stop_mining ( )
{
2022-04-09 16:08:37 +00:00
MutexLock lock ( m_minerLock ) ;
if ( m_miner ) {
delete m_miner ;
2022-01-22 22:09:29 +00:00
m_miner = nullptr ;
}
}
2022-03-15 15:56:37 +00:00
# endif
2022-01-22 22:09:29 +00:00
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
}
2022-08-31 14:37:33 +00:00
bool init_signals ( p2pool * pool , bool init )
2021-08-22 10:20:59 +00:00
{
2022-03-30 19:53:52 +00:00
# ifdef SIGPIPE
signal ( SIGPIPE , SIG_IGN ) ;
# endif
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 ) ] ;
2022-08-31 14:37:33 +00:00
if ( ! init ) {
for ( size_t i = 0 ; i < array_size ( signals ) ; + + i ) {
uv_signal_stop ( & signals [ i ] ) ;
uv_close ( reinterpret_cast < uv_handle_t * > ( & signals [ i ] ) , nullptr ) ;
}
return true ;
}
2021-08-22 10:20:59 +00:00
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 ( )
{
2022-06-03 16:28:10 +00:00
// Can be called only once
if ( m_stopped . exchange ( true ) = = false ) {
uv_async_send ( & m_stopAsync ) ;
}
2021-08-23 21:25:35 +00:00
}
2023-03-10 13:46:53 +00:00
bool p2pool : : zmq_running ( ) const
{
return m_ZMQReader & & m_ZMQReader - > is_running ( ) ;
}
2022-05-11 09:44:42 +00:00
void p2pool : : restart_zmq ( )
{
2022-06-03 16:28:10 +00:00
// If p2pool is stopped, m_restartZMQAsync is most likely already closed
if ( m_stopped ) {
return ;
}
2022-05-11 09:44:42 +00:00
if ( ! is_main_thread ( ) ) {
uv_async_send ( & m_restartZMQAsync ) ;
return ;
}
2022-12-18 10:46:18 +00:00
get_miner_data ( ) ;
2022-05-11 09:44:42 +00:00
delete m_ZMQReader ;
m_ZMQReader = nullptr ;
try {
2022-08-31 14:37:33 +00:00
m_ZMQReader = new ZMQReader ( m_params - > m_host , m_params - > m_zmqPort , m_params - > m_socks5Proxy , this ) ;
2022-05-11 09:44:42 +00:00
m_zmqLastActive = seconds_since_epoch ( ) ;
}
catch ( const std : : exception & e ) {
LOGERR ( 1 , " Couldn't restart ZMQ reader: exception " < < e . what ( ) ) ;
}
}
2021-08-22 10:20:59 +00:00
int p2pool : : run ( )
{
2022-08-31 14:37:33 +00:00
if ( ! m_params - > valid ( ) ) {
2021-08-22 10:20:59 +00:00
LOGERR ( 1 , " Invalid or missing command line. Try \" p2pool --help \" . " ) ;
return 1 ;
}
2022-08-31 14:37:33 +00:00
if ( ! init_signals ( this , true ) ) {
2021-08-22 10:20:59 +00:00
LOGERR ( 1 , " failed to initialize signal handlers " ) ;
return 1 ;
}
2022-06-04 11:16:05 +00:00
// Init default loop user data before running it
uv_loop_t * loop = uv_default_loop_checked ( ) ;
loop - > data = nullptr ;
GetLoopUserData ( loop ) ;
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 ) ;
2023-01-14 11:19:25 +00:00
PANIC_STOP ( ) ;
2021-09-06 07:02:35 +00:00
}
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
2022-03-15 15:56:37 +00:00
# ifdef WITH_RANDOMX
2022-01-22 22:09:29 +00:00
delete m_miner ;
2022-03-15 15:56:37 +00:00
# endif
2021-08-22 10:20:59 +00:00
delete m_stratumServer ;
delete m_p2pServer ;
LOGINFO ( 1 , " stopped " ) ;
return 0 ;
}
} // namespace p2pool