diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index 165c1936e..2965afdef 100644 --- a/src/cryptonote_basic/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -239,6 +239,305 @@ namespace cryptonote { return res.convert_to(); } + // Copyright (c) 2014-2023, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include +#include +#include +#include +#include +#include + +#include "int-util.h" +#include "crypto/hash.h" +#include "cryptonote_config.h" +#include "difficulty.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "difficulty" + +namespace cryptonote { + + using std::size_t; + using std::uint64_t; + using std::vector; + +#if defined(__x86_64__) + static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { + low = mul128(a, b, &high); + } + +#else + + static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { + // __int128 isn't part of the standard, so the previous function wasn't portable. mul128() in Windows is fine, + // but this portable function should be used elsewhere. Credit for this function goes to latexi95. + + uint64_t aLow = a & 0xFFFFFFFF; + uint64_t aHigh = a >> 32; + uint64_t bLow = b & 0xFFFFFFFF; + uint64_t bHigh = b >> 32; + + uint64_t res = aLow * bLow; + uint64_t lowRes1 = res & 0xFFFFFFFF; + uint64_t carry = res >> 32; + + res = aHigh * bLow + carry; + uint64_t highResHigh1 = res >> 32; + uint64_t highResLow1 = res & 0xFFFFFFFF; + + res = aLow * bHigh; + uint64_t lowRes2 = res & 0xFFFFFFFF; + carry = res >> 32; + + res = aHigh * bHigh + carry; + uint64_t highResHigh2 = res >> 32; + uint64_t highResLow2 = res & 0xFFFFFFFF; + + //Addition + + uint64_t r = highResLow1 + lowRes2; + carry = r >> 32; + low = (r << 32) | lowRes1; + r = highResHigh1 + highResLow2 + carry; + uint64_t d3 = r & 0xFFFFFFFF; + carry = r >> 32; + r = highResHigh2 + carry; + high = d3 | (r << 32); + } + +#endif + + static inline bool cadd(uint64_t a, uint64_t b) { + return a + b < a; + } + + static inline bool cadc(uint64_t a, uint64_t b, bool c) { + return a + b < a || (c && a + b == (uint64_t) -1); + } + + bool check_hash_64(const crypto::hash &hash, uint64_t 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); + 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); + bool carry = cadd(cur, low); + cur = high; + mul(swap64le(((const uint64_t *) &hash)[2]), difficulty, low, high); + carry = cadc(cur, low, carry); + carry = cadc(high, top, carry); + return !carry; + } + + uint64_t next_difficulty_64(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds) { + + if(timestamps.size() > DIFFICULTY_WINDOW) + { + timestamps.resize(DIFFICULTY_WINDOW); + cumulative_difficulties.resize(DIFFICULTY_WINDOW); + } + + + size_t length = timestamps.size(); + assert(length == cumulative_difficulties.size()); + if (length <= 1) { + return 1; + } + static_assert(DIFFICULTY_WINDOW >= 2, "Window is too small"); + assert(length <= DIFFICULTY_WINDOW); + sort(timestamps.begin(), timestamps.end()); + 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); + } + assert(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length); + uint64_t time_span = timestamps[cut_end - 1] - timestamps[cut_begin]; + if (time_span == 0) { + time_span = 1; + } + uint64_t total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; + assert(total_work > 0); + uint64_t low, high; + mul(total_work, target_seconds, low, high); + // blockchain errors "difficulty overhead" if this function returns zero. + // TODO: consider throwing an exception instead + if (high != 0 || low + time_span - 1 < low) { + return 0; + } + return (low + time_span - 1) / time_span; + } + +#if defined(_MSC_VER) +#ifdef max +#undef max +#endif +#endif + + const 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()); + +#define FORCE_FULL_128_BITS + + bool check_hash_128(const crypto::hash &hash, difficulty_type difficulty) { +#ifndef FORCE_FULL_128_BITS + // fast check + if (difficulty >= max64bit && ((const uint64_t *) &hash)[3] > 0) + return false; +#endif + // usual slow check + boost::multiprecision::uint512_t hashVal = 0; +#ifdef FORCE_FULL_128_BITS + for(int i = 0; i < 4; i++) { // highest word is zero +#else + for(int i = 1; i < 4; i++) { // highest word is zero +#endif + hashVal <<= 64; + hashVal |= swap64le(((const uint64_t *) &hash)[3 - i]); + } + return hashVal * difficulty <= max256bit; + } + + bool check_hash(const crypto::hash &hash, difficulty_type difficulty) { + if (difficulty <= max64bit) // if can convert to small difficulty - do it + return check_hash_64(hash, difficulty.convert_to()); + else + return check_hash_128(hash, difficulty); + } + + difficulty_type next_difficulty(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds) { + //cutoff DIFFICULTY_LAG + if(timestamps.size() > DIFFICULTY_WINDOW) + { + timestamps.resize(DIFFICULTY_WINDOW); + cumulative_difficulties.resize(DIFFICULTY_WINDOW); + } + + + size_t length = timestamps.size(); + assert(length == cumulative_difficulties.size()); + if (length <= 1) { + return 1; + } + static_assert(DIFFICULTY_WINDOW >= 2, "Window is too small"); + assert(length <= DIFFICULTY_WINDOW); + sort(timestamps.begin(), timestamps.end()); + 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); + } + assert(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length); + uint64_t time_span = timestamps[cut_end - 1] - timestamps[cut_begin]; + if (time_span == 0) { + time_span = 1; + } + difficulty_type total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; + assert(total_work > 0); + 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(); + } + + difficulty_type next_difficulty_v2(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds) { + + // LWMA difficulty algorithm + // Copyright (c) 2017-2018 Zawy + // MIT license http://www.opensource.org/licenses/mit-license.php. + // This is an improved version of Tom Harding's (Deger8) "WT-144" + // Karbowanec, Masari, Bitcoin Gold, and Bitcoin Cash have contributed. + // See https://github.com/zawy12/difficulty-algorithms/issues/3 for other algos. + // Do not use "if solvetime < 0 then solvetime = 1" which allows a catastrophic exploit. + // T= target_solvetime; + // N=45, 55, 70, 90, 120 for T=600, 240, 120, 90, and 60 + + const int64_t T = static_cast(target_seconds); + size_t N = DIFFICULTY_WINDOW_V2; + + if (timestamps.size() > N) { + timestamps.resize(N + 1); + cumulative_difficulties.resize(N + 1); + } + size_t n = timestamps.size(); + assert(n == cumulative_difficulties.size()); + assert(n <= DIFFICULTY_WINDOW_V2); + // If new coin, just "give away" first 5 blocks at low difficulty + if ( n < 6 ) { return 1; } + // If height "n" is from 6 to N, then reset N to n-1. + else if (n < N+1) { N=n-1; } + + // To get an average solvetime to within +/- ~0.1%, use an adjustment factor. + // adjust=0.99 for 90 < N < 130 + const long double adjust = 0.998; + // The divisor k normalizes LWMA. + const long double k = N * (N + 1) / 2; + + long double LWMA(0), sum_inverse_D(0), harmonic_mean_D(0), nextDifficulty(0); + int64_t solveTime(0); + uint64_t difficulty(0), next_difficulty(0); + + // Loop through N most recent blocks. + for (size_t i = 1; i <= N; i++) { + solveTime = static_cast(timestamps[i]) - static_cast(timestamps[i - 1]); + solveTime = std::min((T * 7), std::max(solveTime, (-7 * T))); + difficulty = (cumulative_difficulties[i] - cumulative_difficulties[i - 1]).convert_to(); + LWMA += (int64_t)(solveTime * i) / k; + sum_inverse_D += 1 / static_cast(difficulty); + } + + // Keep LWMA sane in case something unforeseen occurs. + if (static_cast(boost::math::round(LWMA)) < T / 20) + LWMA = static_cast(T / 20); + + harmonic_mean_D = N / sum_inverse_D * adjust; + nextDifficulty = harmonic_mean_D * T / LWMA; + next_difficulty = static_cast(nextDifficulty); + + return next_difficulty; + } + std::string hex(difficulty_type v) { static const char chars[] = "0123456789abcdef"; diff --git a/src/cryptonote_basic/difficulty.h b/src/cryptonote_basic/difficulty.h index ee9378eb9..0430544db 100644 --- a/src/cryptonote_basic/difficulty.h +++ b/src/cryptonote_basic/difficulty.h @@ -58,6 +58,7 @@ namespace cryptonote bool check_hash_128(const crypto::hash &hash, difficulty_type difficulty); bool check_hash(const crypto::hash &hash, difficulty_type difficulty); difficulty_type next_difficulty(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds); - + difficulty_type next_difficulty_v2(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds); + std::string hex(difficulty_type v); } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0e3fdacf7..e99716593 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -967,7 +967,13 @@ start: } size_t target = get_difficulty_target(); - difficulty_type diff = next_difficulty(timestamps, difficulties, target); + difficulty_type diff; + uint8_t version = get_current_hard_fork_version(); + if (version == 1) { + diff = next_difficulty(timestamps, difficulties, target); + } else { + diff = next_difficulty_v2(timestamps, difficulties, target); + } CRITICAL_REGION_LOCAL1(m_difficulty_lock); m_difficulty_for_next_block_top_hash = top_hash; @@ -1026,6 +1032,7 @@ size_t Blockchain::recalculate_difficulties(boost::optional start_heig std::vector timestamps; std::vector difficulties; + uint8_t version = get_current_hard_fork_version(); timestamps.reserve(DIFFICULTY_BLOCKS_COUNT + 1); difficulties.reserve(DIFFICULTY_BLOCKS_COUNT + 1); if (start_height > 1) @@ -1045,7 +1052,9 @@ size_t Blockchain::recalculate_difficulties(boost::optional start_heig for (uint64_t height = start_height; height <= top_height; ++height) { size_t target = DIFFICULTY_TARGET_V2; - difficulty_type recalculated_diff = next_difficulty(timestamps, difficulties, target); + difficulty_type recalculated_diff = (version == 1) + ? next_difficulty(timestamps, difficulties, target) + : next_difficulty_v2(timestamps, difficulties, target); boost::multiprecision::uint256_t recalculated_cum_diff_256 = boost::multiprecision::uint256_t(recalculated_diff) + last_cum_diff; CHECK_AND_ASSERT_THROW_MES(recalculated_cum_diff_256 <= std::numeric_limits::max(), "Difficulty overflow!"); @@ -1299,6 +1308,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: LOG_PRINT_L3("Blockchain::" << __func__); std::vector timestamps; std::vector cumulative_difficulties; + uint8_t version = get_current_hard_fork_version(); // if the alt chain isn't long enough to calculate the difficulty target // based on its blocks alone, need to get more blocks from the main chain @@ -1354,7 +1364,11 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: size_t target = DIFFICULTY_TARGET_V2; // calculate the difficulty target for the block and return it - return next_difficulty(timestamps, cumulative_difficulties, target); + if (version == 1) { + return next_difficulty(timestamps, cumulative_difficulties, target); + } else { + return next_difficulty_v2(timestamps, cumulative_difficulties, target); + } } //------------------------------------------------------------------ // This function does a sanity check on basic things that all miner @@ -3576,6 +3590,34 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context return true; } //------------------------------------------------------------------ +bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verification_context &tvc) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + const uint8_t hf_version = m_hardfork->get_current_version(); + + // Prior to v2, only allow TX v1/v2 + if (hf_version < HF_VERSION_ENABLE_N_OUTS) { + if (tx.version >= TRANSACTION_VERSION_N_OUTS) { + MERROR_VER("N-out TXs are not permitted prior to v" + std::to_string(HF_VERSION_ENABLE_N_OUTS)); + tvc.m_version_mismatch = true; + return false; + } + } + + // After v2 allow N-out TXs for TRANSFER ONLY + if (hf_version >= HF_VERSION_ENABLE_N_OUTS) { + if (tx.version >= TRANSACTION_VERSION_N_OUTS && tx.type != cryptonote::transaction_type::TRANSFER) { + MERROR("N-out TXs are only permitted for TRANSFER TX type"); + tvc.m_version_mismatch = true; + return false; + } + } + + return true; +} +//------------------------------------------------------------------ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const { LOG_PRINT_L3("Blockchain::" << __func__);