Skip to content

Commit

Permalink
New difficulty algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
stellitecoin committed Mar 9, 2018
1 parent 906d7bb commit 5a602ba
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/cryptonote_basic/cryptonote_basic_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ namespace cryptonote {
//-----------------------------------------------------------------------------------------------
size_t get_min_block_size(uint8_t version)
{
if (version < 2)
if (version < 3)
return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
if (version < 5)
return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2;
Expand Down
152 changes: 152 additions & 0 deletions src/cryptonote_basic/difficulty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,18 @@
#include <cassert>
#include <cstddef>
#include <cstdint>
#include "include_base_utils.h"
#include <vector>

#include "common/int-util.h"
#include "crypto/hash.h"
#include "cryptonote_config.h"
#include "misc_language.h"
#include "difficulty.h"

#define MAX_AVERAGE_TIMESPAN (uint64_t) DIFFICULTY_TARGET*6
#define MIN_AVERAGE_TIMESPAN (uint64_t) DIFFICULTY_TARGET/24

#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "difficulty"

Expand Down Expand Up @@ -162,4 +167,151 @@ namespace cryptonote {
return (low + time_span - 1) / time_span;
}

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

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

size_t length = timestamps.size();
assert(length == cumulative_difficulties.size());
if (length <= 1) {
return 1;
}

sort(timestamps.begin(), timestamps.end());
size_t cut_begin, cut_end;
static_assert(2 * DIFFICULTY_CUT_V2 <= DIFFICULTY_BLOCKS_COUNT_V2 - 2, "Cut length is too large");
if (length <= DIFFICULTY_BLOCKS_COUNT_V2 - 2 * DIFFICULTY_CUT_V2) {
cut_begin = 0;
cut_end = length;
}
else {
cut_begin = (length - (DIFFICULTY_BLOCKS_COUNT_V2 - 2 * DIFFICULTY_CUT_V2) + 1) / 2;
cut_end = cut_begin + (DIFFICULTY_BLOCKS_COUNT_V2 - 2 * DIFFICULTY_CUT_V2);
}
assert(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length);
uint64_t total_timespan = timestamps[cut_end - 1] - timestamps[cut_begin];
if (total_timespan == 0) {
total_timespan = 1;
}

uint64_t timespan_median = 0;
if (cut_begin > 0 && length >= cut_begin * 2 + 3){
std::vector<std::uint64_t> time_spans;
for (size_t i = length - cut_begin * 2 - 3; i < length - 1; i++){
uint64_t time_span = timestamps[i + 1] - timestamps[i];
if (time_span == 0) {
time_span = 1;
}
time_spans.push_back(time_span);

//LOG_PRINT_L3("Timespan " << i << ": " << (time_span / 60) / 60 << ":" << (time_span > 3600 ? (time_span % 3600) / 60 : time_span / 60) << ":" << time_span % 60 << " (" << time_span << ")");
}
timespan_median = epee::misc_utils::median(time_spans);
}

uint64_t timespan_length = length - cut_begin * 2 - 1;
//LOG_PRINT_L2("Timespan Median: " << timespan_median << ", Timespan Average: " << total_timespan / timespan_length);

uint64_t total_timespan_median = timespan_median > 0 ? timespan_median * timespan_length : total_timespan * 7 / 10;
uint64_t adjusted_total_timespan = (total_timespan * 8 + total_timespan_median * 3) / 10; // 0.8A + 0.3M (the median of a poisson distribution is 70% of the mean, so 0.25A = 0.25/0.7 = 0.285M)
if (adjusted_total_timespan > MAX_AVERAGE_TIMESPAN * timespan_length){
adjusted_total_timespan = MAX_AVERAGE_TIMESPAN * timespan_length;
}
if (adjusted_total_timespan < MIN_AVERAGE_TIMESPAN * timespan_length){
adjusted_total_timespan = MIN_AVERAGE_TIMESPAN * timespan_length;
}

difficulty_type 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);
if (high != 0) {
return 0;
}

uint64_t next_diff = (low + adjusted_total_timespan - 1) / adjusted_total_timespan;
if (next_diff < 1) next_diff = 1;
//LOG_PRINT_L2("Total timespan: " << total_timespan << ", Adjusted total timespan: " << adjusted_total_timespan << ", Total work: " << total_work << ", Next diff: " << next_diff << ", Hashrate (H/s): " << next_diff / target_seconds);

return next_diff;
}

difficulty_type next_difficulty_v3(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds, bool v4) {

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

size_t length = timestamps.size();
assert(length == cumulative_difficulties.size());
if (length <= 1) {
return 1;
}

uint64_t weighted_timespans = 0;
uint64_t target;

if (true) {
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;
}
// adjust = 0.99 for N=60, leaving the + 1 for now as it's not affecting N
target = 99 * (((length + 1) / 2) * target_seconds) / 100;
} else {
for (size_t i = 1; i < length; i++) {
uint64_t timespan;
if (timestamps[i - 1] >= timestamps[i]) {
timespan = 1;
} else {
timespan = timestamps[i] - timestamps[i - 1];
}
if (timespan > 10 * target_seconds) {
timespan = 10 * target_seconds;
}
weighted_timespans += i * timespan;
}
target = ((length + 1) / 2) * target_seconds;
}

