test: Add test for BIP30 duplicate tx

This commit is contained in:
MarcoFalke 2019-07-09 14:17:58 -04:00
parent 77770d95e2
commit fa8489a155
No known key found for this signature in database
GPG key ID: CE2B75697E69A548

View file

@ -74,6 +74,10 @@ class CBrokenBlock(CBlock):
def normal_serialize(self): def normal_serialize(self):
return super().serialize() return super().serialize()
DUPLICATE_COINBASE_SCRIPT_SIG = b'\x01\x78' # Valid for block at height 120
class FullBlockTest(BitcoinTestFramework): class FullBlockTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
self.num_nodes = 1 self.num_nodes = 1
@ -96,6 +100,13 @@ class FullBlockTest(BitcoinTestFramework):
self.spendable_outputs = [] self.spendable_outputs = []
# Create a new block # 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) b0 = self.next_block(0)
self.save_spendable_output() self.save_spendable_output()
self.send_blocks([b0]) self.send_blocks([b0])
@ -750,7 +761,7 @@ class FullBlockTest(BitcoinTestFramework):
# Test a few invalid tx types # 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) # \-> ??? (17)
# #
@ -776,14 +787,14 @@ class FullBlockTest(BitcoinTestFramework):
# reset to good chain # reset to good chain
self.move_tip(57) self.move_tip(57)
b60 = self.next_block(60, spend=out[17]) b60 = self.next_block(60)
self.send_blocks([b60], True) self.send_blocks([b60], True)
self.save_spendable_output() self.save_spendable_output()
# Test BIP30 # Test BIP30 (reject duplicate)
# #
# -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 ()
# \-> b61 (18) # \-> b61 ()
# #
# Blocks are not allowed to contain a transaction whose id matches that of an earlier, # 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; # not-fully-spent transaction in the same chain. To test, make identical coinbases;
@ -791,20 +802,44 @@ class FullBlockTest(BitcoinTestFramework):
# #
self.log.info("Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)") self.log.info("Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)")
self.move_tip(60) self.move_tip(60)
b61 = self.next_block(61, spend=out[18]) b61 = self.next_block(61)
b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig # Equalize the coinbases b61.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG
b61.vtx[0].rehash() b61.vtx[0].rehash()
b61 = self.update_block(61, []) 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) 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) # 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) # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()
# \-> b62 (18) # \-> b62 (18)
# #
self.log.info("Reject a block with a transaction with a nonfinal locktime") 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) b62 = self.next_block(62)
tx = CTransaction() tx = CTransaction()
tx.nLockTime = 0xffffffff # this locktime is non-final tx.nLockTime = 0xffffffff # this locktime is non-final
@ -817,11 +852,11 @@ class FullBlockTest(BitcoinTestFramework):
# Test a non-final coinbase is also rejected # Test a non-final coinbase is also rejected
# #
# -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()
# \-> b63 (-) # \-> b63 (-)
# #
self.log.info("Reject a block with a coinbase transaction with a nonfinal locktime") 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 = self.next_block(63)
b63.vtx[0].nLockTime = 0xffffffff b63.vtx[0].nLockTime = 0xffffffff
b63.vtx[0].vin[0].nSequence = 0xDEADBEEF b63.vtx[0].vin[0].nSequence = 0xDEADBEEF
@ -837,14 +872,14 @@ class FullBlockTest(BitcoinTestFramework):
# What matters is that the receiving node should not reject the bloated block, and then reject the canonical # 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.) # 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) # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 () -> b64 (18)
# \ # \
# b64a (18) # b64a (18)
# b64a is a bloated block (non-canonical varint) # b64a is a bloated block (non-canonical varint)
# b64 is a good block (same as b64 but w/ 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.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]) regular_block = self.next_block("64a", spend=out[18])
# make it a "broken_block," with non-canonical serialization # make it a "broken_block," with non-canonical serialization
@ -870,7 +905,7 @@ class FullBlockTest(BitcoinTestFramework):
node.disconnect_p2ps() node.disconnect_p2ps()
self.reconnect_p2p() self.reconnect_p2p()
self.move_tip(60) self.move_tip('dup_2')
b64 = CBlock(b64a) b64 = CBlock(b64a)
b64.vtx = copy.deepcopy(b64a.vtx) b64.vtx = copy.deepcopy(b64a.vtx)
assert_equal(b64.hash, b64a.hash) assert_equal(b64.hash, b64a.hash)
@ -882,7 +917,7 @@ class FullBlockTest(BitcoinTestFramework):
# Spend an output created in the block itself # 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.log.info("Accept a block with a transaction spending an output created in the same block")
self.move_tip(64) self.move_tip(64)
@ -895,8 +930,8 @@ class FullBlockTest(BitcoinTestFramework):
# Attempt to spend an output created later in the same block # 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) # -> b64 (18) -> b65 (19)
# \-> b66 (20) # \-> b66 (20)
self.log.info("Reject a block with a transaction spending an output created later in the same block") self.log.info("Reject a block with a transaction spending an output created later in the same block")
self.move_tip(65) self.move_tip(65)
b66 = self.next_block(66) b66 = self.next_block(66)
@ -907,8 +942,8 @@ class FullBlockTest(BitcoinTestFramework):
# Attempt to double-spend a transaction created in a block # Attempt to double-spend a transaction created in a block
# #
# -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) # -> b64 (18) -> b65 (19)
# \-> b67 (20) # \-> b67 (20)
# #
# #
self.log.info("Reject a block with a transaction double spending a transaction created in the same block") self.log.info("Reject a block with a transaction double spending a transaction created in the same block")
@ -922,8 +957,8 @@ class FullBlockTest(BitcoinTestFramework):
# More tests of block subsidy # More tests of block subsidy
# #
# -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) # -> b64 (18) -> b65 (19) -> b69 (20)
# \-> b68 (20) # \-> b68 (20)
# #
# b68 - coinbase with an extra 10 satoshis, # b68 - coinbase with an extra 10 satoshis,
# creates a tx that has 9 satoshis from out[20] go to fees # creates a tx that has 9 satoshis from out[20] go to fees
@ -949,8 +984,8 @@ class FullBlockTest(BitcoinTestFramework):
# Test spending the outpoint of a non-existent transaction # Test spending the outpoint of a non-existent transaction
# #
# -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) # -> b65 (19) -> b69 (20)
# \-> b70 (21) # \-> b70 (21)
# #
self.log.info("Reject a block containing a transaction spending from a non-existent input") self.log.info("Reject a block containing a transaction spending from a non-existent input")
self.move_tip(69) self.move_tip(69)
@ -965,8 +1000,8 @@ class FullBlockTest(BitcoinTestFramework):
# Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks) # 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) # -> b65 (19) -> b69 (20) -> b72 (21)
# \-> b71 (21) # \-> b71 (21)
# #
# b72 is a good block. # 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. # b71 is a copy of 72, but re-adds one of its transactions. However, it has the same hash as b72.
@ -994,8 +1029,8 @@ class FullBlockTest(BitcoinTestFramework):
# Test some invalid scripts and MAX_BLOCK_SIGOPS # Test some invalid scripts and MAX_BLOCK_SIGOPS
# #
# -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21) # -> b69 (20) -> b72 (21)
# \-> b** (22) # \-> b** (22)
# #
# b73 - tx with excessive sigops that are placed after an excessively large script element. # b73 - tx with excessive sigops that are placed after an excessively large script element.