Skip to content

Commit 2e55bce

Browse files
Sjorsachow101
authored andcommitted
test: BIP 8 delayed activation functional test
1 parent 91b2e4b commit 2e55bce

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

test/functional/feature_bip8.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2021 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
# Test BIP 8 softforks
6+
from collections import namedtuple
7+
from test_framework.test_framework import BitcoinTestFramework
8+
from test_framework.util import assert_equal
9+
10+
RestartedNodeInfo = namedtuple("RestartedNodeInfo", ("node", "extra_args", "status"))
11+
12+
class Bip8Test(BitcoinTestFramework):
13+
def set_test_params(self):
14+
self.num_nodes = 4
15+
self.setup_clean_chain = True
16+
self.extra_args = [
17+
['-vbparams=testdummy:@-2:@-2'], # Node 0 has TestDummy inactive
18+
['-vbparams=testdummy:@144:@{}'.format(144 * 3)], # Node 1 has regular activation window
19+
['-vbparams=testdummy:@144:@{}:@{}'.format(144 * 3, 144 * 5)], # Node 2 has minimum activation height
20+
['-vbparams=testdummy:@-2:@-2'], # Node 3 has TestDummy inactive, but will be restarted with different params
21+
]
22+
23+
def setup_network(self):
24+
self.setup_nodes()
25+
26+
def test_height(self, height, status, mine_from, restart):
27+
if height > self.height:
28+
self.log.info("Test status at height {}...".format(height))
29+
while self.height < height:
30+
blockhash = self.nodes[mine_from].generate(1)[0]
31+
block = self.nodes[mine_from].getblock(blockhash, 0)
32+
for node in self.nodes:
33+
node.submitblock(block)
34+
assert_equal(node.getbestblockhash(), blockhash)
35+
self.height += 1
36+
elif height < self.height:
37+
assert mine_from is None
38+
self.log.info("Roll back to height {}...".format(height))
39+
old_block = self.nodes[0].getblockhash(height + 1)
40+
for node in self.nodes:
41+
node.invalidateblock(old_block)
42+
else:
43+
assert mine_from is None
44+
assert height == 0 and self.height == 0
45+
self.log.info("Test status at genesis...")
46+
47+
self.height = height
48+
49+
for (i, node), st in zip(enumerate(self.nodes), status):
50+
self.log.debug("Node #{}...".format(i))
51+
info = node.getblockchaininfo()
52+
assert_equal(info["blocks"], height)
53+
if st is None:
54+
assert "testdummy" not in info["softforks"]
55+
else:
56+
assert_equal(info["softforks"]["testdummy"]["bip8"]["status"], st)
57+
58+
if restart:
59+
# Restart this node and check that the status is what we expect
60+
self.restart_node(restart.node, restart.extra_args)
61+
info = self.nodes[restart.node].getblockchaininfo()
62+
assert_equal(info["blocks"], height)
63+
if restart.status is None:
64+
assert "testdummy" not in info["softforks"]
65+
else:
66+
assert_equal(info["softforks"]["testdummy"]["bip8"]["status"], restart.status)
67+
68+
# Now restart again to go back to the original settings
69+
self.restart_node(restart.node, self.extra_args[restart.node])
70+
info = self.nodes[restart.node].getblockchaininfo()
71+
assert_equal(info["blocks"], height)
72+
if status[restart.node] is None:
73+
assert "testdummy" not in info["softforks"]
74+
else:
75+
assert_equal(info["softforks"]["testdummy"]["bip8"]["status"], status[restart.node])
76+
77+
def run_test(self):
78+
self.log.info("Checking -vbparams")
79+
self.stop_node(3)
80+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@-2:@1"], expected_msg="Error: When one of startheight or timeoutheight is -2, both must be -2")
81+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@1:@-2"], expected_msg="Error: When one of startheight or timeoutheight is -2, both must be -2")
82+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@1:@1"], expected_msg="Error: Invalid startheight (1), must be a multiple of 144")
83+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@144:@1"], expected_msg="Error: Invalid timeoutheight (1), must be a multiple of 144")
84+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@144:@144:@-1"], expected_msg="Error: Invalid minimum activation height (-1), cannot be negative")
85+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@144:@144:@1"], expected_msg="Error: Invalid minimum activation height (1), must be a multiple of 144")
86+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@288:@144"], expected_msg="Error: Invalid timeoutheight (144), must be at least two periods greater than the startheight (288)")
87+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@-3:@144"], expected_msg="Error: Invalid startheight (-3), cannot be negative (except for never or always active special cases)")
88+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@144:@-1"], expected_msg="Error: Invalid timeoutheight (-1), cannot be negative (except for never or always active special cases)")
89+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@{}:@144".format(0x7fffffff + 1)], expected_msg="Error: Invalid startheight (@{})".format(0x7fffffff + 1))
90+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@144:@{}".format(0x7fffffff + 1)], expected_msg="Error: Invalid timeoutheight (@{})".format(0x7fffffff + 1))
91+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@{}:@144".format(-(0x7fffffff + 2))], expected_msg="Error: Invalid startheight (@{})".format(-(0x7fffffff + 2)))
92+
self.nodes[3].assert_start_raises_init_error(extra_args=["-vbparams=testdummy:@144:@{}".format(-(0x7fffffff + 2))], expected_msg="Error: Invalid timeoutheight (@{})".format(-(0x7fffffff + 2)))
93+
self.start_node(3, self.extra_args[3])
94+
95+
self.height = 0
96+
self.test_height(0, [None, "defined", "defined", None], None, RestartedNodeInfo(3, ["-vbparams=testdummy:@144:@{}".format(144*3)], "defined"))
97+
98+
# BIP 8 state transitions from "defined" to "started" or "failed" after
99+
# the last block of the retargeting period has been mined. This means
100+
# any new rules apply to transactions currently in the mempool, which
101+
# might be mined in the next block.
102+
#
103+
# The next retargeting period starts at block 144, so nothing should
104+
# happen at 142 and the state should change at 143.
105+
self.test_height(144-2, [None, "defined", "defined", None], 0, RestartedNodeInfo(3, ["-vbparams=testdummy:@144:@{}".format(144*3)], "defined"))
106+
self.test_height(144-1, [None, "started", "started", None], 0, RestartedNodeInfo(3, ["-vbparams=testdummy:@144:@{}".format(144*3)], "started"))
107+
108+
self.log.info("Test status when not signaling...")
109+
self.test_height(144*2-1, [None, "started", "started", None], 0, RestartedNodeInfo(3, ["-vbparams=testdummy:@144:@{}".format(144*3)], "started"))
110+
self.test_height(144*3-1, [None, "failed", "failed", None], 0, RestartedNodeInfo(3, ["-vbparams=testdummy:@144:@{}".format(144*3)], "failed"))
111+
112+
# The new branch has unique block hashes, because of the signalling and
113+
# because generate uses a deterministic address that depends on the node
114+
# index.
115+
# Also tests that if the last period meets the threshold, the fork activates.
116+
self.log.info("Test status when signaling...")
117+
self.test_height(144-1, [None, "started", "started", None], None, RestartedNodeInfo(3, ["-vbparams=testdummy:@144:@{}".format(144*3)], "started"))
118+
self.test_height(144*2-1, [None, "started", "started", None], 3, RestartedNodeInfo(3, ["-vbparams=testdummy:@144:@{}".format(144*3)], "started"))
119+
self.test_height(144*3-1, [None, "locked_in", "locked_in", None], 1, RestartedNodeInfo(3, ["-vbparams=testdummy:@144:@{}".format(144*3)], "locked_in"))
120+
self.test_height(144*4-1, [None, "active", "locked_in", None], 1, RestartedNodeInfo(3, ["-vbparams=testdummy:@144:@{}".format(144*3)], "active"))
121+
self.test_height(144*5-1, [None, "active", "active", None], 1, RestartedNodeInfo(3, ["-vbparams=testdummy:@144:@{}".format(144*3)], "active"))
122+
123+
if __name__ == '__main__':
124+
Bip8Test().main()

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
'p2p_feefilter.py',
131131
'feature_reindex.py',
132132
'feature_abortnode.py',
133+
'feature_bip8.py',
133134
# vv Tests less than 30s vv
134135
'wallet_keypool_topup.py --legacy-wallet',
135136
'wallet_keypool_topup.py --descriptors',

0 commit comments

Comments
 (0)