aboutsummaryrefslogtreecommitdiff
path: root/test/functional/feature_block.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/feature_block.py')
-rwxr-xr-xtest/functional/feature_block.py115
1 files changed, 79 insertions, 36 deletions
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index ce353b2272..c74270febc 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -74,11 +74,15 @@ class CBrokenBlock(CBlock):
def normal_serialize(self):
return super().serialize()
+
+DUPLICATE_COINBASE_SCRIPT_SIG = b'\x01\x78' # Valid for block at height 120
+
+
class FullBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [[]]
+ self.extra_args = [['-acceptnonstdtxn=1']] # This is a consensus block test, we don't care about tx policy
def run_test(self):
node = self.nodes[0] # convenience reference to the node
@@ -96,6 +100,13 @@ class FullBlockTest(BitcoinTestFramework):
self.spendable_outputs = []
# Create a new block
+ b_dup_cb = self.next_block('dup_cb')
+ b_dup_cb.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG
+ b_dup_cb.vtx[0].rehash()
+ duplicate_tx = b_dup_cb.vtx[0]
+ b_dup_cb = self.update_block('dup_cb', [])
+ self.send_blocks([b_dup_cb])
+
b0 = self.next_block(0)
self.save_spendable_output()
self.send_blocks([b0])
@@ -486,6 +497,14 @@ class FullBlockTest(BitcoinTestFramework):
tx_last = tx_new
b39_outputs += 1
+ # The accounting in the loop above can be off, because it misses the
+ # compact size encoding of the number of transactions in the block.
+ # Make sure we didn't accidentally make too big a block. Note that the
+ # size of the block has non-determinism due to the ECDSA signature in
+ # the first transaction.
+ while (len(b39.serialize()) >= MAX_BLOCK_BASE_SIZE):
+ del b39.vtx[-1]
+
b39 = self.update_block(39, [])
self.send_blocks([b39], True)
self.save_spendable_output()
@@ -750,7 +769,7 @@ class FullBlockTest(BitcoinTestFramework):
# Test a few invalid tx types
#
- # -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
+ # -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 ()
# \-> ??? (17)
#
@@ -776,14 +795,14 @@ class FullBlockTest(BitcoinTestFramework):
# reset to good chain
self.move_tip(57)
- b60 = self.next_block(60, spend=out[17])
+ b60 = self.next_block(60)
self.send_blocks([b60], True)
self.save_spendable_output()
- # Test BIP30
+ # Test BIP30 (reject duplicate)
#
- # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
- # \-> b61 (18)
+ # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 ()
+ # \-> b61 ()
#
# Blocks are not allowed to contain a transaction whose id matches that of an earlier,
# not-fully-spent transaction in the same chain. To test, make identical coinbases;
@@ -791,20 +810,44 @@ class FullBlockTest(BitcoinTestFramework):
#
self.log.info("Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)")
self.move_tip(60)
- b61 = self.next_block(61, spend=out[18])
- b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig # Equalize the coinbases
+ b61 = self.next_block(61)
+ b61.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG
b61.vtx[0].rehash()
b61 = self.update_block(61, [])
- assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize())
+ assert_equal(duplicate_tx.serialize(), b61.vtx[0].serialize())
self.send_blocks([b61], success=False, reject_reason='bad-txns-BIP30', reconnect=True)
+ # Test BIP30 (allow duplicate if spent)
+ #
+ # -> b57 (16) -> b60 ()
+ # \-> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()
+ #
+ self.move_tip(57)
+ b_spend_dup_cb = self.next_block('spend_dup_cb')
+ tx = CTransaction()
+ tx.vin.append(CTxIn(COutPoint(duplicate_tx.sha256, 0)))
+ tx.vout.append(CTxOut(0, CScript([OP_TRUE])))
+ self.sign_tx(tx, duplicate_tx)
+ tx.rehash()
+ b_spend_dup_cb = self.update_block('spend_dup_cb', [tx])
+
+ b_dup_2 = self.next_block('dup_2')
+ b_dup_2.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG
+ b_dup_2.vtx[0].rehash()
+ b_dup_2 = self.update_block('dup_2', [])
+ assert_equal(duplicate_tx.serialize(), b_dup_2.vtx[0].serialize())
+ assert_equal(self.nodes[0].gettxout(txid=duplicate_tx.hash, n=0)['confirmations'], 119)
+ self.send_blocks([b_spend_dup_cb, b_dup_2], success=True)
+ # The duplicate has less confirmations
+ assert_equal(self.nodes[0].gettxout(txid=duplicate_tx.hash, n=0)['confirmations'], 1)
+
# Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests)
#
- # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
- # \-> b62 (18)
+ # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()
+ # \-> b62 (18)
#
self.log.info("Reject a block with a transaction with a nonfinal locktime")
- self.move_tip(60)
+ self.move_tip('dup_2')
b62 = self.next_block(62)
tx = CTransaction()
tx.nLockTime = 0xffffffff # this locktime is non-final
@@ -817,11 +860,11 @@ class FullBlockTest(BitcoinTestFramework):
# Test a non-final coinbase is also rejected
#
- # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
- # \-> b63 (-)
+ # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()
+ # \-> b63 (-)
#
self.log.info("Reject a block with a coinbase transaction with a nonfinal locktime")
- self.move_tip(60)
+ self.move_tip('dup_2')
b63 = self.next_block(63)
b63.vtx[0].nLockTime = 0xffffffff
b63.vtx[0].vin[0].nSequence = 0xDEADBEEF
@@ -837,14 +880,14 @@ class FullBlockTest(BitcoinTestFramework):
# What matters is that the receiving node should not reject the bloated block, and then reject the canonical
# block on the basis that it's the same as an already-rejected block (which would be a consensus failure.)
#
- # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18)
- # \
- # b64a (18)
+ # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 () -> b64 (18)
+ # \
+ # b64a (18)
# b64a is a bloated block (non-canonical varint)
# b64 is a good block (same as b64 but w/ canonical varint)
#
self.log.info("Accept a valid block even if a bloated version of the block has previously been sent")
- self.move_tip(60)
+ self.move_tip('dup_2')
regular_block = self.next_block("64a", spend=out[18])
# make it a "broken_block," with non-canonical serialization
@@ -870,7 +913,7 @@ class FullBlockTest(BitcoinTestFramework):
node.disconnect_p2ps()
self.reconnect_p2p()
- self.move_tip(60)
+ self.move_tip('dup_2')
b64 = CBlock(b64a)
b64.vtx = copy.deepcopy(b64a.vtx)
assert_equal(b64.hash, b64a.hash)
@@ -882,7 +925,7 @@ class FullBlockTest(BitcoinTestFramework):
# Spend an output created in the block itself
#
- # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
+ # -> b_dup_2 () -> b64 (18) -> b65 (19)
#
self.log.info("Accept a block with a transaction spending an output created in the same block")
self.move_tip(64)
@@ -895,8 +938,8 @@ class FullBlockTest(BitcoinTestFramework):
# Attempt to spend an output created later in the same block
#
- # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
- # \-> b66 (20)
+ # -> b64 (18) -> b65 (19)
+ # \-> b66 (20)
self.log.info("Reject a block with a transaction spending an output created later in the same block")
self.move_tip(65)
b66 = self.next_block(66)
@@ -907,8 +950,8 @@ class FullBlockTest(BitcoinTestFramework):
# Attempt to double-spend a transaction created in a block
#
- # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
- # \-> b67 (20)
+ # -> b64 (18) -> b65 (19)
+ # \-> b67 (20)
#
#
self.log.info("Reject a block with a transaction double spending a transaction created in the same block")
@@ -922,8 +965,8 @@ class FullBlockTest(BitcoinTestFramework):
# More tests of block subsidy
#
- # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
- # \-> b68 (20)
+ # -> b64 (18) -> b65 (19) -> b69 (20)
+ # \-> b68 (20)
#
# b68 - coinbase with an extra 10 satoshis,
# creates a tx that has 9 satoshis from out[20] go to fees
@@ -949,8 +992,8 @@ class FullBlockTest(BitcoinTestFramework):
# Test spending the outpoint of a non-existent transaction
#
- # -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
- # \-> b70 (21)
+ # -> b65 (19) -> b69 (20)
+ # \-> b70 (21)
#
self.log.info("Reject a block containing a transaction spending from a non-existent input")
self.move_tip(69)
@@ -965,8 +1008,8 @@ class FullBlockTest(BitcoinTestFramework):
# Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks)
#
- # -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21)
- # \-> b71 (21)
+ # -> b65 (19) -> b69 (20) -> b72 (21)
+ # \-> b71 (21)
#
# b72 is a good block.
# b71 is a copy of 72, but re-adds one of its transactions. However, it has the same hash as b72.
@@ -994,8 +1037,8 @@ class FullBlockTest(BitcoinTestFramework):
# Test some invalid scripts and MAX_BLOCK_SIGOPS
#
- # -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21)
- # \-> b** (22)
+ # -> b69 (20) -> b72 (21)
+ # \-> b** (22)
#
# b73 - tx with excessive sigops that are placed after an excessively large script element.
@@ -1218,7 +1261,7 @@ class FullBlockTest(BitcoinTestFramework):
self.save_spendable_output()
spend = self.get_spendable_output()
- self.send_blocks(blocks, True, timeout=480)
+ self.send_blocks(blocks, True, timeout=960)
chain1_tip = i
# now create alt chain of same length
@@ -1230,14 +1273,14 @@ class FullBlockTest(BitcoinTestFramework):
# extend alt chain to trigger re-org
block = self.next_block("alt" + str(chain1_tip + 1), version=4)
- self.send_blocks([block], True, timeout=480)
+ self.send_blocks([block], True, timeout=960)
# ... and re-org back to the first chain
self.move_tip(chain1_tip)
block = self.next_block(chain1_tip + 1, version=4)
self.send_blocks([block], False, force_send=True)
block = self.next_block(chain1_tip + 2, version=4)
- self.send_blocks([block], True, timeout=480)
+ self.send_blocks([block], True, timeout=960)
self.log.info("Reject a block with an invalid block header version")
b_v1 = self.next_block('b_v1', version=1)