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 10KB

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