Skip to content

Commit

Permalink
Merge pull request monero-project#6 from cryptobender69/dev
Browse files Browse the repository at this point in the history
LWMA DAA for V2
  • Loading branch information
cryptobender69 authored Apr 16, 2018
2 parents 88b956d + 03e96ee commit 545e798
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 32 deletions.
69 changes: 68 additions & 1 deletion src/cryptonote_basic/difficulty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <cstddef>
#include <cstdint>
#include <vector>
#include <boost/math/special_functions/round.hpp>

#include "common/int-util.h"
#include "crypto/hash.h"
Expand Down Expand Up @@ -130,7 +131,7 @@ namespace cryptonote {

size_t length = timestamps.size();
assert(length == cumulative_difficulties.size());
// Wind down block diff to ease upcoming fork resistance, this will return diff of 1000 for blocks < 241499.
// Wind down block diff to ease upcoming fork resistance, this will return diff of 1000 for blocks < 241499.
// Diff stabilize once block 241499 is mined and algo will track diff and target solve post fork.
if (length <= 1) {
return 1;
Expand Down Expand Up @@ -167,4 +168,70 @@ 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) {

// 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<int64_t>(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 double adjust = 0.998;
// The divisor k normalizes 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.
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);
}

// 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);

harmonic_mean_D = N / sum_inverse_D * adjust;
nextDifficulty = harmonic_mean_D * T / LWMA;
next_difficulty = static_cast<uint64_t>(nextDifficulty);

// you can also maintain the difficulty like this
//if(next_difficulty < 19924656977){
// next_difficulty = 19924656977;
//}

return next_difficulty;
}



}
16 changes: 9 additions & 7 deletions src/cryptonote_basic/difficulty.h
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
// Copyright (c) 2014-2018, 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
Expand All @@ -25,7 +25,7 @@
// 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

#pragma once
Expand Down 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);

}
21 changes: 13 additions & 8 deletions src/cryptonote_config.h
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
// Copyright (c) 2014-2018, 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
Expand All @@ -25,7 +25,7 @@
// 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

#pragma once
Expand Down Expand Up @@ -72,13 +72,18 @@
#define ORPHANED_BLOCKS_MAX_COUNT 100


#define DIFFICULTY_TARGET_V2 120 // seconds

#define DIFFICULTY_TARGET_V1 60 // seconds - before first fork
#define DIFFICULTY_WINDOW 720 // blocks
#define DIFFICULTY_LAG 15 // !!!
#define DIFFICULTY_CUT 60 // timestamps to cut after sorting
#define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG

#define DIFFICULTY_TARGET_V2 120 // seconds
#define DIFFICULTY_WINDOW_V2 60
#define DIFFICULTY_BLOCKS_COUNT_V2 DIFFICULTY_WINDOW_V2
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2 500


#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
72 changes: 56 additions & 16 deletions src/cryptonote_core/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@ static const struct {

// // 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 },

// // version 4 starts from block 1220516, which is on or around the 5th of January, 2017. Fork time finalised on 2016-09-18.
// { 4, 1220516, 0, 1483574400 },

// // version 5 starts from block 1288616, which is on or around the 15th of April, 2017. Fork time finalised on 2017-03-14.
// { 5, 1288616, 0, 1489520158 },
// { 5, 1288616, 0, 1489520158 },

// // version 6 starts from block 1400000, which is on or around the 16th of September, 2017. Fork time finalised on 2017-08-18.
{ 6, MAINNET_HARDFORK_V6_HEIGHT, 0, 1503046577 },
Expand Down Expand Up @@ -756,8 +756,20 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> difficulties;
auto height = m_db->height();

uint8_t version = get_current_hard_fork_version();
size_t difficulty_blocks_count;

// pick DIFFICULTY_BLOCKS_COUNT based on version
if (version == 1) {
difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT;
} else {
difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT_V2;
}


// Reset network hashrate to 333.0 MHz when hardfork v2 comes
if ((uint64_t)height >= MAINNET_HARDFORK_V2_HEIGHT + 1 && (uint64_t)height <= MAINNET_HARDFORK_V2_HEIGHT + (uint64_t)DIFFICULTY_BLOCKS_COUNT){
if ((uint64_t)height >= MAINNET_HARDFORK_V2_HEIGHT + 1 && (uint64_t)height <= MAINNET_HARDFORK_V2_HEIGHT + (uint64_t)difficulty_blocks_count){
return (difficulty_type) 19924656977;
}
// ND: Speedup
Expand All @@ -771,9 +783,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() > difficulty_blocks_count)
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_timestamps_and_difficulties_height = height;
Expand All @@ -782,7 +794,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>(difficulty_blocks_count));
if (offset == 0)
++offset;

Expand All @@ -799,7 +811,12 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
m_difficulties = difficulties;
}
size_t target = get_difficulty_target();
return next_difficulty(timestamps, difficulties, target);

if (version == 1) {
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
Expand Down Expand Up @@ -948,15 +965,24 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> cumulative_difficulties;

uint8_t version = get_current_hard_fork_version();
size_t difficulty_blocks_count;
// pick DIFFICULTY_BLOCKS_COUNT based on version
if (version == 1) {
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
// 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);

// 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 = 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);
size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count;

Expand All @@ -971,7 +997,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()) <= 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)
{
Expand All @@ -983,8 +1009,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>(difficulty_blocks_count));
cumulative_difficulties.resize(static_cast<size_t>(difficulty_blocks_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 @@ -993,7 +1019,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 >= difficulty_blocks_count)
break;
}
}
Expand All @@ -1002,7 +1028,12 @@ 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 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
Expand Down Expand Up @@ -3155,8 +3186,17 @@ bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const
// false otherwise
bool Blockchain::check_block_timestamp(const block& b) const
{

uint8_t version = get_current_hard_fork_version();
uint64_t block_future_time_limit;
if (version == 1) {
block_future_time_limit = CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT;
} else {
block_future_time_limit = CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2;
}

LOG_PRINT_L3("Blockchain::" << __func__);
if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT)
if(b.timestamp > get_adjusted_time() + block_future_time_limit)
{
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours");
return false;
Expand Down

0 comments on commit 545e798

Please sign in to comment.