Skip to content

Commit

Permalink
Add more tests to p2p-fullblocktest
Browse files Browse the repository at this point in the history
Github-Pull: #7226
Rebased-From: 9b41a5f
  • Loading branch information
sdaftuar authored and laanwj committed Dec 22, 2015
1 parent 12c469b commit 301f16a
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 12 deletions.
157 changes: 145 additions & 12 deletions qa/rpc-tests/p2p-fullblocktest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@

from test_framework.test_framework import ComparisonTestFramework
from test_framework.util import *
from test_framework.comptool import TestManager, TestInstance
from test_framework.comptool import TestManager, TestInstance, RejectResult
from test_framework.mininode import *
from test_framework.blocktools import *
import logging
import copy
import time
import numbers
from test_framework.key import CECKey
from test_framework.script import CScript, CScriptOp, SignatureHash, SIGHASH_ALL, OP_TRUE
from test_framework.script import CScript, CScriptOp, SignatureHash, SIGHASH_ALL, OP_TRUE, OP_FALSE

class PreviousSpendableOutput(object):
def __init__(self, tx = CTransaction(), n = -1):
Expand Down Expand Up @@ -122,13 +122,29 @@ def accepted():
return TestInstance([[self.tip, True]])

# returns a test case that asserts that the current tip was rejected
def rejected():
return TestInstance([[self.tip, False]])
def rejected(reject = None):
if reject is None:
return TestInstance([[self.tip, False]])
else:
return TestInstance([[self.tip, reject]])

# move the tip back to a previous block
def tip(number):
self.tip = self.blocks[number]

# add transactions to a block produced by next_block
def update_block(block_number, new_transactions):
block = self.blocks[block_number]
old_hash = block.sha256
self.add_transactions_to_block(block, new_transactions)
block.solve()
# Update the internal state just like in next_block
self.tip = block
self.block_heights[block.sha256] = self.block_heights[old_hash]
del self.block_heights[old_hash]
self.blocks[block_number] = block
return block

# creates a new block and advances the tip to that block
block = self.next_block

Expand All @@ -141,23 +157,23 @@ def tip(number):

# Now we need that block to mature so we can spend the coinbase.
test = TestInstance(sync_every_block=False)
for i in range(100):
for i in range(99):
block(1000 + i)
test.blocks_and_transactions.append([self.tip, True])
save_spendable_output()
yield test


# Start by bulding a couple of blocks on top (which output is spent is in parentheses):
# Start by building a couple of blocks on top (which output is spent is
# in parentheses):
# genesis -> b1 (0) -> b2 (1)
out0 = get_spendable_output()
block(1, spend=out0)
save_spendable_output()
yield accepted()

out1 = get_spendable_output()
block(2, spend=out1)
# Inv again, then deliver twice (shouldn't break anything).
b2 = block(2, spend=out1)
yield accepted()


Expand All @@ -168,8 +184,8 @@ def tip(number):
#
# Nothing should happen at this point. We saw b2 first so it takes priority.
tip(1)
block(3, spend=out1)
# Deliver twice (should still not break anything)
b3 = block(3, spend=out1)
txout_b3 = PreviousSpendableOutput(b3.vtx[1], 1)
yield rejected()


Expand Down Expand Up @@ -214,7 +230,7 @@ def tip(number):
# \-> b3 (1) -> b4 (2)
tip(6)
block(9, spend=out4, additional_coinbase_value=1)
yield rejected()
yield rejected(RejectResult(16, 'bad-cb-amount'))


# Create a fork that ends in a block with too much fee (the one that causes the reorg)
Expand All @@ -226,7 +242,7 @@ def tip(number):
yield rejected()

block(11, spend=out4, additional_coinbase_value=1)
yield rejected()
yield rejected(RejectResult(16, 'bad-cb-amount'))


# Try again, but with a valid fork first
Expand All @@ -252,6 +268,10 @@ def tip(number):

yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13.

# Add a block with MAX_BLOCK_SIGOPS and one with one more sigop
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6)
# \-> b3 (1) -> b4 (2)

# Test that a block with a lot of checksigs is okay
lots_of_checksigs = CScript([OP_CHECKSIG] * (1000000 / 50 - 1))
Expand All @@ -264,8 +284,121 @@ def tip(number):
out6 = get_spendable_output()
too_many_checksigs = CScript([OP_CHECKSIG] * (1000000 / 50))
block(16, spend=out6, script=too_many_checksigs)
yield rejected(RejectResult(16, 'bad-blk-sigops'))


