// Copyright (c) 2014-2018 Zano Project // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include "include_base_utils.h" using namespace epee; #include "wallet/wallet2.h" #include "currency_core/blockchain_storage.h" #include "currency_core/basic_pow_helpers.h" using std::size_t; using std::uint64_t; using std::vector; bool parse_file(const std::string& path, std::vector>& blocks, uint64_t reserve_size) { std::ifstream fstr(path); if (!fstr.good()) { LOG_ERROR("unable to open " << path); return false; } LOG_PRINT_L0("Loading array..."); std::string line; blocks.reserve(reserve_size); while (std::getline(fstr, line)) { std::vector array_num; boost::tokenizer<> tok(line); std::transform(tok.begin(), tok.end(), std::back_inserter(array_num), &boost::lexical_cast); array_num.push_back(0); //reserve space for hashrate value blocks.push_back(array_num); } LOG_PRINT_L0("Loaded " << blocks.size() << " lines"); return true; } #define BBR_DIFFICULTY_TARGET 120 // seconds #define BBR_DIFFICULTY_WINDOW 720 // blocks #define BBR_DIFFICULTY_LAG 15 // !!! #define BBR_DIFFICULTY_CUT 60 // timestamps to cut after sorting #define BBR_DIFFICULTY_STARTER 1 #define NEW_DIFFICULTY_WINDOW 360 #define NEW_DIFFICULTY_CUT_OLD 60 // timestamps to cut after sorting on the oldest timestamps #define NEW_DIFFICULTY_CUT_LAST 0 // timestamps to cut after sorting on the most recent timestamps const boost::multiprecision::uint256_t max128bit(std::numeric_limits::max()); currency::wide_difficulty_type bbr_next_difficulty(std::vector& timestamps, std::vector& cumulative_difficulties, size_t target_seconds) { // timestamps - first is latest, back - is oldest timestamps //cutoff DIFFICULTY_LAG if (timestamps.size() > BBR_DIFFICULTY_WINDOW) { timestamps.resize(BBR_DIFFICULTY_WINDOW); cumulative_difficulties.resize(BBR_DIFFICULTY_WINDOW); } size_t length = timestamps.size(); CHECK_AND_ASSERT_MES(length == cumulative_difficulties.size(), 0, "Check \"length == cumulative_difficulties.size()\" failed"); if (length <= 1) { return BBR_DIFFICULTY_STARTER; } static_assert(BBR_DIFFICULTY_WINDOW >= 2, "Window is too small"); CHECK_AND_ASSERT_MES(length <= BBR_DIFFICULTY_WINDOW, 0, "length <= BBR_DIFFICULTY_WINDOW check failed, length=" << length); sort(timestamps.begin(), timestamps.end(), std::greater()); size_t cut_begin, cut_end; static_assert(2 * BBR_DIFFICULTY_CUT <= BBR_DIFFICULTY_WINDOW - 2, "Cut length is too large"); if (length <= BBR_DIFFICULTY_WINDOW - 2 * BBR_DIFFICULTY_CUT) { cut_begin = 0; cut_end = length; } else { cut_begin = (length - (BBR_DIFFICULTY_WINDOW - 2 * BBR_DIFFICULTY_CUT) + 1) / 2; cut_end = cut_begin + (BBR_DIFFICULTY_WINDOW - 2 * BBR_DIFFICULTY_CUT); } CHECK_AND_ASSERT_THROW_MES(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length, "validation in next_difficulty is failed"); uint64_t time_span = timestamps[cut_begin] - timestamps[cut_end - 1]; if (time_span == 0) { time_span = 1; } currency::wide_difficulty_type total_work = cumulative_difficulties[cut_begin] - cumulative_difficulties[cut_end - 1]; boost::multiprecision::uint256_t res = (boost::multiprecision::uint256_t(total_work) * target_seconds + time_span - 1) / time_span; if (res > max128bit) return 0; // to behave like previous implementation, may be better return max128bit? return res.convert_to(); } void get_cut_location_from_len(size_t length, size_t& cut_begin, size_t& cut_end, size_t REDEF_DIFFICULTY_WINDOW, size_t REDEF_DIFFICULTY_CUT_OLD, size_t REDEF_DIFFICULTY_CUT_LAST) { if (length <= REDEF_DIFFICULTY_WINDOW - (REDEF_DIFFICULTY_CUT_OLD+ REDEF_DIFFICULTY_CUT_LAST)) { cut_begin = 0; cut_end = length; } else { cut_begin = REDEF_DIFFICULTY_CUT_LAST; cut_end = cut_begin + (REDEF_DIFFICULTY_WINDOW - (REDEF_DIFFICULTY_CUT_OLD + REDEF_DIFFICULTY_CUT_LAST)); } } currency::wide_difficulty_type bbr_next_difficulty_configurable(std::vector& timestamps, std::vector& cumulative_difficulties, size_t target_seconds, size_t REDEF_DIFFICULTY_WINDOW, size_t REDEF_DIFFICULTY_CUT_OLD, size_t REDEF_DIFFICULTY_CUT_LAST) { // timestamps - first is latest, back - is oldest timestamps //cutoff DIFFICULTY_LAG if (timestamps.size() > REDEF_DIFFICULTY_WINDOW) { timestamps.resize(REDEF_DIFFICULTY_WINDOW); cumulative_difficulties.resize(REDEF_DIFFICULTY_WINDOW); } size_t length = timestamps.size(); CHECK_AND_ASSERT_MES(length == cumulative_difficulties.size(), 0, "Check \"length == cumulative_difficulties.size()\" failed"); if (length <= 1) { return BBR_DIFFICULTY_STARTER; } CHECK_AND_ASSERT_THROW_MES(REDEF_DIFFICULTY_WINDOW >= 2, "Window is too small"); CHECK_AND_ASSERT_MES(length <= REDEF_DIFFICULTY_WINDOW, 0, "length <= REDEF_DIFFICULTY_WINDOW check failed, length=" << length); sort(timestamps.begin(), timestamps.end(), std::greater()); size_t cut_begin, cut_end; CHECK_AND_ASSERT_THROW_MES( (REDEF_DIFFICULTY_CUT_OLD + REDEF_DIFFICULTY_CUT_LAST) <= REDEF_DIFFICULTY_WINDOW - 2, "Cut length is too large"); get_cut_location_from_len(length, cut_begin, cut_end, REDEF_DIFFICULTY_WINDOW, REDEF_DIFFICULTY_CUT_OLD, REDEF_DIFFICULTY_CUT_LAST); CHECK_AND_ASSERT_THROW_MES(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length, "validation in next_difficulty is failed"); uint64_t time_span = timestamps[cut_begin] - timestamps[cut_end - 1]; if (time_span == 0) { time_span = 1; } currency::wide_difficulty_type total_work = cumulative_difficulties[cut_begin] - cumulative_difficulties[cut_end - 1]; boost::multiprecision::uint256_t res = (boost::multiprecision::uint256_t(total_work) * target_seconds + time_span - 1) / time_span; if (res > max128bit) return 0; // to behave like previous implementation, may be better return max128bit? return res.convert_to(); } currency::wide_difficulty_type bbr_next_difficulty_composit(std::vector& timestamps, std::vector& cumulative_difficulties, size_t target_seconds, size_t REDEF_DIFFICULTY_WINDOW, size_t REDEF_DIFFICULTY_CUT_OLD, size_t REDEF_DIFFICULTY_CUT_LAST) { sort(timestamps.begin(), timestamps.end(), std::greater()); std::vector timestamps_local = timestamps; currency::wide_difficulty_type dif = bbr_next_difficulty_configurable(timestamps_local, cumulative_difficulties, target_seconds, REDEF_DIFFICULTY_WINDOW, REDEF_DIFFICULTY_CUT_OLD, REDEF_DIFFICULTY_CUT_LAST); currency::wide_difficulty_type dif2 = bbr_next_difficulty_configurable(timestamps_local, cumulative_difficulties, target_seconds, 240, 5, 5); currency::wide_difficulty_type dif3 = bbr_next_difficulty_configurable(timestamps_local, cumulative_difficulties, target_seconds, 40, 1, 1); return (dif3 + dif2 + dif) / 3; } currency::wide_difficulty_type bbr_next_difficulty2(std::vector& timestamps, std::vector& cumulative_difficulties, size_t target_seconds) { return bbr_next_difficulty_configurable(timestamps, cumulative_difficulties, target_seconds, NEW_DIFFICULTY_WINDOW, NEW_DIFFICULTY_CUT_OLD, NEW_DIFFICULTY_CUT_LAST); } uint64_t get_next_timestamp_by_difficulty_and_hashrate(uint64_t last_timestamp, currency::wide_difficulty_type difficulty, uint64_t hashrate) { uint64_t seconds = (difficulty / hashrate).convert_to(); return last_timestamp + seconds; } void print_blocks(const std::vector>& blocks, const std::string& res_path) { std::stringstream ss; for (size_t i = 0; i != blocks.size(); i++) { ss << std::left << std::setw(10) << i << std::left << std::setw(15) << blocks[i][0]; for (size_t j = 1; j != blocks[i].size()-1; j++) { ss << std::left << std::setw(15) << blocks[i][j]; } ss << std::left << std::setw(20) << blocks[i][blocks[i].size() - 1] << ENDL; } file_io_utils::save_string_to_file(res_path, ss.str()); LOG_PRINT_L0("Done, saved to file " << res_path); } uint64_t get_hashrate_by_timestamp(const std::map timestamp_to_hashrate, uint64_t timestamp) { auto it = timestamp_to_hashrate.lower_bound(timestamp); if (it == timestamp_to_hashrate.end()) { return 0; } if(it->first == timestamp) return it->second; if (it == timestamp_to_hashrate.begin()) { LOG_ERROR("Internal error, lower_bound returned begin for timestamp " << timestamp); return 0; } return (--it)->second;; } template void perform_simulation_for_function(const std::map& timestamp_to_hashrate, uint64_t index_in_result, const std::vector>& blocks, std::vector>& result_blocks, cb_t cb) { std::vector timestamps; std::vector cumul_difficulties; timestamps.reserve(4010); cumul_difficulties.reserve(4010); timestamps.push_back(blocks[0][0]); cumul_difficulties.push_back(blocks[0][1] * 120); currency::wide_difficulty_type curren_difficulty = 0; size_t index_in_result_blocks = 0; while (true) { uint64_t hr = 0; for (size_t i = 0; i != 10; i++) { if (timestamps.size() < BBR_DIFFICULTY_WINDOW) { curren_difficulty = blocks[index_in_result_blocks][1] * 120; } else { std::vector backward_timestamps; backward_timestamps.reserve(BBR_DIFFICULTY_WINDOW); std::copy(timestamps.rbegin(), timestamps.rbegin() + BBR_DIFFICULTY_WINDOW - 1, std::back_inserter(backward_timestamps)); std::vector backward_cumul_difficulties; backward_cumul_difficulties.reserve(BBR_DIFFICULTY_WINDOW); std::copy(cumul_difficulties.rbegin(), cumul_difficulties.rbegin() + BBR_DIFFICULTY_WINDOW - 1, std::back_inserter(backward_cumul_difficulties)); uint64_t ts = timestamps.back(); curren_difficulty = cb(backward_timestamps, backward_cumul_difficulties, BBR_DIFFICULTY_TARGET); } cumul_difficulties.push_back(cumul_difficulties.back() + curren_difficulty); hr = get_hashrate_by_timestamp(timestamp_to_hashrate, timestamps.back()); if (!hr) break; timestamps.push_back(get_next_timestamp_by_difficulty_and_hashrate(timestamps.back(), curren_difficulty, hr)); } if (!hr) break; result_blocks[index_in_result_blocks][index_in_result] = timestamps.back(); result_blocks[index_in_result_blocks][index_in_result + 1] = curren_difficulty.convert_to() / 120; index_in_result_blocks++; std::cout << index_in_result_blocks << "\r"; } if (index_in_result_blocks < 410) { for (size_t k = index_in_result_blocks; k != 410; k++) result_blocks[k][index_in_result] = result_blocks[k-1][index_in_result]; } std::cout << "\n"; } void run_emulation(const std::string& path) { //0 - timestamp, 1 - difficulty/120, 2 net hashrate (h/s) std::vector> blocks; std::vector> result_blocks; blocks.reserve(401); result_blocks.reserve(401); // std::vector timestamps, timestamps_new; // std::vector cumul_difficulties, cumul_difficulties_new; // timestamps.reserve(4010); // cumul_difficulties.reserve(4010); // timestamps_new.reserve(4010); // cumul_difficulties_new.reserve(4010); parse_file(path, blocks, 500); result_blocks.resize(blocks.size() * 2); for (auto& b : result_blocks) {b.resize(20);} std::map timestamp_to_hashrate; for (uint64_t b_no = 0; b_no != blocks.size(); b_no++) { auto& b_line = blocks[b_no]; timestamp_to_hashrate[b_line[0]] = b_line[2]; result_blocks[b_no][0] = b_line[0]; result_blocks[b_no][1] = b_line[2]; } uint64_t current_index = 2; #define PERFORME_SIMULATION_FOR_FUNCTION(func_name, window_size, cut_old, cut_new ) \ perform_simulation_for_function(timestamp_to_hashrate, current_index, blocks, result_blocks, \ [&](std::vector& timestamps, std::vector& cumulative_difficulties, size_t target_seconds) \ { \ return func_name(timestamps, cumulative_difficulties, target_seconds, window_size, cut_old, cut_new); \ }); \ current_index+=2; #define PERFORME_SIMULATION_FOR_FUNCTION_NO_WINDOW(func_name) \ perform_simulation_for_function(timestamp_to_hashrate, current_index, blocks, result_blocks, \ [&](std::vector& timestamps, std::vector& cumulative_difficulties, size_t target_seconds) \ { \ return func_name(timestamps, cumulative_difficulties, target_seconds); \ }); \ current_index+=2; PERFORME_SIMULATION_FOR_FUNCTION(bbr_next_difficulty_configurable, BBR_DIFFICULTY_WINDOW, BBR_DIFFICULTY_CUT, BBR_DIFFICULTY_CUT); PERFORME_SIMULATION_FOR_FUNCTION(bbr_next_difficulty_configurable, 500, 60, 60); PERFORME_SIMULATION_FOR_FUNCTION(bbr_next_difficulty_configurable, 300, 60, 60); PERFORME_SIMULATION_FOR_FUNCTION_NO_WINDOW(currency::next_difficulty_1); print_blocks(result_blocks, path + "result.txt"); LOG_PRINT_L0("Done"); } void hash_rate_analysis(const std::string& path); void run_difficulty_analysis(const std::string& path) { // currency::block b = AUTO_VAL_INIT(b); // std::string s("sdsccasc"); // b.miner_tx.extra.push_back(s); // // crypto::hash mining_hash = currency::null_hash; // bool r = string_tools::parse_tpod_from_hex_string("7759031ee0f014fe45476724df268f61c890b4a4637df1489a6c94c0135efbd8", mining_hash); // uint64_t nonce = 13704307308123612296; // crypto::hash pow_hash = currency::get_block_longhash(6, mining_hash, nonce); // // std::cout << mining_hash << ENDL; // std::cout << pow_hash << ENDL; //crypto::hash mining_hash = currency::get_block_header_mining_hash(b); //crypto::hash id_hash = currency::get_block_hash(b); hash_rate_analysis(path); //run_emulation(path); } void hash_rate_analysis(const std::string& path) { //0 = height, 1 - timestamp, 2 - difficulty, 3 cumulative_diff std::vector> blocks; parse_file(path, blocks, 140000); LOG_PRINT_L0("Calculating hashrate..."); std::stringstream ss; uint64_t curren_hashrate = 0; uint64_t step = 10; uint64_t hash_rate_range = 10; uint64_t second_windowf_or_hashrate = 20*60; for (size_t i = hash_rate_range; i != blocks.size(); i++) { if (i % step == 0) { //curren_hashrate = (blocks[i][3] - blocks[i - hash_rate_range][3])/(blocks[i][1] - blocks[i- hash_rate_range][1]); // uint64_t cumul_dif = 0; // for (size_t j = i; j != 0 && blocks[j][1] > blocks[i][1]- second_windowf_or_hashrate; j--) // { // cumul_dif += blocks[j][2]; // } // curren_hashrate = cumul_dif / second_windowf_or_hashrate; curren_hashrate = (blocks[i][3] - blocks[i - hash_rate_range][3]) / (blocks[i][1] - blocks[i - hash_rate_range][1]); //std::setw(45) << epee::misc_utils::get_time_str(blocks[i][1]) ss << std::left << std::setw(10) << i << std::left << std::setw(15) << blocks[i][1] << std::left << std::setw(15) << blocks[i][2]/120 << std::left << std::setw(20) << curren_hashrate << ENDL; } //blocks[i][4] = curren_hashrate; //ss << std::left << std::setw(10) << i << std::left << std::setw(15) << blocks[i][2] << std::left << std::setw(20) << blocks[i][4] << ENDL; } std::string res_path = path + "hashrate.txt"; file_io_utils::save_string_to_file(res_path, ss.str()); LOG_PRINT_L0("Done, saved to file " << res_path); return; }