You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

bip9-softforks.py 9.7KB

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