|
|
@ -7,7 +7,7 @@ |
|
|
|
|
|
|
|
|
|
|
|
from test_framework.test_framework import ComparisonTestFramework |
|
|
|
from test_framework.test_framework import ComparisonTestFramework |
|
|
|
from test_framework.util import * |
|
|
|
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.mininode import * |
|
|
|
from test_framework.blocktools import * |
|
|
|
from test_framework.blocktools import * |
|
|
|
import logging |
|
|
|
import logging |
|
|
@ -15,7 +15,7 @@ import copy |
|
|
|
import time |
|
|
|
import time |
|
|
|
import numbers |
|
|
|
import numbers |
|
|
|
from test_framework.key import CECKey |
|
|
|
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): |
|
|
|
class PreviousSpendableOutput(object): |
|
|
|
def __init__(self, tx = CTransaction(), n = -1): |
|
|
|
def __init__(self, tx = CTransaction(), n = -1): |
|
|
@ -122,13 +122,29 @@ class FullBlockTest(ComparisonTestFramework): |
|
|
|
return TestInstance([[self.tip, True]]) |
|
|
|
return TestInstance([[self.tip, True]]) |
|
|
|
|
|
|
|
|
|
|
|
# returns a test case that asserts that the current tip was rejected |
|
|
|
# returns a test case that asserts that the current tip was rejected |
|
|
|
def rejected(): |
|
|
|
def rejected(reject = None): |
|
|
|
return TestInstance([[self.tip, False]]) |
|
|
|
if reject is None: |
|
|
|
|
|
|
|
return TestInstance([[self.tip, False]]) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
return TestInstance([[self.tip, reject]]) |
|
|
|
|
|
|
|
|
|
|
|
# move the tip back to a previous block |
|
|
|
# move the tip back to a previous block |
|
|
|
def tip(number): |
|
|
|
def tip(number): |
|
|
|
self.tip = self.blocks[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 |
|
|
|
# creates a new block and advances the tip to that block |
|
|
|
block = self.next_block |
|
|
|
block = self.next_block |
|
|
|
|
|
|
|
|
|
|
@ -141,14 +157,15 @@ class FullBlockTest(ComparisonTestFramework): |
|
|
|
|
|
|
|
|
|
|
|
# Now we need that block to mature so we can spend the coinbase. |
|
|
|
# Now we need that block to mature so we can spend the coinbase. |
|
|
|
test = TestInstance(sync_every_block=False) |
|
|
|
test = TestInstance(sync_every_block=False) |
|
|
|
for i in range(100): |
|
|
|
for i in range(99): |
|
|
|
block(1000 + i) |
|
|
|
block(1000 + i) |
|
|
|
test.blocks_and_transactions.append([self.tip, True]) |
|
|
|
test.blocks_and_transactions.append([self.tip, True]) |
|
|
|
save_spendable_output() |
|
|
|
save_spendable_output() |
|
|
|
yield test |
|
|
|
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) |
|
|
|
# genesis -> b1 (0) -> b2 (1) |
|
|
|
out0 = get_spendable_output() |
|
|
|
out0 = get_spendable_output() |
|
|
|
block(1, spend=out0) |
|
|
|
block(1, spend=out0) |
|
|
@ -156,8 +173,7 @@ class FullBlockTest(ComparisonTestFramework): |
|
|
|
yield accepted() |
|
|
|
yield accepted() |
|
|
|
|
|
|
|
|
|
|
|
out1 = get_spendable_output() |
|
|
|
out1 = get_spendable_output() |
|
|
|
block(2, spend=out1) |
|
|
|
b2 = block(2, spend=out1) |
|
|
|
# Inv again, then deliver twice (shouldn't break anything). |
|
|
|
|
|
|
|
yield accepted() |
|
|
|
yield accepted() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -168,8 +184,8 @@ class FullBlockTest(ComparisonTestFramework): |
|
|
|
# |
|
|
|
# |
|
|
|
# Nothing should happen at this point. We saw b2 first so it takes priority. |
|
|
|
# Nothing should happen at this point. We saw b2 first so it takes priority. |
|
|
|
tip(1) |
|
|
|
tip(1) |
|
|
|
block(3, spend=out1) |
|
|
|
b3 = block(3, spend=out1) |
|
|
|
# Deliver twice (should still not break anything) |
|
|
|
txout_b3 = PreviousSpendableOutput(b3.vtx[1], 1) |
|
|
|
yield rejected() |
|
|
|
yield rejected() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -214,7 +230,7 @@ class FullBlockTest(ComparisonTestFramework): |
|
|
|
# \-> b3 (1) -> b4 (2) |
|
|
|
# \-> b3 (1) -> b4 (2) |
|
|
|
tip(6) |
|
|
|
tip(6) |
|
|
|
block(9, spend=out4, additional_coinbase_value=1) |
|
|
|
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) |
|
|
|
# Create a fork that ends in a block with too much fee (the one that causes the reorg) |
|
|
@ -226,7 +242,7 @@ class FullBlockTest(ComparisonTestFramework): |
|
|
|
yield rejected() |
|
|
|
yield rejected() |
|
|
|
|
|
|
|
|
|
|
|
block(11, spend=out4, additional_coinbase_value=1) |
|
|
|
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 |
|
|
|
# Try again, but with a valid fork first |
|
|
@ -252,6 +268,10 @@ class FullBlockTest(ComparisonTestFramework): |
|
|
|
|
|
|
|
|
|
|
|
yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13. |
|
|
|
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 |
|
|
|
# Test that a block with a lot of checksigs is okay |
|
|
|
lots_of_checksigs = CScript([OP_CHECKSIG] * (1000000 / 50 - 1)) |
|
|
|
lots_of_checksigs = CScript([OP_CHECKSIG] * (1000000 / 50 - 1)) |
|
|
@ -264,8 +284,121 @@ class FullBlockTest(ComparisonTestFramework): |
|
|
|
out6 = get_spendable_output() |
|
|
|
out6 = get_spendable_output() |
|
|
|
too_many_checksigs = CScript([OP_CHECKSIG] * (1000000 / 50)) |
|
|
|
too_many_checksigs = CScript([OP_CHECKSIG] * (1000000 / 50)) |
|
|
|
block(16, spend=out6, script=too_many_checksigs) |
|
|
|
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() |
|
|
|
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__': |
|
|
|
if __name__ == '__main__': |
|
|
|