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/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/validation.cpp b/src/validation.cpp index c9135c442b1a..b2ab1ac9b7fa 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2989,6 +2989,10 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta if (block.GetBlockTime() > nAdjustedTime + 2 * 60 * 60) return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future"); + if (consensusParams.PowAlgorithmForTime(block.GetBlockTime()) != consensusParams.PowAlgorithmForTime(pindexPrev->GetBlockTime()) && block.GetBlockTime() < pindexPrev->GetBlockTime()) { + return state.Invalid(false, REJECT_INVALID, "pow-reversed", "cannot reverse PoW change"); + } + // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded: // check for version 2, 3 and 4 upgrades if((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) ||