diff --git a/src/currency_core/difficulty.cpp b/src/currency_core/difficulty.cpp index ca48c338..95856534 100644 --- a/src/currency_core/difficulty.cpp +++ b/src/currency_core/difficulty.cpp @@ -40,9 +40,9 @@ namespace currency { static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { typedef unsigned __int128 uint128_t; - uint128_t res = (uint128_t) a * (uint128_t) b; - low = (uint64_t) res; - high = (uint64_t) (res >> 64); + uint128_t res = (uint128_t)a * (uint128_t)b; + low = (uint64_t)res; + high = (uint64_t)(res >> 64); } #endif @@ -52,21 +52,21 @@ namespace currency { } static inline bool cadc(uint64_t a, uint64_t b, bool c) { - return a + b < a || (c && a + b == (uint64_t) -1); + return a + b < a || (c && a + b == (uint64_t)-1); } bool check_hash_old(const crypto::hash &hash, difficulty_type difficulty) { uint64_t low, high, top, cur; // First check the highest word, this will most likely fail for a random hash. - mul(swap64le(((const uint64_t *) &hash)[3]), difficulty, top, high); + mul(swap64le(((const uint64_t *)&hash)[3]), difficulty, top, high); if (high != 0) { return false; } - mul(swap64le(((const uint64_t *) &hash)[0]), difficulty, low, cur); - mul(swap64le(((const uint64_t *) &hash)[1]), difficulty, low, high); + mul(swap64le(((const uint64_t *)&hash)[0]), difficulty, low, cur); + mul(swap64le(((const uint64_t *)&hash)[1]), difficulty, low, high); bool carry = cadd(cur, low); cur = high; - mul(swap64le(((const uint64_t *) &hash)[2]), difficulty, low, high); + mul(swap64le(((const uint64_t *)&hash)[2]), difficulty, low, high); carry = cadc(cur, low, carry); carry = cadc(high, top, carry); return !carry; @@ -81,14 +81,14 @@ namespace currency { const wide_difficulty_type max64bit(std::numeric_limits::max()); const boost::multiprecision::uint256_t max128bit(std::numeric_limits::max()); const boost::multiprecision::uint512_t max256bit(std::numeric_limits::max()); - - bool check_hash(const crypto::hash &hash_, wide_difficulty_type difficulty) + + bool check_hash(const crypto::hash &hash_, wide_difficulty_type difficulty) { //revert byte order crypto::hash h = {}; for (size_t i = 0; i != sizeof(h); i++) { - *(((char*)&h) + (sizeof(h)-(i + 1))) = *(((char*)&hash_) +i ); + *(((char*)&h) + (sizeof(h) - (i + 1))) = *(((char*)&hash_) + i); } PROFILE_FUNC("check_hash"); @@ -97,26 +97,26 @@ namespace currency { std::uint64_t dl = difficulty.convert_to(); uint64_t low, high, top, cur; // First check the highest word, this will most likely fail for a random hash. - mul(swap64le(((const uint64_t *) &h)[3]), dl, top, high); - if (high != 0) + mul(swap64le(((const uint64_t *)&h)[3]), dl, top, high); + if (high != 0) return false; - mul(swap64le(((const uint64_t *) &h)[0]), dl, low, cur); - mul(swap64le(((const uint64_t *) &h)[1]), dl, low, high); + mul(swap64le(((const uint64_t *)&h)[0]), dl, low, cur); + mul(swap64le(((const uint64_t *)&h)[1]), dl, low, high); bool carry = cadd(cur, low); cur = high; - mul(swap64le(((const uint64_t *) &h)[2]), dl, low, high); + mul(swap64le(((const uint64_t *)&h)[2]), dl, low, high); carry = cadc(cur, low, carry); carry = cadc(high, top, carry); return !carry; } // fast check - if (((const uint64_t *) &h)[3] > 0) + if (((const uint64_t *)&h)[3] > 0) return false; // usual slow check boost::multiprecision::uint512_t hashVal = 0; - for(int i = 1; i < 4; i++) + for (int i = 1; i < 4; i++) { // highest word is zero - hashVal |= swap64le(((const uint64_t *) &h)[3 - i]); + hashVal |= swap64le(((const uint64_t *)&h)[3 - i]); hashVal << 64; } return (hashVal * difficulty > max256bit); @@ -130,7 +130,7 @@ namespace currency { return res; } - void difficulty_to_boundary_long(wide_difficulty_type difficulty, crypto::hash& result) + void difficulty_to_boundary_long(wide_difficulty_type difficulty, crypto::hash& result) { boost::multiprecision::uint256_t nominal_hash = std::numeric_limits::max(); nominal_hash = nominal_hash / difficulty; @@ -145,7 +145,7 @@ namespace currency { difficulty_type next_difficulty_old(vector timestamps, vector cumulative_difficulties, size_t target_seconds) { //cutoff DIFFICULTY_LAG - if(timestamps.size() > DIFFICULTY_WINDOW) + if (timestamps.size() > DIFFICULTY_WINDOW) { timestamps.resize(DIFFICULTY_WINDOW); cumulative_difficulties.resize(DIFFICULTY_WINDOW); @@ -165,7 +165,8 @@ namespace currency { if (length <= DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) { cut_begin = 0; cut_end = length; - } else { + } + else { cut_begin = (length - (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) + 1) / 2; cut_end = cut_begin + (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT); } @@ -184,11 +185,59 @@ namespace currency { return (low + time_span - 1) / time_span; } - wide_difficulty_type next_difficulty(vector& timestamps, vector& cumulative_difficulties, size_t target_seconds) + 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) + { + cut_begin = 0; + cut_end = length; + } + else + { + cut_begin = REDEF_DIFFICULTY_WINDOW - REDEF_DIFFICULTY_CUT_LAST + 1; + cut_end = cut_begin + (REDEF_DIFFICULTY_WINDOW - (REDEF_DIFFICULTY_CUT_OLD + REDEF_DIFFICULTY_CUT_LAST)); + } + } + + void get_adjustment_zone(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) + { + //cutoff DIFFICULTY_LAG + 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)); + } + } + + wide_difficulty_type get_adjustment_for_zone(vector& timestamps_sorted, vector& cumulative_difficulties, size_t target_seconds, size_t REDEF_DIFFICULTY_WINDOW, size_t REDEF_DIFFICULTY_CUT_OLD, size_t REDEF_DIFFICULTY_CUT_LAST) + { + size_t length = timestamps_sorted.size(); + size_t cut_begin = 0; + size_t cut_end = 0; + get_adjustment_zone(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_sorted[cut_begin] - timestamps_sorted[cut_end - 1]; + if (time_span == 0) + { + time_span = 1; + } + 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(); + } + + wide_difficulty_type next_difficulty(vector& timestamps, vector& cumulative_difficulties, size_t target_seconds) { // timestamps - first is latest, back - is oldest timestamps - //cutoff DIFFICULTY_LAG - if(timestamps.size() > DIFFICULTY_WINDOW) + if (timestamps.size() > DIFFICULTY_WINDOW) { timestamps.resize(DIFFICULTY_WINDOW); cumulative_difficulties.resize(DIFFICULTY_WINDOW); @@ -197,30 +246,32 @@ namespace currency { size_t length = timestamps.size(); CHECK_AND_ASSERT_MES(length == cumulative_difficulties.size(), 0, "Check \"length == cumulative_difficulties.size()\" failed"); - if (length <= 1) { + if (length <= 1) + { return DIFFICULTY_STARTER; } static_assert(DIFFICULTY_WINDOW >= 2, "Window is too small"); + CHECK_AND_ASSERT_MES(length <= DIFFICULTY_WINDOW, 0, "length <= DIFFICULTY_WINDOW check failed, length=" << length); + sort(timestamps.begin(), timestamps.end(), std::greater()); - size_t cut_begin, cut_end; + static_assert(2 * DIFFICULTY_CUT <= DIFFICULTY_WINDOW - 2, "Cut length is too large"); - if (length <= DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) { - cut_begin = 0; - cut_end = length; - } else { - cut_begin = (length - (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) + 1) / 2; - cut_end = cut_begin + (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT); + wide_difficulty_type dif_slow = get_adjustment_for_zone(timestamps, cumulative_difficulties, target_seconds, DIFFICULTY_WINDOW, DIFFICULTY_CUT/2, DIFFICULTY_CUT/2); + wide_difficulty_type dif_medium = get_adjustment_for_zone(timestamps, cumulative_difficulties, target_seconds, DIFFICULTY_WINDOW/3, DIFFICULTY_CUT / 8, DIFFICULTY_CUT / 12); + wide_difficulty_type dif_fast = get_adjustment_for_zone(timestamps, cumulative_difficulties, target_seconds, DIFFICULTY_WINDOW/18, DIFFICULTY_CUT / 10, 2); + uint64_t devider = 1; + wide_difficulty_type summ = dif_slow; + if (dif_medium != 0) + { + summ += dif_medium; + ++devider; } - 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; + if (dif_fast != 0) + { + summ += dif_fast; + ++devider; } - 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(); + return summ / devider; } } diff --git a/tests/functional_tests/difficulty_analysis.cpp b/tests/functional_tests/difficulty_analysis.cpp index 9dfef6f2..b784a88b 100644 --- a/tests/functional_tests/difficulty_analysis.cpp +++ b/tests/functional_tests/difficulty_analysis.cpp @@ -100,14 +100,14 @@ currency::wide_difficulty_type bbr_next_difficulty(std::vector& timest 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) + if (length <= REDEF_DIFFICULTY_WINDOW - (REDEF_DIFFICULTY_CUT_OLD+ REDEF_DIFFICULTY_CUT_LAST)) { cut_begin = 0; cut_end = length; } else { - cut_begin = REDEF_DIFFICULTY_WINDOW - REDEF_DIFFICULTY_CUT_LAST + 1; + cut_begin = REDEF_DIFFICULTY_CUT_LAST; cut_end = cut_begin + (REDEF_DIFFICULTY_WINDOW - (REDEF_DIFFICULTY_CUT_OLD + REDEF_DIFFICULTY_CUT_LAST)); } } @@ -154,7 +154,7 @@ currency::wide_difficulty_type bbr_next_difficulty_composit(std::vector()); 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, 200, 5, 5); + 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; } @@ -300,10 +300,19 @@ void run_emulation(const std::string& path) }); \ 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(bbr_next_difficulty_composit, 720, 60, 60); + PERFORME_SIMULATION_FOR_FUNCTION_NO_WINDOW(currency::next_difficulty); print_blocks(result_blocks, path + "result.txt"); LOG_PRINT_L0("Done");