Skip to content

Commit

Permalink
LWMA (WHM) difficulty by Zawy
Browse files Browse the repository at this point in the history
  • Loading branch information
aivve committed May 24, 2018
1 parent bd65b62 commit ead95a5
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 78 deletions.
1 change: 1 addition & 0 deletions src/CryptoNoteConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const uint64_t DIFFICULTY_TARGET = 60; // seconds
const uint64_t EXPECTED_NUMBER_OF_BLOCKS_PER_DAY = 24 * 60 * 60 / DIFFICULTY_TARGET;
const size_t DIFFICULTY_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks
const size_t DIFFICULTY_WINDOW_V2 = 17; // blocks
const size_t DIFFICULTY_WINDOW_V3 = 66; // blocks
const size_t DIFFICULTY_CUT = 60; // timestamps to cut after sorting
const size_t DIFFICULTY_LAG = 15; // !!!
static_assert(2 * DIFFICULTY_CUT <= DIFFICULTY_WINDOW - 2, "Bad DIFFICULTY_WINDOW or DIFFICULTY_CUT");
Expand Down
48 changes: 46 additions & 2 deletions src/CryptoNoteCore/Blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -694,8 +694,11 @@ difficulty_type Blockchain::getDifficultyForNextBlock() {
std::vector<difficulty_type> commulative_difficulties;
uint8_t BlockMajorVersion = getBlockMajorVersionForHeight(static_cast<uint32_t>(m_blocks.size()));
size_t offset;
if (BlockMajorVersion >= BLOCK_MAJOR_VERSION_2) {
if (BlockMajorVersion == BLOCK_MAJOR_VERSION_2) {
offset = m_blocks.size() - std::min(m_blocks.size(), static_cast<uint64_t>(m_currency.difficultyBlocksCount2()));
}
else if (BlockMajorVersion >= BLOCK_MAJOR_VERSION_3) {
offset = m_blocks.size() - std::min(m_blocks.size(), static_cast<uint64_t>(m_currency.difficultyBlocksCount3()));
}
else {
offset = m_blocks.size() - std::min(m_blocks.size(), static_cast<uint64_t>(m_currency.difficultyBlocksCount()));
Expand Down Expand Up @@ -835,7 +838,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
std::vector<difficulty_type> commulative_difficulties;
uint8_t BlockMajorVersion = getBlockMajorVersionForHeight(static_cast<uint32_t>(m_blocks.size()));

if (BlockMajorVersion >= BLOCK_MAJOR_VERSION_2) {
if (BlockMajorVersion == BLOCK_MAJOR_VERSION_2) {

if (alt_chain.size() < m_currency.difficultyBlocksCount2()) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
Expand Down Expand Up @@ -875,6 +878,47 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
}
}

}
else if (BlockMajorVersion >= BLOCK_MAJOR_VERSION_3) {

if (alt_chain.size() < m_currency.difficultyBlocksCount3()) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height;
size_t main_chain_count = m_currency.difficultyBlocksCount3() - std::min(m_currency.difficultyBlocksCount3(), 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;

if (!main_chain_start_offset)
++main_chain_start_offset; //skip genesis block
for (; main_chain_start_offset < main_chain_stop_offset; ++main_chain_start_offset) {
timestamps.push_back(m_blocks[main_chain_start_offset].bl.timestamp);
commulative_difficulties.push_back(m_blocks[main_chain_start_offset].cumulative_difficulty);
}

if (!((alt_chain.size() + timestamps.size()) <= m_currency.difficultyBlocksCount3())) {
logger(ERROR, BRIGHT_RED) << "Internal error, alt_chain.size()[" << alt_chain.size() << "] + timestamps.size()[" << timestamps.size() <<
"] NOT <= m_currency.difficultyBlocksCount()[" << m_currency.difficultyBlocksCount3() << ']'; return false;
}
for (auto it : alt_chain) {
timestamps.push_back(it->second.bl.timestamp);
commulative_difficulties.push_back(it->second.cumulative_difficulty);
}
}
else {
timestamps.resize(std::min(alt_chain.size(), m_currency.difficultyBlocksCount3()));
commulative_difficulties.resize(std::min(alt_chain.size(), m_currency.difficultyBlocksCount3()));
size_t count = 0;
size_t max_i = timestamps.size() - 1;
BOOST_REVERSE_FOREACH(auto it, alt_chain) {
timestamps[max_i - count] = it->second.bl.timestamp;
commulative_difficulties[max_i - count] = it->second.cumulative_difficulty;
count++;
if (count >= m_currency.difficultyBlocksCount3()) {
break;
}
}
}

}
else {

Expand Down
217 changes: 141 additions & 76 deletions src/CryptoNoteCore/Currency.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers
// Copyright (c) 2016, The Karbowanec developers
// Copyright (c) 2016-2018 zawy12
// Copyright (c) 2016-2018, The Karbowanec developers
//
// This file is part of Bytecoin.
//
Expand Down Expand Up @@ -405,108 +406,172 @@ namespace CryptoNote {
difficulty_type Currency::nextDifficulty(uint8_t blockMajorVersion, std::vector<uint64_t> timestamps,
std::vector<difficulty_type> cumulativeDifficulties) const {

// new difficulty calculation
if (blockMajorVersion >= BLOCK_MAJOR_VERSION_3) {
return nextDifficultyV3(timestamps, cumulativeDifficulties);
}
else if (blockMajorVersion == BLOCK_MAJOR_VERSION_2) {
return nextDifficultyV2(timestamps, cumulativeDifficulties);
}
else {
return nextDifficultyV1(timestamps, cumulativeDifficulties);
}
}

difficulty_type Currency::nextDifficultyV1(std::vector<uint64_t> timestamps,
std::vector<difficulty_type> cumulativeDifficulties) const {
assert(m_difficultyWindow >= 2);

if (timestamps.size() > m_difficultyWindow) {
timestamps.resize(m_difficultyWindow);
cumulativeDifficulties.resize(m_difficultyWindow);
}

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

sort(timestamps.begin(), timestamps.end());

size_t cutBegin, cutEnd;
assert(2 * m_difficultyCut <= m_difficultyWindow - 2);
if (length <= m_difficultyWindow - 2 * m_difficultyCut) {
cutBegin = 0;
cutEnd = length;
}
else {
cutBegin = (length - (m_difficultyWindow - 2 * m_difficultyCut) + 1) / 2;
cutEnd = cutBegin + (m_difficultyWindow - 2 * m_difficultyCut);
}
assert(/*cut_begin >= 0 &&*/ cutBegin + 2 <= cutEnd && cutEnd <= length);
uint64_t timeSpan = timestamps[cutEnd - 1] - timestamps[cutBegin];
if (timeSpan == 0) {
timeSpan = 1;
}

difficulty_type totalWork = cumulativeDifficulties[cutEnd - 1] - cumulativeDifficulties[cutBegin];
assert(totalWork > 0);

uint64_t low, high;
low = mul128(totalWork, m_difficultyTarget, &high);
if (high != 0 || low + timeSpan - 1 < low) {
return 0;
}

return (low + timeSpan - 1) / timeSpan;
}

difficulty_type Currency::nextDifficultyV2(std::vector<uint64_t> timestamps,
std::vector<difficulty_type> cumulativeDifficulties) const {

// Difficulty calculation v. 2
// based on Zawy difficulty algorithm v1.0
// next Diff = Avg past N Diff * TargetInterval / Avg past N solve times
// as described at https://github.com/monero-project/research-lab/issues/3
// Window time span and total difficulty is taken instead of average as suggested by Eugene
// Window time span and total difficulty is taken instead of average as suggested by Nuclear_chaos

if (blockMajorVersion >= BLOCK_MAJOR_VERSION_2) {
size_t m_difficultyWindow_2 = CryptoNote::parameters::DIFFICULTY_WINDOW_V2;
assert(m_difficultyWindow_2 >= 2);

size_t m_difficultyWindow_2 = CryptoNote::parameters::DIFFICULTY_WINDOW_V2;
assert(m_difficultyWindow_2 >= 2);
if (timestamps.size() > m_difficultyWindow_2) {
timestamps.resize(m_difficultyWindow_2);
cumulativeDifficulties.resize(m_difficultyWindow_2);
}

if (timestamps.size() > m_difficultyWindow_2) {
timestamps.resize(m_difficultyWindow_2);
cumulativeDifficulties.resize(m_difficultyWindow_2);
}
size_t length = timestamps.size();
assert(length == cumulativeDifficulties.size());
assert(length <= m_difficultyWindow_2);
if (length <= 1) {
return 1;
}

size_t length = timestamps.size();
assert(length == cumulativeDifficulties.size());
assert(length <= m_difficultyWindow_2);
if (length <= 1) {
return 1;
}
sort(timestamps.begin(), timestamps.end());

sort(timestamps.begin(), timestamps.end());
uint64_t timeSpan = timestamps.back() - timestamps.front();
if (timeSpan == 0) {
timeSpan = 1;
}

uint64_t timeSpan = timestamps.back() - timestamps.front();
if (timeSpan == 0) {
timeSpan = 1;
}
difficulty_type totalWork = cumulativeDifficulties.back() - cumulativeDifficulties.front();
assert(totalWork > 0);

difficulty_type totalWork = cumulativeDifficulties.back() - cumulativeDifficulties.front();
assert(totalWork > 0);
// uint64_t nextDiffZ = totalWork * m_difficultyTarget / timeSpan;

// uint64_t nextDiffZ = totalWork * m_difficultyTarget / timeSpan;
uint64_t low, high;
low = mul128(totalWork, m_difficultyTarget, &high);
// blockchain error "Difficulty overhead" if this function returns zero
if (high != 0) {
return 0;
}

uint64_t low, high;
low = mul128(totalWork, m_difficultyTarget, &high);
// blockchain error "Difficulty overhead" if this function returns zero
if (high != 0) {
return 0;
}
uint64_t nextDiffZ = low / timeSpan;

uint64_t nextDiffZ = low / timeSpan;
// minimum limit
if (nextDiffZ < 1) {
nextDiffZ = 1;
}

// minimum limit
if (nextDiffZ <= 100000) {
nextDiffZ = 100000;
}
return nextDiffZ;
}

return nextDiffZ;
difficulty_type Currency::nextDifficultyV3(std::vector<uint64_t> timestamps,
std::vector<difficulty_type> cumulativeDifficulties) const {

// end of new difficulty calculation
// WHM difficulty algorithm by Zawy
// https://github.com/zawy12/difficulty-algorithms/issues/3
// Linearly - weighted moving average of the solvetimes.
// This is an improved version of Tom Harding's (Deger8) "WT-144".
// This is a basic average that gives more weight to the most recent blocks so it estimates
// *current* difficulty by taking a kind of "slope" into account. A simple average is just
// an estimate of the correct difficulty as it was N / 2 blocks in the past.

} else {
int64_t T = static_cast<int64_t>(m_difficultyTarget);
size_t N = CryptoNote::parameters::DIFFICULTY_WINDOW_V3;

// old difficulty calculation
if (timestamps.size() > N) {
timestamps.resize(N);
cumulativeDifficulties.resize(N);
}
size_t length = timestamps.size();
assert(length == cumulativeDifficulties.size());
assert(length <= N);
if (length <= 1)
return 1;

assert(m_difficultyWindow >= 2);
// To get a more accurate solvetime to within + / -~0.2%, use an adjustment factor.
const double_t adjust = pow(0.9989, 500 / N);
const uint64_t k = static_cast<uint64_t>((N + 1) / 2 * adjust * T);

if (timestamps.size() > m_difficultyWindow) {
timestamps.resize(m_difficultyWindow);
cumulativeDifficulties.resize(m_difficultyWindow);
}
int64_t t = 0, j = 0; // weighted solve times
int64_t solveTime(0);

size_t length = timestamps.size();
assert(length == cumulativeDifficulties.size());
assert(length <= m_difficultyWindow);
if (length <= 1) {
return 1;
}
for (int64_t i = 1; i <= length; i++) { // off-by-one if not <= instead of <
solveTime = static_cast<int64_t>(timestamps[i]) - static_cast<int64_t>(timestamps[i - 1]);
//solveTime = std::min<int64_t>((T * 6), std::max<int64_t>(solveTime, (-5 * T)));
t += solveTime * i;
}

sort(timestamps.begin(), timestamps.end());
// Keep t reasonable in case strange solvetimes occurred in an unforeseeable way.
if (t < N * (N + 1) / 2 * T / 4)
t = N * (N + 1) / 2 * T / 4;

size_t cutBegin, cutEnd;
assert(2 * m_difficultyCut <= m_difficultyWindow - 2);
if (length <= m_difficultyWindow - 2 * m_difficultyCut) {
cutBegin = 0;
cutEnd = length;
}
else {
cutBegin = (length - (m_difficultyWindow - 2 * m_difficultyCut) + 1) / 2;
cutEnd = cutBegin + (m_difficultyWindow - 2 * m_difficultyCut);
}
assert(/*cut_begin >= 0 &&*/ cutBegin + 2 <= cutEnd && cutEnd <= length);
uint64_t timeSpan = timestamps[cutEnd - 1] - timestamps[cutBegin];
if (timeSpan == 0) {
timeSpan = 1;
}
difficulty_type totalWork = cumulativeDifficulties.back() - cumulativeDifficulties.front();
difficulty_type previousDifficulty = cumulativeDifficulties.back() - cumulativeDifficulties.end()[-2];
assert(totalWork > 0);

difficulty_type totalWork = cumulativeDifficulties[cutEnd - 1] - cumulativeDifficulties[cutBegin];
assert(totalWork > 0);
uint64_t low, high, nextDifficulty;
low = mul128(totalWork, k, &high);
if (high != 0)
return 0;

uint64_t low, high;
low = mul128(totalWork, m_difficultyTarget, &high);
if (high != 0 || low + timeSpan - 1 < low) {
return 0;
}
nextDifficulty = low / t;

return (low + timeSpan - 1) / timeSpan;
// end of old difficulty calculation
}
// A symmetric 15 % limit on how fast it rises or falls
nextDifficulty = static_cast<uint64_t>(std::max<uint64_t>(previousDifficulty * 0.85, std::min<uint64_t>(nextDifficulty, previousDifficulty / 0.85)));

return nextDifficulty;
}

bool Currency::checkProofOfWorkV1(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic,
Expand Down
4 changes: 4 additions & 0 deletions src/CryptoNoteCore/Currency.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class Currency {
size_t difficultyCut() const { return m_difficultyCut; }
size_t difficultyBlocksCount() const { return m_difficultyWindow + m_difficultyLag; }
size_t difficultyBlocksCount2() const { return CryptoNote::parameters::DIFFICULTY_WINDOW_V2; }
size_t difficultyBlocksCount3() const { return CryptoNote::parameters::DIFFICULTY_WINDOW_V3; }

size_t maxBlockSizeInitial() const { return m_maxBlockSizeInitial; }
uint64_t maxBlockSizeGrowthSpeedNumerator() const { return m_maxBlockSizeGrowthSpeedNumerator; }
Expand Down Expand Up @@ -121,6 +122,9 @@ class Currency {
bool parseAmount(const std::string& str, uint64_t& amount) const;

difficulty_type nextDifficulty(uint8_t blockMajorVersion, std::vector<uint64_t> timestamps, std::vector<difficulty_type> Difficulties) const;
difficulty_type nextDifficultyV1(std::vector<uint64_t> timestamps, std::vector<difficulty_type> Difficulties) const;
difficulty_type nextDifficultyV2(std::vector<uint64_t> timestamps, std::vector<difficulty_type> Difficulties) const;
difficulty_type nextDifficultyV3(std::vector<uint64_t> timestamps, std::vector<difficulty_type> Difficulties) const;

bool checkProofOfWorkV1(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, Crypto::Hash& proofOfWork) const;
bool checkProofOfWorkV2(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, Crypto::Hash& proofOfWork) const;
Expand Down

0 comments on commit ead95a5

Please sign in to comment.