2019-03-29 05:43:23 +03:00
// Copyright (c) 2018-2019 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include "stratum_server.h"
# include "stratum_helpers.h"
# include "net/abstract_tcp_server2.h"
# include "currency_core/currency_config.h"
# include "currency_core/currency_core.h"
# include "common/command_line.h"
# include "common/int-util.h"
# include "version.h"
# include "currency_protocol/currency_protocol_handler.h"
# undef LOG_DEFAULT_CHANNEL
# define LOG_DEFAULT_CHANNEL "stratum"
ENABLE_CHANNEL_BY_DEFAULT ( " stratum " ) ;
using namespace stratum ;
namespace currency
{
namespace
{
// This username should be used by stratum clients to log in IN CASE stratum server was started with "stratum-miner-address" option.
// Alternatevly, valid and the very same wallet address should be user among all the workers.
# define WORKER_ALLOWED_USERNAME "miner"
# define STRATUM_BIND_IP_DEFAULT "0.0.0.0"
# define STRATUM_THREADS_COUNT_DEFAULT 2
# define STRATUM_BLOCK_TEMPLATE_UPD_PERIOD_DEFAULT 30 // sec
# define STRATUM_TOTAL_HR_PRINT_INTERVAL_S_DEFAULT 60 // sec
# define VDIFF_TARGET_MIN_DEFAULT 100000000ull // = 100 Mh
# define VDIFF_TARGET_MAX_DEFAULT 100000000000ull // = 100 Gh
# define VDIFF_TARGET_TIME_DEFAULT 30 // sec
# define VDIFF_RETARGET_TIME_DEFAULT 240 // sec
# define VDIFF_RETARGET_SHARES_COUNT 12 // enforce retargeting if this many shares are be received (huge performace comparing to current difficulty)
# define VDIFF_VARIANCE_PERCENT_DEFAULT 25 // %
const command_line : : arg_descriptor < bool > arg_stratum = { " stratum " , " Stratum server: enable " } ;
const command_line : : arg_descriptor < std : : string > arg_stratum_bind_ip = { " stratum-bind-ip " , " Stratum server: IP to bind " , STRATUM_BIND_IP_DEFAULT } ;
const command_line : : arg_descriptor < std : : string > arg_stratum_bind_port = { " stratum-bind-port " , " Stratum server: port to listen at " , std : : to_string ( STRATUM_DEFAULT_PORT ) } ;
const command_line : : arg_descriptor < size_t > arg_stratum_threads = { " stratum-threads-count " , " Stratum server: number of server threads " , STRATUM_THREADS_COUNT_DEFAULT } ;
const command_line : : arg_descriptor < std : : string > arg_stratum_miner_address = { " stratum-miner-address " , " Stratum server: miner address. All workers "
" will mine to this address. If not set here, ALL workers should use the very same wallet address as username. "
" If set here - they're allowed to log in with username ' " WORKER_ALLOWED_USERNAME " ' instead of address. " , " " , true } ;
const command_line : : arg_descriptor < size_t > arg_stratum_block_template_update_period = { " stratum-template-update-period " ,
" Stratum server: if there are no new blocks, update block template this often (sec.) " , STRATUM_BLOCK_TEMPLATE_UPD_PERIOD_DEFAULT } ;
const command_line : : arg_descriptor < uint64_t > arg_stratum_hr_print_interval = { " stratum-hr-print-interval " , " Stratum server: how often to print hashrate stats (sec.) " , STRATUM_TOTAL_HR_PRINT_INTERVAL_S_DEFAULT } ;
const command_line : : arg_descriptor < uint64_t > arg_stratum_vdiff_target_min = { " stratum-vdiff-target-min " , " Stratum server: minimum worker difficulty " , VDIFF_TARGET_MIN_DEFAULT } ;
const command_line : : arg_descriptor < uint64_t > arg_stratum_vdiff_target_max = { " stratum-vdiff-target-max " , " Stratum server: maximum worker difficulty " , VDIFF_TARGET_MAX_DEFAULT } ;
const command_line : : arg_descriptor < uint64_t > arg_stratum_vdiff_target_time = { " stratum-vdiff-target-time " , " Stratum server: target time per share (i.e. try to get one share per this many seconds) " , VDIFF_TARGET_TIME_DEFAULT } ;
const command_line : : arg_descriptor < uint64_t > arg_stratum_vdiff_retarget_time = { " stratum-vdiff-retarget-time " , " Stratum server: check to see if we should retarget this often (sec.) " , VDIFF_RETARGET_TIME_DEFAULT } ;
const command_line : : arg_descriptor < uint64_t > arg_stratum_vdiff_retarget_shares = { " stratum-vdiff-retarget-shares " , " Stratum server: enforce retargeting if got this many shares " , VDIFF_RETARGET_SHARES_COUNT } ;
const command_line : : arg_descriptor < uint64_t > arg_stratum_vdiff_variance_percent = { " stratum-vdiff-variance-percent " , " Stratum server: allow average time to very this % from target without retarget " , VDIFF_VARIANCE_PERCENT_DEFAULT } ;
//==============================================================================================================================
static jsonrpc_id_null_t jsonrpc_id_null ; // object of jsonrpc_id_null_t for convenience
// JSON-RPC error codes
# define JSONRPC_ERROR_CODE_DEFAULT -32000 // -32000 to -32099 : Reserved for implementation-defined server-errors.
# define JSONRPC_ERROR_CODE_PARSE -32700
# define JSONRPC_ERROR_CODE_METHOD_NOT_FOUND -32601
# define LP_CC_WORKER( ct, message, log_level) LOG_PRINT_CC( ct, "WORKER " << ct.m_worker_name << ": " << message, log_level)
# define LP_CC_WORKER_GREEN( ct, message, log_level) LOG_PRINT_CC_GREEN( ct, "WORKER " << ct.m_worker_name << ": " << message, log_level)
# define LP_CC_WORKER_RED( ct, message, log_level) LOG_PRINT_CC_RED( ct, "WORKER " << ct.m_worker_name << ": " << message, log_level)
# define LP_CC_WORKER_BLUE( ct, message, log_level) LOG_PRINT_CC_BLUE( ct, "WORKER " << ct.m_worker_name << ": " << message, log_level)
# define LP_CC_WORKER_YELLOW( ct, message, log_level) LOG_PRINT_CC_YELLOW( ct, "WORKER " << ct.m_worker_name << ": " << message, log_level)
# define LP_CC_WORKER_CYAN( ct, message, log_level) LOG_PRINT_CC_CYAN( ct, "WORKER " << ct.m_worker_name << ": " << message, log_level)
# define LP_CC_WORKER_MAGENTA( ct, message, log_level) LOG_PRINT_CC_MAGENTA( ct, "WORKER " << ct.m_worker_name << ": " << message, log_level)
# define HR_TO_STREAM_IN_MHS_1P(hr) std::fixed << std::setprecision(1) << hr / 1000000.0
# define HR_TO_STREAM_IN_MHS_3P(hr) std::fixed << std::setprecision(3) << hr / 1000000.0
// debug stuff
2019-03-30 01:25:43 +03:00
# define DBG_NETWORK_DIFFICULTY 160000000000ull // if non-zero: use this value as net difficulty when checking shares (useful for debugging on testnet, recommended value is 160000000000ull)
# define DBG_CORE_ALWAYS_SYNCRONIZED 1 // if set to 1: allows the server to start even if the core is not syncronized, useful for debugging with --offline-mode
2019-03-29 05:43:23 +03:00
# define STRINGIZE_DETAIL(x) #x
# define STRINGIZE(x) STRINGIZE_DETAIL(x)
# define DP(x) LOG_PRINT_L0("LINE " STRINGIZE(__LINE__) ": " #x " = " << x)
//==============================================================================================================================
struct vdiff_params_t
{
explicit vdiff_params_t ( )
: target_min ( 0 )
, target_max ( 0 )
, target_time_ms ( 0 )
, retarget_time_ms ( 0 )
, retarget_shares_count ( 0 )
, variance_percent ( 0 )
{ }
vdiff_params_t ( uint64_t target_min , uint64_t target_max , uint64_t target_time_s , uint64_t retarget_time_s , uint64_t retarget_shares_count , uint64_t variance_percent )
: target_min ( target_min )
, target_max ( target_max )
, target_time_ms ( target_time_s * 1000 )
, retarget_time_ms ( retarget_time_s * 1000 )
, retarget_shares_count ( retarget_shares_count )
, variance_percent ( variance_percent )
{ }
uint64_t target_min ;
uint64_t target_max ;
uint64_t target_time_ms ;
uint64_t retarget_time_ms ;
uint64_t retarget_shares_count ;
uint64_t variance_percent ;
} ;
struct stratum_connection_context : public epee : : net_utils : : connection_context_base
{
explicit stratum_connection_context ( )
: m_worker_name ( " ? " )
, m_worker_difficulty ( 1 )
, m_ts_started ( 0 )
, m_ts_share_period_timer ( 0 )
, m_ts_difficulty_updated ( 0 )
, m_valid_shares_count ( 0 )
, m_wrong_shares_count ( 0 )
, m_hashes_calculated ( 0 )
, m_blocks_count ( 0 )
{ }
void set_worker_name ( const std : : string & worker_name )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
m_worker_name = worker_name ;
}
uint64_t get_hr_estimate_duration ( )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
return ( epee : : misc_utils : : get_tick_count ( ) - m_ts_started ) / 1000 ;
}
uint64_t estimate_worker_hashrate ( )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
if ( m_ts_started = = 0 )
return 0 ;
uint64_t duration = ( epee : : misc_utils : : get_tick_count ( ) - m_ts_started ) / 1000 ;
if ( duration = = 0 )
return 0 ;
return ( m_hashes_calculated / duration ) . convert_to < uint64_t > ( ) ;
}
uint64_t get_average_share_period_ms ( )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
if ( m_ts_share_period_timer = = 0 )
return 0 ;
uint64_t duration_ms = ( epee : : misc_utils : : get_tick_count ( ) - m_ts_share_period_timer ) ;
if ( m_valid_shares_count = = 0 )
return 0 ;
return duration_ms / m_valid_shares_count ;
}
void reset_average_share_period_ms ( )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
m_ts_share_period_timer = epee : : misc_utils : : get_tick_count ( ) ;
m_valid_shares_count = 0 ;
}
void set_worker_difficulty ( const wide_difficulty_type & d )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
m_worker_difficulty = d ;
m_ts_difficulty_updated = epee : : misc_utils : : get_tick_count ( ) ;
}
void init_and_start_timers ( const vdiff_params_t & vd_params )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
m_ts_started = epee : : misc_utils : : get_tick_count ( ) ;
m_vd_params = vd_params ;
set_worker_difficulty ( m_vd_params . target_min ) ;
reset_average_share_period_ms ( ) ;
}
wide_difficulty_type get_worker_difficulty ( )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
return m_worker_difficulty ;
}
// returns true if worker difficulty has just been changed
bool adjust_worker_difficulty_if_needed ( )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
// check retarget condition if 1) there're too many shares received; 2) difficulty was updated long enough time ago
if ( m_valid_shares_count > = m_vd_params . retarget_shares_count | |
epee : : misc_utils : : get_tick_count ( ) - m_ts_difficulty_updated > m_vd_params . retarget_time_ms )
{
int64_t average_share_period_ms = 0 ;
wide_difficulty_type new_d = 0 ;
if ( m_valid_shares_count ! = 0 )
{
// there were shares, calculate using average share period
average_share_period_ms = static_cast < int64_t > ( get_average_share_period_ms ( ) ) ;
if ( average_share_period_ms ! = 0 & &
uint64_t ( llabs ( average_share_period_ms - int64_t ( m_vd_params . target_time_ms ) ) ) > m_vd_params . target_time_ms * m_vd_params . variance_percent / 100 )
{
new_d = m_worker_difficulty * m_vd_params . target_time_ms / average_share_period_ms ;
}
}
else
{
// no shares are found during retarget_time_ms, perhaps the difficulty is too high, lower it significantly
new_d = ( m_vd_params . target_min + m_worker_difficulty ) / 4 ;
}
if ( new_d ! = 0 )
{
if ( new_d < m_vd_params . target_min )
new_d = m_vd_params . target_min ;
if ( new_d > m_vd_params . target_max )
new_d = m_vd_params . target_max ;
if ( new_d ! = m_worker_difficulty )
{
LP_CC_WORKER_YELLOW ( ( * this ) , " difficulty update: " < < m_worker_difficulty < < " -> " < < new_d < <
" (av. share pediod was: " < < average_share_period_ms < < " , target: " < < m_vd_params . target_time_ms < <
" , shares: " < < m_valid_shares_count < < " , variance: " < < int64_t ( 100 * average_share_period_ms / m_vd_params . target_time_ms - 100 ) < < " %) " , LOG_LEVEL_2 ) ;
set_worker_difficulty ( new_d ) ;
reset_average_share_period_ms ( ) ;
return true ;
}
}
}
return false ;
}
void increment_normal_shares_count ( )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
+ + m_valid_shares_count ;
m_hashes_calculated + = m_worker_difficulty ;
}
void increment_stale_shares_count ( )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
+ + m_valid_shares_count ;
m_hashes_calculated + = m_worker_difficulty ;
}
void increment_wrong_shares_count ( )
{
//++m_wrong_shares_count; not implemented yet
}
void increment_blocks_count ( )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
+ + m_blocks_count ;
}
size_t get_blocks_count ( )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
return m_blocks_count ;
}
uint64_t get_current_valid_shares_count ( )
{
CRITICAL_REGION_LOCAL ( m_lock ) ;
return m_valid_shares_count ;
}
mutable epee : : critical_section m_lock ;
std : : string m_worker_name ;
wide_difficulty_type m_worker_difficulty ;
uint64_t m_ts_started ;
uint64_t m_ts_share_period_timer ;
uint64_t m_ts_difficulty_updated ;
size_t m_valid_shares_count ; // number of shares that satisfy worker's difficulty (valid + stale)
size_t m_wrong_shares_count ;
size_t m_blocks_count ;
wide_difficulty_type m_hashes_calculated ;
vdiff_params_t m_vd_params ;
} ; // struct stratum_connection_context
//==============================================================================================================================
template < typename connection_context_t >
class stratum_protocol_handler ;
template < typename connection_context_t >
struct stratum_protocol_handler_config : public i_blockchain_update_listener
{
typedef stratum_protocol_handler_config < connection_context_t > this_t ;
typedef stratum_protocol_handler < connection_context_t > protocol_handler_t ;
stratum_protocol_handler_config ( )
: m_max_packet_size ( 10240 )
, m_p_core ( nullptr )
, m_network_difficulty ( 0 )
, m_miner_addr ( null_pub_addr )
, m_block_template_ethash ( null_hash )
, m_blockchain_last_block_id ( null_hash )
, m_block_template_height ( 0 )
, m_block_template_update_ts ( 0 )
, m_last_ts_total_hr_was_printed ( epee : : misc_utils : : get_tick_count ( ) )
, m_total_hr_print_interval_ms ( STRATUM_TOTAL_HR_PRINT_INTERVAL_S_DEFAULT * 1000 )
, m_block_template_update_pediod_ms ( STRATUM_BLOCK_TEMPLATE_UPD_PERIOD_DEFAULT * 1000 )
, m_nameless_worker_id ( 0 )
, m_total_blocks_found ( 0 )
, m_stop_flag ( false )
, m_blocktemplate_update_thread ( & this_t : : block_template_update_thread , this )
{
LOG_PRINT_L4 ( " stratum_protocol_handler_config::ctor() " ) ;
}
~ stratum_protocol_handler_config ( )
{
LOG_PRINT_L4 ( " stratum_protocol_handler_config::dtor() " ) ;
m_stop_flag = true ;
m_blocktemplate_update_thread . join ( ) ;
if ( m_p_core )
m_p_core - > remove_blockchain_update_listener ( this ) ;
}
void add_protocol_handler ( protocol_handler_t * p_ph )
{
CRITICAL_REGION_BEGIN ( m_ph_map_lock ) ;
m_protocol_handlers [ p_ph - > get_context ( ) . m_connection_id ] = p_ph ;
CRITICAL_REGION_END ( ) ;
LOG_PRINT_CC ( p_ph - > get_context ( ) , " stratum_protocol_handler_config: protocol handler added " , LOG_LEVEL_4 ) ;
//m_pcommands_handler->on_connection_new(pconn->m_connection_context);
}
void remove_protocol_handler ( protocol_handler_t * p_ph )
{
CRITICAL_REGION_BEGIN ( m_ph_map_lock ) ;
m_protocol_handlers . erase ( p_ph - > get_context ( ) . m_connection_id ) ;
CRITICAL_REGION_END ( ) ;
LOG_PRINT_CC ( p_ph - > get_context ( ) , " stratum_protocol_handler_config: protocol handler removed " , LOG_LEVEL_4 ) ;
//m_pcommands_handler->on_connection_close(pconn->m_connection_context);
}
void test ( )
{
//protocol_handler_t* p_ph = m_protocol_handlers.begin()->second;
//std::string test = " \t { \n \"{ \\\\ \":\"}\", \"id\":1,\"jsonrpc\":\"2.0\",\"result\":[\"0x63de85850f7e02baba2dc0765ebfb01040e56354729be70358ff341b48c608ad\",\"0xa92afa474bb50595e467a1722ed7a74fc00b3307de5022d5b76cf979490f2222\",\"0x00000000dbe6fecebdedd5beb573440e5a884d1b2fbf06fcce912adcb8d8422e\"]}";
//std::string test = "{\"error\": [0], \"id\" : 555, \"result\" : \"AAA!\"}";
//std::string test = "{\"id\":10,\"method\":\"eth_submitWork\",\"params\":[\"0x899624c0078e824f\",\"0xb98617aec14a2872fb45ab755f6273a1ae719d0ff0d441a25bb8235d82cb4123\",\"0x30f599f39276df17656727f16c3230c072dd8f2dd780161625479d352e8b2a97\"]}";
//std::string test = "{\"id\":10,\"method\":\"eth_submitWork\",\"parasms\":[\"0x899624c0078e824f\"]}";
//std::string test = R"({"worker": "", "jsonrpc": "2.0", "params": [], "id": 3, "method": "eth_getWork"})";
//std::string test = R"({"worker": "eth1.0", "jsonrpc": "2.0", "params": ["HeyPnNRKwWzPF3JQMi1dcTJBRr3anA3iEMyY5vokAusYj73grFg8VhiYgWXJ4S31oT5ZV7rCjXn3QgZxQ9xr6DQJ1LThkj3", "x"], "id": 2, "method": "eth_submitLogin"})";
//p_ph->handle_recv(test.c_str(), test.size());
}
void block_template_update_thread ( )
{
log_space : : log_singletone : : set_thread_log_prefix ( " [ST] " ) ;
while ( ! m_stop_flag )
{
if ( is_core_syncronized ( ) & & epee : : misc_utils : : get_tick_count ( ) - m_block_template_update_ts > = m_block_template_update_pediod_ms )
{
update_block_template ( ) ;
}
print_total_hashrate_if_needed ( ) ;
epee : : misc_utils : : sleep_no_w ( 200 ) ;
}
}
bool update_block_template ( bool enforce_update = false )
{
CRITICAL_REGION_LOCAL ( m_work_change_lock ) ;
uint64_t stub ;
crypto : : hash top_block_id = null_hash ;
m_p_core - > get_blockchain_top ( stub , top_block_id ) ;
if ( ! enforce_update & & top_block_id = = m_blockchain_last_block_id & & epee : : misc_utils : : get_tick_count ( ) - m_block_template_update_ts < m_block_template_update_pediod_ms )
return false ; // no new blocks since last update, keep the same work
LOG_PRINT ( " stratum_protocol_handler_config::update_block_template( " < < ( enforce_update ? " true " : " false " ) < < " ) " , LOG_LEVEL_4 ) ;
m_block_template = AUTO_VAL_INIT ( m_block_template ) ;
wide_difficulty_type block_template_difficulty ;
blobdata extra = AUTO_VAL_INIT ( extra ) ;
bool r = m_p_core - > get_block_template ( m_block_template , m_miner_addr , m_miner_addr , block_template_difficulty , m_block_template_height , extra ) ;
CHECK_AND_ASSERT_MES ( r , false , " get_block_template failed " ) ;
# if DBG_NETWORK_DIFFICULTY == 0
m_network_difficulty = block_template_difficulty ;
# else
m_network_difficulty = DBG_NETWORK_DIFFICULTY ; // for debug purpose only
# endif
m_blockchain_last_block_id = top_block_id ;
m_block_template_hash_blob = get_block_hashing_blob ( m_block_template ) ;
if ( access_nonce_in_block_blob ( m_block_template_hash_blob ) ! = 0 )
{
LOG_PRINT_RED ( " non-zero nonce in generated block template " , LOG_LEVEL_0 ) ;
access_nonce_in_block_blob ( m_block_template_hash_blob ) = 0 ;
}
m_prev_block_template_ethash = m_block_template_ethash ;
m_block_template_ethash = crypto : : cn_fast_hash ( m_block_template_hash_blob . data ( ) , m_block_template_hash_blob . size ( ) ) ;
m_block_template_update_ts = epee : : misc_utils : : get_tick_count ( ) ;
set_work_for_all_workers ( ) ; // notify all workers of updated work
return true ;
}
void set_work_for_all_workers ( protocol_handler_t * ph_to_skip = nullptr )
{
LOG_PRINT ( " stratum_protocol_handler_config::set_work_for_all_workers() " , LOG_LEVEL_4 ) ;
CRITICAL_REGION_LOCAL ( m_ph_map_lock ) ;
for ( auto & ph : m_protocol_handlers )
{
if ( ph . second = = ph_to_skip )
continue ;
ph . second - > get_context ( ) . adjust_worker_difficulty_if_needed ( ) ; // some miners seem to not give a f*ck about updated job taget if the block hash wasn't changed, so change difficulty only on work update
std : : string new_work_json = get_work_json ( ph . second - > get_context ( ) . get_worker_difficulty ( ) ) ;
ph . second - > set_work ( new_work_json ) ;
ph . second - > send_notification ( new_work_json ) ;
}
}
std : : string get_work_json ( const wide_difficulty_type & worker_difficulty )
{
CRITICAL_REGION_LOCAL ( m_work_change_lock ) ;
if ( ! is_core_syncronized ( ) )
return R " ( " result " :[]) " ;
crypto : : hash target_boundary = null_hash ;
difficulty_to_boundary_long ( worker_difficulty , target_boundary ) ;
ethash_hash256 seed_hash = ethash_calculate_epoch_seed ( ethash_height_to_epoch ( m_block_template_height ) ) ;
return R " ( " result " :[ " ) " + pod_to_net_format(m_block_template_ethash) + R " ( " , " ) " + pod_to_net_format(seed_hash) + R " ( " , " ) " + pod_to_net_format_reverse(target_boundary) + R " ( " ]) " ;
}
void update_work ( protocol_handler_t * p_ph )
{
LOG_PRINT_CC ( p_ph - > get_context ( ) , " stratum_protocol_handler_config::update_work() " , LOG_LEVEL_4 ) ;
bool updated = false ;
if ( is_core_syncronized ( ) )
{
updated = update_block_template ( ) ;
}
else
{
LP_CC_WORKER_YELLOW ( p_ph - > get_context ( ) , " core is NOT synchronized, respond with empty job package " , LOG_LEVEL_2 ) ;
}
if ( ! updated )
p_ph - > set_work ( get_work_json ( p_ph - > get_context ( ) . get_worker_difficulty ( ) ) ) ;
}
bool handle_work ( protocol_handler_t * p_ph , const jsonrpc_id_t & id , const std : : string & worker , uint64_t nonce , const crypto : : hash & block_ethash )
{
CRITICAL_REGION_LOCAL ( m_work_change_lock ) ;
bool r = false ;
if ( ! is_core_syncronized ( ) )
{
// TODO: make an option for more aggressive mining in case there's a little difference in blockchain size (1..2)
LP_CC_WORKER_BLUE ( p_ph - > get_context ( ) , " Work received, but the core is NOT syncronized. Skip... " , LOG_LEVEL_1 ) ;
p_ph - > send_response_default ( id ) ;
return true ;
}
const uint64_t height = get_block_height ( m_block_template ) ;
// make sure worker sent work with correct block ethash
if ( block_ethash ! = m_block_template_ethash )
{
if ( block_ethash = = m_prev_block_template_ethash )
{
// Got stale share, do nothing. In future it can be used for more aggressive mining strategies
LP_CC_WORKER_BLUE ( p_ph - > get_context ( ) , " got stale share, skip it " , LOG_LEVEL_1 ) ;
p_ph - > send_response_default ( id ) ;
p_ph - > get_context ( ) . increment_stale_shares_count ( ) ;
return true ;
}
LP_CC_WORKER_RED ( p_ph - > get_context ( ) , " wrong work submitted, ethhash " < < block_ethash < < " , expected: " < < m_block_template_ethash , LOG_LEVEL_0 ) ;
p_ph - > send_response_error ( id , JSONRPC_ERROR_CODE_DEFAULT , " wrong work " ) ;
p_ph - > get_context ( ) . increment_wrong_shares_count ( ) ;
return false ;
}
crypto : : hash block_pow_hash = get_block_longhash ( height , m_block_template_ethash , nonce ) ;
wide_difficulty_type worker_difficulty = p_ph - > get_context ( ) . get_worker_difficulty ( ) ;
if ( ! check_hash ( block_pow_hash , worker_difficulty ) )
{
LP_CC_WORKER_RED ( p_ph - > get_context ( ) , " block pow hash " < < block_pow_hash < < " doesn't meet worker difficulty: " < < worker_difficulty < < ENDL < <
" nonce: " < < nonce < < " (0x " < < epee : : string_tools : : pod_to_hex ( nonce ) < < " ) " , LOG_LEVEL_0 ) ;
p_ph - > send_response_error ( id , JSONRPC_ERROR_CODE_DEFAULT , " not enough work was done " ) ;
p_ph - > get_context ( ) . increment_wrong_shares_count ( ) ;
return false ;
}
p_ph - > send_response_default ( id ) ;
p_ph - > get_context ( ) . increment_normal_shares_count ( ) ;
m_shares_per_minute . chick ( ) ;
if ( ! check_hash ( block_pow_hash , m_network_difficulty ) )
{
// work is enough for worker difficulty, but not enough for network difficulty -- it's okay, move on!
LP_CC_WORKER_GREEN ( p_ph - > get_context ( ) , " share found for difficulty " < < worker_difficulty < < " , nonce: 0x " < < epee : : string_tools : : pod_to_hex ( nonce ) , LOG_LEVEL_1 ) ;
LP_CC_WORKER_GREEN ( p_ph - > get_context ( ) , " shares: " < < p_ph - > get_context ( ) . get_current_valid_shares_count ( ) < < " , share period av: " < < p_ph - > get_context ( ) . get_average_share_period_ms ( ) < < " , target: " < < p_ph - > get_context ( ) . m_vd_params . target_time_ms
< < " , variance: " < < int64_t ( 100 * p_ph - > get_context ( ) . get_average_share_period_ms ( ) / p_ph - > get_context ( ) . m_vd_params . target_time_ms - 100 ) < < " % " , LOG_LEVEL_3 ) ;
return true ;
}
// seems we've just found a block!
// create a block template and push it to the core
m_block_template . nonce = nonce ;
crypto : : hash block_hash = get_block_hash ( m_block_template ) ;
LP_CC_WORKER_GREEN ( p_ph - > get_context ( ) , " block found " < < block_hash < < " at height " < < height < < " for difficulty " < < m_network_difficulty < < " pow: " < < block_pow_hash < < ENDL < <
" nonce: " < < nonce < < " (0x " < < epee : : string_tools : : pod_to_hex ( nonce ) < < " ) " , LOG_LEVEL_1 ) ;
block_verification_context bvc = AUTO_VAL_INIT ( bvc ) ;
r = m_p_core - > handle_incoming_block ( m_block_template , bvc , false ) ;
if ( r )
{
if ( ! bvc . m_verification_failed & & ! bvc . added_to_altchain & & bvc . m_added_to_main_chain & & ! bvc . m_already_exists & & ! bvc . m_marked_as_orphaned )
{
LP_CC_WORKER_GREEN ( p_ph - > get_context ( ) , " found block " < < block_hash < < " at height " < < height < < " was successfully added to the blockchain, difficulty " < < m_network_difficulty , LOG_LEVEL_0 ) ;
r = update_block_template ( ) ;
CHECK_AND_ASSERT_MES_NO_RET ( r , " Stratum: internal error. Block template wasn't updated as expected after handling found block. " ) ;
p_ph - > get_context ( ) . increment_blocks_count ( ) ;
+ + m_total_blocks_found ;
}
else
{
LP_CC_WORKER_RED ( p_ph - > get_context ( ) , " block " < < block_hash < < " at height " < < height < < " was NOT added to the blockchain: " < < ENDL < <
" verification_failed: " < < bvc . m_verification_failed < < ENDL < <
" added_to_altchain: " < < bvc . added_to_altchain < < ENDL < <
" added_to_main_chain: " < < bvc . m_added_to_main_chain < < ENDL < <
" already_exists: " < < bvc . m_already_exists < < ENDL < <
" marked_as_orphaned: " < < bvc . m_marked_as_orphaned , LOG_LEVEL_0 ) ;
}
}
else
{
LP_CC_WORKER_RED ( p_ph - > get_context ( ) , " block " < < block_hash < < " was rejected by the core " , LOG_LEVEL_0 ) ;
}
return true ;
}
bool handle_login ( protocol_handler_t * p_ph , const jsonrpc_id_t & id , const std : : string & user_str , const std : : string & pass_str , const std : : string & worker_str , uint64_t start_difficulty )
{
CRITICAL_REGION_LOCAL ( m_work_change_lock ) ;
bool r = false , error = false ;
std : : stringstream error_str ;
if ( user_str = = WORKER_ALLOWED_USERNAME )
{
// it's a valid login only in case miner address was already set
if ( m_miner_addr = = null_pub_addr )
{
error = true ;
error_str < < " trying to log in with ' " WORKER_ALLOWED_USERNAME " ' username, while mining address WAS NOT previously set in daemon. Set mining address in daemon OR use it as a username. " ;
}
}
else
{
// mining address is used as username, make sure it's correct and match with previously set
account_public_address address = null_pub_addr ;
r = get_account_address_from_str ( address , user_str ) ;
if ( ! r )
{
error = true ;
error_str < < " can't parse wallet address from username: " < < user_str < < " . " ;
}
if ( m_miner_addr ! = null_pub_addr & & address ! = m_miner_addr )
{
error = true ;
error_str < < " wallet address " < < user_str < < " doesn't match the address previously set in daemon and/or other workers. " ;
}
set_miner_address ( address ) ;
}
if ( error )
{
LP_CC_WORKER_RED ( p_ph - > get_context ( ) , error_str . str ( ) < < " Connection will be dropped. " , LOG_LEVEL_0 ) ;
p_ph - > send_response_error ( id , JSONRPC_ERROR_CODE_DEFAULT , error_str . str ( ) ) ;
return false ;
}
p_ph - > get_context ( ) . set_worker_name ( worker_str ) ;
if ( start_difficulty ! = 0 )
p_ph - > get_context ( ) . set_worker_difficulty ( start_difficulty ) ;
LP_CC_WORKER_GREEN ( p_ph - > get_context ( ) , " logged in with username " < < user_str < < " , start difficulty: " < < ( start_difficulty ! = 0 ? std : : to_string ( start_difficulty ) : " default " ) , LOG_LEVEL_0 ) ;
p_ph - > send_response_default ( id ) ;
// send initial work
update_work ( p_ph ) ;
return true ;
}
bool handle_submit_hashrate ( protocol_handler_t * p_ph , uint64_t rate , const crypto : : hash & rate_submit_id )
{
LP_CC_WORKER_CYAN ( p_ph - > get_context ( ) , " reported hashrate: " < < HR_TO_STREAM_IN_MHS_3P ( rate ) < < " Mh/s " < <
" , estimated hashrate: " < < HR_TO_STREAM_IN_MHS_3P ( p_ph - > get_context ( ) . estimate_worker_hashrate ( ) ) < < " Mh/s, run time: " < <
epee : : misc_utils : : get_time_interval_string ( p_ph - > get_context ( ) . get_hr_estimate_duration ( ) ) , LOG_LEVEL_3 ) ;
return true ;
}
// required member for epee::net_utils::boosted_tcp_server concept
void on_send_stop_signal ( )
{
LOG_PRINT_L4 ( " stratum_protocol_handler_config::on_send_stop_signal() " ) ;
CRITICAL_REGION_LOCAL ( m_ph_map_lock ) ;
m_protocol_handlers . clear ( ) ;
}
// i_blockchain_update_listener member
virtual void on_blockchain_update ( ) override
{
LOG_PRINT_L3 ( " stratum_protocol_handler_config::on_blockchain_update() " ) ;
if ( ! is_core_syncronized ( ) )
return ; // don't notify workers when blockchain is synchronizing
update_block_template ( ) ;
}
void set_core ( currency : : core * c )
{
m_p_core = c ;
m_p_core - > add_blockchain_update_listener ( this ) ;
}
void set_miner_address ( const account_public_address & miner_addr )
{
m_miner_addr = miner_addr ;
}
bool is_core_syncronized ( )
{
if ( ! m_p_core )
return false ;
# if DBG_CORE_ALWAYS_SYNCRONIZED == 1
return true ; // standalone mode, usefull for debugging WITH --offline-mode option
# endif
// TODO!!! Bad design, need more correct way of getting this information
currency : : i_currency_protocol * proto = m_p_core - > get_protocol ( ) ;
currency : : t_currency_protocol_handler < currency : : core > * protocol = dynamic_cast < currency : : t_currency_protocol_handler < currency : : core > * > ( proto ) ;
if ( ! protocol )
return false ;
return protocol - > is_synchronized ( ) ;
}
void set_vdiff_params ( const vdiff_params_t & p )
{
m_vdiff_params = p ;
}
const vdiff_params_t & get_vdiff_params ( ) const
{
return m_vdiff_params ;
}
void print_total_hashrate_if_needed ( )
{
if ( epee : : misc_utils : : get_tick_count ( ) - m_last_ts_total_hr_was_printed < m_total_hr_print_interval_ms )
return ;
if ( ! is_core_syncronized ( ) )
{
LOG_PRINT_L0 ( " Blockchain is synchronizing... " ) ;
}
CRITICAL_REGION_LOCAL ( m_ph_map_lock ) ;
if ( m_protocol_handlers . empty ( ) )
{
LOG_PRINT_CYAN ( " Blocks found: [ " < < m_total_blocks_found < < " ], no miners connected " , LOG_LEVEL_0 ) ;
}
else
{
std : : stringstream ss ;
uint64_t total_reported_hr = 0 , total_estimated_hr = 0 ;
for ( auto & ph : m_protocol_handlers )
{
uint64_t reported_hr = 0 , estimated_hr = 0 ;
ph . second - > get_hashrate ( reported_hr , estimated_hr ) ;
total_reported_hr + = reported_hr ;
total_estimated_hr + = estimated_hr ;
ss < < ph . second - > get_context ( ) . m_worker_name < < " : [ " < < ph . second - > get_context ( ) . get_blocks_count ( ) < < " ] " < < HR_TO_STREAM_IN_MHS_1P ( reported_hr ) < < " ( " < < HR_TO_STREAM_IN_MHS_1P ( estimated_hr ) < < " ), " ;
}
auto s = ss . str ( ) ;
LOG_PRINT_CYAN ( " Blocks found: [ " < < m_total_blocks_found < < " ], total speed: " < < HR_TO_STREAM_IN_MHS_3P ( total_reported_hr ) < < " Mh/s as reported by miners ( " < < HR_TO_STREAM_IN_MHS_3P ( total_estimated_hr ) < < " Mh/s estimated by the server), current shares/min: " < < m_shares_per_minute . get_speed ( ) < < ENDL < <
m_protocol_handlers . size ( ) < < " worker(s): " < < s . substr ( 0 , s . length ( ) > 2 ? s . length ( ) - 2 : 0 ) , LOG_LEVEL_0 ) ;
}
m_last_ts_total_hr_was_printed = epee : : misc_utils : : get_tick_count ( ) ;
}
void set_total_hr_print_interval_s ( uint64_t s )
{
m_total_hr_print_interval_ms = s * 1000 ;
}
void set_block_template_update_period ( uint64_t s )
{
m_block_template_update_pediod_ms = s * 1000 ;
}
size_t get_number_id_for_nameless_worker ( )
{
CRITICAL_REGION_LOCAL ( m_generic_lock ) ;
return m_nameless_worker_id + + ;
}
size_t m_max_packet_size ;
private :
typedef std : : unordered_map < boost : : uuids : : uuid , protocol_handler_t * , boost : : hash < boost : : uuids : : uuid > > protocol_handlers_map ;
typedef epee : : math_helper : : speed < 60 * 1000 /* ms */ > shares_per_minute_rate_t ;
protocol_handlers_map m_protocol_handlers ;
mutable epee : : critical_section m_ph_map_lock ;
mutable epee : : critical_section m_work_change_lock ;
mutable epee : : critical_section m_generic_lock ;
// job data
block m_block_template ;
std : : string m_block_template_hash_blob ;
crypto : : hash m_block_template_ethash ;
crypto : : hash m_blockchain_last_block_id ;
uint64_t m_block_template_height ;
std : : atomic < uint64_t > m_block_template_update_ts ;
// previous job (for handling stale shares)
crypto : : hash m_prev_block_template_ethash ;
vdiff_params_t m_vdiff_params ;
wide_difficulty_type m_network_difficulty ;
core * m_p_core ;
account_public_address m_miner_addr ; // all workers will share the same miner address
std : : atomic < uint64_t > m_last_ts_total_hr_was_printed ;
uint64_t m_total_hr_print_interval_ms ;
uint64_t m_block_template_update_pediod_ms ;
size_t m_nameless_worker_id ;
size_t m_total_blocks_found ;
shares_per_minute_rate_t m_shares_per_minute ;
std : : atomic < bool > m_stop_flag ;
std : : thread m_blocktemplate_update_thread ;
} ; // struct stratum_protocol_handler_config
//==============================================================================================================================
template < typename connection_context_t >
class stratum_protocol_handler
{
public :
typedef stratum_protocol_handler < connection_context_t > this_t ;
typedef epee : : net_utils : : connection < this_t > connection_t ;
typedef connection_context_t connection_context ; // required type for epee::net_utils::boosted_tcp_server concept
typedef stratum_protocol_handler_config < connection_context_t > config_type ; // required type for epee::net_utils::boosted_tcp_server concept
// required member for epee::net_utils::boosted_tcp_server concept
stratum_protocol_handler ( connection_t * p_connection , config_type & config , connection_context_t & context )
: m_p_connection ( p_connection )
, m_config ( config )
, m_context ( context )
, m_connection_initialized ( false )
, m_last_reported_hashrate ( 0 )
{
LOG_PRINT_CC ( m_context , " stratum_protocol_handler::ctor() " , LOG_LEVEL_4 ) ;
}
~ stratum_protocol_handler ( )
{
if ( m_connection_initialized )
{
m_config . remove_protocol_handler ( this ) ;
m_connection_initialized = false ;
}
LOG_PRINT_CC ( m_context , " stratum_protocol_handler::dtor() " , LOG_LEVEL_4 ) ;
}
// required member for epee::net_utils::boosted_tcp_server concept
bool handle_recv ( const void * p_data , size_t data_size )
{
const char * str = static_cast < const char * > ( p_data ) ;
if ( ! m_connection_initialized )
return false ;
if ( data_size > m_config . m_max_packet_size )
{
LP_CC_WORKER_RED ( m_context , " Maximum packet size exceeded! maximum: " < < m_config . m_max_packet_size < < " , received: " < < data_size < < " . Connection will be closed. " , LOG_LEVEL_0 ) ;
return false ;
}
m_json_helper . feed ( str , data_size ) ;
LP_CC_WORKER ( m_context , " data received: " < < data_size < < " bytes: " < < ENDL < < std : : string ( str , data_size ) , LOG_LEVEL_4 ) ;
if ( m_json_helper . has_objects ( ) )
{
std : : string json ;
while ( m_json_helper . pop_object ( json ) )
{
epee : : serialization : : portable_storage ps ;
if ( ps . load_from_json ( json ) )
{
if ( ! handle_json_request ( ps , json ) )
{
LP_CC_WORKER_RED ( m_context , " JSON request handling failed. JSON: " < < ENDL < < json < < ENDL , LOG_LEVEL_1 ) ;
}
}
else
{
LP_CC_WORKER_RED ( m_context , " JSON object detected, but can't be parsed. JSON: " < < ENDL < < json < < ENDL , LOG_LEVEL_1 ) ;
}
}
}
return true ;
}
bool handle_json_request ( epee : : serialization : : portable_storage & ps , const std : : string & json )
{
std : : stringstream error_stream ;
jsonrpc_id_t id = jsonrpc_id_null ;
read_jsonrpc_id ( ps , id ) ;
std : : string method ;
if ( ps_get_value_noexcept ( ps , " method " , method , nullptr ) )
{
epee : : serialization : : portable_storage : : hsection params_section = ps . open_section ( " params " , nullptr ) ;
auto handler_it = m_methods_handlers . find ( method ) ;
if ( handler_it = = m_methods_handlers . end ( ) )
{
error_stream < < " unknown method is requested: " < < method < < ENDL < < " JSON request: " < < json ;
LP_CC_WORKER_RED ( m_context , error_stream . str ( ) , LOG_LEVEL_1 ) ;
send_response_error ( id , JSONRPC_ERROR_CODE_METHOD_NOT_FOUND , error_stream . str ( ) ) ;
return false ;
}
return ( this - > * ( handler_it - > second ) ) ( id , ps , params_section ) ;
}
// if it's no a method call -- it should be result
std : : string result ;
if ( ps_get_value_noexcept ( ps , " result " , result , nullptr ) )
{
std : : string error ;
ps_get_value_noexcept ( ps , " error " , error , nullptr ) ;
epee : : serialization : : portable_storage : : hsection error_section = ps . open_section ( " error " , nullptr ) ;
if ( error_section ! = nullptr | | ! error . empty ( ) )
{
LP_CC_WORKER_RED ( m_context , " received an error: " < < json , LOG_LEVEL_1 ) ;
return true ; // means it is handled ok
}
}
LP_CC_WORKER_YELLOW ( m_context , " Unrecongized request received: " < < json , LOG_LEVEL_2 ) ;
return true ; // means it handled ok
}
static void init ( )
{
if ( m_methods_handlers . empty ( ) )
{
m_methods_handlers . insert ( std : : make_pair ( " eth_submitLogin " , & this_t : : handle_method_eth_submitLogin ) ) ;
m_methods_handlers . insert ( std : : make_pair ( " eth_getWork " , & this_t : : handle_method_eth_getWork ) ) ;
m_methods_handlers . insert ( std : : make_pair ( " eth_submitHashrate " , & this_t : : handle_method_eth_submitHashrate ) ) ;
m_methods_handlers . insert ( std : : make_pair ( " eth_submitWork " , & this_t : : handle_method_eth_submitWork ) ) ;
}
}
bool handle_method_eth_submitLogin ( const jsonrpc_id_t & id , epee : : serialization : : portable_storage & ps , epee : : serialization : : portable_storage : : hsection params_section )
{
std : : string user_str , pass_str ;
epee : : serialization : : harray params_array = ps . get_first_value ( " params " , user_str , nullptr ) ;
if ( params_array ! = nullptr )
ps . get_next_value ( params_array , pass_str ) ;
std : : string worker_str ;
ps_get_value_noexcept ( ps , " worker " , worker_str , nullptr ) ;
if ( worker_str . empty ( ) )
worker_str = std : : to_string ( m_config . get_number_id_for_nameless_worker ( ) ) ;
uint64_t start_difficulty = 0 ; // default
size_t start_diff_delim_pos = user_str . find ( ' - ' ) ;
if ( start_diff_delim_pos ! = std : : string : : npos )
{
// extract start difficulty from username presuming it complies format "username.difficulty"
std : : string start_difficulty_str = user_str . substr ( start_diff_delim_pos + 1 ) ;
TRY_ENTRY ( )
start_difficulty = std : : stoull ( start_difficulty_str ) ;
CATCH_ENTRY_CUSTOM ( " submitLogin " , { LOG_PRINT_L0 ( worker_str < < " : Can't parse start difficulty from " < < start_difficulty_str ) ; } , false ) ;
user_str = user_str . substr ( 0 , start_diff_delim_pos ) ;
}
LOG_PRINT_CC ( m_context , " Stratum [submitLogin] USER: " < < user_str < < " , pass: " < < pass_str < < " , worker: " < < worker_str < < " , start diff.: " < < ( start_difficulty = = 0 ? std : : string ( " default " ) : std : : to_string ( start_difficulty ) ) , LOG_LEVEL_3 ) ;
return m_config . handle_login ( this , id , user_str , pass_str , worker_str , start_difficulty ) ;
}
bool handle_method_eth_getWork ( const jsonrpc_id_t & id , epee : : serialization : : portable_storage & ps , epee : : serialization : : portable_storage : : hsection params_section )
{
m_config . update_work ( this ) ;
CRITICAL_REGION_LOCAL ( m_work_change_lock ) ;
if ( ! m_cached_work_json . empty ( ) )
send_response ( id , m_cached_work_json ) ;
return true ;
}
bool handle_method_eth_submitHashrate ( const jsonrpc_id_t & id , epee : : serialization : : portable_storage & ps , epee : : serialization : : portable_storage : : hsection params_section )
{
std : : string rate_str , rate_submit_id_str ;
epee : : serialization : : harray params_array = ps . get_first_value ( " params " , rate_str , nullptr ) ;
bool r = params_array ! = nullptr & & ps . get_next_value ( params_array , rate_submit_id_str ) ;
CHECK_AND_ASSERT_MES ( r , false , " Incorrect parameters " ) ;
uint64_t rate = 0 ;
CHECK_AND_ASSERT_MES ( pod_from_net_format_reverse ( rate_str , rate , true ) , false , " Can't parse rate from " < < rate_str ) ;
crypto : : hash rate_submit_id = null_hash ;
CHECK_AND_ASSERT_MES ( pod_from_net_format ( rate_submit_id_str , rate_submit_id ) , false , " Can't parse rate_submit_id from " < < rate_submit_id_str ) ;
m_last_reported_hashrate = rate ;
return m_config . handle_submit_hashrate ( this , rate , rate_submit_id ) ;
}
bool handle_method_eth_submitWork ( const jsonrpc_id_t & id , epee : : serialization : : portable_storage & ps , epee : : serialization : : portable_storage : : hsection params_section )
{
bool r = true ;
std : : string nonce_str , header_str , mixhash_str ;
epee : : serialization : : harray params_array = ps . get_first_value ( " params " , nonce_str , nullptr ) ;
r = params_array ! = nullptr & & ps . get_next_value ( params_array , header_str ) ;
r = params_array ! = nullptr & & ps . get_next_value ( params_array , mixhash_str ) ;
CHECK_AND_ASSERT_MES ( r , false , " Incorrect parameters " ) ;
std : : string worker ;
ps_get_value_noexcept ( ps , " worker " , worker , nullptr ) ;
uint64_t nonce = 0 ;
CHECK_AND_ASSERT_MES ( pod_from_net_format_reverse ( nonce_str , nonce , true ) , false , " Can't parse nonce from " < < nonce_str ) ;
crypto : : hash header_hash = null_hash ;
CHECK_AND_ASSERT_MES ( pod_from_net_format ( header_str , header_hash ) , false , " Can't parse header hash from " < < header_str ) ;
return m_config . handle_work ( this , id , worker , nonce , header_hash ) ;
}
void send ( const std : : string & data )
{
static_cast < epee : : net_utils : : i_service_endpoint * > ( m_p_connection ) - > do_send ( data . c_str ( ) , data . size ( ) ) ;
LOG_PRINT_CC ( m_context , " DATA sent >>>>>>>>>>>>> " < < ENDL < < data , LOG_LEVEL_4 ) ;
}
void send_notification ( const std : : string & json )
{
// JSON-RPC 2.0 spec: "A Notification is a Request object without an "id" member."
send ( R " ({ " jsonrpc " : " 2.0 " ,) " + json + " } " " \n " ) ; // LF character is not specified by JSON-RPC standard, but it is REQUIRED by ethminer 0.12 to work
}
void send_response_method ( const jsonrpc_id_t & id , const std : : string & method , const std : : string & response )
{
send_response ( id , std : : string ( R " ( " method " : " ) " ) + method + R " ( " ,) " + response ) ;
}
void send_response ( const jsonrpc_id_t & id , const std : : string & response )
{
send ( R " ({ " jsonrpc " : " 2.0 " , " id " :) " + jsonrpc_id_to_value_str ( id ) + " , " + response + " } " " \n " ) ; // LF character is not specified by JSON-RPC standard, but it is REQUIRED by ethminer 0.12 to work
}
void send_response_default ( const jsonrpc_id_t & id )
{
send_response ( id , R " ( " result " :true) " ) ;
}
void send_response_error ( const jsonrpc_id_t & id , int64_t error_code , const std : : string & error_message )
{
send_response ( id , R " ( " error " :{ " code " :) " + std : : to_string ( error_code ) + R " (, " message " : " ) " + error_message + R " ( " }) " ) ;
}
void set_work ( const std : : string & json )
{
CRITICAL_REGION_LOCAL ( m_work_change_lock ) ;
if ( m_cached_work_json ! = json )
{
LP_CC_WORKER ( m_context , " work updated: " < < json , LOG_LEVEL_2 ) ;
}
m_cached_work_json = json ;
}
// required member for epee::net_utils::boosted_tcp_server concept
void handle_qued_callback ( )
{
}
// required member for epee::net_utils::boosted_tcp_server concept
bool after_init_connection ( )
{
LOG_PRINT_CC ( m_context , " stratum_protocol_handler::after_init_connection() " , LOG_LEVEL_4 ) ;
if ( ! m_connection_initialized )
{
m_connection_initialized = true ;
m_config . add_protocol_handler ( this ) ;
m_context . init_and_start_timers ( m_config . get_vdiff_params ( ) ) ;
}
LP_CC_WORKER_CYAN ( m_context , " connected " , LOG_LEVEL_1 ) ;
return true ;
}
// required member for epee::net_utils::boosted_tcp_server concept
bool release_protocol ( )
{
LOG_PRINT_CC ( m_context , " stratum_protocol_handler::release_protocol() " , LOG_LEVEL_4 ) ;
LP_CC_WORKER ( m_context , " disconnected " , LOG_LEVEL_0 ) ;
return true ;
}
connection_context_t & get_context ( ) { return m_context ; }
const connection_context_t & get_context ( ) const { return m_context ; }
void get_hashrate ( uint64_t & last_reported_hr , uint64_t & estimated_hr )
{
last_reported_hr = m_last_reported_hashrate ;
estimated_hr = m_context . estimate_worker_hashrate ( ) ;
}
private :
connection_t * m_p_connection ; // m_p_connection owns this protocol handler as data member, so back link is a simple pointer
config_type & m_config ;
connection_context_t & m_context ;
json_helper m_json_helper ;
std : : string m_cached_work_json ;
epee : : critical_section m_work_change_lock ;
uint64_t m_last_reported_hashrate ;
typedef bool ( this_t : : * method_handler_func_t ) ( const jsonrpc_id_t & id , epee : : serialization : : portable_storage & ps , epee : : serialization : : portable_storage : : hsection params_section ) ;
static std : : unordered_map < std : : string , method_handler_func_t > m_methods_handlers ;
std : : atomic < bool > m_connection_initialized ;
} ; // class stratum_protocol_handler
//==============================================================================================================================
typedef stratum_protocol_handler < stratum_connection_context > protocol_handler_t ;
typedef epee : : net_utils : : boosted_tcp_server < protocol_handler_t > tcp_server_t ;
// static memeber definition
template < typename connection_context_t >
std : : unordered_map < std : : string , typename stratum_protocol_handler < connection_context_t > : : method_handler_func_t > stratum_protocol_handler < connection_context_t > : : m_methods_handlers ;
} // anonumous namespace
//------------------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------------------
struct stratum_server_impl
{
tcp_server_t server ;
} ;
//------------------------------------------------------------------------------------------------------------------------------
stratum_server : : stratum_server ( core * c )
: m_p_core ( c )
{
m_impl = new stratum_server_impl ( ) ;
}
//------------------------------------------------------------------------------------------------------------------------------
stratum_server : : ~ stratum_server ( )
{
delete m_impl ;
m_impl = nullptr ;
}
//------------------------------------------------------------------------------------------------------------------------------
void stratum_server : : init_options ( boost : : program_options : : options_description & desc )
{
stratum_protocol_handler < stratum_connection_context > : : init ( ) ;
command_line : : add_arg ( desc , arg_stratum ) ;
command_line : : add_arg ( desc , arg_stratum_bind_ip ) ;
command_line : : add_arg ( desc , arg_stratum_bind_port ) ;
command_line : : add_arg ( desc , arg_stratum_threads ) ;
command_line : : add_arg ( desc , arg_stratum_miner_address ) ;
command_line : : add_arg ( desc , arg_stratum_vdiff_target_min ) ;
command_line : : add_arg ( desc , arg_stratum_vdiff_target_max ) ;
command_line : : add_arg ( desc , arg_stratum_vdiff_target_time ) ;
command_line : : add_arg ( desc , arg_stratum_vdiff_retarget_time ) ;
command_line : : add_arg ( desc , arg_stratum_vdiff_retarget_shares ) ;
command_line : : add_arg ( desc , arg_stratum_vdiff_variance_percent ) ;
command_line : : add_arg ( desc , arg_stratum_block_template_update_period ) ;
command_line : : add_arg ( desc , arg_stratum_hr_print_interval ) ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool stratum_server : : should_start ( const boost : : program_options : : variables_map & vm )
{
return command_line : : get_arg ( vm , arg_stratum ) ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool stratum_server : : init ( const boost : : program_options : : variables_map & vm )
{
bool r = false ;
m_impl - > server . set_threads_prefix ( " ST " ) ;
std : : string bind_ip_str = command_line : : get_arg ( vm , arg_stratum_bind_ip ) ;
std : : string bind_port_str = command_line : : get_arg ( vm , arg_stratum_bind_port ) ;
m_threads_count = command_line : : get_arg ( vm , arg_stratum_threads ) ;
auto & config = m_impl - > server . get_config_object ( ) ;
config . set_core ( m_p_core ) ;
if ( command_line : : has_arg ( vm , arg_stratum_miner_address ) )
{
std : : string miner_address_str = command_line : : get_arg ( vm , arg_stratum_miner_address ) ;
account_public_address miner_address = null_pub_addr ;
r = get_account_address_from_str ( miner_address , miner_address_str ) ;
CHECK_AND_ASSERT_MES ( r , false , " Stratum server: invalid miner address given: " < < miner_address_str ) ;
config . set_miner_address ( miner_address ) ;
}
config . set_vdiff_params (
vdiff_params_t (
command_line : : get_arg ( vm , arg_stratum_vdiff_target_min ) ,
command_line : : get_arg ( vm , arg_stratum_vdiff_target_max ) ,
command_line : : get_arg ( vm , arg_stratum_vdiff_target_time ) ,
command_line : : get_arg ( vm , arg_stratum_vdiff_retarget_time ) ,
command_line : : get_arg ( vm , arg_stratum_vdiff_retarget_shares ) ,
command_line : : get_arg ( vm , arg_stratum_vdiff_variance_percent )
)
) ;
config . set_block_template_update_period ( command_line : : get_arg ( vm , arg_stratum_block_template_update_period ) ) ;
config . set_total_hr_print_interval_s ( command_line : : get_arg ( vm , arg_stratum_hr_print_interval ) ) ;
LOG_PRINT_L0 ( " Stratum server: start listening at " < < bind_ip_str < < " : " < < bind_port_str < < " ... " ) ;
r = m_impl - > server . init_server ( bind_port_str , bind_ip_str ) ;
CHECK_AND_ASSERT_MES ( r , false , " Stratum server: initialization failure " ) ;
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool stratum_server : : run ( bool wait /* = true */ )
{
//m_impl->server.get_config_object().test();
LOG_PRINT ( " Stratum server: start net server with " < < m_threads_count < < " threads... " , LOG_LEVEL_0 ) ;
if ( ! m_impl - > server . run_server ( m_threads_count , wait ) )
{
LOG_ERROR ( " Stratum server: net server failure " ) ;
return false ;
}
if ( wait )
LOG_PRINT ( " Stratum server: net server stopped " , LOG_LEVEL_0 ) ;
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool stratum_server : : deinit ( )
{
return m_impl - > server . deinit_server ( ) ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool stratum_server : : timed_wait_server_stop ( uint64_t ms )
{
return m_impl - > server . timed_wait_server_stop ( ms ) ;
}
//------------------------------------------------------------------------------------------------------------------------------
bool stratum_server : : send_stop_signal ( )
{
m_impl - > server . send_stop_signal ( ) ;
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
} // namespace currency