scripted-diff: Use the C++11 keyword nullptr to denote the pointer literal instead...
[bitcoinplatinum.git] / test / functional / bip9-softforks.py
blobf00232c9ffe0398f4456b4881c48144873f5d336
1 #!/usr/bin/env python3
2 # Copyright (c) 2015-2016 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 9 soft forks.
7 Connect to a single node.
8 regtest lock-in with 108/144 block signalling
9 activation after a further 144 blocks
10 mine 2 block and save coinbases for later use
11 mine 141 blocks to transition from DEFINED to STARTED
12 mine 100 blocks signalling readiness and 44 not in order to fail to change state this period
13 mine 108 blocks signalling readiness and 36 blocks not signalling readiness (STARTED->LOCKED_IN)
14 mine a further 143 blocks (LOCKED_IN)
15 test that enforcement has not triggered (which triggers ACTIVE)
16 test that enforcement has triggered
17 """
18 from io import BytesIO
19 import shutil
20 import time
21 import itertools
23 from test_framework.test_framework import ComparisonTestFramework
24 from test_framework.util import *
25 from test_framework.mininode import CTransaction, NetworkThread
26 from test_framework.blocktools import create_coinbase, create_block
27 from test_framework.comptool import TestInstance, TestManager
28 from test_framework.script import CScript, OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP
30 class BIP9SoftForksTest(ComparisonTestFramework):
32 def __init__(self):
33 super().__init__()
34 self.num_nodes = 1
35 self.extra_args = [['-whitelist=127.0.0.1']]
37 def run_test(self):
38 self.test = TestManager(self, self.options.tmpdir)
39 self.test.add_all_connections(self.nodes)
40 NetworkThread().start() # Start up network handling in another thread
41 self.test.run()
43 def create_transaction(self, node, coinbase, to_address, amount):
44 from_txid = node.getblock(coinbase)['tx'][0]
45 inputs = [{ "txid" : from_txid, "vout" : 0}]
46 outputs = { to_address : amount }
47 rawtx = node.createrawtransaction(inputs, outputs)
48 tx = CTransaction()
49 f = BytesIO(hex_str_to_bytes(rawtx))
50 tx.deserialize(f)
51 tx.nVersion = 2
52 return tx
54 def sign_transaction(self, node, tx):
55 signresult = node.signrawtransaction(bytes_to_hex_str(tx.serialize()))
56 tx = CTransaction()
57 f = BytesIO(hex_str_to_bytes(signresult['hex']))
58 tx.deserialize(f)
59 return tx
61 def generate_blocks(self, number, version, test_blocks = []):
62 for i in range(number):
63 block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1)
64 block.nVersion = version
65 block.rehash()
66 block.solve()
67 test_blocks.append([block, True])
68 self.last_block_time += 1
69 self.tip = block.sha256
70 self.height += 1
71 return test_blocks
73 def get_bip9_status(self, key):
74 info = self.nodes[0].getblockchaininfo()
75 return info['bip9_softforks'][key]
77 def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignature, bitno):
78 assert_equal(self.get_bip9_status(bipName)['status'], 'defined')
79 assert_equal(self.get_bip9_status(bipName)['since'], 0)
81 # generate some coins for later
82 self.coinbase_blocks = self.nodes[0].generate(2)
83 self.height = 3 # height of the next block to build
84 self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0)
85 self.nodeaddress = self.nodes[0].getnewaddress()
86 self.last_block_time = int(time.time())
88 assert_equal(self.get_bip9_status(bipName)['status'], 'defined')
89 assert_equal(self.get_bip9_status(bipName)['since'], 0)
90 tmpl = self.nodes[0].getblocktemplate({})
91 assert(bipName not in tmpl['rules'])
92 assert(bipName not in tmpl['vbavailable'])
93 assert_equal(tmpl['vbrequired'], 0)
94 assert_equal(tmpl['version'], 0x20000000)
96 # Test 1
97 # Advance from DEFINED to STARTED
98 test_blocks = self.generate_blocks(141, 4)
99 yield TestInstance(test_blocks, sync_every_block=False)
101 assert_equal(self.get_bip9_status(bipName)['status'], 'started')
102 assert_equal(self.get_bip9_status(bipName)['since'], 144)
103 assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0)
104 assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0)
105 tmpl = self.nodes[0].getblocktemplate({})
106 assert(bipName not in tmpl['rules'])
107 assert_equal(tmpl['vbavailable'][bipName], bitno)
108 assert_equal(tmpl['vbrequired'], 0)
109 assert(tmpl['version'] & activated_version)
111 # Test 1-A
112 # check stats after max number of "signalling not" blocks such that LOCKED_IN still possible this period
113 test_blocks = self.generate_blocks(36, 4, test_blocks) # 0x00000004 (signalling not)
114 test_blocks = self.generate_blocks(10, activated_version) # 0x20000001 (signalling ready)
115 yield TestInstance(test_blocks, sync_every_block=False)
117 assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 46)
118 assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 10)
119 assert_equal(self.get_bip9_status(bipName)['statistics']['possible'], True)
121 # Test 1-B
122 # check stats after one additional "signalling not" block -- LOCKED_IN no longer possible this period
123 test_blocks = self.generate_blocks(1, 4, test_blocks) # 0x00000004 (signalling not)
124 yield TestInstance(test_blocks, sync_every_block=False)
126 assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 47)
127 assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 10)
128 assert_equal(self.get_bip9_status(bipName)['statistics']['possible'], False)
130 # Test 1-C
131 # finish period with "ready" blocks, but soft fork will still fail to advance to LOCKED_IN
132 test_blocks = self.generate_blocks(97, activated_version) # 0x20000001 (signalling ready)
133 yield TestInstance(test_blocks, sync_every_block=False)
135 assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0)
136 assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0)
137 assert_equal(self.get_bip9_status(bipName)['statistics']['possible'], True)
138 assert_equal(self.get_bip9_status(bipName)['status'], 'started')
140 # Test 2
141 # Fail to achieve LOCKED_IN 100 out of 144 signal bit 1
142 # using a variety of bits to simulate multiple parallel softforks
143 test_blocks = self.generate_blocks(50, activated_version) # 0x20000001 (signalling ready)
144 test_blocks = self.generate_blocks(20, 4, test_blocks) # 0x00000004 (signalling not)
145 test_blocks = self.generate_blocks(50, activated_version, test_blocks) # 0x20000101 (signalling ready)
146 test_blocks = self.generate_blocks(24, 4, test_blocks) # 0x20010000 (signalling not)
147 yield TestInstance(test_blocks, sync_every_block=False)
149 assert_equal(self.get_bip9_status(bipName)['status'], 'started')
150 assert_equal(self.get_bip9_status(bipName)['since'], 144)
151 assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0)
152 assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0)
153 tmpl = self.nodes[0].getblocktemplate({})
154 assert(bipName not in tmpl['rules'])
155 assert_equal(tmpl['vbavailable'][bipName], bitno)
156 assert_equal(tmpl['vbrequired'], 0)
157 assert(tmpl['version'] & activated_version)
159 # Test 3
160 # 108 out of 144 signal bit 1 to achieve LOCKED_IN
161 # using a variety of bits to simulate multiple parallel softforks
162 test_blocks = self.generate_blocks(57, activated_version) # 0x20000001 (signalling ready)
163 test_blocks = self.generate_blocks(26, 4, test_blocks) # 0x00000004 (signalling not)
164 test_blocks = self.generate_blocks(50, activated_version, test_blocks) # 0x20000101 (signalling ready)
165 test_blocks = self.generate_blocks(10, 4, test_blocks) # 0x20010000 (signalling not)
166 yield TestInstance(test_blocks, sync_every_block=False)
168 # check counting stats and "possible" flag before last block of this period achieves LOCKED_IN...
169 assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 143)
170 assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 107)
171 assert_equal(self.get_bip9_status(bipName)['statistics']['possible'], True)
172 assert_equal(self.get_bip9_status(bipName)['status'], 'started')
174 # ...continue with Test 3
175 test_blocks = self.generate_blocks(1, activated_version) # 0x20000001 (signalling ready)
176 yield TestInstance(test_blocks, sync_every_block=False)
178 assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
179 assert_equal(self.get_bip9_status(bipName)['since'], 576)
180 tmpl = self.nodes[0].getblocktemplate({})
181 assert(bipName not in tmpl['rules'])
183 # Test 4
184 # 143 more version 536870913 blocks (waiting period-1)
185 test_blocks = self.generate_blocks(143, 4)
186 yield TestInstance(test_blocks, sync_every_block=False)
188 assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
189 assert_equal(self.get_bip9_status(bipName)['since'], 576)
190 tmpl = self.nodes[0].getblocktemplate({})
191 assert(bipName not in tmpl['rules'])
193 # Test 5
194 # Check that the new rule is enforced
195 spendtx = self.create_transaction(self.nodes[0],
196 self.coinbase_blocks[0], self.nodeaddress, 1.0)
197 invalidate(spendtx)
198 spendtx = self.sign_transaction(self.nodes[0], spendtx)
199 spendtx.rehash()
200 invalidatePostSignature(spendtx)
201 spendtx.rehash()
202 block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1)
203 block.nVersion = activated_version
204 block.vtx.append(spendtx)
205 block.hashMerkleRoot = block.calc_merkle_root()
206 block.rehash()
207 block.solve()
209 self.last_block_time += 1
210 self.tip = block.sha256
211 self.height += 1
212 yield TestInstance([[block, True]])
214 assert_equal(self.get_bip9_status(bipName)['status'], 'active')
215 assert_equal(self.get_bip9_status(bipName)['since'], 720)
216 tmpl = self.nodes[0].getblocktemplate({})
217 assert(bipName in tmpl['rules'])
218 assert(bipName not in tmpl['vbavailable'])
219 assert_equal(tmpl['vbrequired'], 0)
220 assert(not (tmpl['version'] & (1 << bitno)))
222 # Test 6
223 # Check that the new sequence lock rules are enforced
224 spendtx = self.create_transaction(self.nodes[0],
225 self.coinbase_blocks[1], self.nodeaddress, 1.0)
226 invalidate(spendtx)
227 spendtx = self.sign_transaction(self.nodes[0], spendtx)
228 spendtx.rehash()
229 invalidatePostSignature(spendtx)
230 spendtx.rehash()
232 block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1)
233 block.nVersion = 5
234 block.vtx.append(spendtx)
235 block.hashMerkleRoot = block.calc_merkle_root()
236 block.rehash()
237 block.solve()
238 self.last_block_time += 1
239 yield TestInstance([[block, False]])
241 # Restart all
242 self.test.clear_all_connections()
243 self.stop_nodes()
244 shutil.rmtree(self.options.tmpdir + "/node0")
245 self.setup_chain()
246 self.setup_network()
247 self.test.add_all_connections(self.nodes)
248 NetworkThread().start()
249 self.test.test_nodes[0].wait_for_verack()
251 def get_tests(self):
252 for test in itertools.chain(
253 self.test_BIP('csv', 0x20000001, self.sequence_lock_invalidate, self.donothing, 0),
254 self.test_BIP('csv', 0x20000001, self.mtp_invalidate, self.donothing, 0),
255 self.test_BIP('csv', 0x20000001, self.donothing, self.csv_invalidate, 0)
257 yield test
259 def donothing(self, tx):
260 return
262 def csv_invalidate(self, tx):
263 """Modify the signature in vin 0 of the tx to fail CSV
264 Prepends -1 CSV DROP in the scriptSig itself.
266 tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP] +
267 list(CScript(tx.vin[0].scriptSig)))
269 def sequence_lock_invalidate(self, tx):
270 """Modify the nSequence to make it fails once sequence lock rule is
271 activated (high timespan).
273 tx.vin[0].nSequence = 0x00FFFFFF
274 tx.nLockTime = 0
276 def mtp_invalidate(self, tx):
277 """Modify the nLockTime to make it fails once MTP rule is activated."""
278 # Disable Sequence lock, Activate nLockTime
279 tx.vin[0].nSequence = 0x90FFFFFF
280 tx.nLockTime = self.last_block_time
282 if __name__ == '__main__':
283 BIP9SoftForksTest().main()