diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index 384f70bc104ee..cffe9cdafd795 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -27,4 +27,11 @@ static const size_t MIN_SERIALIZABLE_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * /** Interpret sequence numbers as relative lock-time constraints. */ static constexpr unsigned int LOCKTIME_VERIFY_SEQUENCE = (1 << 0); +/** + * Maximum number of seconds that the timestamp of the first + * block of a difficulty adjustment period is allowed to + * be earlier than the last block of the previous period (BIP94). + */ +static constexpr int64_t MAX_TIMEWARP = 600; + #endif // BITCOIN_CONSENSUS_CONSENSUS_H diff --git a/src/node/miner.cpp b/src/node/miner.cpp index fa2d979b86a63..5c476e154f443 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -33,6 +33,14 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam int64_t nOldTime = pblock->nTime; int64_t nNewTime{std::max(pindexPrev->GetMedianTimePast() + 1, TicksSinceEpoch(NodeClock::now()))}; + if (consensusParams.enforce_BIP94) { + // Height of block to be mined. + const int height{pindexPrev->nHeight + 1}; + if (height % consensusParams.DifficultyAdjustmentInterval() == 0) { + nNewTime = std::max(nNewTime, pindexPrev->GetBlockTime() - MAX_TIMEWARP); + } + } + if (nOldTime < nNewTime) { pblock->nTime = nNewTime; } diff --git a/src/validation.cpp b/src/validation.cpp index 8c80e35c01723..8f75b2e30a0cb 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -107,13 +107,6 @@ const std::vector CHECKLEVEL_DOC { * */ static constexpr int PRUNE_LOCK_BUFFER{10}; -/** - * Maximum number of seconds that the timestamp of the first - * block of a difficulty adjustment period is allowed to - * be earlier than the last block of the previous period (BIP94). - */ -static constexpr int64_t MAX_TIMEWARP = 600; - GlobalMutex g_best_block_mutex; std::condition_variable g_best_block_cv; uint256 g_best_block; diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py index b183023b2f79f..c0df120c65a26 100755 --- a/test/functional/mining_basic.py +++ b/test/functional/mining_basic.py @@ -28,6 +28,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, + assert_greater_than_or_equal, assert_raises_rpc_error, get_fee, ) @@ -139,24 +140,24 @@ def test_timewarp(self): self.log.info("First block template of retarget period can't use wall clock time") self.nodes[0].setmocktime(t) - assert_raises_rpc_error(-1, "time-timewarp-attack, block's timestamp is too early on diff adjustment block", - lambda: node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)) - - # Create template with an acceptable timestamp and then modify it - self.nodes[0].setmocktime(t + MAX_FUTURE_BLOCK_TIME) + # The template will have an adjusted timestamp, which we then modify tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS) + assert_greater_than_or_equal(tmpl['curtime'], t + MAX_FUTURE_BLOCK_TIME - MAX_TIMEWARP) block = CBlock() block.nVersion = tmpl["version"] block.hashPrevBlock = int(tmpl["previousblockhash"], 16) - block.nTime = t + block.nTime = tmpl["curtime"] block.nBits = int(tmpl["bits"], 16) block.nNonce = 0 block.vtx = [create_coinbase(height=int(tmpl["height"]))] block.solve() + assert_template(node, block, None) - self.nodes[0].setmocktime(t) - assert_raises_rpc_error(-25, 'time-timewarp-attack', lambda: node.submitheader(hexdata=CBlockHeader(block).serialize().hex())) + bad_block = copy.deepcopy(block) + bad_block.nTime = t + bad_block.solve() + assert_raises_rpc_error(-25, 'time-timewarp-attack', lambda: node.submitheader(hexdata=CBlockHeader(bad_block).serialize().hex())) def run_test(self): node = self.nodes[0]