From f2c48e15e5867426ab1f888b28fe1fc556f020e4 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 8 Jun 2016 12:30:32 +1200 Subject: [PATCH] Implement new difficulty algorithm (#931) The algorithm is based on DigiShield v3/v4. --- src/chainparams.cpp | 6 ++++- src/consensus/params.h | 8 +++++-- src/pow.cpp | 53 ++++++++++++++++++++---------------------- src/rpcmining.cpp | 14 +++++++---- 4 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index f9729e485..341ceb5c9 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -39,7 +39,9 @@ public: // TODO generate harder genesis block //consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks + consensus.nPowAveragingWindow = 17; + consensus.nPowMaxAdjustDown = 16; // 16% adjustment down + consensus.nPowMaxAdjustUp = 8; // 8% adjustment up consensus.nPowTargetSpacing = 2.5 * 60; consensus.fPowAllowMinDifficultyBlocks = false; /** @@ -216,6 +218,8 @@ public: consensus.nMajorityRejectBlockOutdated = 950; consensus.nMajorityWindow = 1000; consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down + consensus.nPowMaxAdjustUp = 0; // Turn off adjustment up pchMessageStart[0] = 0xaa; pchMessageStart[1] = 0xe8; pchMessageStart[2] = 0x3f; diff --git a/src/consensus/params.h b/src/consensus/params.h index 1dea7c47b..d65b84b36 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -36,9 +36,13 @@ struct Params { /** Proof of work parameters */ uint256 powLimit; bool fPowAllowMinDifficultyBlocks; + int64_t nPowAveragingWindow; + int64_t nPowMaxAdjustDown; + int64_t nPowMaxAdjustUp; int64_t nPowTargetSpacing; - int64_t nPowTargetTimespan; - int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; } + int64_t AveragingWindowTimespan() const { return nPowAveragingWindow * nPowTargetSpacing; } + int64_t MinActualTimespan() const { return (AveragingWindowTimespan() * (100 - nPowMaxAdjustUp )) / 100; } + int64_t MaxActualTimespan() const { return (AveragingWindowTimespan() * (100 + nPowMaxAdjustDown)) / 100; } }; } // namespace Consensus diff --git a/src/pow.cpp b/src/pow.cpp index 125d59125..3f065f9ca 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -24,8 +24,6 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead if (pindexLast == NULL) return nProofOfWorkLimit; - // Only change once per difficulty adjustment interval - if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0) { if (params.fPowAllowMinDifficultyBlocks) { @@ -34,36 +32,35 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead // then allow mining of a min-difficulty block. if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*2) return nProofOfWorkLimit; - else - { - // Return the last non-special-min-difficulty-rules-block - const CBlockIndex* pindex = pindexLast; - while (pindex->pprev && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit) - pindex = pindex->pprev; - return pindex->nBits; - } } - return pindexLast->nBits; } - // Go back by what we want to be 14 days worth of blocks - int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1); - assert(nHeightFirst >= 0); - const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst); - assert(pindexFirst); + // Find the first block in the averaging interval + const CBlockIndex* pindexFirst = pindexLast; + for (int i = 0; pindexFirst && i < params.nPowAveragingWindow; i++) { + pindexFirst = pindexFirst->pprev; + } - return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params); + // Check we have enough blocks + if (pindexFirst == NULL) + return nProofOfWorkLimit; + + return CalculateNextWorkRequired(pindexLast, pindexFirst->GetMedianTimePast(), params); } unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params) { // Limit adjustment step - int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime; - LogPrintf(" nActualTimespan = %d before bounds\n", nActualTimespan); - if (nActualTimespan < params.nPowTargetTimespan/4) - nActualTimespan = params.nPowTargetTimespan/4; - if (nActualTimespan > params.nPowTargetTimespan*4) - nActualTimespan = params.nPowTargetTimespan*4; + // Use medians to prevent time-warp attacks + int64_t nActualTimespan = pindexLast->GetMedianTimePast() - nFirstBlockTime; + LogPrint("pow", " nActualTimespan = %d before dampening\n", nActualTimespan); + nActualTimespan = params.AveragingWindowTimespan() + (nActualTimespan - params.AveragingWindowTimespan())/4; + LogPrint("pow", " nActualTimespan = %d before bounds\n", nActualTimespan); + + if (nActualTimespan < params.MinActualTimespan()) + nActualTimespan = params.MinActualTimespan(); + if (nActualTimespan > params.MaxActualTimespan()) + nActualTimespan = params.MaxActualTimespan(); // Retarget const arith_uint256 bnPowLimit = UintToArith256(params.powLimit); @@ -71,17 +68,17 @@ unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nF arith_uint256 bnOld; bnNew.SetCompact(pindexLast->nBits); bnOld = bnNew; - bnNew /= params.nPowTargetTimespan; + bnNew /= params.AveragingWindowTimespan(); bnNew *= nActualTimespan; 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()); + LogPrint("pow", "GetNextWorkRequired RETARGET\n"); + LogPrint("pow", "params.AveragingWindowTimespan() = %d nActualTimespan = %d\n", params.AveragingWindowTimespan(), nActualTimespan); + LogPrint("pow", "Before: %08x %s\n", pindexLast->nBits, bnOld.ToString()); + LogPrint("pow", "After: %08x %s\n", bnNew.GetCompact(), bnNew.ToString()); return bnNew.GetCompact(); } diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index c8a809c19..49bb6387b 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -33,7 +33,7 @@ using namespace std; /** * Return average network hashes per second based on the last 'lookup' blocks, - * or from the last difficulty change if 'lookup' is nonpositive. + * or over the difficulty averaging window if 'lookup' is nonpositive. * If 'height' is nonnegative, compute the estimate at the time when a given block was found. */ Value GetNetworkHashPS(int lookup, int height) { @@ -45,9 +45,13 @@ Value GetNetworkHashPS(int lookup, int height) { if (pb == NULL || !pb->nHeight) return 0; - // If lookup is -1, then use blocks since last difficulty change. + // If lookup is -1, then use difficulty averaging window. if (lookup <= 0) - lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval() + 1; + lookup = pb->nHeight - Params().GetConsensus().nPowAveragingWindow; + + // If lookup is still negative, then use blocks since genesis. + if (lookup <= 0) + lookup = pb->nHeight; // If lookup is larger than chain, then set it to chain length. if (lookup > pb->nHeight) @@ -79,10 +83,10 @@ Value getnetworkhashps(const Array& params, bool fHelp) throw runtime_error( "getnetworkhashps ( blocks height )\n" "\nReturns the estimated network hashes per second based on the last n blocks.\n" - "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n" + "Pass in [blocks] to override # of blocks, -1 specifies over difficulty averaging window.\n" "Pass in [height] to estimate the network speed at the time when a certain block was found.\n" "\nArguments:\n" - "1. blocks (numeric, optional, default=120) The number of blocks, or -1 for blocks since last difficulty change.\n" + "1. blocks (numeric, optional, default=120) The number of blocks, or -1 for blocks over difficulty averaging window.\n" "2. height (numeric, optional, default=-1) To estimate at the time of the given height.\n" "\nResult:\n" "x (numeric) Hashes per second estimated\n"