mirror of
https://git.wownero.com/wownero/wownero.git
synced 2024-08-15 01:03:23 +00:00
added LWMA
This commit is contained in:
parent
328b7be391
commit
2c959a9b21
5 changed files with 126 additions and 15 deletions
|
@ -33,6 +33,7 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <boost/math/special_functions/round.hpp>
|
||||||
|
|
||||||
#include "common/int-util.h"
|
#include "common/int-util.h"
|
||||||
#include "crypto/hash.h"
|
#include "crypto/hash.h"
|
||||||
|
@ -162,4 +163,81 @@ namespace cryptonote {
|
||||||
return (low + time_span - 1) / time_span;
|
return (low + time_span - 1) / time_span;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 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.
|
||||||
|
|
||||||
|
// This algorithm is: next_difficulty = harmonic_mean(Difficulties) * T / LWMA(Solvetimes)
|
||||||
|
// The harmonic_mean(Difficulties) = 1/average(Targets) so it is also:
|
||||||
|
// next_target = avg(Targets) * LWMA(Solvetimes) / T.
|
||||||
|
// This is "the best algorithm" because it has lowest root-mean-square error between
|
||||||
|
// needed & actual difficulty during hash attacks while having the lowest standard
|
||||||
|
// deviation during stable hashrate. That is, it's the fastest for a given stability and vice versa.
|
||||||
|
// Do not use "if solvetime < 1 then solvetime = 1" which allows a catastrophic exploit.
|
||||||
|
// Do not sort timestamps. "Solvetimes" and "LWMA" variables must allow negatives.
|
||||||
|
// Do not use MTP as most recent block. Do not use (POW)Limits, filtering, or tempering.
|
||||||
|
// Do not forget to set N (aka DIFFICULTY_WINDOW in Cryptonote) to recommendation below.
|
||||||
|
// The nodes' future time limit (FTL) aka CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT needs to
|
||||||
|
// be reduced from 60*60*2 to 500 seconds to prevent timestamp manipulation from miner's with
|
||||||
|
// > 50% hash power. If this is too small, it can be increased to 1000 at a cost in protection.
|
||||||
|
|
||||||
|
// Cryptonote clones: #define DIFFICULTY_BLOCKS_COUNT_V2 DIFFICULTY_WINDOW_V2 + 1
|
||||||
|
|
||||||
|
|
||||||
|
difficulty_type next_difficulty_v2(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_V2;
|
||||||
|
|
||||||
|
// Return a difficulty of 1 for first 3 blocks if it's the start of the chain.
|
||||||
|
if (timestamps.size() < 4) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
// 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.999 for 80 < N < 120(?)
|
||||||
|
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 * 7), std::max<int64_t>(solveTime, (-7 * T)));
|
||||||
|
difficulty = cumulative_difficulties[i] - cumulative_difficulties[i - 1];
|
||||||
|
LWMA += solveTime * i / k;
|
||||||
|
sum_inverse_D += 1 / static_cast<double>(difficulty);
|
||||||
|
}
|
||||||
|
|
||||||
|
harmonic_mean_D = N / sum_inverse_D;
|
||||||
|
|
||||||
|
// Keep LWMA sane in case something unforeseen occurs.
|
||||||
|
if (static_cast<int64_t>(boost::math::round(LWMA)) < T / 20)
|
||||||
|
LWMA = static_cast<double>(T / 20);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,4 +53,5 @@ namespace cryptonote
|
||||||
*/
|
*/
|
||||||
bool check_hash(const crypto::hash &hash, difficulty_type difficulty);
|
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(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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#define CURRENT_TRANSACTION_VERSION 2
|
#define CURRENT_TRANSACTION_VERSION 2
|
||||||
#define CURRENT_BLOCK_MAJOR_VERSION 7
|
#define CURRENT_BLOCK_MAJOR_VERSION 7
|
||||||
#define CURRENT_BLOCK_MINOR_VERSION 7
|
#define CURRENT_BLOCK_MINOR_VERSION 7
|
||||||
|
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2 300*2
|
||||||
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
|
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
|
||||||
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 10
|
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 10
|
||||||
|
|
||||||
|
@ -74,9 +75,11 @@
|
||||||
|
|
||||||
#define DIFFICULTY_TARGET_V2 300
|
#define DIFFICULTY_TARGET_V2 300
|
||||||
#define DIFFICULTY_TARGET_V1 300
|
#define DIFFICULTY_TARGET_V1 300
|
||||||
|
#define DIFFICULTY_WINDOW_V2 60
|
||||||
#define DIFFICULTY_WINDOW 720 // blocks
|
#define DIFFICULTY_WINDOW 720 // blocks
|
||||||
#define DIFFICULTY_LAG 15 // !!!
|
#define DIFFICULTY_LAG 15 // !!!
|
||||||
#define DIFFICULTY_CUT 60 // timestamps to cut after sorting
|
#define DIFFICULTY_CUT 60 // timestamps to cut after sorting
|
||||||
|
#define DIFFICULTY_BLOCKS_COUNT_V2 DIFFICULTY_WINDOW_V2 + 1 // added +1 to make N=N
|
||||||
#define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG
|
#define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,8 @@ static const struct {
|
||||||
time_t time;
|
time_t time;
|
||||||
} testnet_hard_forks[] = {
|
} testnet_hard_forks[] = {
|
||||||
//{ 1, 1, 0, 1341378000 },
|
//{ 1, 1, 0, 1341378000 },
|
||||||
{ 7, 1, 0, 1519605000 }
|
{ 7, 1, 0, 1519605000 },
|
||||||
|
{ 8, 10, 0, 1523255371 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint64_t testnet_hard_fork_version_1_till = ((uint64_t)(1));
|
static const uint64_t testnet_hard_fork_version_1_till = ((uint64_t)(1));
|
||||||
|
@ -780,20 +781,29 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
|
||||||
std::vector<uint64_t> timestamps;
|
std::vector<uint64_t> timestamps;
|
||||||
std::vector<difficulty_type> difficulties;
|
std::vector<difficulty_type> difficulties;
|
||||||
auto height = m_db->height();
|
auto height = m_db->height();
|
||||||
|
|
||||||
|
uint8_t version = get_current_hard_fork_version();
|
||||||
|
size_t difficulty_blocks_count;
|
||||||
|
if (version == 7) {
|
||||||
|
difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT;
|
||||||
|
} else {
|
||||||
|
difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT_V2;
|
||||||
|
}
|
||||||
|
|
||||||
// ND: Speedup
|
// ND: Speedup
|
||||||
// 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty,
|
// 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
|
// 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
|
// pop the oldest one from the list. This only requires 1x read per height instead
|
||||||
// of doing 735 (DIFFICULTY_BLOCKS_COUNT).
|
// of doing 735 (DIFFICULTY_BLOCKS_COUNT).
|
||||||
if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1))
|
if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= difficulty_blocks_count)
|
||||||
{
|
{
|
||||||
uint64_t index = height - 1;
|
uint64_t index = height - 1;
|
||||||
m_timestamps.push_back(m_db->get_block_timestamp(index));
|
m_timestamps.push_back(m_db->get_block_timestamp(index));
|
||||||
m_difficulties.push_back(m_db->get_block_cumulative_difficulty(index));
|
m_difficulties.push_back(m_db->get_block_cumulative_difficulty(index));
|
||||||
|
|
||||||
while (m_timestamps.size() > DIFFICULTY_BLOCKS_COUNT)
|
while (m_timestamps.size() > difficulty_blocks_count)
|
||||||
m_timestamps.erase(m_timestamps.begin());
|
m_timestamps.erase(m_timestamps.begin());
|
||||||
while (m_difficulties.size() > DIFFICULTY_BLOCKS_COUNT)
|
while (m_difficulties.size() > difficulty_blocks_count)
|
||||||
m_difficulties.erase(m_difficulties.begin());
|
m_difficulties.erase(m_difficulties.begin());
|
||||||
|
|
||||||
m_timestamps_and_difficulties_height = height;
|
m_timestamps_and_difficulties_height = height;
|
||||||
|
@ -802,7 +812,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
|
||||||
}
|
}
|
||||||
else
|
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>(difficulty_blocks_count));
|
||||||
if (offset == 0)
|
if (offset == 0)
|
||||||
++offset;
|
++offset;
|
||||||
|
|
||||||
|
@ -818,8 +828,12 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
|
||||||
m_timestamps = timestamps;
|
m_timestamps = timestamps;
|
||||||
m_difficulties = difficulties;
|
m_difficulties = difficulties;
|
||||||
}
|
}
|
||||||
size_t target = get_difficulty_target();
|
size_t target = DIFFICULTY_TARGET_V2;
|
||||||
|
if (version == 7) {
|
||||||
return next_difficulty(timestamps, difficulties, target);
|
return next_difficulty(timestamps, difficulties, target);
|
||||||
|
} else {
|
||||||
|
return next_difficulty_v2(timestamps, difficulties, target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
// This function removes blocks from the blockchain until it gets to the
|
// This function removes blocks from the blockchain until it gets to the
|
||||||
|
@ -967,16 +981,23 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
|
||||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||||
std::vector<uint64_t> timestamps;
|
std::vector<uint64_t> timestamps;
|
||||||
std::vector<difficulty_type> cumulative_difficulties;
|
std::vector<difficulty_type> cumulative_difficulties;
|
||||||
|
uint8_t version = get_current_hard_fork_version();
|
||||||
|
size_t difficulty_blocks_count;
|
||||||
|
if (version == 7) {
|
||||||
|
difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT;
|
||||||
|
} else {
|
||||||
|
difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT_V2;
|
||||||
|
}
|
||||||
|
|
||||||
// if the alt chain isn't long enough to calculate the difficulty target
|
// 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
|
// 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()< difficulty_blocks_count)
|
||||||
{
|
{
|
||||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||||
|
|
||||||
// Figure out start and stop offsets for main chain blocks
|
// 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_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 = difficulty_blocks_count - std::min(static_cast<size_t>(difficulty_blocks_count), alt_chain.size());
|
||||||
main_chain_count = std::min(main_chain_count, main_chain_stop_offset);
|
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;
|
size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count;
|
||||||
|
|
||||||
|
@ -991,7 +1012,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?
|
// 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()) <= difficulty_blocks_count, false, "Internal error, alt_chain.size()[" << alt_chain.size() << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT);
|
||||||
|
|
||||||
for (auto it : alt_chain)
|
for (auto it : alt_chain)
|
||||||
{
|
{
|
||||||
|
@ -1003,8 +1024,8 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
|
||||||
// and timestamps from it alone
|
// and timestamps from it alone
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
timestamps.resize(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
|
timestamps.resize(static_cast<size_t>(difficulty_blocks_count));
|
||||||
cumulative_difficulties.resize(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
|
cumulative_difficulties.resize(static_cast<size_t>(difficulty_blocks_count));
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
size_t max_i = timestamps.size()-1;
|
size_t max_i = timestamps.size()-1;
|
||||||
// get difficulties and timestamps from most recent blocks in alt chain
|
// get difficulties and timestamps from most recent blocks in alt chain
|
||||||
|
@ -1013,7 +1034,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
|
||||||
timestamps[max_i - count] = it->second.bl.timestamp;
|
timestamps[max_i - count] = it->second.bl.timestamp;
|
||||||
cumulative_difficulties[max_i - count] = it->second.cumulative_difficulty;
|
cumulative_difficulties[max_i - count] = it->second.cumulative_difficulty;
|
||||||
count++;
|
count++;
|
||||||
if(count >= DIFFICULTY_BLOCKS_COUNT)
|
if(count >= difficulty_blocks_count)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1022,8 +1043,13 @@ 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;
|
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
|
// calculate the difficulty target for the block and return it
|
||||||
|
if (version == 7) {
|
||||||
return next_difficulty(timestamps, cumulative_difficulties, target);
|
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
|
// This function does a sanity check on basic things that all miner
|
||||||
// transactions have in common, such as:
|
// transactions have in common, such as:
|
||||||
|
@ -3174,7 +3200,8 @@ bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const
|
||||||
bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) const
|
bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) const
|
||||||
{
|
{
|
||||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||||
if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT)
|
uint64_t cryptonote_block_future_time_limit = get_current_hard_fork_version() < 7 ? CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT : CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2;
|
||||||
|
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 + 2 hours");
|
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -370,6 +370,8 @@ namespace nodetool
|
||||||
{
|
{
|
||||||
std::set<std::string> full_addrs;
|
std::set<std::string> full_addrs;
|
||||||
if (nettype == cryptonote::TESTNET) {
|
if (nettype == cryptonote::TESTNET) {
|
||||||
|
full_addrs.insert("167.160.87.144:11180");
|
||||||
|
full_addrs.insert("5.255.86.129:11180");
|
||||||
} else {
|
} else {
|
||||||
full_addrs.insert("66.70.218.230:34567");
|
full_addrs.insert("66.70.218.230:34567");
|
||||||
full_addrs.insert("34.209.48.213:34567");
|
full_addrs.insert("34.209.48.213:34567");
|
||||||
|
|
Loading…
Reference in a new issue