From f0f9fd02926cc4b52ac1b73f494238d674a8c86c Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Tue, 18 Apr 2017 19:15:10 +0100 Subject: [PATCH] Add Dogecoin difficulty calculations --- src/dogecoin.cpp | 61 ++++++++++++++++++++++++ src/dogecoin.h | 2 + src/pow.cpp | 3 +- src/test/dogecoin_tests.cpp | 95 +++++++++++++++++++++++++++++++++++-- 4 files changed, 157 insertions(+), 4 deletions(-) diff --git a/src/dogecoin.cpp b/src/dogecoin.cpp index 72a9f3cc5..e946acad6 100644 --- a/src/dogecoin.cpp +++ b/src/dogecoin.cpp @@ -5,7 +5,9 @@ #include #include +#include "arith_uint256.h" #include "dogecoin.h" +#include "util.h" int static generateMTRandom(unsigned int s, int range) { @@ -14,6 +16,65 @@ int static generateMTRandom(unsigned int s, int range) return dist(gen); } +unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params) +{ + int nHeight = pindexLast->nHeight + 1; + bool fNewDifficultyProtocol = (nHeight >= 145000); + // bool fNewDifficultyProtocol = (nHeight >= params.GetDigiShieldForkBlock()); + const int64_t retargetTimespan = fNewDifficultyProtocol + ? 60 // params.DigiShieldTargetTimespan() + : params.nPowTargetTimespan; + + const int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime; + int64_t nModulatedTimespan = nActualTimespan; + int64_t nMaxTimespan; + int64_t nMinTimespan; + + if (fNewDifficultyProtocol) //DigiShield implementation - thanks to RealSolid & WDC for this code + { + // amplitude filter - thanks to daft27 for this code + nModulatedTimespan = retargetTimespan + (nModulatedTimespan - retargetTimespan) / 8; + + nMinTimespan = retargetTimespan - (retargetTimespan / 4); + nMaxTimespan = retargetTimespan + (retargetTimespan / 2); + } else if (nHeight > 10000) { + nMinTimespan = retargetTimespan / 4; + nMaxTimespan = retargetTimespan * 4; + } else if (nHeight > 5000) { + nMinTimespan = retargetTimespan / 8; + nMaxTimespan = retargetTimespan * 4; + } else { + nMinTimespan = retargetTimespan / 16; + nMaxTimespan = retargetTimespan * 4; + } + + // Limit adjustment step + if (nModulatedTimespan < nMinTimespan) + nModulatedTimespan = nMinTimespan; + else if (nModulatedTimespan > nMaxTimespan) + nModulatedTimespan = nMaxTimespan; + + // Retarget + const arith_uint256 bnPowLimit = UintToArith256(params.powLimit); + arith_uint256 bnNew; + arith_uint256 bnOld; + bnNew.SetCompact(pindexLast->nBits); + bnOld = bnNew; + bnNew *= nModulatedTimespan; + bnNew /= retargetTimespan; + + if (bnNew > bnPowLimit) + bnNew = bnPowLimit; + + /// debug print + LogPrintf("GetNextWorkRequired RETARGET\n"); + LogPrintf("params.nPowTargetTimespan = %d nActualTimespan = %d\n", params.nPowTargetTimespan, nActualTimespan); + LogPrintf("Before: %08x %s\n", pindexLast->nBits, bnOld.ToString()); + LogPrintf("After: %08x %s\n", bnNew.GetCompact(), bnNew.ToString()); + + return bnNew.GetCompact(); +} + CAmount GetDogecoinBlockSubsidy(int nHeight, const Consensus::Params& consensusParams, uint256 prevHash) { int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; diff --git a/src/dogecoin.h b/src/dogecoin.h index 50a63076d..cedbfac78 100644 --- a/src/dogecoin.h +++ b/src/dogecoin.h @@ -2,7 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "chain.h" #include "chainparams.h" #include "amount.h" CAmount GetDogecoinBlockSubsidy(int nHeight, const Consensus::Params& consensusParams, uint256 prevHash); +unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, int64_t nLastRetargetTime, const Consensus::Params& params); diff --git a/src/pow.cpp b/src/pow.cpp index c629ee4a4..64628b73f 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -7,6 +7,7 @@ #include "arith_uint256.h" #include "chain.h" +#include "dogecoin.h" #include "primitives/block.h" #include "uint256.h" #include "util.h" @@ -53,7 +54,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst); assert(pindexFirst); - return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params); + return CalculateDogecoinNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params); } unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params) diff --git a/src/test/dogecoin_tests.cpp b/src/test/dogecoin_tests.cpp index 413adc708..db6d7c89a 100644 --- a/src/test/dogecoin_tests.cpp +++ b/src/test/dogecoin_tests.cpp @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(subsidy_limit_test) CAmount nSubsidy = GetDogecoinBlockSubsidy(nHeight, params, prevHash); CAmount nExpectedSubsidy = (500000 >> (nHeight / 100000)) * COIN; BOOST_CHECK(MoneyRange(nSubsidy)); - BOOST_CHECK(nSubsidy == nExpectedSubsidy); + BOOST_CHECK_EQUAL(nSubsidy, nExpectedSubsidy); nSum += nSubsidy * nStepSize; } @@ -95,10 +95,99 @@ BOOST_AUTO_TEST_CASE(subsidy_limit_test) // Test reward at 600k+ is constant CAmount nConstantSubsidy = GetDogecoinBlockSubsidy(600000, params, prevHash); - BOOST_CHECK(nConstantSubsidy == 10000 * COIN); + BOOST_CHECK_EQUAL(nConstantSubsidy, 10000 * COIN); nConstantSubsidy = GetDogecoinBlockSubsidy(700000, params, prevHash); - BOOST_CHECK(nConstantSubsidy == 10000 * COIN); + BOOST_CHECK_EQUAL(nConstantSubsidy, 10000 * COIN); +} + +BOOST_AUTO_TEST_CASE(get_next_work_difficulty_limit) +{ + SelectParams(CBaseChainParams::MAIN); + const Consensus::Params& params = Params().GetConsensus(); + + CBlockIndex pindexLast; + int64_t nLastRetargetTime = 1386474927; // Block # 1 + + pindexLast.nHeight = 239; + pindexLast.nTime = 1386475638; // Block #239 + pindexLast.nBits = 0x1e0ffff0; + BOOST_CHECK_EQUAL(CalculateDogecoinNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1e00ffff); +} + +BOOST_AUTO_TEST_CASE(get_next_work_pre_digishield) +{ + SelectParams(CBaseChainParams::MAIN); + const Consensus::Params& params = Params().GetConsensus(); + + CBlockIndex pindexLast; + int64_t nLastRetargetTime = 1386942008; // Block 9359 + + pindexLast.nHeight = 9599; + pindexLast.nTime = 1386954113; + pindexLast.nBits = 0x1c1a1206; + BOOST_CHECK_EQUAL(CalculateDogecoinNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1c15ea59); +} + +BOOST_AUTO_TEST_CASE(get_next_work_digishield) +{ + SelectParams(CBaseChainParams::MAIN); + const Consensus::Params& params = Params().GetConsensus(); + + CBlockIndex pindexLast; + int64_t nLastRetargetTime = 1395094427; + + // First hard-fork at 145,000, which applies to block 145,001 onwards + pindexLast.nHeight = 145000; + pindexLast.nTime = 1395094679; + pindexLast.nBits = 0x1b499dfd; + BOOST_CHECK_EQUAL(CalculateDogecoinNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1b671062); +} + +BOOST_AUTO_TEST_CASE(get_next_work_digishield_modulated_upper) +{ + SelectParams(CBaseChainParams::MAIN); + const Consensus::Params& params = Params().GetConsensus(); + + CBlockIndex pindexLast; + int64_t nLastRetargetTime = 1395100835; + + // Test the upper bound on modulated time using mainnet block #145,107 + pindexLast.nHeight = 145107; + pindexLast.nTime = 1395101360; + pindexLast.nBits = 0x1b3439cd; + BOOST_CHECK_EQUAL(CalculateDogecoinNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1b4e56b3); +} + +BOOST_AUTO_TEST_CASE(get_next_work_digishield_modulated_lower) +{ + SelectParams(CBaseChainParams::MAIN); + const Consensus::Params& params = Params().GetConsensus(); + + CBlockIndex pindexLast; + int64_t nLastRetargetTime = 1395380517; + + // Test the lower bound on modulated time using mainnet block #149,423 + pindexLast.nHeight = 149423; + pindexLast.nTime = 1395380447; + pindexLast.nBits = 0x1b446f21; + BOOST_CHECK_EQUAL(CalculateDogecoinNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1b335358); +} + +BOOST_AUTO_TEST_CASE(get_next_work_digishield_rounding) +{ + SelectParams(CBaseChainParams::MAIN); + const Consensus::Params& params = Params().GetConsensus(); + + CBlockIndex pindexLast; + int64_t nLastRetargetTime = 1395094679; + + // Test case for correct rounding of modulated time - this depends on + // handling of integer division, and is not obvious from the code + pindexLast.nHeight = 145001; + pindexLast.nTime = 1395094727; + pindexLast.nBits = 0x1b671062; + BOOST_CHECK_EQUAL(CalculateDogecoinNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1b6558a4); } BOOST_AUTO_TEST_SUITE_END()