uint64_t minimum_timespan = target_seconds * length / 2;
if (weighted_timespans < minimum_timespan) {
weighted_timespans = minimum_timespan;
}

difficulty_type total_work = cumulative_difficulties.back() - cumulative_difficulties.front();
assert(total_work > 0);

uint64_t low, high;
mul(total_work, target, low, high);
if (high != 0) {
return 0;
}
return low / weighted_timespans;
}

}
2 changes: 2 additions & 0 deletions src/cryptonote_basic/difficulty.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,6 @@ 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_v2(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
difficulty_type next_difficulty_v3(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds, bool v4);
}
7 changes: 7 additions & 0 deletions src/cryptonote_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@
#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_V2 60*24
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 10

#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 60
#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 12

// MONEY_SUPPLY - total number coins to be generated
#define MONEY_SUPPLY ((uint64_t)(2100000000000))
Expand All @@ -74,10 +76,15 @@
#define DIFFICULTY_TARGET 60 // seconds
#define DIFFICULTY_TARGET_V1 60 // seconds
#define DIFFICULTY_WINDOW 720 // blocks
#define DIFFICULTY_WINDOW_V2 17
#define DIFFICULTY_LAG 15 // !!!
#define DIFFICULTY_CUT 60 // timestamps to cut after sorting
#define DIFFICULTY_CUT_V2 6
#define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG
#define DIFFICULTY_BLOCKS_COUNT_V2 DIFFICULTY_WINDOW_V2 + DIFFICULTY_CUT_V2*2

#define DIFFICULTY_WINDOW_V3 90
#define DIFFICULTY_BLOCKS_COUNT_V3 DIFFICULTY_WINDOW_V3

#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS DIFFICULTY_TARGET * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS 1
Expand Down
49 changes: 29 additions & 20 deletions src/cryptonote_core/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ static const struct {
} mainnet_hard_forks[] = {
// version 1 from the start of the blockchain
{ 1, 1, 0, 1341378000 },

// version 2 starts from block 1009827, which is on or around the 20th of March, 2016. Fork time finalised on 2015-09-20. No fork voting occurs for the v2 fork.
//{ 2, 1009827, 0, 1442763710 },
// Version 2 starts from block 67500, fork time finalaised on 2018-03-09
{2,67500,0,1520584977},

// version 3 starts from block 1141317, which is on or around the 24th of September, 2016. Fork time finalised on 2016-03-21.
//{ 3, 1141317, 0, 1458558528 },
Expand All @@ -104,8 +104,10 @@ static const struct {

// version 6 starts from block 1400000, which is on or around the 16th of September, 2017. Fork time finalised on 2017-08-18.
//{ 6, 1400000, 0, 1503046577 },


};
static const uint64_t mainnet_hard_fork_version_1_till = 1009826;
static const uint64_t mainnet_hard_fork_version_1_till = (uint64_t)-1;

static const struct {
uint8_t version;
Expand All @@ -126,7 +128,7 @@ static const struct {

//{ 6, 971400, 0, 1501709789 },
};
static const uint64_t testnet_hard_fork_version_1_till = 624633;
static const uint64_t testnet_hard_fork_version_1_till = (uint64_t)-1;

//------------------------------------------------------------------
Blockchain::Blockchain(tx_memory_pool& tx_pool) :
Expand Down Expand Up @@ -713,7 +715,8 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> difficulties;
auto height = m_db->height();
// ND: Speedup
size_t difficult_block_count = get_current_hard_fork_version() < 2 ? DIFFICULTY_BLOCKS_COUNT : DIFFICULTY_BLOCKS_COUNT_V3;
// ND: Speedup
// 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty,
// then when the next block difficulty is queried, push the latest height data and
// pop the oldest one from the list. This only requires 1x read per height instead
Expand All @@ -724,9 +727,9 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
m_timestamps.push_back(m_db->get_block_timestamp(index));
m_difficulties.push_back(m_db->get_block_cumulative_difficulty(index));

while (m_timestamps.size() > DIFFICULTY_BLOCKS_COUNT)
while (m_timestamps.size() > difficult_block_count)
m_timestamps.erase(m_timestamps.begin());
while (m_difficulties.size() > DIFFICULTY_BLOCKS_COUNT)
while (m_difficulties.size() > difficult_block_count)
m_difficulties.erase(m_difficulties.begin());

m_timestamps_and_difficulties_height = height;
Expand All @@ -735,7 +738,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
}
else
{
size_t offset = height - std::min < size_t > (height, static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
size_t offset = height - std::min < size_t >(height, static_cast<size_t>(difficult_block_count));
if (offset == 0)
++offset;

Expand All @@ -752,7 +755,9 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
m_difficulties = difficulties;
}
size_t target = DIFFICULTY_TARGET;
return next_difficulty(timestamps, difficulties, target);
return get_current_hard_fork_version() < 2 ?
next_difficulty(timestamps, difficulties, target) :
next_difficulty_v3(timestamps, difficulties, target, true);
}
//------------------------------------------------------------------
// This function removes blocks from the blockchain until it gets to the
Expand Down Expand Up @@ -900,16 +905,18 @@ 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 difficult_block_count = get_current_hard_fork_version() < 2 ? DIFFICULTY_BLOCKS_COUNT : DIFFICULTY_BLOCKS_COUNT_V3;


// 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
if(alt_chain.size()< DIFFICULTY_BLOCKS_COUNT)
if(alt_chain.size()< difficult_block_count)
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);

