diff --git a/src/pow.h b/src/pow.h --- a/src/pow.h +++ b/src/pow.h @@ -27,4 +27,11 @@ */ bool CheckProofOfWork(uint256 hash, uint32_t nBits, const Consensus::Params &); +/** + * Bitcoin cash's difficulty adjustment mechanism. + */ +uint32_t GetNextCashWorkRequired(const CBlockIndex *pindexPrev, + const CBlockHeader *pblock, + const Consensus::Params ¶ms); + #endif // BITCOIN_POW_H diff --git a/src/pow.cpp b/src/pow.cpp --- a/src/pow.cpp +++ b/src/pow.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers +// Copyright (c) 2017 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -10,6 +11,85 @@ #include "primitives/block.h" #include "uint256.h" +/* + Compute a PoW target based on the previous block's difficulty, and + the difference in MTP between the previous block and the block 6 + blocks before that. + + If the time difference falls outside a window, bump the target up or + down a small amount, otherwise leave the target unchanged. + Difficulty bumps down are larger than bumps up, to render a + difficulty ramp attacks unattractive, but still small. +*/ +uint32_t GetNextCashWorkRequired(const CBlockIndex *pindexPrev, + const CBlockHeader *pblock, + const Consensus::Params ¶ms) { + const uint32_t nProofOfWorkLimit = + UintToArith256(params.powLimit).GetCompact(); + + // Height of next block + uint32_t nHeight = pindexPrev ? pindexPrev->nHeight + 1 : 0; + + // First 6 blocks have genesis block difficulty + if (nHeight <= 6) { + return nProofOfWorkLimit; + } + + uint32_t nBits; + + // Special difficulty rule for testnet: + if (params.fPowAllowMinDifficultyBlocks) { + // If the new block takes twice as long as expected allow + // mining of a min-difficulty block. + if (pblock->GetBlockTime() > + pindexPrev->GetBlockTime() + 2 * params.nPowTargetSpacing) { + return nProofOfWorkLimit; + } + + // Skip blocks using above special min-difficulty rules and + // apply the standard algorithm to the difficulty of the most + // recent non-minimum-difficulty block + const CBlockIndex *pindex = pindexPrev; + + while (pindex->pprev && pindex->nBits == nProofOfWorkLimit) { + pindex = pindex->pprev; + } + + nBits = pindex->nBits; + } else { + nBits = pindexPrev->nBits; + } + + // Determine the MTP difference - the difference between the MTP + // of the previous block, and the block 6 blocks earlier + const CBlockIndex *pindex6 = pindexPrev->GetAncestor(nHeight - 7); + assert(pindex6); + int64_t mtp6blocks = + pindexPrev->GetMedianTimePast() - pindex6->GetMedianTimePast(); + + // If too fast (< 30 mins), increase the target by 1/256. + if (mtp6blocks < params.nPowTargetSpacing * 30 / 10) { + arith_uint256 nTarget; + nTarget.SetCompact(nBits); + nTarget -= (nTarget >> 8); + return nTarget.GetCompact(); + } + + // If too slow (> 128 mins), decrease the target by 1/64. + if (mtp6blocks > params.nPowTargetSpacing * 128 / 10) { + arith_uint256 nTarget; + nTarget.SetCompact(nBits); + nTarget += (nTarget >> 6); + nBits = nTarget.GetCompact(); + // We can't go below the minimum target + if (nBits > nProofOfWorkLimit) nBits = nProofOfWorkLimit; + return nBits; + } + + // Otherwise difficulty is unchanged + return nBits; +} + uint32_t GetNextWorkRequired(const CBlockIndex *pindexPrev, const CBlockHeader *pblock, const Consensus::Params ¶ms) { diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp --- a/src/test/pow_tests.cpp +++ b/src/test/pow_tests.cpp @@ -187,4 +187,157 @@ powLimit.GetCompact()); } +BOOST_AUTO_TEST_CASE(cash_difficulty_test) { + SelectParams(CBaseChainParams::MAIN); + const Consensus::Params ¶ms = Params().GetConsensus(); + + std::vector blocks(2500); + + const arith_uint256 powLimit = UintToArith256(params.powLimit); + uint32_t powLimitBits = powLimit.GetCompact(); + arith_uint256 currentPow = powLimit >> 4; + uint32_t initialBits = currentPow.GetCompact(); + + // Genesis block. + blocks[0] = CBlockIndex(); + blocks[0].nHeight = 0; + blocks[0].nTime = 1269211443; + blocks[0].nBits = initialBits; + + // Block counter. + size_t i; + + // Pile up some blocks every 10 mins to establish some history. + for (i = 1; i < 2050; i++) { + blocks[i] = GetBlockIndex(&blocks[i - 1], 600, initialBits); + } + + CBlockHeader blkHeaderDummy; + uint32_t nBits = + GetNextCashWorkRequired(&blocks[2049], &blkHeaderDummy, params); + + // Difficulty stays the same as long as we produce a block every 10 mins. + for (size_t j = 0; j < 10; i++, j++) { + blocks[i] = GetBlockIndex(&blocks[i - 1], 600, nBits); + BOOST_CHECK_EQUAL( + GetNextCashWorkRequired(&blocks[i], &blkHeaderDummy, params), + nBits); + } + + // Make sure we skip over blocks that are out of wack. To do so, we produce + // a block that is far in the future, and then produce a block with the + // expected timestamp. + blocks[i] = GetBlockIndex(&blocks[i - 1], 6000, nBits); + BOOST_CHECK_EQUAL( + GetNextCashWorkRequired(&blocks[i++], &blkHeaderDummy, params), nBits); + blocks[i] = GetBlockIndex(&blocks[i - 1], 2 * 600 - 6000, nBits); + BOOST_CHECK_EQUAL( + GetNextCashWorkRequired(&blocks[i++], &blkHeaderDummy, params), nBits); + + // The system should continue unaffected by the block with a bogus + // timestamps. + for (size_t j = 0; j < 20; i++, j++) { + blocks[i] = GetBlockIndex(&blocks[i - 1], 600, nBits); + BOOST_CHECK_EQUAL( + GetNextCashWorkRequired(&blocks[i], &blkHeaderDummy, params), + nBits); + } + + // We start emitting blocks faster. Because we use a 6-block MTP + // difference, and 299s is at the boundary, it takes 11 blocks to + // have an impact. Then we should see difficulty increase slowly. + for (size_t j = 0; j < 15; i++, j++) { + blocks[i] = GetBlockIndex(&blocks[i - 1], 299, nBits); + uint32_t nextBits = + GetNextCashWorkRequired(&blocks[i], &blkHeaderDummy, params); + + if (j < 10) { + BOOST_CHECK_EQUAL(nextBits, nBits); + } else { + arith_uint256 currentTarget; + currentTarget.SetCompact(nBits); + arith_uint256 nextTarget; + nextTarget.SetCompact(nextBits); + + // Make sure that difficulty increases slowly. + BOOST_CHECK(nextTarget < currentTarget); + BOOST_CHECK((currentTarget - nextTarget) < (currentTarget / 250)); + BOOST_CHECK((currentTarget - nextTarget) > (currentTarget / 260)); + } + + nBits = nextBits; + } + + // Back to normal; difficulty stops increasing only after the 600s + // blocks enter the median + for (size_t j = 0; j < 15; i++, j++) { + blocks[i] = GetBlockIndex(&blocks[i - 1], 600, nBits); + uint32_t nextBits = + GetNextCashWorkRequired(&blocks[i], &blkHeaderDummy, params); + + if (j < 5) { + BOOST_CHECK(nextBits < nBits); + } else { + BOOST_CHECK_EQUAL(nextBits, nBits); + } + + nBits = nextBits; + } + + // Start emitting blocks slowly. Because we use a 6-block MTP + // difference and gaps are 1 hour, it takes until j is 6 for the MTP + // diff to go above the window and start to decrease difficulty. + for (size_t j = 0; j < 9; i++, j++) { + blocks[i] = GetBlockIndex(&blocks[i - 1], 3600, nBits); + uint32_t nextBits = + GetNextCashWorkRequired(&blocks[i], &blkHeaderDummy, params); + + if (j < 6) { + BOOST_CHECK_EQUAL(nextBits, nBits); + } else { + arith_uint256 currentTarget; + currentTarget.SetCompact(nBits); + arith_uint256 nextTarget; + nextTarget.SetCompact(nextBits); + + // Make sure that difficulty decreases as expected + BOOST_CHECK(nextTarget > currentTarget); + BOOST_CHECK((nextTarget - currentTarget) < (currentTarget / 60)); + BOOST_CHECK((nextTarget - currentTarget) > (currentTarget / 70)); + } + + nBits = nextBits; + } + + // Back to normal but difficulty continues to decrease until the + // MTP diff covers 5 10 minute blocks and 1 1 hour block + for (size_t j = 0; j < 15; i++, j++) { + blocks[i] = GetBlockIndex(&blocks[i - 1], 600, nBits); + uint32_t nextBits = + GetNextCashWorkRequired(&blocks[i], &blkHeaderDummy, params); + + if (j < 9) { + BOOST_CHECK(nextBits > nBits); + } else { + BOOST_CHECK_EQUAL(nextBits, nBits); + } + + nBits = nextBits; + } + + // Once the difficulty reached the minimum allowed level, it + // doesn't get any easier. + for (size_t j = 0; j < 200; i++, j++) { + blocks[i] = GetBlockIndex(&blocks[i - 1], 6000, nBits); + uint32_t nextBits = + GetNextCashWorkRequired(&blocks[i], &blkHeaderDummy, params); + + // Check the difficulty reaches the limit but doesn't fall below it. + BOOST_CHECK(nextBits <= powLimitBits); + if (j > 190) BOOST_CHECK(nextBits == powLimitBits); + + nBits = nextBits; + } +} + BOOST_AUTO_TEST_SUITE_END()