mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-12-06 02:33:28 +01:00
Merge bitcoin/bitcoin#30681: Have miner account for timewarp mitigation, activate on regtest, lower nPowTargetTimespan to 144 and add test
59ff17e5afminer: adjust clock to timewarp rule (Sjors Provoost)e929054e12Add timewarp attack mitigation test (Sjors Provoost)e85f386c4bconsensus: enable BIP94 on regtest (Sjors Provoost)dd154b0568consensus: lower regtest nPowTargetTimespan to 144 (Sjors Provoost) Pull request description: Because #30647 reduced the timewarp attack threshold from 7200s to 600s, our miner code will fail to propose a block template (on testnet4) if the last block of the previous period has a timestamp two hours in the future. This PR fixes that and also adds a test. The non-test changes in the last commit should be in v28, otherwise miners have to patch it themselves. If necessary I can split that out into a separate PR, but I prefer to get the tests in as well. In order to add the test, we activate BIP94 on regtest. In order for the test to run faster, we reduce its difficulty retarget period to 144, the same number that's already used for softfork activation logic. Regtest does not actually adjust its difficulty, so this change has no effect (except for `getnetworkhashps`, see commit). An alternative approach would be to run this test on testnet4, by hardcoding its first 2015 in the test suite. But since the timewarp mitigation is a serious candidate for a future mainnet softfork, it seems better to just deploy it on regtest. The next commits add a test and fix the miner code. The `MAX_TIMEWARP` constant is moved to `consensus.h` so both validation and miner code have access to it. ACKs for top commit: achow101: ACK59ff17e5affjahr: ACK59ff17e5afglozow: ACK59ff17e5afTree-SHA512: 50af9fdcba9b0d5c57e1efd5feffd870bd11b5318f1f8b0aabf684657f2d33ab108d5f00b1475fe0d38e8e0badc97249ef8dda20c7f47fcc1698bc1008798830
This commit is contained in:
@@ -28,12 +28,16 @@ from test_framework.p2p import P2PDataStore
|
||||
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,
|
||||
)
|
||||
from test_framework.wallet import MiniWallet
|
||||
|
||||
|
||||
DIFFICULTY_ADJUSTMENT_INTERVAL = 144
|
||||
MAX_FUTURE_BLOCK_TIME = 2 * 3600
|
||||
MAX_TIMEWARP = 600
|
||||
VERSIONBITS_TOP_BITS = 0x20000000
|
||||
VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT = 28
|
||||
DEFAULT_BLOCK_MIN_TX_FEE = 1000 # default `-blockmintxfee` setting [sat/kvB]
|
||||
@@ -115,6 +119,46 @@ class MiningTest(BitcoinTestFramework):
|
||||
assert tx_below_min_feerate['txid'] not in block_template_txids
|
||||
assert tx_below_min_feerate['txid'] not in block_txids
|
||||
|
||||
def test_timewarp(self):
|
||||
self.log.info("Test timewarp attack mitigation (BIP94)")
|
||||
node = self.nodes[0]
|
||||
|
||||
self.log.info("Mine until the last block of the retarget period")
|
||||
blockchain_info = self.nodes[0].getblockchaininfo()
|
||||
n = DIFFICULTY_ADJUSTMENT_INTERVAL - blockchain_info['blocks'] % DIFFICULTY_ADJUSTMENT_INTERVAL - 2
|
||||
t = blockchain_info['time']
|
||||
|
||||
for _ in range(n):
|
||||
t += 600
|
||||
self.nodes[0].setmocktime(t)
|
||||
self.generate(self.wallet, 1, sync_fun=self.no_op)
|
||||
|
||||
self.log.info("Create block two hours in the future")
|
||||
self.nodes[0].setmocktime(t + MAX_FUTURE_BLOCK_TIME)
|
||||
self.generate(self.wallet, 1, sync_fun=self.no_op)
|
||||
assert_equal(node.getblock(node.getbestblockhash())['time'], t + MAX_FUTURE_BLOCK_TIME)
|
||||
|
||||
self.log.info("First block template of retarget period can't use wall clock time")
|
||||
self.nodes[0].setmocktime(t)
|
||||
# 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 = 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)
|
||||
|
||||
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]
|
||||
self.wallet = MiniWallet(node)
|
||||
@@ -322,6 +366,7 @@ class MiningTest(BitcoinTestFramework):
|
||||
assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
|
||||
|
||||
self.test_blockmintxfee_parameter()
|
||||
self.test_timewarp()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -58,7 +58,7 @@ TIME_RANGE_STEP = 600 # ten-minute steps
|
||||
TIME_RANGE_MTP = TIME_GENESIS_BLOCK + (HEIGHT - 6) * TIME_RANGE_STEP
|
||||
TIME_RANGE_TIP = TIME_GENESIS_BLOCK + (HEIGHT - 1) * TIME_RANGE_STEP
|
||||
TIME_RANGE_END = TIME_GENESIS_BLOCK + HEIGHT * TIME_RANGE_STEP
|
||||
DIFFICULTY_ADJUSTMENT_INTERVAL = 2016
|
||||
DIFFICULTY_ADJUSTMENT_INTERVAL = 144
|
||||
|
||||
|
||||
class BlockchainTest(BitcoinTestFramework):
|
||||
|
||||
Reference in New Issue
Block a user