diff --git a/src/gtest/test_pow.cpp b/src/gtest/test_pow.cpp index ac3ce59e1..80e0d0921 100644 --- a/src/gtest/test_pow.cpp +++ b/src/gtest/test_pow.cpp @@ -68,3 +68,44 @@ TEST(PoW, DifficultyAveraging) { params), GetNextWorkRequired(&blocks[lastBlk], nullptr, params)); } + +TEST(PoW, MinDifficultyRules) { + SelectParams(CBaseChainParams::TESTNET); + const Consensus::Params& params = Params().GetConsensus(); + size_t lastBlk = 2*params.nPowAveragingWindow; + size_t firstBlk = lastBlk - params.nPowAveragingWindow; + + // Start with blocks evenly-spaced and equal difficulty + std::vector blocks(lastBlk+1); + for (int i = 0; i <= lastBlk; i++) { + blocks[i].pprev = i ? &blocks[i - 1] : nullptr; + blocks[i].nHeight = i; + blocks[i].nTime = 1269211443 + i * params.nPowTargetSpacing; + blocks[i].nBits = 0x1e7fffff; /* target 0x007fffff000... */ + blocks[i].nChainWork = i ? blocks[i - 1].nChainWork + GetBlockProof(blocks[i - 1]) : arith_uint256(0); + } + + // Create a new block at the target spacing + CBlockHeader next; + next.nTime = blocks[lastBlk].nTime + params.nPowTargetSpacing; + + // Result should be unchanged, modulo integer division precision loss + arith_uint256 bnRes; + bnRes.SetCompact(0x1e7fffff); + bnRes /= params.AveragingWindowTimespan(); + bnRes *= params.AveragingWindowTimespan(); + EXPECT_EQ(GetNextWorkRequired(&blocks[lastBlk], &next, params), bnRes.GetCompact()); + + // Delay last block up to the edge of the min-difficulty limit + next.nTime += params.nPowTargetSpacing * 5; + + // Result should be unchanged, modulo integer division precision loss + EXPECT_EQ(GetNextWorkRequired(&blocks[lastBlk], &next, params), bnRes.GetCompact()); + + // Delay last block over the min-difficulty limit + next.nTime += 1; + + // Result should be the minimum difficulty + EXPECT_EQ(GetNextWorkRequired(&blocks[lastBlk], &next, params), + UintToArith256(params.powLimit).GetCompact()); +} diff --git a/src/gtest/test_rpc.cpp b/src/gtest/test_rpc.cpp index ad80393f5..3733379a8 100644 --- a/src/gtest/test_rpc.cpp +++ b/src/gtest/test_rpc.cpp @@ -9,33 +9,6 @@ #include "streams.h" #include "utilstrencodings.h" -TEST(rpc, GetDifficultyTestnetRules) { - SelectParams(CBaseChainParams::TESTNET); - - CBlockIndex prev; - prev.nTime = 1472700000; - prev.nBits = 0x201fffff; - - CBlockIndex curr; - curr.pprev = &prev; - curr.nTime = 1472700300; - curr.nBits = 0x207fffff; - - // Time interval is within 5 minutes, so the min-difficulty block should be - // interpreted as a valid network difficulty. - EXPECT_EQ(1, GetDifficulty(&curr)); - EXPECT_EQ(1, GetNetworkDifficulty(&curr)); - - curr.nTime += 1; - - // Time interval is over 5 minutes, so the min-difficulty block should be - // ignored for network difficulty determination. - EXPECT_EQ(1, GetDifficulty(&curr)); - // We have to check this directly, because of some combination of rounding - // and truncation issues that result in Google Test displaying 4 != 4 - EXPECT_EQ((double)0x7fffff/(double)0x1fffff, GetNetworkDifficulty(&curr)); -} - extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false); TEST(rpc, check_blockToJSON_returns_minified_solution) { diff --git a/src/pow.cpp b/src/pow.cpp index 0eb81414a..40e69bb52 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -24,20 +24,14 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead if (pindexLast == NULL) return nProofOfWorkLimit; - const CBlockIndex* pindexBits = pindexLast; { if (params.fPowAllowMinDifficultyBlocks) { // Special difficulty rule for testnet: - // If the new block's timestamp is more than 2* 2.5 minutes + // If the new block's timestamp is more than 6 * 2.5 minutes // then allow mining of a min-difficulty block. - if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*2) + if (pblock && pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing * 6) return nProofOfWorkLimit; - else { - // Get the last non-min-difficulty (or at worst the genesis difficulty) - while (pindexBits->pprev && pindexBits->nBits == nProofOfWorkLimit) - pindexBits = pindexBits->pprev; - } } } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index c7afe7af8..9a9b0fd57 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -38,29 +38,21 @@ double GetDifficultyINTERNAL(const CBlockIndex* blockindex, bool networkDifficul blockindex = chainActive.Tip(); } - uint32_t powLimit = - UintToArith256(Params().GetConsensus().powLimit).GetCompact(); - { - if (networkDifficulty && Params().GetConsensus().fPowAllowMinDifficultyBlocks) - { - // Special difficulty rule for testnet: - // If a block's timestamp is more than 2*nPowTargetSpacing minutes after - // the previous block, then it is permitted to be min-difficulty. So - // get the last non-min-difficulty (or at worst the genesis difficulty). - auto window = Params().GetConsensus().nPowTargetSpacing*2; - while (blockindex->pprev && blockindex->nBits == powLimit && - blockindex->GetBlockTime() > blockindex->pprev->GetBlockTime() + window) { - blockindex = blockindex->pprev; - } - } + uint32_t bits; + if (networkDifficulty) { + bits = GetNextWorkRequired(blockindex, nullptr, Params().GetConsensus()); + } else { + bits = blockindex->nBits; } - int nShift = (blockindex->nBits >> 24) & 0xff; + uint32_t powLimit = + UintToArith256(Params().GetConsensus().powLimit).GetCompact(); + int nShift = (bits >> 24) & 0xff; int nShiftAmount = (powLimit >> 24) & 0xff; double dDiff = (double)(powLimit & 0x00ffffff) / - (double)(blockindex->nBits & 0x00ffffff); + (double)(bits & 0x00ffffff); while (nShift < nShiftAmount) {