2014-03-03 22:07:58 +00:00
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include <sstream>
# include <numeric>
# include <boost/utility/value_init.hpp>
# include <boost/interprocess/detail/atomic.hpp>
# include <boost/limits.hpp>
# include <boost/foreach.hpp>
# include "misc_language.h"
# include "include_base_utils.h"
# include "cryptonote_basic_impl.h"
# include "cryptonote_format_utils.h"
# include "file_io_utils.h"
# include "common/command_line.h"
# include "string_coding.h"
# include "storages/portable_storage_template_helper.h"
using namespace epee ;
# include "miner.h"
namespace cryptonote
{
namespace
{
const command_line : : arg_descriptor < std : : string > arg_extra_messages = { " extra-messages-file " , " Specify file for extra messages to include into coinbase transactions " , " " , true } ;
const command_line : : arg_descriptor < std : : string > arg_start_mining = { " start-mining " , " Specify wallet address to mining for " , " " , true } ;
const command_line : : arg_descriptor < uint32_t > arg_mining_threads = { " mining-threads " , " Specify mining threads count " , 0 , true } ;
}
miner : : miner ( i_miner_handler * phandler ) : m_stop ( 1 ) ,
m_template ( boost : : value_initialized < block > ( ) ) ,
m_template_no ( 0 ) ,
m_diffic ( 0 ) ,
m_thread_index ( 0 ) ,
m_phandler ( phandler ) ,
m_height ( 0 ) ,
m_pausers_count ( 0 ) ,
m_threads_total ( 0 ) ,
m_starter_nonce ( 0 ) ,
m_last_hr_merge_time ( 0 ) ,
m_hashes ( 0 ) ,
m_do_print_hashrate ( false ) ,
2014-04-07 15:02:15 +00:00
m_do_mining ( false ) ,
m_current_hash_rate ( 0 )
2014-03-03 22:07:58 +00:00
{
}
//-----------------------------------------------------------------------------------------------------
miner : : ~ miner ( )
{
stop ( ) ;
}
//-----------------------------------------------------------------------------------------------------
bool miner : : set_block_template ( const block & bl , const difficulty_type & di , uint64_t height )
{
CRITICAL_REGION_LOCAL ( m_template_lock ) ;
m_template = bl ;
m_diffic = di ;
m_height = height ;
+ + m_template_no ;
m_starter_nonce = crypto : : rand < uint32_t > ( ) ;
return true ;
}
//-----------------------------------------------------------------------------------------------------
bool miner : : on_block_chain_update ( )
{
if ( ! is_mining ( ) )
return true ;
return request_block_template ( ) ;
}
//-----------------------------------------------------------------------------------------------------
bool miner : : request_block_template ( )
{
block bl = AUTO_VAL_INIT ( bl ) ;
difficulty_type di = AUTO_VAL_INIT ( di ) ;
uint64_t height = AUTO_VAL_INIT ( height ) ;
cryptonote : : blobdata extra_nonce ;
if ( m_extra_messages . size ( ) & & m_config . current_extra_message_index < m_extra_messages . size ( ) )
{
extra_nonce = m_extra_messages [ m_config . current_extra_message_index ] ;
}
if ( ! m_phandler - > get_block_template ( bl , m_mine_address , di , height , extra_nonce ) )
{
LOG_ERROR ( " Failed to get_block_template(), stopping mining " ) ;
return false ;
}
set_block_template ( bl , di , height ) ;
return true ;
}
//-----------------------------------------------------------------------------------------------------
bool miner : : on_idle ( )
{
m_update_block_template_interval . do_call ( [ & ] ( ) {
if ( is_mining ( ) ) request_block_template ( ) ;
return true ;
} ) ;
m_update_merge_hr_interval . do_call ( [ & ] ( ) {
merge_hr ( ) ;
return true ;
} ) ;
return true ;
}
//-----------------------------------------------------------------------------------------------------
void miner : : do_print_hashrate ( bool do_hr )
{
m_do_print_hashrate = do_hr ;
}
//-----------------------------------------------------------------------------------------------------
void miner : : merge_hr ( )
{
if ( m_last_hr_merge_time & & is_mining ( ) )
{
m_current_hash_rate = m_hashes * 1000 / ( ( misc_utils : : get_tick_count ( ) - m_last_hr_merge_time + 1 ) ) ;
CRITICAL_REGION_LOCAL ( m_last_hash_rates_lock ) ;
m_last_hash_rates . push_back ( m_current_hash_rate ) ;
if ( m_last_hash_rates . size ( ) > 19 )
m_last_hash_rates . pop_front ( ) ;
if ( m_do_print_hashrate )
{
uint64_t total_hr = std : : accumulate ( m_last_hash_rates . begin ( ) , m_last_hash_rates . end ( ) , 0 ) ;
float hr = static_cast < float > ( total_hr ) / static_cast < float > ( m_last_hash_rates . size ( ) ) ;
std : : cout < < " hashrate: " < < std : : setprecision ( 4 ) < < std : : fixed < < hr < < ENDL ;
}
}
m_last_hr_merge_time = misc_utils : : get_tick_count ( ) ;
m_hashes = 0 ;
}
//-----------------------------------------------------------------------------------------------------
void miner : : init_options ( boost : : program_options : : options_description & desc )
{
command_line : : add_arg ( desc , arg_extra_messages ) ;
command_line : : add_arg ( desc , arg_start_mining ) ;
command_line : : add_arg ( desc , arg_mining_threads ) ;
}
//-----------------------------------------------------------------------------------------------------
bool miner : : init ( const boost : : program_options : : variables_map & vm )
{
if ( command_line : : has_arg ( vm , arg_extra_messages ) )
{
std : : string buff ;
bool r = file_io_utils : : load_file_to_string ( command_line : : get_arg ( vm , arg_extra_messages ) , buff ) ;
CHECK_AND_ASSERT_MES ( r , false , " Failed to load file with extra messages: " < < command_line : : get_arg ( vm , arg_extra_messages ) ) ;
std : : vector < std : : string > extra_vec ;
boost : : split ( extra_vec , buff , boost : : is_any_of ( " \n " ) , boost : : token_compress_on ) ;
m_extra_messages . resize ( extra_vec . size ( ) ) ;
for ( size_t i = 0 ; i ! = extra_vec . size ( ) ; i + + )
{
string_tools : : trim ( extra_vec [ i ] ) ;
if ( ! extra_vec [ i ] . size ( ) )
continue ;
std : : string buff = string_encoding : : base64_decode ( extra_vec [ i ] ) ;
if ( buff ! = " 0 " )
m_extra_messages [ i ] = buff ;
}
m_config_folder_path = boost : : filesystem : : path ( command_line : : get_arg ( vm , arg_extra_messages ) ) . parent_path ( ) . string ( ) ;
m_config = AUTO_VAL_INIT ( m_config ) ;
epee : : serialization : : load_t_from_json_file ( m_config , m_config_folder_path + " / " + MINER_CONFIG_FILE_NAME ) ;
LOG_PRINT_L0 ( " Loaded " < < m_extra_messages . size ( ) < < " extra messages, current index " < < m_config . current_extra_message_index ) ;
}
if ( command_line : : has_arg ( vm , arg_start_mining ) )
{
if ( ! cryptonote : : get_account_address_from_str ( m_mine_address , command_line : : get_arg ( vm , arg_start_mining ) ) )
{
LOG_ERROR ( " Target account address " < < command_line : : get_arg ( vm , arg_start_mining ) < < " has wrong format, starting daemon canceled " ) ;
return false ;
}
m_threads_total = 1 ;
m_do_mining = true ;
if ( command_line : : has_arg ( vm , arg_mining_threads ) )
{
m_threads_total = command_line : : get_arg ( vm , arg_mining_threads ) ;
}
}
return true ;
}
//-----------------------------------------------------------------------------------------------------
2014-05-25 19:36:12 +00:00
bool miner : : is_mining ( ) const
2014-03-03 22:07:58 +00:00
{
return ! m_stop ;
}
2014-05-25 19:36:12 +00:00
//-----------------------------------------------------------------------------------------------------
const account_public_address & miner : : get_mining_address ( ) const
{
return m_mine_address ;
}
//-----------------------------------------------------------------------------------------------------
uint32_t miner : : get_threads_count ( ) const {
return m_threads_total ;
}
2014-03-03 22:07:58 +00:00
//-----------------------------------------------------------------------------------------------------
2014-04-30 20:50:06 +00:00
bool miner : : start ( const account_public_address & adr , size_t threads_count , const boost : : thread : : attributes & attrs )
2014-03-03 22:07:58 +00:00
{
m_mine_address = adr ;
m_threads_total = static_cast < uint32_t > ( threads_count ) ;
m_starter_nonce = crypto : : rand < uint32_t > ( ) ;
CRITICAL_REGION_LOCAL ( m_threads_lock ) ;
if ( is_mining ( ) )
{
LOG_ERROR ( " Starting miner but it's already started " ) ;
return false ;
}
if ( ! m_threads . empty ( ) )
{
LOG_ERROR ( " Unable to start miner because there are active mining threads " ) ;
return false ;
}
if ( ! m_template_no )
request_block_template ( ) ; //lets update block template
boost : : interprocess : : ipcdetail : : atomic_write32 ( & m_stop , 0 ) ;
boost : : interprocess : : ipcdetail : : atomic_write32 ( & m_thread_index , 0 ) ;
for ( size_t i = 0 ; i ! = threads_count ; i + + )
2014-04-30 20:50:06 +00:00
{
m_threads . push_back ( boost : : thread ( attrs , boost : : bind ( & miner : : worker_thread , this ) ) ) ;
}
2014-03-03 22:07:58 +00:00
LOG_PRINT_L0 ( " Mining has started with " < < threads_count < < " threads, good luck! " )
return true ;
}
//-----------------------------------------------------------------------------------------------------
2014-05-25 19:36:12 +00:00
uint64_t miner : : get_speed ( ) const
2014-03-03 22:07:58 +00:00
{
2014-05-25 19:36:12 +00:00
if ( is_mining ( ) ) {
2014-04-07 15:02:15 +00:00
return m_current_hash_rate ;
2014-05-25 19:36:12 +00:00
}
else {
2014-04-07 15:02:15 +00:00
return 0 ;
2014-05-25 19:36:12 +00:00
}
2014-03-03 22:07:58 +00:00
}
//-----------------------------------------------------------------------------------------------------
void miner : : send_stop_signal ( )
{
boost : : interprocess : : ipcdetail : : atomic_write32 ( & m_stop , 1 ) ;
}
//-----------------------------------------------------------------------------------------------------
bool miner : : stop ( )
{
send_stop_signal ( ) ;
CRITICAL_REGION_LOCAL ( m_threads_lock ) ;
BOOST_FOREACH ( boost : : thread & th , m_threads )
th . join ( ) ;
m_threads . clear ( ) ;
LOG_PRINT_L0 ( " Mining has been stopped, " < < m_threads . size ( ) < < " finished " ) ;
return true ;
}
//-----------------------------------------------------------------------------------------------------
bool miner : : find_nonce_for_given_block ( block & bl , const difficulty_type & diffic , uint64_t height )
{
for ( ; bl . nonce ! = std : : numeric_limits < uint32_t > : : max ( ) ; bl . nonce + + )
{
crypto : : hash h ;
get_block_longhash ( bl , h , height ) ;
if ( check_hash ( h , diffic ) )
{
return true ;
}
}
return false ;
}
//-----------------------------------------------------------------------------------------------------
void miner : : on_synchronized ( )
{
if ( m_do_mining )
{
2014-04-30 20:50:06 +00:00
boost : : thread : : attributes attrs ;
attrs . set_stack_size ( THREAD_STACK_SIZE ) ;
start ( m_mine_address , m_threads_total , attrs ) ;
2014-03-03 22:07:58 +00:00
}
}
//-----------------------------------------------------------------------------------------------------
void miner : : pause ( )
{
CRITICAL_REGION_LOCAL ( m_miners_count_lock ) ;
+ + m_pausers_count ;
if ( m_pausers_count = = 1 & & is_mining ( ) )
LOG_PRINT_L2 ( " MINING PAUSED " ) ;
}
//-----------------------------------------------------------------------------------------------------
void miner : : resume ( )
{
CRITICAL_REGION_LOCAL ( m_miners_count_lock ) ;
- - m_pausers_count ;
if ( m_pausers_count < 0 )
{
m_pausers_count = 0 ;
LOG_PRINT_RED_L0 ( " Unexpected miner::resume() called " ) ;
}
if ( ! m_pausers_count & & is_mining ( ) )
LOG_PRINT_L2 ( " MINING RESUMED " ) ;
}
//-----------------------------------------------------------------------------------------------------
bool miner : : worker_thread ( )
{
uint32_t th_local_index = boost : : interprocess : : ipcdetail : : atomic_inc32 ( & m_thread_index ) ;
LOG_PRINT_L0 ( " Miner thread was started [ " < < th_local_index < < " ] " ) ;
log_space : : log_singletone : : set_thread_log_prefix ( std : : string ( " [miner " ) + std : : to_string ( th_local_index ) + " ] " ) ;
uint32_t nonce = m_starter_nonce + th_local_index ;
uint64_t height = 0 ;
difficulty_type local_diff = 0 ;
uint32_t local_template_ver = 0 ;
block b ;
while ( ! m_stop )
{
if ( m_pausers_count ) //anti split workaround
{
misc_utils : : sleep_no_w ( 100 ) ;
continue ;
}
if ( local_template_ver ! = m_template_no )
{
CRITICAL_REGION_BEGIN ( m_template_lock ) ;
b = m_template ;
local_diff = m_diffic ;
height = m_height ;
CRITICAL_REGION_END ( ) ;
local_template_ver = m_template_no ;
nonce = m_starter_nonce + th_local_index ;
}
if ( ! local_template_ver ) //no any set_block_template call
{
LOG_PRINT_L2 ( " Block template not set yet " ) ;
epee : : misc_utils : : sleep_no_w ( 1000 ) ;
continue ;
}
b . nonce = nonce ;
crypto : : hash h ;
get_block_longhash ( b , h , height ) ;
if ( check_hash ( h , local_diff ) )
{
//we lucky!
+ + m_config . current_extra_message_index ;
LOG_PRINT_GREEN ( " Found block for difficulty: " < < local_diff , LOG_LEVEL_0 ) ;
if ( ! m_phandler - > handle_block_found ( b ) )
{
- - m_config . current_extra_message_index ;
} else
{
//success update, lets update config
epee : : serialization : : store_t_to_json_file ( m_config , m_config_folder_path + " / " + MINER_CONFIG_FILE_NAME ) ;
}
}
nonce + = m_threads_total ;
+ + m_hashes ;
}
LOG_PRINT_L0 ( " Miner thread stopped [ " < < th_local_index < < " ] " ) ;
return true ;
}
//-----------------------------------------------------------------------------------------------------
}