diff options
author | Ava Chow <github@achow101.com> | 2024-08-22 12:15:19 -0400 |
---|---|---|
committer | Ava Chow <github@achow101.com> | 2024-08-22 12:15:19 -0400 |
commit | 338b9d82dc89c7e6474230da6e6cd43bef024b48 (patch) | |
tree | 28aae2470371f290153c15caf84a98e6e2f18465 /test | |
parent | 5ce2285b8739e12eebd1d53358038ec2277c662b (diff) | |
parent | 59ff17e5af4e382cbe16f183767beef1bdcd9131 (diff) |
Merge bitcoin/bitcoin#30681: Have miner account for timewarp mitigation, activate on regtest, lower nPowTargetTimespan to 144 and add test
59ff17e5af4e382cbe16f183767beef1bdcd9131 miner: adjust clock to timewarp rule (Sjors Provoost)
e929054e12210353812f440c685a23329e7040f7 Add timewarp attack mitigation test (Sjors Provoost)
e85f386c4b157b7d1ac16aface9bd2c614e62b46 consensus: enable BIP94 on regtest (Sjors Provoost)
dd154b05689c60fad45df0df6d31cec12e09ab21 consensus: 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:
ACK 59ff17e5af4e382cbe16f183767beef1bdcd9131
fjahr:
ACK 59ff17e5af4e382cbe16f183767beef1bdcd9131
glozow:
ACK 59ff17e5af4e382cbe16f183767beef1bdcd9131
Tree-SHA512: 50af9fdcba9b0d5c57e1efd5feffd870bd11b5318f1f8b0aabf684657f2d33ab108d5f00b1475fe0d38e8e0badc97249ef8dda20c7f47fcc1698bc1008798830
Diffstat (limited to 'test')
-rwxr-xr-x | test/functional/mining_basic.py | 45 | ||||
-rwxr-xr-x | test/functional/rpc_blockchain.py | 2 |
2 files changed, 46 insertions, 1 deletions
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py index 6a364a4815..c0df120c65 100755 --- a/test/functional/mining_basic.py +++ b/test/functional/mining_basic.py @@ -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__': diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index e5aca7f138..98147237b1 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -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): |