diff --git a/contrib/epee/include/rolling_median.h b/contrib/epee/include/rolling_median.h index a88bdbf65..2a262a46d 100644 --- a/contrib/epee/include/rolling_median.h +++ b/contrib/epee/include/rolling_median.h @@ -217,6 +217,11 @@ public: return sz; } + size_t capacity() const + { + return static_cast(N); + } + //Inserts item, maintains median in O(lg nItems) void insert(Item v) { diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index e072b8981..03a79cb80 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -60,6 +60,7 @@ #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 300000 //size of block (bytes) after which reward for block calculated using block size - second change, from v5 #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V17 10000000 //STRESSNET SPECIFIC size of block (bytes) after which reward for block calculated using block size - third change, from v17 #define CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE 100000 // size in blocks of the long term block weight median window +#define CRYPTONOTE_STRESSNET_BLOCK_WEIGHT_WINDOW_SIZE 10080 // size in blocks of the long term block weight median window [2 weeks] [STRESSNET ONLY!!!!] #define CRYPTONOTE_SHORT_TERM_BLOCK_SURGE_FACTOR_V10 50 #define CRYPTONOTE_SHORT_TERM_BLOCK_SURGE_FACTOR_V17 8 #define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 115962cf7..7f2fee6c0 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1564,9 +1564,10 @@ uint64_t Blockchain::get_long_term_block_weight_median(uint64_t start_height, si PERF_TIMER(get_long_term_block_weights); CHECK_AND_ASSERT_THROW_MES(count > 0, "count == 0"); + const uint64_t blockchain_height = m_db->height(); + CHECK_AND_ASSERT_THROW_MES(start_height < blockchain_height, "start of block LTM range lies outside of chain"); bool cached = false; - uint64_t blockchain_height = m_db->height(); uint64_t tip_height = start_height + count - 1; crypto::hash tip_hash = crypto::null_hash; if (tip_height < blockchain_height) @@ -1581,15 +1582,17 @@ uint64_t Blockchain::get_long_term_block_weight_median(uint64_t start_height, si if (cached) { MTRACE("requesting " << count << " from " << start_height << ", cached"); + assert(static_cast(m_long_term_block_weights_cache_rolling_median.size()) == count); return m_long_term_block_weights_cache_rolling_median.median(); } // in the vast majority of uncached cases, most is still cached, // as we just move the window one block up: + const size_t rmcap = m_long_term_block_weights_cache_rolling_median.capacity(); if (tip_height > 0 && tip_height < blockchain_height) { const size_t rmsize = m_long_term_block_weights_cache_rolling_median.size(); - if (count == rmsize || (count == rmsize + 1 && rmsize < CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE)) + if ((count == rmsize && rmsize == rmcap) || (count == rmsize + 1 && rmsize < rmcap)) { crypto::hash old_tip_hash = m_db->get_block_hash_from_height(tip_height - 1); if (old_tip_hash == m_long_term_block_weights_cache_tip_hash) @@ -1597,17 +1600,32 @@ uint64_t Blockchain::get_long_term_block_weight_median(uint64_t start_height, si MTRACE("requesting " << count << " from " << start_height << ", incremental"); m_long_term_block_weights_cache_tip_hash = tip_hash; m_long_term_block_weights_cache_rolling_median.insert(m_db->get_block_long_term_weight(tip_height)); + assert(static_cast(m_long_term_block_weights_cache_rolling_median.size()) == count); return m_long_term_block_weights_cache_rolling_median.median(); } } } + // The target capacity for the new rolling median cache A) must fit the current query, + // B) shouldn't re-alloc on incremental calls, and C) shouldn't trigger invalidations every + // call because its capacity is regularly higher than query count. We use + // m_long_term_block_weights_window as a hint for the upper bound on capacity. + // Clearing the median cache before a potentially fallible block weight fetch leaves the + // cache invalidated correctly. + const size_t target_cap = std::max(count, m_long_term_block_weights_window); + if (rmcap == target_cap) + m_long_term_block_weights_cache_rolling_median.clear(); // avoid reallocation + else + m_long_term_block_weights_cache_rolling_median = epee::misc_utils::rolling_median_t(target_cap); + MTRACE("requesting " << count << " from " << start_height << ", uncached"); - std::vector weights = m_db->get_long_term_block_weights(start_height, count); + const std::vector weights = m_db->get_long_term_block_weights(start_height, count); + assert(weights.size() == std::min(count, blockchain_height - start_height)); m_long_term_block_weights_cache_tip_hash = tip_hash; - m_long_term_block_weights_cache_rolling_median.clear(); - for (uint64_t w: weights) + for (const uint64_t w: weights) m_long_term_block_weights_cache_rolling_median.insert(w); + + assert(static_cast(m_long_term_block_weights_cache_rolling_median.size()) == weights.size()); return m_long_term_block_weights_cache_rolling_median.median(); } //------------------------------------------------------------------ @@ -5084,6 +5102,19 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti const uint8_t hf_version = get_current_hard_fork_version(); const uint64_t full_reward_zone = get_min_block_weight(hf_version); + // Long-term block weight median window is reduced for stressnet for quicker scaling tests + // However, mainnet's long-term block weight window should still be 100,000 + // https://libera.monerologs.net/monero-research-lab/20260114#c645360 + const bool use_stressnet_lt_window = m_nettype == network_type::TESTNET; + if (use_stressnet_lt_window) + { + // Always set on stressnet, even before v17, to handle reorgs. + // The window should only be reduced after the FCMP++ fork to avoid premature HFs w/ testnet. + const bool short_lt_window = hf_version >= HF_VERSION_2026_SCALING; + m_long_term_block_weights_window = short_lt_window + ? CRYPTONOTE_STRESSNET_BLOCK_WEIGHT_WINDOW_SIZE : CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE; + } + if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT) { std::vector weights;