From 2413416b8af595002cd82352703ff5e2d7287a81 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 27 Jun 2017 03:44:31 +0000 Subject: [PATCH 1/8] chainparams: Define (unset) hardfork time --- src/chainparams.cpp | 6 ++++++ src/consensus/params.h | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 383d9849af97..e37ac1dccdef 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -99,6 +99,8 @@ class CMainParams : public CChainParams { // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000003f94d1ad391682fe038bf5"); + consensus.HardforkTime = std::numeric_limits::max(); + // By default assume that the signatures in ancestors of this block are valid. consensus.defaultAssumeValid = uint256S("0x00000000000000000013176bf8d7dfeab4e1db31dc93bc311b436e82ab226b90"); //453354 @@ -203,6 +205,8 @@ class CTestNetParams : public CChainParams { // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000001f057509eba81aed91"); + consensus.HardforkTime = std::numeric_limits::max(); + // By default assume that the signatures in ancestors of this block are valid. consensus.defaultAssumeValid = uint256S("0x00000000000128796ee387cf110ccb9d2f36cffaf7f73079c995377c65ac0dcc"); //1079274 @@ -288,6 +292,8 @@ class CRegTestParams : public CChainParams { // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); + consensus.HardforkTime = std::numeric_limits::max(); + // By default assume that the signatures in ancestors of this block are valid. consensus.defaultAssumeValid = uint256S("0x00"); diff --git a/src/consensus/params.h b/src/consensus/params.h index 6240e82857eb..1c5739cffa4d 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -62,6 +62,10 @@ struct Params { int64_t nPowTargetTimespan; int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; } uint256 nMinimumChainWork; + + /** Hardfork parameters */ + int64_t HardforkTime; + uint256 defaultAssumeValid; }; } // namespace Consensus From aa4293d0c8f8eeb99a3747a5f2aac7ec551b595c Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 27 Jun 2017 00:40:44 +0000 Subject: [PATCH 2/8] consensus/params: PowAlgorithmForTime method --- src/consensus/params.h | 4 ++++ src/hash.h | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/consensus/params.h b/src/consensus/params.h index 1c5739cffa4d..59168bfd5d6f 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 @@ -65,6 +66,9 @@ struct Params { /** Hardfork parameters */ int64_t HardforkTime; + HashAlgorithm PowAlgorithmForTime(int64_t nTime) const { + return HashAlgorithm::SHA256d; + } uint256 defaultAssumeValid; }; diff --git a/src/hash.h b/src/hash.h index eacb8f04fef3..dbb9e87f6307 100644 --- a/src/hash.h +++ b/src/hash.h @@ -15,6 +15,13 @@ #include +enum class HashAlgorithm { + SHA256, + SHA256d, + RIPEMD160, + HASH160, +}; + typedef uint256 ChainCode; /** A hasher class for Bitcoin's 256-bit hash (double SHA-256). */ From 5e7629710b37abf92cfe979dad93fc1d7735c173 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 27 Jun 2017 00:44:22 +0000 Subject: [PATCH 3/8] validation: Forbid reversal of PoW change --- src/validation.cpp | 4 ++++ 1 file changed, 4 insertions(+) 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) || From b2900b9f09e9e053eda655512a408fb03dd4e29b Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 27 Jun 2017 01:53:37 +0000 Subject: [PATCH 4/8] pow: GetNextWorkRequired: Refactor so all non-limit answers go through the same return --- src/pow.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/pow.cpp b/src/pow.cpp index e57fd866f8a6..a7a39467e5e0 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,26 @@ 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); - - return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params); + return nBits; } unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params) From 00e215ff3fd75070c4f4dceba0f2ae0539d7c5cd Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 27 Jun 2017 01:54:05 +0000 Subject: [PATCH 5/8] pow: GetNextWorkRequired: Adjust difficulty for first non-SHA2 block --- src/chainparams.cpp | 3 +++ src/consensus/params.h | 1 + src/pow.cpp | 12 ++++++++++++ 3 files changed, 16 insertions(+) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index e37ac1dccdef..f56057082a6d 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -100,6 +100,7 @@ class CMainParams : public CChainParams { 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 @@ -206,6 +207,7 @@ class CTestNetParams : public CChainParams { 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 @@ -293,6 +295,7 @@ class CRegTestParams : public CChainParams { consensus.nMinimumChainWork = uint256S("0x00"); 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("0x00"); diff --git a/src/consensus/params.h b/src/consensus/params.h index 59168bfd5d6f..ed26263f75a4 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -66,6 +66,7 @@ struct Params { /** Hardfork parameters */ int64_t HardforkTime; + int nPowChangeTargetShift; HashAlgorithm PowAlgorithmForTime(int64_t nTime) const { return HashAlgorithm::SHA256d; } diff --git a/src/pow.cpp b/src/pow.cpp index a7a39467e5e0..36f740537d7b 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -51,6 +51,18 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead nBits = CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params); } + 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 nBits; } From a84edb1bf22d92b74e36dda6203cd3ba1328903b Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 27 Jun 2017 03:37:22 +0000 Subject: [PATCH 6/8] CBlockHeader::GetHash: Check with consensus parameters for PoW algo to use --- src/chainparams.cpp | 6 +++--- src/primitives/block.cpp | 33 ++++++++++++++++++++++++++++++++- src/primitives/block.h | 5 +++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index f56057082a6d..c6002d83a04e 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -118,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")); @@ -220,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")); @@ -308,7 +308,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/primitives/block.cpp b/src/primitives/block.cpp index 9a979094ccb1..419ca5f0d557 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -8,11 +8,42 @@ #include "hash.h" #include "tinyformat.h" #include "utilstrencodings.h" +#include "chainparams.h" +#include "consensus/params.h" #include "crypto/common.h" +#include "streams.h" + +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; + } + + 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 From e149e70580684baa81b6d56bed18b19280b2101e Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 27 Jun 2017 03:44:31 +0000 Subject: [PATCH 7/8] chainparams: Define (unset) PoW change hardfork param --- src/consensus/params.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/consensus/params.h b/src/consensus/params.h index ed26263f75a4..8428152ae6f8 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -66,9 +66,14 @@ struct Params { /** Hardfork parameters */ int64_t HardforkTime; + HashAlgorithm PowChangeAlgo; int nPowChangeTargetShift; HashAlgorithm PowAlgorithmForTime(int64_t nTime) const { - return HashAlgorithm::SHA256d; + if (nTime >= HardforkTime) { + return PowChangeAlgo; + } else { + return HashAlgorithm::SHA256d; + } } uint256 defaultAssumeValid; From 51fd66a221d5c14b56e5c568db4d240407490436 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 27 Jun 2017 04:21:56 +0000 Subject: [PATCH 8/8] chainparams: Rotating PoW algorithm for regtest --- src/chainparams.cpp | 3 ++- src/consensus/params.h | 4 ++++ src/hash.h | 3 ++- src/primitives/block.cpp | 5 +++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index c6002d83a04e..6428e15e9e93 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -294,7 +294,8 @@ class CRegTestParams : public CChainParams { // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); - consensus.HardforkTime = std::numeric_limits::max(); + 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. diff --git a/src/consensus/params.h b/src/consensus/params.h index 8428152ae6f8..ad24d1d47183 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -70,6 +70,10 @@ struct Params { 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; diff --git a/src/hash.h b/src/hash.h index dbb9e87f6307..795d73f82199 100644 --- a/src/hash.h +++ b/src/hash.h @@ -15,11 +15,12 @@ #include -enum class HashAlgorithm { +enum class HashAlgorithm: unsigned int { SHA256, SHA256d, RIPEMD160, HASH160, + NUM_HASH_ALGOS, }; typedef uint256 ChainCode; diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 419ca5f0d557..380e0b6db700 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -13,6 +13,8 @@ #include "crypto/common.h" #include "streams.h" +#include + uint256 CBlockHeader::GetHash(const Consensus::Params& consensusParams) const { CDataStream ss(SER_GETHASH, PROTOCOL_VERSION); @@ -35,6 +37,9 @@ uint256 CBlockHeader::GetHash(const Consensus::Params& consensusParams) const case HashAlgorithm::HASH160: CHash160().Write(pbegin, ss.size()).Finalize((unsigned char*)&hash); break; + case HashAlgorithm::NUM_HASH_ALGOS: + // Should be impossible + abort(); } return hash;