mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
Merge #16363: test: Add test for BIP30 duplicate tx
fa8489a15511f61a372473927e73c34692bbec23 test: Add test for BIP30 duplicate tx (MarcoFalke) 77770d95e2838d7665fa8f621e9e83d79f9b3196 test: Properly serialize BIP34 coinbase height (MarcoFalke) Pull request description: This adds a test for BIP30 to check that duplicate txs can exist in the blockchain given the first one was completely spent when the second one is added. (Requested by ajtowns in https://github.com/bitcoin/bitcoin/pull/16333#issuecomment-508604071) We can not add a test that a later duplicate tx overwrites a previous one, because BIP30 is always enforced on regtest. If someone feels strongly about such a test, some Bitcoin Core code would have to be modified, which can be done in a follow up pull request. Also, add a commit to fix the BIP34 test failures reported in https://github.com/bitcoin/bitcoin/pull/14633#issue-227712540 ACKs for top commit: laanwj: Code review ACK fa8489a15511f61a372473927e73c34692bbec23 Tree-SHA512: c707d0bdc93937263876b603425b53322a2a9f9ec3f50716ae2fa9de8ddc644beb22b26c1bfde7f4aab102633e096b354ef380db919176bd2cb44a2828f884aa
This commit is contained in:
parent
ef78dbce53
commit
d0f1663305
@ -65,6 +65,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
|
||||||
@ -95,6 +99,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])
|
||||||
@ -717,7 +728,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)
|
||||||
#
|
#
|
||||||
|
|
||||||
@ -743,14 +754,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;
|
||||||
@ -758,20 +769,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
|
||||||
@ -784,11 +819,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
|
||||||
@ -804,14 +839,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
|
||||||
@ -837,7 +872,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)
|
||||||
@ -849,7 +884,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)
|
||||||
@ -862,8 +897,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)
|
||||||
@ -874,8 +909,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")
|
||||||
@ -889,8 +924,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
|
||||||
@ -916,8 +951,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)
|
||||||
@ -932,8 +967,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.
|
||||||
@ -961,8 +996,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.
|
||||||
|
@ -12,9 +12,8 @@ from .messages import (
|
|||||||
CTransaction,
|
CTransaction,
|
||||||
CTxIn,
|
CTxIn,
|
||||||
CTxOut,
|
CTxOut,
|
||||||
ser_string,
|
|
||||||
)
|
)
|
||||||
from .script import CScript, OP_TRUE, OP_CHECKSIG
|
from .script import CScript, CScriptNum, CScriptOp, OP_TRUE, OP_CHECKSIG
|
||||||
from .util import assert_equal, hex_str_to_bytes
|
from .util import assert_equal, hex_str_to_bytes
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
@ -33,20 +32,13 @@ def create_block(hashprev, coinbase, ntime=None):
|
|||||||
block.calc_sha256()
|
block.calc_sha256()
|
||||||
return block
|
return block
|
||||||
|
|
||||||
def serialize_script_num(value):
|
def script_BIP34_coinbase_height(height):
|
||||||
r = bytearray(0)
|
if height <= 16:
|
||||||
if value == 0:
|
res = CScriptOp.encode_op_n(height)
|
||||||
return r
|
# Append dummy to increase scriptSig size above 2 (see bad-cb-length consensus rule)
|
||||||
neg = value < 0
|
return CScript([res, OP_TRUE])
|
||||||
absvalue = -value if neg else value
|
return CScript([CScriptNum(height)])
|
||||||
while (absvalue):
|
|
||||||
r.append(int(absvalue & 0xff))
|
|
||||||
absvalue >>= 8
|
|
||||||
if r[-1] & 0x80:
|
|
||||||
r.append(0x80 if neg else 0)
|
|
||||||
elif neg:
|
|
||||||
r[-1] |= 0x80
|
|
||||||
return r
|
|
||||||
|
|
||||||
def create_coinbase(height, pubkey=None, dip4_activated=False):
|
def create_coinbase(height, pubkey=None, dip4_activated=False):
|
||||||
"""Create a coinbase transaction, assuming no miner fees.
|
"""Create a coinbase transaction, assuming no miner fees.
|
||||||
@ -54,8 +46,7 @@ def create_coinbase(height, pubkey=None, dip4_activated=False):
|
|||||||
If pubkey is passed in, the coinbase output will be a P2PK output;
|
If pubkey is passed in, the coinbase output will be a P2PK output;
|
||||||
otherwise an anyone-can-spend output."""
|
otherwise an anyone-can-spend output."""
|
||||||
coinbase = CTransaction()
|
coinbase = CTransaction()
|
||||||
coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff),
|
coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), script_BIP34_coinbase_height(height), 0xffffffff))
|
||||||
ser_string(serialize_script_num(height)), 0xffffffff))
|
|
||||||
coinbaseoutput = CTxOut()
|
coinbaseoutput = CTxOut()
|
||||||
coinbaseoutput.nValue = 500 * COIN
|
coinbaseoutput.nValue = 500 * COIN
|
||||||
halvings = int(height / 150) # regtest
|
halvings = int(height / 150) # regtest
|
||||||
|
Loading…
Reference in New Issue
Block a user