diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 5b563c58ae1e..ba7fe98825c5 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -73,9 +73,25 @@ def sha256(s): def ripemd160(s): return hashlib.new('ripemd160', s).digest() +def hash160(s): + return ripemd160(sha256(s)) + def hash256(s): return sha256(sha256(s)) +def upgrade_160_hash_to_256(algo): + def algowrapper(s): + return algo(s) + (b'\0' * 12) + return algowrapper + +powalgos = (sha256, hash256, upgrade_160_hash_to_256(ripemd160), upgrade_160_hash_to_256(hash160)) +def powhash(s, nTime): + if nTime < 1296688603: + algo = hash256 + else: + algo = powalgos[int(nTime / 3600) % len(powalgos)] + return algo(s) + def ser_compact_size(l): r = b"" if l < 253: @@ -577,8 +593,9 @@ def calc_sha256(self): r += struct.pack("(this)->GetAncestor(height); } +int64_t CBlockIndex::GetEarliestNextBlockTime(const Consensus::Params& consensusParams) const +{ + int64_t nMinTime = GetMedianTimePast() + 1; + int64_t nMaxTime = GetBlockTime(); + const auto current_pow_algo = consensusParams.PowAlgorithmForTime(nMaxTime); + if (nMinTime < nMaxTime && current_pow_algo != consensusParams.PowAlgorithmForTime(nMinTime)) { + int64_t nTryTime; + ++nMinTime; + while (nMinTime < nMaxTime) { + nTryTime = nMinTime + ((nMaxTime - nMinTime) / 2); + if (current_pow_algo != consensusParams.PowAlgorithmForTime(nTryTime)) { + nMinTime = nTryTime + 1; + } else { + nMaxTime = nTryTime - 1; + } + } + } + return nMinTime; +} + void CBlockIndex::BuildSkip() { if (pprev) diff --git a/src/chain.h b/src/chain.h index acb29b667b76..f4b42b97a5e6 100644 --- a/src/chain.h +++ b/src/chain.h @@ -306,6 +306,8 @@ class CBlockIndex return pbegin[(pend - pbegin)/2]; } + int64_t GetEarliestNextBlockTime(const Consensus::Params&) const; + std::string ToString() const { return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 383d9849af97..6428e15e9e93 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -99,6 +99,9 @@ class CMainParams : public CChainParams { // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000003f94d1ad391682fe038bf5"); + consensus.HardforkTime = std::numeric_limits::max(); + consensus.nPowChangeTargetShift = 20; + // By default assume that the signatures in ancestors of this block are valid. consensus.defaultAssumeValid = uint256S("0x00000000000000000013176bf8d7dfeab4e1db31dc93bc311b436e82ab226b90"); //453354 @@ -115,7 +118,7 @@ class CMainParams : public CChainParams { nPruneAfterHeight = 100000; genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN); - consensus.hashGenesisBlock = genesis.GetHash(); + consensus.hashGenesisBlock = genesis.GetHash(consensus); assert(consensus.hashGenesisBlock == uint256S("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")); assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); @@ -203,6 +206,9 @@ class CTestNetParams : public CChainParams { // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000001f057509eba81aed91"); + consensus.HardforkTime = std::numeric_limits::max(); + consensus.nPowChangeTargetShift = 20; + // By default assume that the signatures in ancestors of this block are valid. consensus.defaultAssumeValid = uint256S("0x00000000000128796ee387cf110ccb9d2f36cffaf7f73079c995377c65ac0dcc"); //1079274 @@ -214,7 +220,7 @@ class CTestNetParams : public CChainParams { nPruneAfterHeight = 1000; genesis = CreateGenesisBlock(1296688602, 414098458, 0x1d00ffff, 1, 50 * COIN); - consensus.hashGenesisBlock = genesis.GetHash(); + consensus.hashGenesisBlock = genesis.GetHash(consensus); assert(consensus.hashGenesisBlock == uint256S("0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943")); assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); @@ -288,6 +294,10 @@ class CRegTestParams : public CChainParams { // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); + consensus.HardforkTime = 1296688603; // Just past the genesis block + consensus.PowChangeAlgo = HashAlgorithm::NUM_HASH_ALGOS; + consensus.nPowChangeTargetShift = 20; + // By default assume that the signatures in ancestors of this block are valid. consensus.defaultAssumeValid = uint256S("0x00"); @@ -299,7 +309,7 @@ class CRegTestParams : public CChainParams { nPruneAfterHeight = 1000; genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN); - consensus.hashGenesisBlock = genesis.GetHash(); + consensus.hashGenesisBlock = genesis.GetHash(consensus); assert(consensus.hashGenesisBlock == uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); diff --git a/src/consensus/params.h b/src/consensus/params.h index 6240e82857eb..ad24d1d47183 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_CONSENSUS_PARAMS_H #define BITCOIN_CONSENSUS_PARAMS_H +#include "hash.h" #include "uint256.h" #include #include @@ -62,6 +63,23 @@ struct Params { int64_t nPowTargetTimespan; int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; } uint256 nMinimumChainWork; + + /** Hardfork parameters */ + int64_t HardforkTime; + HashAlgorithm PowChangeAlgo; + int nPowChangeTargetShift; + HashAlgorithm PowAlgorithmForTime(int64_t nTime) const { + if (nTime >= HardforkTime) { + if (PowChangeAlgo == HashAlgorithm::NUM_HASH_ALGOS) { + // Indicates a rotating hash algo, for testing + return (HashAlgorithm)((nTime / 3600) % (unsigned int)HashAlgorithm::NUM_HASH_ALGOS); + } + return PowChangeAlgo; + } else { + return HashAlgorithm::SHA256d; + } + } + uint256 defaultAssumeValid; }; } // namespace Consensus diff --git a/src/hash.h b/src/hash.h index eacb8f04fef3..795d73f82199 100644 --- a/src/hash.h +++ b/src/hash.h @@ -15,6 +15,14 @@ #include +enum class HashAlgorithm: unsigned int { + SHA256, + SHA256d, + RIPEMD160, + HASH160, + NUM_HASH_ALGOS, +}; + typedef uint256 ChainCode; /** A hasher class for Bitcoin's 256-bit hash (double SHA-256). */ diff --git a/src/miner.cpp b/src/miner.cpp index a12dcec2ce7e..72e85998668e 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -60,7 +60,7 @@ class ScoreCompare int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { int64_t nOldTime = pblock->nTime; - int64_t nNewTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + int64_t nNewTime = std::max(pindexPrev->GetEarliestNextBlockTime(consensusParams), GetAdjustedTime()); if (nOldTime < nNewTime) pblock->nTime = nNewTime; diff --git a/src/pow.cpp b/src/pow.cpp index e57fd866f8a6..36f740537d7b 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -18,6 +18,8 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead if (pindexLast == NULL) return nProofOfWorkLimit; + uint32_t nBits; + // Only change once per difficulty adjustment interval if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0) { @@ -30,23 +32,38 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead return nProofOfWorkLimit; else { - // Return the last non-special-min-difficulty-rules-block + // Look back to 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; + nBits = pindex->nBits; } + } else { + nBits = pindexLast->nBits; } - return pindexLast->nBits; + } else { + // 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); + + nBits = CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params); } - // 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); + if (params.PowAlgorithmForTime(pblock->nTime) != params.PowAlgorithmForTime(pindexLast->nTime)) { + // Adjust target for PoW change + arith_uint256 bnNew; + bnNew.SetCompact(nBits); + bnNew <<= params.nPowChangeTargetShift; + const arith_uint256 bnPowLimit = UintToArith256(params.powLimit); + if (bnNew > bnPowLimit) { + bnNew = bnPowLimit; + } + nBits = bnNew.GetCompact(); + } - return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params); + return nBits; } unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params) diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 9a979094ccb1..380e0b6db700 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -8,11 +8,47 @@ #include "hash.h" #include "tinyformat.h" #include "utilstrencodings.h" +#include "chainparams.h" +#include "consensus/params.h" #include "crypto/common.h" +#include "streams.h" + +#include + +uint256 CBlockHeader::GetHash(const Consensus::Params& consensusParams) const +{ + CDataStream ss(SER_GETHASH, PROTOCOL_VERSION); + ss << *this; + + const auto pbegin = (const unsigned char *)&ss.begin()[0]; + uint256 hash; + + const HashAlgorithm algo = consensusParams.PowAlgorithmForTime(nTime); + switch (algo) { + case HashAlgorithm::SHA256: + CSHA256().Write(pbegin, ss.size()).Finalize((unsigned char*)&hash); + break; + case HashAlgorithm::SHA256d: + CHash256().Write(pbegin, ss.size()).Finalize((unsigned char*)&hash); + break; + case HashAlgorithm::RIPEMD160: + CRIPEMD160().Write(pbegin, ss.size()).Finalize((unsigned char*)&hash); + break; + case HashAlgorithm::HASH160: + CHash160().Write(pbegin, ss.size()).Finalize((unsigned char*)&hash); + break; + case HashAlgorithm::NUM_HASH_ALGOS: + // Should be impossible + abort(); + } + + return hash; +} uint256 CBlockHeader::GetHash() const { - return SerializeHash(*this); + const Consensus::Params& consensusParams = Params().GetConsensus(); + return GetHash(consensusParams); } std::string CBlock::ToString() const diff --git a/src/primitives/block.h b/src/primitives/block.h index 4c6eb20ad5e3..aacf4f5abc57 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -10,6 +10,10 @@ #include "serialize.h" #include "uint256.h" +namespace Consensus { + struct Params; +} + /** Nodes collect new transactions into a block, hash them into a hash tree, * and scan through nonce values to make the block's hash satisfy proof-of-work * requirements. When they solve the proof-of-work, they broadcast the block @@ -60,6 +64,7 @@ class CBlockHeader return (nBits == 0); } + uint256 GetHash(const Consensus::Params&) const; uint256 GetHash() const; int64_t GetBlockTime() const diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 33e234a95efb..eb26bf0eb72a 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -678,7 +678,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue)); result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast))); result.push_back(Pair("target", hashTarget.GetHex())); - result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); + result.push_back(Pair("mintime", (int64_t)pindexPrev->GetEarliestNextBlockTime(consensusParams))); result.push_back(Pair("mutable", aMutable)); result.push_back(Pair("noncerange", "00000000ffffffff")); int64_t nSigOpLimit = MAX_BLOCK_SIGOPS_COST; diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index f856d8a91a70..33e193d21eae 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -6,6 +6,7 @@ #include "coins.h" #include "consensus/consensus.h" #include "consensus/merkle.h" +#include "consensus/params.h" #include "consensus/validation.h" #include "validation.h" #include "miner.h" @@ -187,6 +188,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { // Note that by default, these tests run with size accounting enabled. const CChainParams& chainparams = Params(CBaseChainParams::MAIN); + const Consensus::Params& consensusParams = chainparams.GetConsensus(); CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; std::unique_ptr pblocktemplate; CMutableTransaction tx,tx2; @@ -211,7 +213,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { CBlock *pblock = &pblocktemplate->block; // pointer for convenience pblock->nVersion = 1; - pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1; + pblock->nTime = chainActive.Tip()->GetEarliestNextBlockTime(consensusParams); CMutableTransaction txCoinbase(*pblock->vtx[0]); txCoinbase.nVersion = 1; txCoinbase.vin[0].scriptSig = CScript(); @@ -393,7 +395,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) } // non-final txs in mempool - SetMockTime(chainActive.Tip()->GetMedianTimePast()+1); + SetMockTime(chainActive.Tip()->GetEarliestNextBlockTime(consensusParams)); int flags = LOCKTIME_VERIFY_SEQUENCE|LOCKTIME_MEDIAN_TIME_PAST; // height map std::vector prevheights; diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp index 0b2fe0ef9db1..db12c5cf791d 100644 --- a/src/test/skiplist_tests.cpp +++ b/src/test/skiplist_tests.cpp @@ -3,6 +3,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "chain.h" +#include "chainparams.h" +#include "consensus/params.h" #include "util.h" #include "test/test_bitcoin.h" #include "test/test_random.h" @@ -102,6 +104,7 @@ BOOST_AUTO_TEST_CASE(getlocator_test) BOOST_AUTO_TEST_CASE(findearliestatleast_test) { + const Consensus::Params& consensusParams = Params(CBaseChainParams::MAIN).GetConsensus(); std::vector vHashMain(100000); std::vector vBlocksMain(100000); for (unsigned int i=0; iGetBlockTime()) && block.GetBlockTime() < pindexPrev->GetBlockTime()) { + return state.Invalid(false, REJECT_INVALID, "pow-reversed", "cannot reverse PoW change"); + } + // Check timestamp against prev - if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) + if (block.GetBlockTime() < pindexPrev->GetEarliestNextBlockTime(consensusParams)) return state.Invalid(false, REJECT_INVALID, "time-too-old", "block's timestamp is too early"); // Check timestamp