aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorSjors Provoost <sjors@sprovoost.nl>2024-08-20 18:49:59 +0200
committerSjors Provoost <sjors@sprovoost.nl>2024-08-20 18:49:59 +0200
commite929054e12210353812f440c685a23329e7040f7 (patch)
tree1b3bafc1b14f16c2b45604117f6d67ffa3f7ebcd /test
parente85f386c4b157b7d1ac16aface9bd2c614e62b46 (diff)
Add timewarp attack mitigation test
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/mining_basic.py44
1 files changed, 44 insertions, 0 deletions
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index 6a364a4815..b183023b2f 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -34,6 +34,9 @@ from test_framework.util import (
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 +118,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)
+ 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)
+ tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
+
+ block = CBlock()
+ block.nVersion = tmpl["version"]
+ block.hashPrevBlock = int(tmpl["previousblockhash"], 16)
+ block.nTime = t
+ block.nBits = int(tmpl["bits"], 16)
+ block.nNonce = 0
+ block.vtx = [create_coinbase(height=int(tmpl["height"]))]
+ block.solve()
+
+ self.nodes[0].setmocktime(t)
+ assert_raises_rpc_error(-25, 'time-timewarp-attack', lambda: node.submitheader(hexdata=CBlockHeader(block).serialize().hex()))
+
def run_test(self):
node = self.nodes[0]
self.wallet = MiniWallet(node)
@@ -322,6 +365,7 @@ class MiningTest(BitcoinTestFramework):
assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
self.test_blockmintxfee_parameter()
+ self.test_timewarp()
if __name__ == '__main__':