Dash Core  0.12.2.1
P2P Digital Currency
bip9-softforks.py
Go to the documentation of this file.
1 #!/usr/bin/env python2
2 # Copyright (c) 2015 The Bitcoin Core developers
3 # Distributed under the MIT/X11 software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 #
6 
7 from test_framework.test_framework import ComparisonTestFramework
8 from test_framework.util import *
9 from test_framework.mininode import CTransaction, NetworkThread
10 from test_framework.blocktools import create_coinbase, create_block
11 from test_framework.comptool import TestInstance, TestManager
12 from test_framework.script import CScript, OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP
13 from io import BytesIO
14 import time
15 import itertools
16 
17 '''
18 This test is meant to exercise BIP forks
19 Connect to a single node.
20 regtest lock-in with 108/144 block signalling
21 activation after a further 144 blocks
22 mine 2 block and save coinbases for later use
23 mine 141 blocks to transition from DEFINED to STARTED
24 mine 100 blocks signalling readiness and 44 not in order to fail to change state this period
25 mine 108 blocks signalling readiness and 36 blocks not signalling readiness (STARTED->LOCKED_IN)
26 mine a further 143 blocks (LOCKED_IN)
27 test that enforcement has not triggered (which triggers ACTIVE)
28 test that enforcement has triggered
29 '''
30 
31 
33 
34  def __init__(self):
35  self.num_nodes = 1
36 
37  def setup_network(self):
38  self.nodes = start_nodes(1, self.options.tmpdir,
39  extra_args=[['-debug', '-whitelist=127.0.0.1']],
40  binary=[self.options.testbinary])
41 
42  def run_test(self):
43  self.test = TestManager(self, self.options.tmpdir)
44  self.test.add_all_connections(self.nodes)
45  NetworkThread().start() # Start up network handling in another thread
46  self.test.run()
47 
48  def create_transaction(self, node, coinbase, to_address, amount):
49  from_txid = node.getblock(coinbase)['tx'][0]
50  inputs = [{ "txid" : from_txid, "vout" : 0}]
51  outputs = { to_address : amount }
52  rawtx = node.createrawtransaction(inputs, outputs)
53  tx = CTransaction()
54  f = BytesIO(hex_str_to_bytes(rawtx))
55  tx.deserialize(f)
56  tx.nVersion = 2
57  return tx
58 
59  def sign_transaction(self, node, tx):
60  signresult = node.signrawtransaction(bytes_to_hex_str(tx.serialize()))
61  tx = CTransaction()
62  f = BytesIO(hex_str_to_bytes(signresult['hex']))
63  tx.deserialize(f)
64  return tx
65 
66  def generate_blocks(self, number, version, test_blocks = []):
67  for i in xrange(number):
68  block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1)
69  block.nVersion = version
70  block.rehash()
71  block.solve()
72  test_blocks.append([block, True])
73  self.last_block_time += 1
74  self.tip = block.sha256
75  self.height += 1
76  return test_blocks
77 
78  def get_bip9_status(self, key):
79  info = self.nodes[0].getblockchaininfo()
80  for row in info['bip9_softforks']:
81  if row['id'] == key:
82  return row
83  raise IndexError ('key:"%s" not found' % key)
84 
85 
86  def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignature, bitno):
87  wait_to_sync(self.nodes[0])
88  # generate some coins for later
89  self.coinbase_blocks = self.nodes[0].generate(2)
90  self.height = 3 # height of the next block to build
91  self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0)
92  self.nodeaddress = self.nodes[0].getnewaddress()
93  self.last_block_time = int(time.time())
94 
95  assert_equal(self.get_bip9_status(bipName)['status'], 'defined')
96  tmpl = self.nodes[0].getblocktemplate({})
97  assert(bipName not in tmpl['rules'])
98  assert(bipName not in tmpl['vbavailable'])
99  assert_equal(tmpl['vbrequired'], 0)
100  assert_equal(tmpl['version'], 0x20000000)
101 
102  # Test 1
103  # Advance from DEFINED to STARTED
104  test_blocks = self.generate_blocks(141, 4)
105  yield TestInstance(test_blocks, sync_every_block=False)
106 
107  assert_equal(self.get_bip9_status(bipName)['status'], 'started')
108  tmpl = self.nodes[0].getblocktemplate({})
109  assert(bipName not in tmpl['rules'])
110  assert_equal(tmpl['vbavailable'][bipName], bitno)
111  assert_equal(tmpl['vbrequired'], 0)
112  assert(tmpl['version'] & activated_version)
113 
114  # Test 2
115  # Fail to achieve LOCKED_IN 100 out of 144 signal bit 1
116  # using a variety of bits to simulate multiple parallel softforks
117  test_blocks = self.generate_blocks(50, activated_version) # 0x20000001 (signalling ready)
118  test_blocks = self.generate_blocks(20, 4, test_blocks) # 0x00000004 (signalling not)
119  test_blocks = self.generate_blocks(50, activated_version, test_blocks) # 0x20000101 (signalling ready)
120  test_blocks = self.generate_blocks(24, 4, test_blocks) # 0x20010000 (signalling not)
121  yield TestInstance(test_blocks, sync_every_block=False)
122 
123  assert_equal(self.get_bip9_status(bipName)['status'], 'started')
124  tmpl = self.nodes[0].getblocktemplate({})
125  assert(bipName not in tmpl['rules'])
126  assert_equal(tmpl['vbavailable'][bipName], bitno)
127  assert_equal(tmpl['vbrequired'], 0)
128  assert(tmpl['version'] & activated_version)
129 
130  # Test 3
131  # 108 out of 144 signal bit 1 to achieve LOCKED_IN
132  # using a variety of bits to simulate multiple parallel softforks
133  test_blocks = self.generate_blocks(58, activated_version) # 0x20000001 (signalling ready)
134  test_blocks = self.generate_blocks(26, 4, test_blocks) # 0x00000004 (signalling not)
135  test_blocks = self.generate_blocks(50, activated_version, test_blocks) # 0x20000101 (signalling ready)
136  test_blocks = self.generate_blocks(10, 4, test_blocks) # 0x20010000 (signalling not)
137  yield TestInstance(test_blocks, sync_every_block=False)
138 
139  assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
140  tmpl = self.nodes[0].getblocktemplate({})
141  assert(bipName not in tmpl['rules'])
142 
143  # Test 4
144  # 143 more version 536870913 blocks (waiting period-1)
145  test_blocks = self.generate_blocks(143, 4)
146  yield TestInstance(test_blocks, sync_every_block=False)
147 
148  assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
149  tmpl = self.nodes[0].getblocktemplate({})
150  assert(bipName not in tmpl['rules'])
151 
152  # Test 5
153  # Check that the new rule is enforced
154  spendtx = self.create_transaction(self.nodes[0],
155  self.coinbase_blocks[0], self.nodeaddress, 1.0)
156  invalidate(spendtx)
157  spendtx = self.sign_transaction(self.nodes[0], spendtx)
158  spendtx.rehash()
159  invalidatePostSignature(spendtx)
160  spendtx.rehash()
161  block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1)
162  block.nVersion = activated_version
163  block.vtx.append(spendtx)
164  block.hashMerkleRoot = block.calc_merkle_root()
165  block.rehash()
166  block.solve()
167 
168  self.last_block_time += 1
169  self.tip = block.sha256
170  self.height += 1
171  yield TestInstance([[block, True]])
172 
173  assert_equal(self.get_bip9_status(bipName)['status'], 'active')
174  tmpl = self.nodes[0].getblocktemplate({})
175  assert(bipName in tmpl['rules'])
176  assert(bipName not in tmpl['vbavailable'])
177  assert_equal(tmpl['vbrequired'], 0)
178  assert(not (tmpl['version'] & (1 << bitno)))
179 
180  # Test 6
181  # Check that the new sequence lock rules are enforced
182  spendtx = self.create_transaction(self.nodes[0],
183  self.coinbase_blocks[1], self.nodeaddress, 1.0)
184  invalidate(spendtx)
185  spendtx = self.sign_transaction(self.nodes[0], spendtx)
186  spendtx.rehash()
187  invalidatePostSignature(spendtx)
188  spendtx.rehash()
189 
190  block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1)
191  block.nVersion = 5
192  block.vtx.append(spendtx)
193  block.hashMerkleRoot = block.calc_merkle_root()
194  block.rehash()
195  block.solve()
196  self.last_block_time += 1
197  yield TestInstance([[block, False]])
198 
199  # Restart all
200  stop_nodes(self.nodes)
202  shutil.rmtree(self.options.tmpdir)
203  self.setup_chain()
204  self.setup_network()
205  self.test.clear_all_connections()
206  self.test.add_all_connections(self.nodes)
207  NetworkThread().start() # Start up network handling in another thread
208 
209 
210  def get_tests(self):
211  for test in itertools.chain(
212  self.test_BIP('csv', 0x20000001, self.sequence_lock_invalidate, self.donothing, 0),
213  self.test_BIP('csv', 0x20000001, self.mtp_invalidate, self.donothing, 0),
214  self.test_BIP('csv', 0x20000001, self.donothing, self.csv_invalidate, 0)
215  ):
216  yield test
217 
218  def donothing(self, tx):
219  return
220 
221  def csv_invalidate(self, tx):
222  '''Modify the signature in vin 0 of the tx to fail CSV
223  Prepends -1 CSV DROP in the scriptSig itself.
224  '''
225  tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP] +
226  list(CScript(tx.vin[0].scriptSig)))
227 
229  '''Modify the nSequence to make it fails once sequence lock rule is activated (high timespan)
230  '''
231  tx.vin[0].nSequence = 0x00FFFFFF
232  tx.nLockTime = 0
233 
234  def mtp_invalidate(self, tx):
235  '''Modify the nLockTime to make it fails once MTP rule is activated
236  '''
237  # Disable Sequence lock, Activate nLockTime
238  tx.vin[0].nSequence = 0x90FFFFFF
239  tx.nLockTime = self.last_block_time
240 
241 if __name__ == '__main__':
def wait_bitcoinds()
Definition: util.py:337
def create_block(hashprev, coinbase, nTime=None)
Definition: blocktools.py:11
def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignature, bitno)
def hex_str_to_bytes(hex_str)
Definition: util.py:111
UniValue getblocktemplate(const UniValue &params, bool fHelp)
Definition: mining.cpp:337
def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None)
Definition: util.py:305
def wait_to_sync(node)
Definition: util.py:87
UniValue getnewaddress(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:113
def create_coinbase(height, pubkey=None)
Definition: blocktools.py:43
def sign_transaction(self, node, tx)
def create_transaction(self, node, coinbase, to_address, amount)
def stop_nodes(nodes)
Definition: util.py:328
UniValue generate(const UniValue &params, bool fHelp)
Definition: mining.cpp:122
UniValue getbestblockhash(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:148
def generate_blocks(self, number, version, test_blocks=[])
def assert_equal(thing1, thing2)
Definition: util.py:461
UniValue getblockchaininfo(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:743
def bytes_to_hex_str(byte_str)
Definition: util.py:108