diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/blockchain_db/blockchain_db.cpp | 12 | ||||
-rw-r--r-- | src/blockchain_db/lmdb/db_lmdb.cpp | 1 | ||||
-rw-r--r-- | src/cryptonote_basic/miner.cpp | 405 | ||||
-rw-r--r-- | src/cryptonote_basic/miner.h | 53 | ||||
-rw-r--r-- | src/daemon/command_parser_executor.cpp | 13 | ||||
-rw-r--r-- | src/daemon/command_server.cpp | 2 | ||||
-rw-r--r-- | src/daemon/daemon.cpp | 2 | ||||
-rw-r--r-- | src/daemon/rpc_command_executor.cpp | 5 | ||||
-rw-r--r-- | src/daemon/rpc_command_executor.h | 2 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.cpp | 5 | ||||
-rw-r--r-- | src/rpc/core_rpc_server_commands_defs.h | 4 | ||||
-rw-r--r-- | src/wallet/CMakeLists.txt | 2 |
12 files changed, 479 insertions, 27 deletions
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 1cb0ada0c..7ac046bc9 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -130,12 +130,6 @@ uint64_t BlockchainDB::add_block( const block& blk uint64_t prev_height = height(); - // call out to subclass implementation to add the block & metadata - time1 = epee::misc_utils::get_tick_count(); - add_block(blk, block_size, cumulative_difficulty, coins_generated, blk_hash); - TIME_MEASURE_FINISH(time1); - time_add_block1 += time1; - // call out to add the transactions time1 = epee::misc_utils::get_tick_count(); @@ -151,6 +145,12 @@ uint64_t BlockchainDB::add_block( const block& blk TIME_MEASURE_FINISH(time1); time_add_transaction += time1; + // call out to subclass implementation to add the block & metadata + time1 = epee::misc_utils::get_tick_count(); + add_block(blk, block_size, cumulative_difficulty, coins_generated, blk_hash); + TIME_MEASURE_FINISH(time1); + time_add_block1 += time1; + m_hardfork->add(blk, prev_height); block_txn_stop(); diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 863f54246..0a35325e4 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -628,6 +628,7 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const CURSOR(blocks) CURSOR(block_info) + // this call to mdb_cursor_put will change height() MDB_val_copy<blobdata> blob(block_to_blob(blk)); result = mdb_cursor_put(m_cur_blocks, &key, &blob, MDB_APPEND); if (result) diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 88c631f80..117c81878 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -60,6 +60,10 @@ namespace cryptonote 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}; + const command_line::arg_descriptor<bool> arg_bg_mining_enable = {"bg-mining-enable", "enable/disable background mining", true, true}; + const command_line::arg_descriptor<uint64_t> arg_bg_mining_min_idle_interval_seconds = {"bg-mining-min-idle-interval", "Specify min lookback interval in seconds for determining idle state", miner::BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS, true}; + const command_line::arg_descriptor<uint8_t> arg_bg_mining_idle_threshold_percentage = {"bg-mining-idle-threshold", "Specify minimum avg idle percentage over lookback interval", miner::BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE, true}; + const command_line::arg_descriptor<uint8_t> arg_bg_mining_miner_target_percentage = {"bg-mining-miner-target", "Specificy maximum percentage cpu use by miner(s)", miner::BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE, true}; } @@ -77,7 +81,12 @@ namespace cryptonote m_hashes(0), m_do_print_hashrate(false), m_do_mining(false), - m_current_hash_rate(0) + m_current_hash_rate(0), + m_is_background_mining_enabled(false), + m_min_idle_seconds(BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS), + m_idle_threshold(BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE), + m_mining_target(BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE), + m_miner_extra_sleep(BACKGROUND_MINING_DEFAULT_MINER_EXTRA_SLEEP_MILLIS) { } @@ -137,7 +146,7 @@ namespace cryptonote merge_hr(); return true; }); - + return true; } //----------------------------------------------------------------------------------------------------- @@ -171,6 +180,10 @@ namespace cryptonote command_line::add_arg(desc, arg_extra_messages); command_line::add_arg(desc, arg_start_mining); command_line::add_arg(desc, arg_mining_threads); + command_line::add_arg(desc, arg_bg_mining_enable); + command_line::add_arg(desc, arg_bg_mining_min_idle_interval_seconds); + command_line::add_arg(desc, arg_bg_mining_idle_threshold_percentage); + command_line::add_arg(desc, arg_bg_mining_miner_target_percentage); } //----------------------------------------------------------------------------------------------------- bool miner::init(const boost::program_options::variables_map& vm, bool testnet) @@ -213,6 +226,17 @@ namespace cryptonote } } + // Background mining parameters + // Let init set all parameters even if background mining is not enabled, they can start later with params set + if(command_line::has_arg(vm, arg_bg_mining_enable)) + set_is_background_mining_enabled( command_line::get_arg(vm, arg_bg_mining_enable) ); + if(command_line::has_arg(vm, arg_bg_mining_min_idle_interval_seconds)) + set_min_idle_seconds( command_line::get_arg(vm, arg_bg_mining_min_idle_interval_seconds) ); + if(command_line::has_arg(vm, arg_bg_mining_idle_threshold_percentage)) + set_idle_threshold( command_line::get_arg(vm, arg_bg_mining_idle_threshold_percentage) ); + if(command_line::has_arg(vm, arg_bg_mining_miner_target_percentage)) + set_mining_target( command_line::get_arg(vm, arg_bg_mining_miner_target_percentage) ); + return true; } //----------------------------------------------------------------------------------------------------- @@ -230,7 +254,7 @@ namespace cryptonote return m_threads_total; } //----------------------------------------------------------------------------------------------------- - bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs) + bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background) { m_mine_address = adr; m_threads_total = static_cast<uint32_t>(threads_count); @@ -253,13 +277,21 @@ namespace cryptonote boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0); boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0); - + set_is_background_mining_enabled(do_background); + for(size_t i = 0; i != threads_count; i++) { m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this))); } LOG_PRINT_L0("Mining has started with " << threads_count << " threads, good luck!" ); + + if( get_is_background_mining_enabled() ) + { + m_background_mining_thread = boost::thread(attrs, boost::bind(&miner::background_worker_thread, this)); + LOG_PRINT_L0("Background mining controller thread started" ); + } + return true; } //----------------------------------------------------------------------------------------------------- @@ -291,9 +323,18 @@ namespace cryptonote send_stop_signal(); CRITICAL_REGION_LOCAL(m_threads_lock); + // In case background mining was active and the miner threads are waiting + // on the background miner to signal start. + m_is_background_mining_started_cond.notify_all(); + for(boost::thread& th: m_threads) th.join(); + // The background mining thread could be sleeping for a long time, so we + // interrupt it just in case + m_background_mining_thread.interrupt(); + m_background_mining_thread.join(); + MINFO("Mining has been stopped, " << m_threads.size() << " finished" ); m_threads.clear(); return true; @@ -321,7 +362,7 @@ namespace cryptonote boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); - start(m_mine_address, m_threads_total, attrs); + start(m_mine_address, m_threads_total, attrs, get_is_background_mining_enabled()); } } //----------------------------------------------------------------------------------------------------- @@ -364,6 +405,19 @@ namespace cryptonote misc_utils::sleep_no_w(100); continue; } + else if( m_is_background_mining_enabled ) + { + misc_utils::sleep_no_w(m_miner_extra_sleep); + while( !m_is_background_mining_started ) + { + MGINFO("background mining is enabled, but not started, waiting until start triggers"); + boost::unique_lock<boost::mutex> started_lock( m_is_background_mining_started_mutex ); + m_is_background_mining_started_cond.wait( started_lock ); + if( m_stop ) break; + } + + if( m_stop ) continue; + } if(local_template_ver != m_template_no) { @@ -410,5 +464,344 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------------- -} + bool miner::get_is_background_mining_enabled() const + { + return m_is_background_mining_enabled; + } + //----------------------------------------------------------------------------------------------------- + /** + * This has differing behaviour depending on if mining has been started/etc. + * Note: add documentation + */ + bool miner::set_is_background_mining_enabled(bool is_background_mining_enabled) + { + m_is_background_mining_enabled = is_background_mining_enabled; + // Extra logic will be required if we make this function public in the future + // and allow toggling smart mining without start/stop + //m_is_background_mining_enabled_cond.notify_one(); + return true; + } + //----------------------------------------------------------------------------------------------------- + uint64_t miner::get_min_idle_seconds() const + { + return m_min_idle_seconds; + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_min_idle_seconds(uint64_t min_idle_seconds) + { + if(min_idle_seconds > BACKGROUND_MINING_MAX_MIN_IDLE_INTERVAL_IN_SECONDS) return false; + if(min_idle_seconds < BACKGROUND_MINING_MIN_MIN_IDLE_INTERVAL_IN_SECONDS) return false; + m_min_idle_seconds = min_idle_seconds; + return true; + } + //----------------------------------------------------------------------------------------------------- + uint8_t miner::get_idle_threshold() const + { + return m_idle_threshold; + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_idle_threshold(uint8_t idle_threshold) + { + if(idle_threshold > BACKGROUND_MINING_MAX_IDLE_THRESHOLD_PERCENTAGE) return false; + if(idle_threshold < BACKGROUND_MINING_MIN_IDLE_THRESHOLD_PERCENTAGE) return false; + m_idle_threshold = idle_threshold; + return true; + } + //----------------------------------------------------------------------------------------------------- + uint8_t miner::get_mining_target() const + { + return m_mining_target; + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_mining_target(uint8_t mining_target) + { + if(mining_target > BACKGROUND_MINING_MAX_MINING_TARGET_PERCENTAGE) return false; + if(mining_target < BACKGROUND_MINING_MIN_MINING_TARGET_PERCENTAGE) return false; + m_mining_target = mining_target; + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::background_worker_thread() + { + uint64_t prev_total_time, current_total_time; + uint64_t prev_idle_time, current_idle_time; + uint64_t previous_process_time = 0, current_process_time = 0; + m_is_background_mining_started = false; + + if(!get_system_times(prev_total_time, prev_idle_time)) + { + LOG_ERROR("get_system_times call failed, background mining will NOT work!"); + return false; + } + + while(!m_stop) + { + + try + { + // Commenting out the below since we're going with privatizing the bg mining enabled + // function, but I'll leave the code/comments here for anyone that wants to modify the + // patch in the future + // ------------------------------------------------------------------------------------- + // All of this might be overkill if we just enforced some simple requirements + // about changing this variable before/after the miner starts, but I envision + // in the future a checkbox that you can tick on/off for background mining after + // you've clicked "start mining". There's still an issue here where if background + // mining is disabled when start is called, this thread is never created, and so + // enabling after does nothing, something I have to fix in the future. However, + // this should take care of the case where mining is started with bg-enabled, + // and then the user decides to un-check background mining, and just do + // regular full-speed mining. I might just be over-doing it and thinking up + // non-existant use-cases, so if the concensus is to simplify, we can remove all this fluff. + /* + while( !m_is_background_mining_enabled ) + { + MGINFO("background mining is disabled, waiting until enabled!"); + boost::unique_lock<boost::mutex> enabled_lock( m_is_background_mining_enabled_mutex ); + m_is_background_mining_enabled_cond.wait( enabled_lock ); + } + */ + + // If we're already mining, then sleep for the miner monitor interval. + // If we're NOT mining, then sleep for the idle monitor interval + boost::this_thread::sleep_for( + m_is_background_mining_started ? + boost::chrono::seconds( BACKGROUND_MINING_MINER_MONITOR_INVERVAL_IN_SECONDS ) : + boost::chrono::seconds( get_min_idle_seconds() ) ); + } + catch(const boost::thread_interrupted&) + { + MDEBUG("background miner thread interrupted "); + continue; // if interrupted because stop called, loop should end .. + } + + bool on_ac_power = !on_battery_power(); + if( m_is_background_mining_started ) + { + // figure out if we need to stop, and monitor mining usage + + // If we get here, then previous values are initialized. + // Let's get some current data for comparison. + + if(!get_system_times(current_total_time, current_idle_time)) + { + MERROR("get_system_times call failed"); + continue; + } + + if(!get_process_time(current_process_time)) + { + MERROR("get_process_time call failed!"); + continue; + } + + uint64_t total_diff = (current_total_time - prev_total_time); + uint64_t idle_diff = (current_idle_time - prev_idle_time); + uint64_t process_diff = (current_process_time - previous_process_time); + uint8_t idle_percentage = get_percent_of_total(idle_diff, total_diff); + uint8_t process_percentage = get_percent_of_total(process_diff, total_diff); + + MGINFO("idle percentage is " << unsigned(idle_percentage) << "\%, miner percentage is " << unsigned(process_percentage) << "\%, ac power : " << on_ac_power); + if( idle_percentage + process_percentage < get_idle_threshold() || !on_ac_power ) + { + MGINFO("cpu is " << unsigned(idle_percentage) << "% idle, idle threshold is " << unsigned(get_idle_threshold()) << "\%, ac power : " << on_ac_power << ", background mining stopping, thanks for your contribution!"); + m_is_background_mining_started = false; + + // reset process times + previous_process_time = 0; + current_process_time = 0; + } + else + { + previous_process_time = current_process_time; + + // adjust the miner extra sleep variable + int64_t miner_extra_sleep_change = (-1 * (get_mining_target() - process_percentage) ); + int64_t new_miner_extra_sleep = m_miner_extra_sleep + miner_extra_sleep_change; + // if you start the miner with few threads on a multicore system, this could + // fall below zero because all the time functions aggregate across all processors. + // I'm just hard limiting to 5 millis min sleep here, other options? + m_miner_extra_sleep = std::max( new_miner_extra_sleep , (int64_t)5 ); + MDEBUG("m_miner_extra_sleep " << m_miner_extra_sleep); + } + + prev_total_time = current_total_time; + prev_idle_time = current_idle_time; + } + else if( on_ac_power ) + { + // figure out if we need to start + + if(!get_system_times(current_total_time, current_idle_time)) + { + MERROR("get_system_times call failed"); + continue; + } + + uint64_t total_diff = (current_total_time - prev_total_time); + uint64_t idle_diff = (current_idle_time - prev_idle_time); + uint8_t idle_percentage = get_percent_of_total(idle_diff, total_diff); + + MGINFO("idle percentage is " << unsigned(idle_percentage)); + if( idle_percentage >= get_idle_threshold() && on_ac_power ) + { + MGINFO("cpu is " << unsigned(idle_percentage) << "% idle, idle threshold is " << unsigned(get_idle_threshold()) << "\%, ac power : " << on_ac_power << ", background mining started, good luck!"); + m_is_background_mining_started = true; + m_is_background_mining_started_cond.notify_all(); + + // Wait for a little mining to happen .. + boost::this_thread::sleep_for(boost::chrono::seconds( 1 )); + + // Starting data ... + if(!get_process_time(previous_process_time)) + { + m_is_background_mining_started = false; + MERROR("get_process_time call failed!"); + } + } + + prev_total_time = current_total_time; + prev_idle_time = current_idle_time; + } + } + + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::get_system_times(uint64_t& total_time, uint64_t& idle_time) + { + #ifdef _WIN32 + + FILETIME idleTime; + FILETIME kernelTime; + FILETIME userTime; + if ( GetSystemTimes( &idleTime, &kernelTime, &userTime ) != -1 ) + { + total_time = + ( (((uint64_t)(kernelTime.dwHighDateTime)) << 32) | ((uint64_t)kernelTime.dwLowDateTime) ) + + ( (((uint64_t)(userTime.dwHighDateTime)) << 32) | ((uint64_t)userTime.dwLowDateTime) ); + + idle_time = ( (((uint64_t)(idleTime.dwHighDateTime)) << 32) | ((uint64_t)idleTime.dwLowDateTime) ); + + return true; + } + + #elif defined(__linux__) + + const std::string STR_CPU("cpu"); + const std::size_t STR_CPU_LEN = STR_CPU.size(); + const std::string STAT_FILE_PATH = "/proc/stat"; + + if( !epee::file_io_utils::is_file_exist(STAT_FILE_PATH) ) + { + LOG_ERROR("'" << STAT_FILE_PATH << "' file does not exist"); + return false; + } + + std::ifstream stat_file_stream(STAT_FILE_PATH); + if( stat_file_stream.fail() ) + { + LOG_ERROR("failed to open '" << STAT_FILE_PATH << "'"); + return false; + } + + std::string line; + std::getline(stat_file_stream, line); + std::istringstream stat_file_iss(line); + stat_file_iss.ignore(65536, ' '); // skip cpu label ... + uint64_t utime, ntime, stime, itime; + if( !(stat_file_iss >> utime && stat_file_iss >> ntime && stat_file_iss >> stime && stat_file_iss >> itime) ) + { + LOG_ERROR("failed to read '" << STAT_FILE_PATH << "'"); + return false; + } + + idle_time = itime; + total_time = utime + ntime + stime + itime; + + return true; + + #endif + + return false; // unsupported systemm.. + } + //----------------------------------------------------------------------------------------------------- + bool miner::get_process_time(uint64_t& total_time) + { + #ifdef _WIN32 + + FILETIME createTime; + FILETIME exitTime; + FILETIME kernelTime; + FILETIME userTime; + if ( GetProcessTimes( GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime ) != -1 ) + { + total_time = + ( (((uint64_t)(kernelTime.dwHighDateTime)) << 32) | ((uint64_t)kernelTime.dwLowDateTime) ) + + ( (((uint64_t)(userTime.dwHighDateTime)) << 32) | ((uint64_t)userTime.dwLowDateTime) ); + + return true; + } + + #elif defined(__linux__) && defined(_SC_CLK_TCK) + + struct tms tms; + if ( times(&tms) != (clock_t)-1 ) + { + total_time = tms.tms_utime + tms.tms_stime; + return true; + } + + #endif + + return false; // unsupported system.. + } + //----------------------------------------------------------------------------------------------------- + uint8_t miner::get_percent_of_total(uint64_t other, uint64_t total) + { + return (uint8_t)( ceil( (other * 1.f / total * 1.f) * 100) ); + } + //----------------------------------------------------------------------------------------------------- + bool miner::on_battery_power() + { + #ifdef _WIN32 + + SYSTEM_POWER_STATUS power_status; + if ( GetSystemPowerStatus( &power_status ) != 0 ) + { + return power_status.ACLineStatus != 1; + } + + #elif defined(__linux__) + + // i've only tested on UBUNTU, these paths might be different on other systems + // need to figure out a way to make this more flexible + const std::string POWER_SUPPLY_STATUS_PATH = "/sys/class/power_supply/ACAD/online"; + + if( !epee::file_io_utils::is_file_exist(POWER_SUPPLY_STATUS_PATH) ) + { + LOG_ERROR("'" << POWER_SUPPLY_STATUS_PATH << "' file does not exist, can't determine if on AC power"); + return false; + } + + std::ifstream power_stream(POWER_SUPPLY_STATUS_PATH); + if( power_stream.fail() ) + { + LOG_ERROR("failed to open '" << POWER_SUPPLY_STATUS_PATH << "'"); + return false; + } + + return power_stream.get() != '1'; + + #endif + + LOG_ERROR("couldn't query power status"); + return false; // shouldn't get here unless no support for querying battery status + // TODO: return enum with ability to signify failure in querying for power status + // and change bg-mining logic so that it stops. As @vtnerd states, with the current + // setup "If someone enabled background mining on a system that fails to grab ac + // status, it will just continually check with little hope of ever being resolved + // automagically". This is also the case for time/idle stats functions. + } +} diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index f24f6e960..a66083ead 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -35,7 +35,14 @@ #include "cryptonote_basic.h" #include "difficulty.h" #include "math_helper.h" - +#ifdef _WIN32 +#include <windows.h> +#elif defined(__linux__) +#include <unistd.h> +#include <sys/resource.h> +#include <sys/times.h> +#include <time.h> +#endif namespace cryptonote { @@ -60,7 +67,7 @@ namespace cryptonote static void init_options(boost::program_options::options_description& desc); bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height); bool on_block_chain_update(); - bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs); + bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background = false); uint64_t get_speed() const; uint32_t get_threads_count() const; void send_stop_signal(); @@ -74,6 +81,26 @@ namespace cryptonote void pause(); void resume(); void do_print_hashrate(bool do_hr); + bool get_is_background_mining_enabled() const; + uint64_t get_min_idle_seconds() const; + bool set_min_idle_seconds(uint64_t min_idle_seconds); + uint8_t get_idle_threshold() const; + bool set_idle_threshold(uint8_t idle_threshold); + uint8_t get_mining_target() const; + bool set_mining_target(uint8_t mining_target); + + static constexpr uint8_t BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE = 90; + static constexpr uint8_t BACKGROUND_MINING_MIN_IDLE_THRESHOLD_PERCENTAGE = 50; + static constexpr uint8_t BACKGROUND_MINING_MAX_IDLE_THRESHOLD_PERCENTAGE = 99; + static constexpr uint16_t BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS = 10; + static constexpr uint16_t BACKGROUND_MINING_MIN_MIN_IDLE_INTERVAL_IN_SECONDS = 10; + static constexpr uint16_t BACKGROUND_MINING_MAX_MIN_IDLE_INTERVAL_IN_SECONDS = 3600; + static constexpr uint8_t BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE = 40; + static constexpr uint8_t BACKGROUND_MINING_MIN_MINING_TARGET_PERCENTAGE = 5; + static constexpr uint8_t BACKGROUND_MINING_MAX_MINING_TARGET_PERCENTAGE = 50; + static constexpr uint8_t BACKGROUND_MINING_MINER_MONITOR_INVERVAL_IN_SECONDS = 10; + static constexpr uint64_t BACKGROUND_MINING_DEFAULT_MINER_EXTRA_SLEEP_MILLIS = 400; // ramp up + static constexpr uint64_t BACKGROUND_MINING_MIN_MINER_EXTRA_SLEEP_MILLIS = 5; private: bool worker_thread(); @@ -119,8 +146,24 @@ namespace cryptonote bool m_do_print_hashrate; bool m_do_mining; + // background mining stuffs .. + + bool set_is_background_mining_enabled(bool is_background_mining_enabled); + bool background_worker_thread(); + std::atomic<bool> m_is_background_mining_enabled; + boost::mutex m_is_background_mining_enabled_mutex; + boost::condition_variable m_is_background_mining_enabled_cond; + std::atomic<bool> m_is_background_mining_started; + boost::mutex m_is_background_mining_started_mutex; + boost::condition_variable m_is_background_mining_started_cond; + boost::thread m_background_mining_thread; + uint64_t m_min_idle_seconds; + uint8_t m_idle_threshold; + uint8_t m_mining_target; + std::atomic<uint64_t> m_miner_extra_sleep; + static bool get_system_times(uint64_t& total_time, uint64_t& idle_time); + static bool get_process_time(uint64_t& total_time); + static uint8_t get_percent_of_total(uint64_t some_time, uint64_t total_time); + static bool on_battery_power(); }; } - - - diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index fd73654ac..8ed529737 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -271,17 +271,24 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg if(testnet) std::cout << "Mining to a testnet address, make sure this is intentional!" << std::endl; uint64_t threads_count = 1; - if(args.size() > 2) + bool do_background_mining = false; + if(args.size() > 3) { return false; } - else if(args.size() == 2) + + if(args.size() == 3) + { + do_background_mining = args[2] == "true"; + } + + if(args.size() >= 2) { bool ok = epee::string_tools::get_xtype_from_string(threads_count, args[1]); threads_count = (ok && 0 < threads_count) ? threads_count : 1; } - m_executor.start_mining(adr, threads_count, testnet); + m_executor.start_mining(adr, threads_count, testnet, do_background_mining); return true; } diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 34868b576..4133b90d9 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -96,7 +96,7 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "start_mining" , std::bind(&t_command_parser_executor::start_mining, &m_parser, p::_1) - , "Start mining for specified address, start_mining <addr> [<threads>], default 1 thread" + , "Start mining for specified address, start_mining <addr> [<threads>] [do_background_mining], default 1 thread, no background mining" ); m_command_lookup.set_handler( "stop_mining" diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index e40136a71..19013cbc8 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -141,6 +141,7 @@ bool t_daemon::run(bool interactive) } mp_internals->rpc.stop(); + mp_internals->core.get().get_miner().stop(); MGINFO("Node stopped."); return true; } @@ -162,6 +163,7 @@ void t_daemon::stop() { throw std::runtime_error{"Can't stop stopped daemon"}; } + mp_internals->core.get().get_miner().stop(); mp_internals->p2p.stop(); mp_internals->rpc.stop(); mp_internals.reset(nullptr); // Ensure resources are cleaned up before we return diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 469f83014..00b349b15 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -377,7 +377,7 @@ bool t_rpc_command_executor::show_status() { % (unsigned long long)(ires.target_height >= ires.height ? ires.target_height : ires.height) % get_sync_percentage(ires) % (ires.testnet ? "testnet" : "mainnet") - % (mining_busy ? "syncing" : mres.active ? "mining at " + get_mining_speed(mres.speed) : "not mining") + % (mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining") % get_mining_speed(ires.difficulty / ires.target) % (unsigned)hfres.version % get_fork_extra_info(hfres.earliest_height, ires.height, ires.target) @@ -929,11 +929,12 @@ bool t_rpc_command_executor::print_transaction_pool_stats() { return true; } -bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet) { +bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining = false) { cryptonote::COMMAND_RPC_START_MINING::request req; cryptonote::COMMAND_RPC_START_MINING::response res; req.miner_address = cryptonote::get_account_address_as_str(testnet, address); req.threads_count = num_threads; + req.do_background_mining = do_background_mining; std::string fail_message = "Mining did not start"; diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 4691844fa..5b7b76448 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -106,7 +106,7 @@ public: bool print_transaction_pool_stats(); - bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet); + bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining); bool stop_mining(); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 5442b6ac4..8165dacbc 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -635,7 +635,7 @@ namespace cryptonote boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); - if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs)) + if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining)) { res.status = "Failed, mining not started"; LOG_PRINT_L0(res.status); @@ -663,6 +663,7 @@ namespace cryptonote const miner& lMiner = m_core.get_miner(); res.active = lMiner.is_mining(); + res.is_background_mining_enabled = lMiner.get_is_background_mining_enabled(); if ( lMiner.is_mining() ) { res.speed = lMiner.get_speed(); @@ -736,7 +737,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res) { - mlog_set_categories(req.categories.c_str()); + mlog_set_log(req.categories.c_str()); res.status = CORE_RPC_STATUS_OK; return true; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 83ef2ebd4..3ab1ea175 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -500,10 +500,12 @@ namespace cryptonote { std::string miner_address; uint64_t threads_count; + bool do_background_mining; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(miner_address) KV_SERIALIZE(threads_count) + KV_SERIALIZE(do_background_mining) END_KV_SERIALIZE_MAP() }; @@ -608,6 +610,7 @@ namespace cryptonote uint64_t speed; uint32_t threads_count; std::string address; + bool is_background_mining_enabled; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -615,6 +618,7 @@ namespace cryptonote KV_SERIALIZE(speed) KV_SERIALIZE(threads_count) KV_SERIALIZE(address) + KV_SERIALIZE(is_background_mining_enabled) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 8626001ce..3c103bc29 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -126,7 +126,7 @@ endif() # build and install libwallet_merged only if we building for GUI if (BUILD_GUI_DEPS) - set(libs_to_merge wallet cryptonote_core mnemonics common crypto ringct) + set(libs_to_merge wallet cryptonote_core cryptonote_basic mnemonics common crypto ringct) foreach(lib ${libs_to_merge}) list(APPEND objlibs $<TARGET_OBJECTS:obj_${lib}>) # matches naming convention in src/CMakeLists.txt |