Skip to content

Commit

Permalink
Update for v8 with modified LWMA.
Browse files Browse the repository at this point in the history
  • Loading branch information
thaerkh committed May 3, 2018
1 parent 8539a4e commit cc89d3d
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 72 deletions.
111 changes: 53 additions & 58 deletions src/cryptonote_basic/difficulty.cpp
Expand Up @@ -162,71 +162,66 @@ namespace cryptonote {
return (low + time_span - 1) / time_span;
}

difficulty_type next_difficulty_v7(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds) {

if (timestamps.size() > DIFFICULTY_BLOCKS_COUNT_V7)
{
timestamps.resize(DIFFICULTY_BLOCKS_COUNT_V7);
cumulative_difficulties.resize(DIFFICULTY_BLOCKS_COUNT_V7);
}

size_t length = timestamps.size();
assert(length == cumulative_difficulties.size());
if (length <= 1) {
// LWMA difficulty algorithm
// Background: https://github.com/zawy12/difficulty-algorithms/issues/3
// Copyright (c) 2017-2018 Zawy (pseudocode)
// MIT license http://www.opensource.org/licenses/mit-license.php
// Copyright (c) 2018 The Masari Project (10x max for quicker recoveries, minimum to be symmetric with FTL)
// Copyright (c) 2018 Wownero Inc., a Monero Enterprise Alliance partner company
// Copyright (c) 2018 The Karbowanec developers (initial code)
// Copyright (c) 2018 Haven Protocol (refinements)
// Degnr8, Karbowanec, Masari, Bitcoin Gold, Bitcoin Candy, and Haven have contributed.
difficulty_type next_difficulty_v8(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds) {

const int64_t T = static_cast<int64_t>(target_seconds);
size_t N = DIFFICULTY_WINDOW_V8;
int64_t FTL = static_cast<int64_t>(CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V8);

// Return a difficulty of 1 for first 3 blocks if it's the start of the chain.
if (timestamps.size() < 4) {
return 1;
}

uint64_t weighted_timespans = 0;

uint64_t previous_max = timestamps[0];
for (size_t i = 1; i < length; i++) {
uint64_t timespan;
uint64_t max_timestamp;

if (timestamps[i] > previous_max) {
max_timestamp = timestamps[i];
} else {
max_timestamp = previous_max;
}

timespan = max_timestamp - previous_max;
if (timespan == 0) {
timespan = 1;
} else if (timespan > 10 * target_seconds) {
timespan = 10 * target_seconds;
}

weighted_timespans += i * timespan;
previous_max = max_timestamp;
// Otherwise, use a smaller N if the start of the chain is less than N+1.
else if ( timestamps.size() < N+1 ) {
N = timestamps.size() - 1;
}

// N = length - 1
uint64_t minimum_timespan = target_seconds * (length - 1) / 2;
if (weighted_timespans < minimum_timespan) {
weighted_timespans = minimum_timespan;
// Otherwise make sure timestamps and cumulative_difficulties are correct size.
else {
timestamps.resize(N+1);
cumulative_difficulties.resize(N+1);
}
// To get an average solvetime to within +/- ~0.1%, use an adjustment factor.
// adjust=0.998 for N = 60
const double adjust = 0.998;
// The divisor k normalizes the LWMA sum to a standard LWMA.
const double k = N * (N + 1) / 2;

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. N is most recently solved block.
for (size_t i = 1; i <= N; i++) {
solveTime = static_cast<int64_t>(timestamps[i]) - static_cast<int64_t>(timestamps[i - 1]);
solveTime = std::min<int64_t>((T * 10), std::max<int64_t>(solveTime, -FTL));
difficulty = cumulative_difficulties[i] - cumulative_difficulties[i - 1];
LWMA += (int64_t)(solveTime * i) / k;
sum_inverse_D += 1 / static_cast<double>(difficulty);
}

difficulty_type total_work = cumulative_difficulties.back() - cumulative_difficulties.front();
assert(total_work > 0);
harmonic_mean_D = N / sum_inverse_D;

uint64_t low, high;
// adjust = 0.99 for N=60 ; length = N + 1
uint64_t target = 99 * (length / 2) * target_seconds / 100;
// dividing first gives negligibly smaller difficulties but gives another few magnitudes of overflow protection.
uint64_t whm_work = (total_work / weighted_timespans);
if (whm_work == 0) {
mul(total_work, target, low, high); // This should be unnecessary when difficulty is smaller than the weighted timespans
if (high != 0) {
return 0;
}
return low / weighted_timespans;
} else {
mul(whm_work, target, low, high);
if (high != 0) {
return 0;
}
return low;
}
// Limit LWMA same as Bitcoin's 1/4 in case something unforeseen occurs.
if (static_cast<int64_t>(boost::math::round(LWMA)) < T / 4)
LWMA = static_cast<double>(T / 4);

nextDifficulty = harmonic_mean_D * T / LWMA * adjust;

// No limits should be employed, but this is correct way to employ a 20% symmetrical limit:
// nextDifficulty=max(previous_Difficulty*0.8,min(previous_Difficulty/0.8, next_Difficulty));
next_difficulty = static_cast<uint64_t>(nextDifficulty);
return next_difficulty;
}

}
2 changes: 1 addition & 1 deletion src/cryptonote_basic/difficulty.h
Expand Up @@ -53,5 +53,5 @@ namespace cryptonote
*/
bool check_hash(const crypto::hash &hash, difficulty_type difficulty);
difficulty_type next_difficulty(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
difficulty_type next_difficulty_v7(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
difficulty_type next_difficulty_v8(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
}
8 changes: 4 additions & 4 deletions src/cryptonote_config.h
Expand Up @@ -45,12 +45,12 @@
#define CURRENT_BLOCK_MAJOR_VERSION 1
#define CURRENT_BLOCK_MINOR_VERSION 0
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V7 60*20
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V8 60*5

#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 10

#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 60
#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V7 12
#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V8 12

// MONEY_SUPPLY - total number coins to be generated
#define MONEY_SUPPLY ((uint64_t)(-1))
Expand Down Expand Up @@ -82,8 +82,8 @@
#define DIFFICULTY_CUT 60 // timestamps to cut after sorting
#define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG

#define DIFFICULTY_WINDOW_V7 60
#define DIFFICULTY_BLOCKS_COUNT_V7 DIFFICULTY_WINDOW_V7 + 1
#define DIFFICULTY_WINDOW_V8 60
#define DIFFICULTY_BLOCKS_COUNT_V8 DIFFICULTY_WINDOW_V8 + 1

#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 DIFFICULTY_TARGET_V1 * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 DIFFICULTY_TARGET_V2 * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS
Expand Down
18 changes: 9 additions & 9 deletions src/cryptonote_core/blockchain.cpp
Expand Up @@ -740,7 +740,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> difficulties;
auto height = m_db->height();
size_t difficulty_blocks_count = get_current_hard_fork_version() < 7 ? DIFFICULTY_BLOCKS_COUNT : DIFFICULTY_BLOCKS_COUNT_V7;
size_t difficulty_blocks_count = get_current_hard_fork_version() < 8 ? DIFFICULTY_BLOCKS_COUNT : DIFFICULTY_BLOCKS_COUNT_V8;

// ND: Speedup
// 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty,
Expand Down Expand Up @@ -781,7 +781,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
m_difficulties = difficulties;
}
size_t target = get_difficulty_target();
return get_current_hard_fork_version() < 7 ? next_difficulty(timestamps, difficulties, target) : next_difficulty_v7(timestamps, difficulties, target);
return get_current_hard_fork_version() < 8 ? next_difficulty(timestamps, difficulties, target) : next_difficulty_v8(timestamps, difficulties, target);
}
//------------------------------------------------------------------
// This function removes blocks from the blockchain until it gets to the
Expand Down Expand Up @@ -929,7 +929,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
LOG_PRINT_L3("Blockchain::" << __func__);
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> cumulative_difficulties;
size_t difficulty_blocks_count = get_current_hard_fork_version() < 7 ? DIFFICULTY_BLOCKS_COUNT : DIFFICULTY_BLOCKS_COUNT_V7;
size_t difficulty_blocks_count = get_current_hard_fork_version() < 8 ? DIFFICULTY_BLOCKS_COUNT : DIFFICULTY_BLOCKS_COUNT_V8;

// 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
Expand Down Expand Up @@ -985,7 +985,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
size_t target = get_ideal_hard_fork_version(bei.height) < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;

// calculate the difficulty target for the block and return it
return get_current_hard_fork_version() < 7 ? next_difficulty(timestamps, cumulative_difficulties, target) : next_difficulty_v7(timestamps, cumulative_difficulties, target);
return get_current_hard_fork_version() < 8 ? next_difficulty(timestamps, cumulative_difficulties, target) : next_difficulty_v8(timestamps, cumulative_difficulties, target);
}
//------------------------------------------------------------------
// This function does a sanity check on basic things that all miner
Expand Down Expand Up @@ -1264,7 +1264,7 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect
{
LOG_PRINT_L3("Blockchain::" << __func__);

size_t blockchain_timestamp_check_window = get_current_hard_fork_version() < 7 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V7;
size_t blockchain_timestamp_check_window = get_current_hard_fork_version() < 8 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V8;
if(timestamps.size() >= blockchain_timestamp_check_window)
return true;

Expand Down Expand Up @@ -3100,7 +3100,7 @@ bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const
LOG_PRINT_L3("Blockchain::" << __func__);
uint64_t median_ts = epee::misc_utils::median(timestamps);

size_t blockchain_timestamp_check_window = get_current_hard_fork_version() < 7 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V7;
size_t blockchain_timestamp_check_window = get_current_hard_fork_version() < 8 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V8;
if(b.timestamp < median_ts)
{
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << blockchain_timestamp_check_window << " blocks, " << median_ts);
Expand All @@ -3120,11 +3120,11 @@ bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const
bool Blockchain::check_block_timestamp(const block& b) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
uint64_t cryptonote_block_future_time_limit = get_current_hard_fork_version() < 7 ? CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT : CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V7;
size_t blockchain_timestamp_check_window = get_current_hard_fork_version() < 7 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V7;
uint64_t cryptonote_block_future_time_limit = get_current_hard_fork_version() < 8 ? CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT : CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V8;
size_t blockchain_timestamp_check_window = get_current_hard_fork_version() < 8 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V8;
if(b.timestamp > get_adjusted_time() + cryptonote_block_future_time_limit)
{
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + " << (get_current_hard_fork_version() < 7 ? "2 hours" : "20 minutes"));
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + " << (get_current_hard_fork_version() < 8 ? "2 hours" : "5 minutes"));
return false;
}

Expand Down

0 comments on commit cc89d3d

Please sign in to comment.