From 2413416b8af595002cd82352703ff5e2d7287a81 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 27 Jun 2017 03:44:31 +0000 Subject: [PATCH 01/11] 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 02/11] 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 03/11] 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 04/11] 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 05/11] 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 607a72b90c620f4311ed62e7db2a61d51f09dde4 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 27 Jun 2017 03:37:22 +0000 Subject: [PATCH 06/11] CBlockHeader::GetHash: Check with consensus parameters for PoW algo to use --- src/Makefile.am | 2 +- src/chainparams.cpp | 6 +++--- src/primitives/block.cpp | 33 ++++++++++++++++++++++++++++++++- src/primitives/block.h | 5 +++++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 76dbbced6cd4..fb2f45311210 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -270,7 +270,6 @@ libbitcoin_consensus_a_SOURCES = \ hash.cpp \ hash.h \ prevector.h \ - primitives/block.cpp \ primitives/block.h \ primitives/transaction.cpp \ primitives/transaction.h \ @@ -306,6 +305,7 @@ libbitcoin_common_a_SOURCES = \ keystore.cpp \ netaddress.cpp \ netbase.cpp \ + primitives/block.cpp \ protocol.cpp \ scheduler.cpp \ script/sign.cpp \ 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 05fc4aba7c65e1e55c9602638c8a6cc5cd6803f5 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 27 Jun 2017 03:44:31 +0000 Subject: [PATCH 07/11] 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 39b96dd3e01fb8acf695bf9a3038667703c5344b Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 27 Jun 2017 04:21:56 +0000 Subject: [PATCH 08/11] 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; From c251fab498bbb01f4e87044c7ad7627dd06e1b1c Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 5 Jul 2017 05:29:01 +0000 Subject: [PATCH 09/11] test_framework: powhash abstraction --- qa/rpc-tests/test_framework/mininode.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 5b563c58ae1e..db2b071904eb 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -76,6 +76,9 @@ def ripemd160(s): def hash256(s): return sha256(sha256(s)) +def powhash(s): + return hash256(s) + def ser_compact_size(l): r = b"" if l < 253: @@ -577,8 +580,9 @@ def calc_sha256(self): r += struct.pack(" Date: Wed, 5 Jul 2017 05:31:41 +0000 Subject: [PATCH 10/11] test_framework: Adapt powhash to rotating regtest PoW --- qa/rpc-tests/test_framework/mininode.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index db2b071904eb..ba7fe98825c5 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -73,11 +73,24 @@ 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 powhash(s): - return hash256(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"" @@ -580,7 +593,7 @@ def calc_sha256(self): r += struct.pack(" Date: Sat, 8 Jul 2017 13:12:19 +0000 Subject: [PATCH 11/11] GetEarliestNextBlockTime replaces GetMedianTimePast only for minimum block time --- src/chain.cpp | 22 ++++++++++++++++++++++ src/chain.h | 2 ++ src/miner.cpp | 2 +- src/rpc/mining.cpp | 2 +- src/test/miner_tests.cpp | 6 ++++-- src/test/skiplist_tests.cpp | 5 ++++- src/validation.cpp | 10 +++++----- 7 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/chain.cpp b/src/chain.cpp index a5b369c4fc4c..bde337a7f565 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -4,6 +4,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "chain.h" +#include "chainparams.h" +#include "consensus/params.h" /** * CChain implementation @@ -111,6 +113,26 @@ const CBlockIndex* CBlockIndex::GetAncestor(int height) const return const_cast(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/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/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 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) ||