# Attempt to spend a transaction created on a different fork
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1])
# \-> b3 (1) -> b4 (2)
tip(15)
block(17, spend=txout_b3)
yield rejected(RejectResult(16, 'bad-txns-inputs-missingorspent'))

# Attempt to spend a transaction created on a different fork (on a fork this time)
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5)
# \-> b18 (b3.vtx[1]) -> b19 (6)
# \-> b3 (1) -> b4 (2)
tip(13)
block(18, spend=txout_b3)
yield rejected()

block(19, spend=out6)
yield rejected()

# Attempt to spend a coinbase at depth too low
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7)
# \-> b3 (1) -> b4 (2)
tip(15)
out7 = get_spendable_output()
block(20, spend=out7)
yield rejected(RejectResult(16, 'bad-txns-premature-spend-of-coinbase'))

# Attempt to spend a coinbase at depth too low (on a fork this time)
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5)
# \-> b21 (6) -> b22 (5)
# \-> b3 (1) -> b4 (2)
tip(13)
block(21, spend=out6)
yield rejected()

block(22, spend=out5)
yield rejected()

# Create a block on either side of MAX_BLOCK_SIZE and make sure its accepted/rejected
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6)
# \-> b24 (6) -> b25 (7)
# \-> b3 (1) -> b4 (2)
tip(15)
b23 = block(23, spend=out6)
old_hash = b23.sha256
tx = CTransaction()
script_length = MAX_BLOCK_SIZE - len(b23.serialize()) - 69
script_output = CScript([chr(0)*script_length])
tx.vout.append(CTxOut(0, script_output))
tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 1)))
b23 = update_block(23, [tx])
# Make sure the math above worked out to produce a max-sized block
assert_equal(len(b23.serialize()), MAX_BLOCK_SIZE)
yield accepted()

# Make the next block one byte bigger and check that it fails
tip(15)
b24 = block(24, spend=out6)
script_length = MAX_BLOCK_SIZE - len(b24.serialize()) - 69
script_output = CScript([chr(0)*(script_length+1)])
tx.vout = [CTxOut(0, script_output)]
b24 = update_block(24, [tx])
assert_equal(len(b24.serialize()), MAX_BLOCK_SIZE+1)
yield rejected(RejectResult(16, 'bad-blk-length'))

b25 = block(25, spend=out7)
yield rejected()

# Create blocks with a coinbase input script size out of range
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7)
# \-> ... (6) -> ... (7)
# \-> b3 (1) -> b4 (2)
tip(15)
b26 = block(26, spend=out6)
b26.vtx[0].vin[0].scriptSig = chr(0)
b26.vtx[0].rehash()
# update_block causes the merkle root to get updated, even with no new
# transactions, and updates the required state.
b26 = update_block(26, [])
yield rejected(RejectResult(16, 'bad-cb-length'))

# Extend the b26 chain to make sure bitcoind isn't accepting b26
b27 = block(27, spend=out7)
yield rejected()

# Now try a too-large-coinbase script
tip(15)
b28 = block(28, spend=out6)
b28.vtx[0].vin[0].scriptSig = chr(0)*101
b28.vtx[0].rehash()
b28 = update_block(28, [])
yield rejected(RejectResult(16, 'bad-cb-length'))

# Extend the b28 chain to make sure bitcoind isn't accepted b28
b29 = block(29, spend=out7)
# TODO: Should get a reject message back with "bad-prevblk", except
# there's a bug that prevents this from being detected. Just note
# failure for now, and add the reject result later.
yield rejected()

# b30 has a max-sized coinbase scriptSig.
tip(23)
b30 = block(30)
b30.vtx[0].vin[0].scriptSig = chr(0)*100
b30.vtx[0].rehash()
b30 = update_block(30, [])
yield accepted()


if __name__ == '__main__':
Expand Down
1 change: 1 addition & 0 deletions qa/rpc-tests/test_framework/mininode.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
MY_SUBVERSION = "/python-mininode-tester:0.0.1/"

MAX_INV_SZ = 50000
MAX_BLOCK_SIZE = 1000000

# Keep our own socket map for asyncore, so that we can track disconnects
# ourselves (to workaround an issue with closing an asyncore socket when
Expand Down

0 comments on commit 301f16a

Please sign in to comment.