// Figure out start and stop offsets for main chain blocks
size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height;
size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT), alt_chain.size());
size_t main_chain_count = difficult_block_count - std::min(static_cast<size_t>(difficult_block_count), alt_chain.size());
main_chain_count = std::min(main_chain_count, main_chain_stop_offset);
size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count;

Expand All @@ -924,7 +931,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
}

// make sure we haven't accidentally grabbed too many blocks...maybe don't need this check?
CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()[" << alt_chain.size() << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT);
CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= difficult_block_count, false, "Internal error, alt_chain.size()[" << alt_chain.size() << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << difficult_block_count);

for (auto it : alt_chain)
{
Expand All @@ -936,8 +943,8 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
// and timestamps from it alone
else
{
timestamps.resize(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
cumulative_difficulties.resize(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
timestamps.resize(static_cast<size_t>(difficult_block_count));
cumulative_difficulties.resize(static_cast<size_t>(difficult_block_count));
size_t count = 0;
size_t max_i = timestamps.size()-1;
// get difficulties and timestamps from most recent blocks in alt chain
Expand All @@ -946,7 +953,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
timestamps[max_i - count] = it->second.bl.timestamp;
cumulative_difficulties[max_i - count] = it->second.cumulative_difficulty;
count++;
if(count >= DIFFICULTY_BLOCKS_COUNT)
if(count >= difficult_block_count)
break;
}
}
Expand All @@ -955,7 +962,9 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
size_t target = DIFFICULTY_TARGET;

// calculate the difficulty target for the block and return it
return next_difficulty(timestamps, cumulative_difficulties, target);
return get_current_hard_fork_version() < 2 ?
next_difficulty(timestamps, cumulative_difficulties, target) :
next_difficulty_v3(timestamps, cumulative_difficulties, target, true);
}
//------------------------------------------------------------------
// This function does a sanity check on basic things that all miner
Expand Down Expand Up @@ -1021,7 +1030,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
return false;
}
// From hard fork 2, we allow a miner to claim less block reward than is allowed, in case a miner wants less dust
if (m_hardfork->get_current_version() < 2)
if (m_hardfork->get_current_version() < 3)
{
if(base_reward + fee != money_in_use && already_generated_coins > 0)
{
Expand Down Expand Up @@ -2296,7 +2305,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
CRITICAL_REGION_LOCAL(m_blockchain_lock);

// from hard fork 2, we forbid dust and compound outputs
if (m_hardfork->get_current_version() >= 2) {
if (m_hardfork->get_current_version() >= 3) {
for (auto &o: tx.vout) {
if (tx.version == 1)
{
Expand Down Expand Up @@ -2435,7 +2444,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,

// from hard fork 2, we require mixin at least 2 unless one output cannot mix with 2 others
// if one output cannot mix with 2 others, we accept at most 1 output that can mix
if (hf_version >= 2)
if (hf_version >= 3)
{
size_t n_unmixable = 0, n_mixable = 0;
size_t mixin = std::numeric_limits<size_t>::max();
Expand Down
4 changes: 2 additions & 2 deletions src/cryptonote_core/cryptonote_tx_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@ namespace cryptonote
// from hard fork 4, we use a single "dusty" output. This makes the tx even smaller,
// and avoids the quantization. These outputs will be added as rct outputs with identity
// masks, to they can be used as rct inputs.
if (hard_fork_version >= 2 && hard_fork_version < 4) {
if (hard_fork_version >= 3 && hard_fork_version < 4) {
block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD;
}

std::vector<uint64_t> out_amounts;
decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD,
decompose_amount_into_digits(block_reward, hard_fork_version >= 3 ? 0 : ::config::DEFAULT_DUST_THRESHOLD,
[&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); },
[&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });

Expand Down
Loading

0 comments on commit 5a602ba

Please sign in to comment.