diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index fd743a6..9a14c5e 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -294,6 +294,15 @@ #define DIFFICULTY_EDA_THRESHOLD 6 #define DIFFICULTY_EDA_MAX_DROP_PERCENT 75 +// Deterministic local trend correction layered after LWMA/EDA. +// Uses recent vs older solve-time windows to react faster to sustained +// hashrate changes without replacing the base difficulty algorithm. +#define DIFFICULTY_TREND_RECENT_WINDOW 5 +#define DIFFICULTY_TREND_OLDER_WINDOW 15 +#define DIFFICULTY_TREND_SMOOTHING_NUM 3 +#define DIFFICULTY_TREND_SMOOTHING_DEN 10 +#define DIFFICULTY_TREND_MAX_ADJUST_PERCENT 5 + //The limit is enough for the mandatory transaction content with 16 outputs (547 bytes), //a custom tag (1 byte) and up to 32 bytes of custom data for each recipient. // (1+32) + (1+1+16*32) + (1+16*32) = 1060 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index ccebbbf..33cfd3e 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -155,6 +155,53 @@ namespace return adjusted < calculated_difficulty ? adjusted : calculated_difficulty; } + + cryptonote::difficulty_type apply_difficulty_trend_adjustment( + cryptonote::difficulty_type calculated_difficulty, + const std::vector ×tamps) + { + const size_t required_points = DIFFICULTY_TREND_RECENT_WINDOW + DIFFICULTY_TREND_OLDER_WINDOW + 1; + if (timestamps.size() < required_points) + return calculated_difficulty; + + const size_t ts_size = timestamps.size(); + uint64_t sum_recent = 0; + for (size_t i = ts_size - DIFFICULTY_TREND_RECENT_WINDOW; i < ts_size; ++i) + { + const uint64_t solve_time = timestamps[i] > timestamps[i - 1] ? (timestamps[i] - timestamps[i - 1]) : 1; + sum_recent += solve_time; + } + + const size_t older_end = ts_size - DIFFICULTY_TREND_RECENT_WINDOW; + const size_t older_start = older_end - DIFFICULTY_TREND_OLDER_WINDOW; + uint64_t sum_older = 0; + for (size_t i = older_start + 1; i <= older_end; ++i) + { + const uint64_t solve_time = timestamps[i] > timestamps[i - 1] ? (timestamps[i] - timestamps[i - 1]) : 1; + sum_older += solve_time; + } + + if (sum_recent == 0) + return calculated_difficulty; + + const uint64_t ratio_scaled = + (sum_older * DIFFICULTY_TREND_RECENT_WINDOW * 10000ULL) / + (sum_recent * DIFFICULTY_TREND_OLDER_WINDOW); + const int64_t deviation = static_cast(ratio_scaled) - 10000; + int64_t adjust_scaled = 10000 + + (deviation * DIFFICULTY_TREND_SMOOTHING_NUM) / DIFFICULTY_TREND_SMOOTHING_DEN; + + const int64_t max_adjust = DIFFICULTY_TREND_MAX_ADJUST_PERCENT * 100; + if (adjust_scaled > 10000 + max_adjust) + adjust_scaled = 10000 + max_adjust; + if (adjust_scaled < 10000 - max_adjust) + adjust_scaled = 10000 - max_adjust; + + cryptonote::difficulty_type adjusted = (calculated_difficulty * adjust_scaled) / 10000; + if (adjusted < 1) + adjusted = 1; + return adjusted; + } } //#include "serialization/json_archive.h" @@ -1026,7 +1073,10 @@ difficulty_type Blockchain::get_difficulty_for_next_block() diff = next_difficulty_v2(timestamps, difficulties, target); } if (next_version >= HF_VERSION_EMERGENCY_DIFFICULTY) + { diff = apply_emergency_difficulty_cap(diff, timestamps, difficulties, target); + diff = apply_difficulty_trend_adjustment(diff, timestamps); + } CRITICAL_REGION_LOCAL1(m_difficulty_lock); m_difficulty_for_next_block_top_hash = top_hash; @@ -1421,7 +1471,10 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: } else { difficulty_type diff = next_difficulty_v2(timestamps, cumulative_difficulties, target); if (next_version >= HF_VERSION_EMERGENCY_DIFFICULTY) + { diff = apply_emergency_difficulty_cap(diff, timestamps, cumulative_difficulties, target); + diff = apply_difficulty_trend_adjustment(diff, timestamps); + } return diff; } }