Dash Core  0.12.2.1
P2P Digital Currency
p2p-fullblocktest.py
Go to the documentation of this file.
1 #!/usr/bin/env python2
2 
3 #
4 # Distributed under the MIT/X11 software license, see the accompanying
5 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 #
7 
8 from test_framework.test_framework import ComparisonTestFramework
9 from test_framework.util import *
10 from test_framework.comptool import TestManager, TestInstance, RejectResult
11 from test_framework.blocktools import *
12 import time
13 from test_framework.key import CECKey
14 from test_framework.script import CScript, SignatureHash, SIGHASH_ALL, OP_TRUE, OP_FALSE
15 
17  def __init__(self, tx = CTransaction(), n = -1):
18  self.tx = tx
19  self.n = n # the output we're spending
20 
21 '''
22 This reimplements tests from the bitcoinj/FullBlockTestGenerator used
23 by the pull-tester.
24 
25 We use the testing framework in which we expect a particular answer from
26 each test.
27 '''
28 
30 
31  ''' Can either run this test as 1 node with expected answers, or two and compare them.
32  Change the "outcome" variable from each TestInstance object to only do the comparison. '''
33  def __init__(self):
34  self.num_nodes = 1
35  self.block_heights = {}
37  self.coinbase_key.set_secretbytes(b"horsebattery")
38  self.coinbase_pubkey = self.coinbase_key.get_pubkey()
39  self.block_time = int(time.time())+1
40  self.tip = None
41  self.blocks = {}
42 
43  def run_test(self):
44  test = TestManager(self, self.options.tmpdir)
45  test.add_all_connections(self.nodes)
46  NetworkThread().start() # Start up network handling in another thread
48  test.run()
49 
50  def add_transactions_to_block(self, block, tx_list):
51  [ tx.rehash() for tx in tx_list ]
52  block.vtx.extend(tx_list)
53  block.hashMerkleRoot = block.calc_merkle_root()
54  block.rehash()
55  return block
56 
57  # Create a block on top of self.tip, and advance self.tip to point to the new block
58  # if spend is specified, then 1 satoshi will be spent from that to an anyone-can-spend output,
59  # and rest will go to fees.
60  def next_block(self, number, spend=None, additional_coinbase_value=0, script=None):
61  if self.tip == None:
62  base_block_hash = self.genesis_hash
63  else:
64  base_block_hash = self.tip.sha256
65  # First create the coinbase
66  height = self.block_heights[base_block_hash] + 1
67  coinbase = create_coinbase(height, self.coinbase_pubkey)
68  coinbase.vout[0].nValue += additional_coinbase_value
69  if (spend != None):
70  coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 # all but one satoshi to fees
71  coinbase.rehash()
72  block = create_block(base_block_hash, coinbase, self.block_time)
73  if (spend != None):
74  tx = CTransaction()
75  tx.vin.append(CTxIn(COutPoint(spend.tx.sha256, spend.n), b"", 0xffffffff)) # no signature yet
76  # This copies the java comparison tool testing behavior: the first
77  # txout has a garbage scriptPubKey, "to make sure we're not
78  # pre-verifying too much" (?)
79  tx.vout.append(CTxOut(0, CScript([random.randint(0,255), height & 255])))
80  if script == None:
81  tx.vout.append(CTxOut(1, CScript([OP_TRUE])))
82  else:
83  tx.vout.append(CTxOut(1, script))
84  # Now sign it if necessary
85  scriptSig = b""
86  scriptPubKey = bytearray(spend.tx.vout[spend.n].scriptPubKey)
87  if (scriptPubKey[0] == OP_TRUE): # looks like an anyone-can-spend
88  scriptSig = CScript([OP_TRUE])
89  else:
90  # We have to actually sign it
91  (sighash, err) = SignatureHash(spend.tx.vout[spend.n].scriptPubKey, tx, 0, SIGHASH_ALL)
92  scriptSig = CScript([self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))])
93  tx.vin[0].scriptSig = scriptSig
94  # Now add the transaction to the block
95  block = self.add_transactions_to_block(block, [tx])
96  block.solve()
97  self.tip = block
98  self.block_heights[block.sha256] = height
99  self.block_time += 1
100  assert number not in self.blocks
101  self.blocks[number] = block
102  return block
103 
104  def get_tests(self):
105  self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
106  self.block_heights[self.genesis_hash] = 0
107  spendable_outputs = []
108 
109  # save the current tip so it can be spent by a later block
110  def save_spendable_output():
111  spendable_outputs.append(self.tip)
112 
113  # get an output that we previous marked as spendable
114  def get_spendable_output():
115  return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)
116 
117  # returns a test case that asserts that the current tip was accepted
118  def accepted():
119  return TestInstance([[self.tip, True]])
120 
121  # returns a test case that asserts that the current tip was rejected
122  def rejected(reject = None):
123  if reject is None:
124  return TestInstance([[self.tip, False]])
125  else:
126  return TestInstance([[self.tip, reject]])
127 
128  # move the tip back to a previous block
129  def tip(number):
130  self.tip = self.blocks[number]
131 
132  # add transactions to a block produced by next_block
133  def update_block(block_number, new_transactions):
134  block = self.blocks[block_number]
135  old_hash = block.sha256
136  self.add_transactions_to_block(block, new_transactions)
137  block.solve()
138  # Update the internal state just like in next_block
139  self.tip = block
140  self.block_heights[block.sha256] = self.block_heights[old_hash]
141  del self.block_heights[old_hash]
142  self.blocks[block_number] = block
143  return block
144 
145  # creates a new block and advances the tip to that block
146  block = self.next_block
147 
148 
149  # Create a new block
150  block(0)
151  save_spendable_output()
152  yield accepted()
153 
154 
155  # Now we need that block to mature so we can spend the coinbase.
156  test = TestInstance(sync_every_block=False)
157  for i in range(99):
158  block(1000 + i)
159  test.blocks_and_transactions.append([self.tip, True])
160  save_spendable_output()
161  yield test
162 
163 
164  # Start by building a couple of blocks on top (which output is spent is
165  # in parentheses):
166  # genesis -> b1 (0) -> b2 (1)
167  out0 = get_spendable_output()
168  block(1, spend=out0)
169  save_spendable_output()
170  yield accepted()
171 
172  out1 = get_spendable_output()
173  b2 = block(2, spend=out1)
174  yield accepted()
175 
176 
177  # so fork like this:
178  #
179  # genesis -> b1 (0) -> b2 (1)
180  # \-> b3 (1)
181  #
182  # Nothing should happen at this point. We saw b2 first so it takes priority.
183  tip(1)
184  b3 = block(3, spend=out1)
185  txout_b3 = PreviousSpendableOutput(b3.vtx[1], 1)
186  yield rejected()
187 
188 
189  # Now we add another block to make the alternative chain longer.
190  #
191  # genesis -> b1 (0) -> b2 (1)
192  # \-> b3 (1) -> b4 (2)
193  out2 = get_spendable_output()
194  block(4, spend=out2)
195  yield accepted()
196 
197 
198  # ... and back to the first chain.
199  # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
200  # \-> b3 (1) -> b4 (2)
201  tip(2)
202  block(5, spend=out2)
203  save_spendable_output()
204  yield rejected()
205 
206  out3 = get_spendable_output()
207  block(6, spend=out3)
208  yield accepted()
209 
210 
211  # Try to create a fork that double-spends
212  # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
213  # \-> b7 (2) -> b8 (4)
214  # \-> b3 (1) -> b4 (2)
215  tip(5)
216  block(7, spend=out2)
217  yield rejected()
218 
219  out4 = get_spendable_output()
220  block(8, spend=out4)
221  yield rejected()
222 
223 
224  # Try to create a block that has too much fee
225  # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
226  # \-> b9 (4)
227  # \-> b3 (1) -> b4 (2)
228  tip(6)
229  block(9, spend=out4, additional_coinbase_value=1)
230  yield rejected(RejectResult(16, b'bad-cb-amount'))
231 
232 
233  # Create a fork that ends in a block with too much fee (the one that causes the reorg)
234  # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
235  # \-> b10 (3) -> b11 (4)
236  # \-> b3 (1) -> b4 (2)
237  tip(5)
238  block(10, spend=out3)
239  yield rejected()
240 
241  block(11, spend=out4, additional_coinbase_value=1)
242  yield rejected(RejectResult(16, b'bad-cb-amount'))
243 
244 
245  # Try again, but with a valid fork first
246  # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
247  # \-> b12 (3) -> b13 (4) -> b14 (5)
248  # (b12 added last)
249  # \-> b3 (1) -> b4 (2)
250  tip(5)
251  b12 = block(12, spend=out3)
252  save_spendable_output()
253  #yield TestInstance([[b12, False]])
254  b13 = block(13, spend=out4)
255  # Deliver the block header for b12, and the block b13.
256  # b13 should be accepted but the tip won't advance until b12 is delivered.
257  yield TestInstance([[CBlockHeader(b12), None], [b13, False]])
258 
259  save_spendable_output()
260  out5 = get_spendable_output()
261  # b14 is invalid, but the node won't know that until it tries to connect
262  # Tip still can't advance because b12 is missing
263  block(14, spend=out5, additional_coinbase_value=1)
264  yield rejected()
265 
266  yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13.
267 
268  # Add a block with MAX_BLOCK_SIGOPS and one with one more sigop
269  # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
270  # \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6)
271  # \-> b3 (1) -> b4 (2)
272 
273  # Test that a block with a lot of checksigs is okay
274  lots_of_checksigs = CScript([OP_CHECKSIG] * (1000000 // 50 - 1))
275  tip(13)
276  block(15, spend=out5, script=lots_of_checksigs)
277  yield accepted()
278 
279 
280  # Test that a block with too many checksigs is rejected
281  out6 = get_spendable_output()
282  too_many_checksigs = CScript([OP_CHECKSIG] * (1000000 // 50))
283  block(16, spend=out6, script=too_many_checksigs)
284  yield rejected(RejectResult(16, b'bad-blk-sigops'))
285 
286 
287  # Attempt to spend a transaction created on a different fork
288  # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
289  # \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1])
290  # \-> b3 (1) -> b4 (2)
291  tip(15)
292  block(17, spend=txout_b3)
293  yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
294 
295  # Attempt to spend a transaction created on a different fork (on a fork this time)
296  # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
297  # \-> b12 (3) -> b13 (4) -> b15 (5)
298  # \-> b18 (b3.vtx[1]) -> b19 (6)
299  # \-> b3 (1) -> b4 (2)
300  tip(13)
301  block(18, spend=txout_b3)
302  yield rejected()
303 
304  block(19, spend=out6)
305  yield rejected()
306 
307  # Attempt to spend a coinbase at depth too low
308  # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
309  # \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7)
310  # \-> b3 (1) -> b4 (2)
311  tip(15)
312  out7 = get_spendable_output()
313  block(20, spend=out7)
314  yield rejected(RejectResult(16, b'bad-txns-premature-spend-of-coinbase'))
315 
316  # Attempt to spend a coinbase at depth too low (on a fork this time)
317  # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
318  # \-> b12 (3) -> b13 (4) -> b15 (5)
319  # \-> b21 (6) -> b22 (5)
320  # \-> b3 (1) -> b4 (2)
321  tip(13)
322  block(21, spend=out6)
323  yield rejected()
324 
325  block(22, spend=out5)
326  yield rejected()
327 
328  # Create a block on either side of MAX_BLOCK_SIZE and make sure its accepted/rejected
329  # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
330  # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6)
331  # \-> b24 (6) -> b25 (7)
332  # \-> b3 (1) -> b4 (2)
333  tip(15)
334  b23 = block(23, spend=out6)
335  old_hash = b23.sha256
336  tx = CTransaction()
337  script_length = MAX_BLOCK_SIZE - len(b23.serialize()) - 69
338  script_output = CScript([b'\x00' * script_length])
339  tx.vout.append(CTxOut(0, script_output))
340  tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 1)))
341  b23 = update_block(23, [tx])
342  # Make sure the math above worked out to produce a max-sized block
343  assert_equal(len(b23.serialize()), MAX_BLOCK_SIZE)
344  yield accepted()
345 
346  # Make the next block one byte bigger and check that it fails
347  tip(15)
348  b24 = block(24, spend=out6)
349  script_length = MAX_BLOCK_SIZE - len(b24.serialize()) - 69
350  script_output = CScript([b'\x00' * (script_length+1)])
351  tx.vout = [CTxOut(0, script_output)]
352  b24 = update_block(24, [tx])
353  assert_equal(len(b24.serialize()), MAX_BLOCK_SIZE+1)
354  yield rejected(RejectResult(16, b'bad-blk-length'))
355 
356  b25 = block(25, spend=out7)
357  yield rejected()
358 
359  # Create blocks with a coinbase input script size out of range
360  # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
361  # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7)
362  # \-> ... (6) -> ... (7)
363  # \-> b3 (1) -> b4 (2)
364  tip(15)
365  b26 = block(26, spend=out6)
366  b26.vtx[0].vin[0].scriptSig = b'\x00'
367  b26.vtx[0].rehash()
368  # update_block causes the merkle root to get updated, even with no new
369  # transactions, and updates the required state.
370  b26 = update_block(26, [])
371  yield rejected(RejectResult(16, b'bad-cb-length'))
372 
373  # Extend the b26 chain to make sure bitcoind isn't accepting b26
374  b27 = block(27, spend=out7)
375  yield rejected()
376 
377  # Now try a too-large-coinbase script
378  tip(15)
379  b28 = block(28, spend=out6)
380  b28.vtx[0].vin[0].scriptSig = b'\x00' * 101
381  b28.vtx[0].rehash()
382  b28 = update_block(28, [])
383  yield rejected(RejectResult(16, b'bad-cb-length'))
384 
385  # Extend the b28 chain to make sure bitcoind isn't accepted b28
386  b29 = block(29, spend=out7)
387  # TODO: Should get a reject message back with "bad-prevblk", except
388  # there's a bug that prevents this from being detected. Just note
389  # failure for now, and add the reject result later.
390  yield rejected()
391 
392  # b30 has a max-sized coinbase scriptSig.
393  tip(23)
394  b30 = block(30)
395  b30.vtx[0].vin[0].scriptSig = b'\x00' * 100
396  b30.vtx[0].rehash()
397  b30 = update_block(30, [])
398  yield accepted()
399 
400 
401 if __name__ == '__main__':
402  FullBlockTest().main()
def create_block(hashprev, coinbase, nTime=None)
Definition: blocktools.py:11
def add_transactions_to_block(self, block, tx_list)
def __init__(self, tx=CTransaction(), n=-1)
def sync_masternodes(rpc_connections)
Definition: util.py:142
def SignatureHash(script, txTo, inIdx, hashtype)
Definition: script.py:848
def create_coinbase(height, pubkey=None)
Definition: blocktools.py:43
def next_block(self, number, spend=None, additional_coinbase_value=0, script=None)
UniValue getbestblockhash(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:148
def assert_equal(thing1, thing2)
Definition: util.py:461