Changeset View
Changeset View
Standalone View
Standalone View
test/functional/feature_block.py
Show First 20 Lines • Show All 177 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
# Try to create a block that has too much fee | # Try to create a block that has too much fee | ||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | ||||
# \-> b9 (4) | # \-> b9 (4) | ||||
# \-> b3 (1) -> b4 (2) | # \-> b3 (1) -> b4 (2) | ||||
self.log.info( | self.log.info( | ||||
"Reject a block where the miner creates too much coinbase reward") | "Reject a block where the miner creates too much coinbase reward") | ||||
self.move_tip(6) | self.move_tip(6) | ||||
b9 = self.next_block(9, spend=out[4], additional_coinbase_value=1) | b9 = self.next_block(9, spend=out[4], additional_coinbase_value=1) | ||||
self.sync_blocks([b9], success=False, reject_code=16, | self.sync_blocks([b9], success=False, | ||||
reject_reason=b'bad-cb-amount', reconnect=True) | reject_reason='bad-cb-amount', reconnect=True) | ||||
# 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) | ||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | ||||
# \-> b10 (3) -> b11 (4) | # \-> b10 (3) -> b11 (4) | ||||
# \-> b3 (1) -> b4 (2) | # \-> b3 (1) -> b4 (2) | ||||
self.log.info( | self.log.info( | ||||
"Reject a chain where the miner creates too much coinbase reward, even if the chain is longer") | "Reject a chain where the miner creates too much coinbase reward, even if the chain is longer") | ||||
self.move_tip(5) | self.move_tip(5) | ||||
b10 = self.next_block(10, spend=out[3]) | b10 = self.next_block(10, spend=out[3]) | ||||
self.sync_blocks([b10], False) | self.sync_blocks([b10], False) | ||||
b11 = self.next_block(11, spend=out[4], additional_coinbase_value=1) | b11 = self.next_block(11, spend=out[4], additional_coinbase_value=1) | ||||
self.sync_blocks([b11], success=False, reject_code=16, | self.sync_blocks([b11], success=False, | ||||
reject_reason=b'bad-cb-amount', reconnect=True) | reject_reason='bad-cb-amount', reconnect=True) | ||||
# Try again, but with a valid fork first | # Try again, but with a valid fork first | ||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | ||||
# \-> b12 (3) -> b13 (4) -> b14 (5) | # \-> b12 (3) -> b13 (4) -> b14 (5) | ||||
# \-> b3 (1) -> b4 (2) | # \-> b3 (1) -> b4 (2) | ||||
self.log.info( | self.log.info( | ||||
"Reject a chain where the miner creates too much coinbase reward, even if the chain is longer (on a forked chain)") | "Reject a chain where the miner creates too much coinbase reward, even if the chain is longer (on a forked chain)") | ||||
self.move_tip(5) | self.move_tip(5) | ||||
b12 = self.next_block(12, spend=out[3]) | b12 = self.next_block(12, spend=out[3]) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
b13 = self.next_block(13, spend=out[4]) | b13 = self.next_block(13, spend=out[4]) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
b14 = self.next_block(14, spend=out[5], additional_coinbase_value=1) | b14 = self.next_block(14, spend=out[5], additional_coinbase_value=1) | ||||
self.sync_blocks([b12, b13, b14], success=False, reject_code=16, | self.sync_blocks([b12, b13, b14], success=False, | ||||
reject_reason=b'bad-cb-amount', reconnect=True) | reject_reason='bad-cb-amount', reconnect=True) | ||||
# New tip should be b13. | # New tip should be b13. | ||||
assert_equal(node.getbestblockhash(), b13.hash) | assert_equal(node.getbestblockhash(), b13.hash) | ||||
# Add a block with MAX_BLOCK_SIGOPS_PER_MB and one with one more sigop | # Add a block with MAX_BLOCK_SIGOPS_PER_MB and one with one more sigop | ||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | ||||
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6) | # \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6) | ||||
# \-> b3 (1) -> b4 (2) | # \-> b3 (1) -> b4 (2) | ||||
self.log.info("Accept a block with lots of checksigs") | self.log.info("Accept a block with lots of checksigs") | ||||
lots_of_checksigs = CScript( | lots_of_checksigs = CScript( | ||||
[OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB - 1)) | [OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB - 1)) | ||||
self.move_tip(13) | self.move_tip(13) | ||||
b15 = self.next_block(15, spend=out[5], script=lots_of_checksigs) | b15 = self.next_block(15, spend=out[5], script=lots_of_checksigs) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
self.sync_blocks([b15], True) | self.sync_blocks([b15], True) | ||||
self.log.info("Reject a block with too many checksigs") | self.log.info("Reject a block with too many checksigs") | ||||
too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB)) | too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB)) | ||||
b16 = self.next_block(16, spend=out[6], script=too_many_checksigs) | b16 = self.next_block(16, spend=out[6], script=too_many_checksigs) | ||||
self.sync_blocks([b16], success=False, reject_code=16, | self.sync_blocks([b16], success=False, | ||||
reject_reason=b'bad-blk-sigops', reconnect=True) | reject_reason='bad-blk-sigops', reconnect=True) | ||||
# Attempt to spend a transaction created on a different fork | # Attempt to spend a transaction created on a different fork | ||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | ||||
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1]) | # \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1]) | ||||
# \-> b3 (1) -> b4 (2) | # \-> b3 (1) -> b4 (2) | ||||
self.log.info("Reject a block with a spend from a re-org'ed out tx") | self.log.info("Reject a block with a spend from a re-org'ed out tx") | ||||
self.move_tip(15) | self.move_tip(15) | ||||
b17 = self.next_block(17, spend=txout_b3) | b17 = self.next_block(17, spend=txout_b3) | ||||
self.sync_blocks([b17], success=False, reject_code=16, | self.sync_blocks([b17], success=False, | ||||
reject_reason=b'bad-txns-inputs-missingorspent', reconnect=True) | reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | ||||
# Attempt to spend a transaction created on a different fork (on a fork this time) | # Attempt to spend a transaction created on a different fork (on a fork this time) | ||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | ||||
# \-> b12 (3) -> b13 (4) -> b15 (5) | # \-> b12 (3) -> b13 (4) -> b15 (5) | ||||
# \-> b18 (b3.vtx[1]) -> b19 (6) | # \-> b18 (b3.vtx[1]) -> b19 (6) | ||||
# \-> b3 (1) -> b4 (2) | # \-> b3 (1) -> b4 (2) | ||||
self.log.info( | self.log.info( | ||||
"Reject a block with a spend from a re-org'ed out tx (on a forked chain)") | "Reject a block with a spend from a re-org'ed out tx (on a forked chain)") | ||||
self.move_tip(13) | self.move_tip(13) | ||||
b18 = self.next_block(18, spend=txout_b3) | b18 = self.next_block(18, spend=txout_b3) | ||||
self.sync_blocks([b18], False) | self.sync_blocks([b18], False) | ||||
b19 = self.next_block(19, spend=out[6]) | b19 = self.next_block(19, spend=out[6]) | ||||
self.sync_blocks([b19], success=False, reject_code=16, | self.sync_blocks([b19], success=False, | ||||
reject_reason=b'bad-txns-inputs-missingorspent', reconnect=True) | reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | ||||
# Attempt to spend a coinbase at depth too low | # Attempt to spend a coinbase at depth too low | ||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | ||||
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7) | # \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7) | ||||
# \-> b3 (1) -> b4 (2) | # \-> b3 (1) -> b4 (2) | ||||
self.log.info("Reject a block spending an immature coinbase.") | self.log.info("Reject a block spending an immature coinbase.") | ||||
self.move_tip(15) | self.move_tip(15) | ||||
b20 = self.next_block(20, spend=out[7]) | b20 = self.next_block(20, spend=out[7]) | ||||
self.sync_blocks([b20], success=False, reject_code=16, | self.sync_blocks([b20], success=False, | ||||
reject_reason=b'bad-txns-premature-spend-of-coinbase') | reject_reason='bad-txns-premature-spend-of-coinbase') | ||||
# Attempt to spend a coinbase at depth too low (on a fork this time) | # Attempt to spend a coinbase at depth too low (on a fork this time) | ||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | ||||
# \-> b12 (3) -> b13 (4) -> b15 (5) | # \-> b12 (3) -> b13 (4) -> b15 (5) | ||||
# \-> b21 (6) -> b22 (5) | # \-> b21 (6) -> b22 (5) | ||||
# \-> b3 (1) -> b4 (2) | # \-> b3 (1) -> b4 (2) | ||||
self.log.info( | self.log.info( | ||||
"Reject a block spending an immature coinbase (on a forked chain)") | "Reject a block spending an immature coinbase (on a forked chain)") | ||||
self.move_tip(13) | self.move_tip(13) | ||||
b21 = self.next_block(21, spend=out[6]) | b21 = self.next_block(21, spend=out[6]) | ||||
self.sync_blocks([b21], False) | self.sync_blocks([b21], False) | ||||
b22 = self.next_block(22, spend=out[5]) | b22 = self.next_block(22, spend=out[5]) | ||||
self.sync_blocks([b22], success=False, reject_code=16, | self.sync_blocks([b22], success=False, | ||||
reject_reason=b'bad-txns-premature-spend-of-coinbase') | reject_reason='bad-txns-premature-spend-of-coinbase') | ||||
# Create a block on either side of LEGACY_MAX_BLOCK_SIZE and make sure its accepted/rejected | # Create a block on either side of LEGACY_MAX_BLOCK_SIZE and make sure its accepted/rejected | ||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | ||||
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) | # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) | ||||
# \-> b24 (6) -> b25 (7) | # \-> b24 (6) -> b25 (7) | ||||
# \-> b3 (1) -> b4 (2) | # \-> b3 (1) -> b4 (2) | ||||
self.log.info("Accept a block of size LEGACY_MAX_BLOCK_SIZE") | self.log.info("Accept a block of size LEGACY_MAX_BLOCK_SIZE") | ||||
self.move_tip(15) | self.move_tip(15) | ||||
Show All 18 Lines | def run_test(self): | ||||
"Reject a block with coinbase input script size out of range") | "Reject a block with coinbase input script size out of range") | ||||
self.move_tip(15) | self.move_tip(15) | ||||
b26 = self.next_block(26, spend=out[6]) | b26 = self.next_block(26, spend=out[6]) | ||||
b26.vtx[0].vin[0].scriptSig = b'\x00' | b26.vtx[0].vin[0].scriptSig = b'\x00' | ||||
b26.vtx[0].rehash() | b26.vtx[0].rehash() | ||||
# update_block causes the merkle root to get updated, even with no new | # update_block causes the merkle root to get updated, even with no new | ||||
# transactions, and updates the required state. | # transactions, and updates the required state. | ||||
b26 = self.update_block(26, []) | b26 = self.update_block(26, []) | ||||
self.sync_blocks([b26], success=False, reject_code=16, | self.sync_blocks([b26], success=False, | ||||
reject_reason=b'bad-cb-length', reconnect=True) | reject_reason='bad-cb-length', reconnect=True) | ||||
# Extend the b26 chain to make sure bitcoind isn't accepting b26 | # Extend the b26 chain to make sure bitcoind isn't accepting b26 | ||||
b27 = self.next_block(27, spend=out[7]) | b27 = self.next_block(27, spend=out[7]) | ||||
self.sync_blocks([b27], False) | self.sync_blocks([b27], False) | ||||
# Now try a too-large-coinbase script | # Now try a too-large-coinbase script | ||||
self.move_tip(15) | self.move_tip(15) | ||||
b28 = self.next_block(28, spend=out[6]) | b28 = self.next_block(28, spend=out[6]) | ||||
b28.vtx[0].vin[0].scriptSig = b'\x00' * 101 | b28.vtx[0].vin[0].scriptSig = b'\x00' * 101 | ||||
b28.vtx[0].rehash() | b28.vtx[0].rehash() | ||||
b28 = self.update_block(28, []) | b28 = self.update_block(28, []) | ||||
self.sync_blocks([b28], success=False, reject_code=16, | self.sync_blocks([b28], success=False, | ||||
reject_reason=b'bad-cb-length', reconnect=True) | reject_reason='bad-cb-length', reconnect=True) | ||||
# Extend the b28 chain to make sure bitcoind isn't accepting b28 | # Extend the b28 chain to make sure bitcoind isn't accepting b28 | ||||
b29 = self.next_block(29, spend=out[7]) | b29 = self.next_block(29, spend=out[7]) | ||||
self.sync_blocks([b29], False) | self.sync_blocks([b29], False) | ||||
# b30 has a max-sized coinbase scriptSig. | # b30 has a max-sized coinbase scriptSig. | ||||
self.move_tip(23) | self.move_tip(23) | ||||
b30 = self.next_block(30) | b30 = self.next_block(30) | ||||
Show All 23 Lines | def run_test(self): | ||||
# this goes over the limit because the coinbase has one sigop | # this goes over the limit because the coinbase has one sigop | ||||
self.log.info("Reject a block with too many OP_CHECKMULTISIG sigops") | self.log.info("Reject a block with too many OP_CHECKMULTISIG sigops") | ||||
too_many_multisigs = CScript( | too_many_multisigs = CScript( | ||||
[OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS_PER_MB // 20)) | [OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS_PER_MB // 20)) | ||||
b32 = self.next_block(32, spend=out[9], script=too_many_multisigs) | b32 = self.next_block(32, spend=out[9], script=too_many_multisigs) | ||||
assert_equal(get_legacy_sigopcount_block( | assert_equal(get_legacy_sigopcount_block( | ||||
b32), MAX_BLOCK_SIGOPS_PER_MB + 1) | b32), MAX_BLOCK_SIGOPS_PER_MB + 1) | ||||
self.sync_blocks([b32], success=False, reject_code=16, | self.sync_blocks([b32], success=False, | ||||
reject_reason=b'bad-blk-sigops', reconnect=True) | reject_reason='bad-blk-sigops', reconnect=True) | ||||
# CHECKMULTISIGVERIFY | # CHECKMULTISIGVERIFY | ||||
self.log.info( | self.log.info( | ||||
"Accept a block with the max number of OP_CHECKMULTISIGVERIFY sigops") | "Accept a block with the max number of OP_CHECKMULTISIGVERIFY sigops") | ||||
self.move_tip(31) | self.move_tip(31) | ||||
lots_of_multisigs = CScript( | lots_of_multisigs = CScript( | ||||
[OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS_PER_MB - 1) // 20) + [OP_CHECKSIG] * 19) | [OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS_PER_MB - 1) // 20) + [OP_CHECKSIG] * 19) | ||||
b33 = self.next_block(33, spend=out[9], script=lots_of_multisigs) | b33 = self.next_block(33, spend=out[9], script=lots_of_multisigs) | ||||
self.sync_blocks([b33], True) | self.sync_blocks([b33], True) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
self.log.info( | self.log.info( | ||||
"Reject a block with too many OP_CHECKMULTISIGVERIFY sigops") | "Reject a block with too many OP_CHECKMULTISIGVERIFY sigops") | ||||
too_many_multisigs = CScript( | too_many_multisigs = CScript( | ||||
[OP_CHECKMULTISIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB // 20)) | [OP_CHECKMULTISIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB // 20)) | ||||
b34 = self.next_block(34, spend=out[10], script=too_many_multisigs) | b34 = self.next_block(34, spend=out[10], script=too_many_multisigs) | ||||
self.sync_blocks([b34], success=False, reject_code=16, | self.sync_blocks([b34], success=False, | ||||
reject_reason=b'bad-blk-sigops', reconnect=True) | reject_reason='bad-blk-sigops', reconnect=True) | ||||
# CHECKSIGVERIFY | # CHECKSIGVERIFY | ||||
self.log.info( | self.log.info( | ||||
"Accept a block with the max number of OP_CHECKSIGVERIFY sigops") | "Accept a block with the max number of OP_CHECKSIGVERIFY sigops") | ||||
self.move_tip(33) | self.move_tip(33) | ||||
lots_of_checksigs = CScript( | lots_of_checksigs = CScript( | ||||
[OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB - 1)) | [OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB - 1)) | ||||
b35 = self.next_block(35, spend=out[10], script=lots_of_checksigs) | b35 = self.next_block(35, spend=out[10], script=lots_of_checksigs) | ||||
self.sync_blocks([b35], True) | self.sync_blocks([b35], True) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
self.log.info("Reject a block with too many OP_CHECKSIGVERIFY sigops") | self.log.info("Reject a block with too many OP_CHECKSIGVERIFY sigops") | ||||
too_many_checksigs = CScript( | too_many_checksigs = CScript( | ||||
[OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB)) | [OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB)) | ||||
b36 = self.next_block(36, spend=out[11], script=too_many_checksigs) | b36 = self.next_block(36, spend=out[11], script=too_many_checksigs) | ||||
self.sync_blocks([b36], success=False, reject_code=16, | self.sync_blocks([b36], success=False, | ||||
reject_reason=b'bad-blk-sigops', reconnect=True) | reject_reason='bad-blk-sigops', reconnect=True) | ||||
# Check spending of a transaction in a block which failed to connect | # Check spending of a transaction in a block which failed to connect | ||||
# | # | ||||
# b6 (3) | # b6 (3) | ||||
# b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) | # b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) | ||||
# \-> b37 (11) | # \-> b37 (11) | ||||
# \-> b38 (11/37) | # \-> b38 (11/37) | ||||
# | # | ||||
# save 37's spendable output, but then double-spend out11 to invalidate the block | # save 37's spendable output, but then double-spend out11 to invalidate the block | ||||
self.log.info( | self.log.info( | ||||
"Reject a block spending transaction from a block which failed to connect") | "Reject a block spending transaction from a block which failed to connect") | ||||
self.move_tip(35) | self.move_tip(35) | ||||
b37 = self.next_block(37, spend=out[11]) | b37 = self.next_block(37, spend=out[11]) | ||||
txout_b37 = PreviousSpendableOutput(b37.vtx[1], 0) | txout_b37 = PreviousSpendableOutput(b37.vtx[1], 0) | ||||
tx = self.create_and_sign_transaction(out[11].tx, out[11].n, 0) | tx = self.create_and_sign_transaction(out[11].tx, out[11].n, 0) | ||||
b37 = self.update_block(37, [tx]) | b37 = self.update_block(37, [tx]) | ||||
self.sync_blocks([b37], success=False, reject_code=16, | self.sync_blocks([b37], success=False, | ||||
reject_reason=b'bad-txns-inputs-missingorspent', reconnect=True) | reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | ||||
# attempt to spend b37's first non-coinbase tx, at which point b37 was still considered valid | # attempt to spend b37's first non-coinbase tx, at which point b37 was still considered valid | ||||
self.move_tip(35) | self.move_tip(35) | ||||
b38 = self.next_block(38, spend=txout_b37) | b38 = self.next_block(38, spend=txout_b37) | ||||
self.sync_blocks([b38], success=False, reject_code=16, | self.sync_blocks([b38], success=False, | ||||
reject_reason=b'bad-txns-inputs-missingorspent', reconnect=True) | reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | ||||
# Check P2SH SigOp counting | # Check P2SH SigOp counting | ||||
# | # | ||||
# | # | ||||
# 13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b41 (12) | # 13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b41 (12) | ||||
# \-> b40 (12) | # \-> b40 (12) | ||||
# | # | ||||
# b39 - create some P2SH outputs that will require 6 sigops to spend: | # b39 - create some P2SH outputs that will require 6 sigops to spend: | ||||
▲ Show 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
(numTxs * b39_sigops_per_output + sigops) + 1 | (numTxs * b39_sigops_per_output + sigops) + 1 | ||||
tx = CTransaction() | tx = CTransaction() | ||||
tx.vin.append(CTxIn(lastOutpoint, b'')) | tx.vin.append(CTxIn(lastOutpoint, b'')) | ||||
tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b40_sigops_to_fill))) | tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b40_sigops_to_fill))) | ||||
pad_tx(tx) | pad_tx(tx) | ||||
tx.rehash() | tx.rehash() | ||||
new_txs.append(tx) | new_txs.append(tx) | ||||
self.update_block(40, new_txs) | self.update_block(40, new_txs) | ||||
self.sync_blocks([b40], success=False, reject_code=16, | self.sync_blocks([b40], success=False, | ||||
reject_reason=b'bad-blk-sigops', reconnect=True) | reject_reason='bad-blk-sigops', reconnect=True) | ||||
# same as b40, but one less sigop | # same as b40, but one less sigop | ||||
self.log.info("Accept a block with the max number of P2SH sigops") | self.log.info("Accept a block with the max number of P2SH sigops") | ||||
self.move_tip(39) | self.move_tip(39) | ||||
b41 = self.next_block(41, spend=None) | b41 = self.next_block(41, spend=None) | ||||
self.update_block(41, [b40tx for b40tx in b40.vtx[1:] if b40tx != tx]) | self.update_block(41, [b40tx for b40tx in b40.vtx[1:] if b40tx != tx]) | ||||
b41_sigops_to_fill = b40_sigops_to_fill - 1 | b41_sigops_to_fill = b40_sigops_to_fill - 1 | ||||
tx = CTransaction() | tx = CTransaction() | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
b45.vtx.append(non_coinbase) | b45.vtx.append(non_coinbase) | ||||
b45.hashMerkleRoot = b45.calc_merkle_root() | b45.hashMerkleRoot = b45.calc_merkle_root() | ||||
b45.calc_sha256() | b45.calc_sha256() | ||||
b45.solve() | b45.solve() | ||||
self.block_heights[b45.sha256] = self.block_heights[ | self.block_heights[b45.sha256] = self.block_heights[ | ||||
self.tip.sha256] + 1 | self.tip.sha256] + 1 | ||||
self.tip = b45 | self.tip = b45 | ||||
self.blocks[45] = b45 | self.blocks[45] = b45 | ||||
self.sync_blocks([b45], success=False, reject_code=16, | self.sync_blocks([b45], success=False, | ||||
reject_reason=b'bad-cb-missing', reconnect=True) | reject_reason='bad-cb-missing', reconnect=True) | ||||
self.log.info("Reject a block with no transactions") | self.log.info("Reject a block with no transactions") | ||||
self.move_tip(44) | self.move_tip(44) | ||||
b46 = CBlock() | b46 = CBlock() | ||||
b46.nTime = b44.nTime + 1 | b46.nTime = b44.nTime + 1 | ||||
b46.hashPrevBlock = b44.sha256 | b46.hashPrevBlock = b44.sha256 | ||||
b46.nBits = 0x207fffff | b46.nBits = 0x207fffff | ||||
b46.vtx = [] | b46.vtx = [] | ||||
b46.hashMerkleRoot = 0 | b46.hashMerkleRoot = 0 | ||||
b46.solve() | b46.solve() | ||||
self.block_heights[b46.sha256] = self.block_heights[b44.sha256] + 1 | self.block_heights[b46.sha256] = self.block_heights[b44.sha256] + 1 | ||||
self.tip = b46 | self.tip = b46 | ||||
assert 46 not in self.blocks | assert 46 not in self.blocks | ||||
self.blocks[46] = b46 | self.blocks[46] = b46 | ||||
self.sync_blocks([b46], success=False, reject_code=16, | self.sync_blocks([b46], success=False, | ||||
reject_reason=b'bad-cb-missing', reconnect=True) | reject_reason='bad-cb-missing', reconnect=True) | ||||
self.log.info("Reject a block with invalid work") | self.log.info("Reject a block with invalid work") | ||||
self.move_tip(44) | self.move_tip(44) | ||||
b47 = self.next_block(47, solve=False) | b47 = self.next_block(47, solve=False) | ||||
target = uint256_from_compact(b47.nBits) | target = uint256_from_compact(b47.nBits) | ||||
while b47.sha256 < target: | while b47.sha256 < target: | ||||
b47.nNonce += 1 | b47.nNonce += 1 | ||||
b47.rehash() | b47.rehash() | ||||
self.sync_blocks([b47], False, request_block=False) | self.sync_blocks([b47], False, request_block=False) | ||||
self.log.info("Reject a block with a timestamp >2 hours in the future") | self.log.info("Reject a block with a timestamp >2 hours in the future") | ||||
self.move_tip(44) | self.move_tip(44) | ||||
b48 = self.next_block(48, solve=False) | b48 = self.next_block(48, solve=False) | ||||
b48.nTime = int(time.time()) + 60 * 60 * 3 | b48.nTime = int(time.time()) + 60 * 60 * 3 | ||||
b48.solve() | b48.solve() | ||||
self.sync_blocks([b48], False, request_block=False) | self.sync_blocks([b48], False, request_block=False) | ||||
self.log.info("Reject a block with invalid merkle hash") | self.log.info("Reject a block with invalid merkle hash") | ||||
self.move_tip(44) | self.move_tip(44) | ||||
b49 = self.next_block(49) | b49 = self.next_block(49) | ||||
b49.hashMerkleRoot += 1 | b49.hashMerkleRoot += 1 | ||||
b49.solve() | b49.solve() | ||||
self.sync_blocks([b49], success=False, reject_code=16, | self.sync_blocks([b49], success=False, | ||||
reject_reason=b'bad-txnmrklroot', reconnect=True) | reject_reason='bad-txnmrklroot', reconnect=True) | ||||
self.log.info("Reject a block with incorrect POW limit") | self.log.info("Reject a block with incorrect POW limit") | ||||
self.move_tip(44) | self.move_tip(44) | ||||
b50 = self.next_block(50) | b50 = self.next_block(50) | ||||
b50.nBits = b50.nBits - 1 | b50.nBits = b50.nBits - 1 | ||||
b50.solve() | b50.solve() | ||||
self.sync_blocks([b50], False, request_block=False, reconnect=True) | self.sync_blocks([b50], False, request_block=False, reconnect=True) | ||||
self.log.info("Reject a block with two coinbase transactions") | self.log.info("Reject a block with two coinbase transactions") | ||||
self.move_tip(44) | self.move_tip(44) | ||||
b51 = self.next_block(51) | b51 = self.next_block(51) | ||||
cb2 = create_coinbase(51, self.coinbase_pubkey) | cb2 = create_coinbase(51, self.coinbase_pubkey) | ||||
b51 = self.update_block(51, [cb2]) | b51 = self.update_block(51, [cb2]) | ||||
self.sync_blocks([b51], success=False, reject_code=16, | self.sync_blocks([b51], success=False, | ||||
reject_reason=b'bad-tx-coinbase', reconnect=True) | reject_reason='bad-tx-coinbase', reconnect=True) | ||||
self.log.info("Reject a block with duplicate transactions") | self.log.info("Reject a block with duplicate transactions") | ||||
self.move_tip(44) | self.move_tip(44) | ||||
b52 = self.next_block(52, spend=out[15]) | b52 = self.next_block(52, spend=out[15]) | ||||
b52 = self.update_block(52, [b52.vtx[1]]) | b52 = self.update_block(52, [b52.vtx[1]]) | ||||
self.sync_blocks([b52], success=False, reject_code=16, | self.sync_blocks([b52], success=False, | ||||
reject_reason=b'tx-duplicate', reconnect=True) | reject_reason='tx-duplicate', reconnect=True) | ||||
# Test block timestamps | # Test block timestamps | ||||
# -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) | # -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) | ||||
# \-> b54 (15) | # \-> b54 (15) | ||||
# | # | ||||
self.move_tip(43) | self.move_tip(43) | ||||
b53 = self.next_block(53, spend=out[14]) | b53 = self.next_block(53, spend=out[14]) | ||||
self.sync_blocks([b53], False) | self.sync_blocks([b53], False) | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
self.log.info( | self.log.info( | ||||
"Reject a block with a duplicate transaction in the Merkle Tree (but with a valid Merkle Root)") | "Reject a block with a duplicate transaction in the Merkle Tree (but with a valid Merkle Root)") | ||||
self.move_tip(55) | self.move_tip(55) | ||||
b56 = copy.deepcopy(b57) | b56 = copy.deepcopy(b57) | ||||
self.blocks[56] = b56 | self.blocks[56] = b56 | ||||
assert_equal(len(b56.vtx), 3) | assert_equal(len(b56.vtx), 3) | ||||
b56 = self.update_block(56, [b57.vtx[2]]) | b56 = self.update_block(56, [b57.vtx[2]]) | ||||
assert_equal(b56.hash, b57.hash) | assert_equal(b56.hash, b57.hash) | ||||
self.sync_blocks([b56], success=False, reject_code=16, | self.sync_blocks([b56], success=False, | ||||
reject_reason=b'bad-txns-duplicate', reconnect=True) | reject_reason='bad-txns-duplicate', reconnect=True) | ||||
# b57p2 - a good block with 6 tx'es, don't submit until end | # b57p2 - a good block with 6 tx'es, don't submit until end | ||||
self.move_tip(55) | self.move_tip(55) | ||||
b57p2 = self.next_block("57p2") | b57p2 = self.next_block("57p2") | ||||
tx = self.create_and_sign_transaction(out[16].tx, out[16].n, 1) | tx = self.create_and_sign_transaction(out[16].tx, out[16].n, 1) | ||||
tx1 = self.create_tx(tx, 0, 1) | tx1 = self.create_tx(tx, 0, 1) | ||||
tx2 = self.create_tx(tx1, 0, 1) | tx2 = self.create_tx(tx1, 0, 1) | ||||
tx3 = self.create_tx(tx2, 0, 1) | tx3 = self.create_tx(tx2, 0, 1) | ||||
tx4 = self.create_tx(tx3, 0, 1) | tx4 = self.create_tx(tx3, 0, 1) | ||||
b57p2 = self.update_block("57p2", [tx, tx1, tx2, tx3, tx4]) | b57p2 = self.update_block("57p2", [tx, tx1, tx2, tx3, tx4]) | ||||
# b56p2 - copy b57p2, duplicate two non-consecutive tx's | # b56p2 - copy b57p2, duplicate two non-consecutive tx's | ||||
self.log.info( | self.log.info( | ||||
"Reject a block with two duplicate transactions in the Merkle Tree (but with a valid Merkle Root)") | "Reject a block with two duplicate transactions in the Merkle Tree (but with a valid Merkle Root)") | ||||
self.move_tip(55) | self.move_tip(55) | ||||
b56p2 = copy.deepcopy(b57p2) | b56p2 = copy.deepcopy(b57p2) | ||||
self.blocks["b56p2"] = b56p2 | self.blocks["b56p2"] = b56p2 | ||||
assert_equal(len(b56p2.vtx), 6) | assert_equal(len(b56p2.vtx), 6) | ||||
b56p2 = self.update_block("b56p2", b56p2.vtx[4:6], reorder=False) | b56p2 = self.update_block("b56p2", b56p2.vtx[4:6], reorder=False) | ||||
assert_equal(b56p2.hash, b57p2.hash) | assert_equal(b56p2.hash, b57p2.hash) | ||||
self.sync_blocks([b56p2], success=False, reject_code=16, | self.sync_blocks([b56p2], success=False, | ||||
reject_reason=b'bad-txns-duplicate', reconnect=True) | reject_reason='bad-txns-duplicate', reconnect=True) | ||||
self.move_tip("57p2") | self.move_tip("57p2") | ||||
self.sync_blocks([b57p2], True) | self.sync_blocks([b57p2], True) | ||||
self.move_tip(57) | self.move_tip(57) | ||||
# The tip is not updated because 57p2 seen first | # The tip is not updated because 57p2 seen first | ||||
self.sync_blocks([b57], False) | self.sync_blocks([b57], False) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
Show All 12 Lines | def run_test(self): | ||||
tx = CTransaction() | tx = CTransaction() | ||||
assert(len(out[17].tx.vout) < 42) | assert(len(out[17].tx.vout) < 42) | ||||
tx.vin.append( | tx.vin.append( | ||||
CTxIn(COutPoint(out[17].tx.sha256, 42), CScript([OP_TRUE]), 0xffffffff)) | CTxIn(COutPoint(out[17].tx.sha256, 42), CScript([OP_TRUE]), 0xffffffff)) | ||||
tx.vout.append(CTxOut(0, b"")) | tx.vout.append(CTxOut(0, b"")) | ||||
pad_tx(tx) | pad_tx(tx) | ||||
tx.calc_sha256() | tx.calc_sha256() | ||||
b58 = self.update_block(58, [tx]) | b58 = self.update_block(58, [tx]) | ||||
self.sync_blocks([b58], success=False, reject_code=16, | self.sync_blocks([b58], success=False, | ||||
reject_reason=b'bad-txns-inputs-missingorspent', reconnect=True) | reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | ||||
# tx with output value > input value | # tx with output value > input value | ||||
self.log.info( | self.log.info( | ||||
"Reject a block with a transaction with outputs > inputs") | "Reject a block with a transaction with outputs > inputs") | ||||
self.move_tip(57) | self.move_tip(57) | ||||
b59 = self.next_block(59) | b59 = self.next_block(59) | ||||
tx = self.create_and_sign_transaction(out[17].tx, out[17].n, 51 * COIN) | tx = self.create_and_sign_transaction(out[17].tx, out[17].n, 51 * COIN) | ||||
b59 = self.update_block(59, [tx]) | b59 = self.update_block(59, [tx]) | ||||
self.sync_blocks([b59], success=False, reject_code=16, | self.sync_blocks([b59], success=False, | ||||
reject_reason=b'bad-txns-in-belowout', reconnect=True) | reject_reason='bad-txns-in-belowout', reconnect=True) | ||||
# 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, spend=out[17]) | ||||
self.sync_blocks([b60], True) | self.sync_blocks([b60], True) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
# Test BIP30 | # Test BIP30 | ||||
Show All 9 Lines | def run_test(self): | ||||
"Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)") | "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, spend=out[18]) | ||||
# Equalize the coinbases | # Equalize the coinbases | ||||
b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig | b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig | ||||
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(b60.vtx[0].serialize(), b61.vtx[0].serialize()) | ||||
self.sync_blocks([b61], success=False, reject_code=16, | self.sync_blocks([b61], success=False, | ||||
reject_reason=b'bad-txns-BIP30', reconnect=True) | reject_reason='bad-txns-BIP30', reconnect=True) | ||||
# 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) | # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) | ||||
# \-> b62 (18) | # \-> b62 (18) | ||||
# | # | ||||
self.log.info( | self.log.info( | ||||
"Reject a block with a transaction with a nonfinal locktime") | "Reject a block with a transaction with a nonfinal locktime") | ||||
self.move_tip(60) | self.move_tip(60) | ||||
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 | ||||
assert(out[18].n < len(out[18].tx.vout)) | assert(out[18].n < len(out[18].tx.vout)) | ||||
# don't set nSequence | # don't set nSequence | ||||
tx.vin.append(CTxIn(COutPoint(out[18].tx.sha256, out[18].n))) | tx.vin.append(CTxIn(COutPoint(out[18].tx.sha256, out[18].n))) | ||||
tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) | tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) | ||||
assert(tx.vin[0].nSequence < 0xffffffff) | assert(tx.vin[0].nSequence < 0xffffffff) | ||||
tx.calc_sha256() | tx.calc_sha256() | ||||
b62 = self.update_block(62, [tx]) | b62 = self.update_block(62, [tx]) | ||||
self.sync_blocks([b62], success=False, reject_code=16, | self.sync_blocks([b62], success=False, | ||||
reject_reason=b'bad-txns-nonfinal') | reject_reason='bad-txns-nonfinal') | ||||
# 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) | # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) | ||||
# \-> b63 (-) | # \-> b63 (-) | ||||
# | # | ||||
self.log.info( | self.log.info( | ||||
"Reject a block with a coinbase transaction with a nonfinal locktime") | "Reject a block with a coinbase transaction with a nonfinal locktime") | ||||
self.move_tip(60) | self.move_tip(60) | ||||
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 | ||||
b63.vtx[0].rehash() | b63.vtx[0].rehash() | ||||
b63 = self.update_block(63, []) | b63 = self.update_block(63, []) | ||||
self.sync_blocks([b63], success=False, reject_code=16, | self.sync_blocks([b63], success=False, | ||||
reject_reason=b'bad-txns-nonfinal') | reject_reason='bad-txns-nonfinal') | ||||
# This checks that a block with a bloated VARINT between the block_header and the array of tx such that | # This checks that a block with a bloated VARINT between the block_header and the array of tx such that | ||||
# the block is > LEGACY_MAX_BLOCK_SIZE with the bloated varint, but <= LEGACY_MAX_BLOCK_SIZE without the bloated varint, | # the block is > LEGACY_MAX_BLOCK_SIZE with the bloated varint, but <= LEGACY_MAX_BLOCK_SIZE without the bloated varint, | ||||
# does not cause a subsequent, identical block with canonical encoding to be rejected. The test does not | # does not cause a subsequent, identical block with canonical encoding to be rejected. The test does not | ||||
# care whether the bloated block is accepted or rejected; it only cares that the second block is accepted. | # care whether the bloated block is accepted or rejected; it only cares that the second block is accepted. | ||||
# | # | ||||
# 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.) | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
"Reject a block with a transaction double spending a transaction created in the same block") | "Reject a block with a transaction double spending a transaction created in the same block") | ||||
self.move_tip(65) | self.move_tip(65) | ||||
b67 = self.next_block(67) | b67 = self.next_block(67) | ||||
tx1 = self.create_and_sign_transaction( | tx1 = self.create_and_sign_transaction( | ||||
out[20].tx, out[20].n, out[20].tx.vout[0].nValue) | out[20].tx, out[20].n, out[20].tx.vout[0].nValue) | ||||
tx2 = self.create_and_sign_transaction(tx1, 0, 1) | tx2 = self.create_and_sign_transaction(tx1, 0, 1) | ||||
tx3 = self.create_and_sign_transaction(tx1, 0, 2) | tx3 = self.create_and_sign_transaction(tx1, 0, 2) | ||||
b67 = self.update_block(67, [tx1, tx2, tx3]) | b67 = self.update_block(67, [tx1, tx2, tx3]) | ||||
self.sync_blocks([b67], success=False, reject_code=16, | self.sync_blocks([b67], success=False, | ||||
reject_reason=b'bad-txns-inputs-missingorspent', reconnect=True) | reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | ||||
# 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) | # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> 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 | ||||
# this fails because the coinbase is trying to claim 1 satoshi too much in fees | # this fails because the coinbase is trying to claim 1 satoshi too much in fees | ||||
# | # | ||||
# b69 - coinbase with extra 10 satoshis, and a tx that gives a 10 satoshi fee | # b69 - coinbase with extra 10 satoshis, and a tx that gives a 10 satoshi fee | ||||
# this succeeds | # this succeeds | ||||
# | # | ||||
self.log.info( | self.log.info( | ||||
"Reject a block trying to claim too much subsidy in the coinbase transaction") | "Reject a block trying to claim too much subsidy in the coinbase transaction") | ||||
self.move_tip(65) | self.move_tip(65) | ||||
b68 = self.next_block(68, additional_coinbase_value=10) | b68 = self.next_block(68, additional_coinbase_value=10) | ||||
tx = self.create_and_sign_transaction( | tx = self.create_and_sign_transaction( | ||||
out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 9) | out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 9) | ||||
b68 = self.update_block(68, [tx]) | b68 = self.update_block(68, [tx]) | ||||
self.sync_blocks([b68], success=False, reject_code=16, | self.sync_blocks([b68], success=False, | ||||
reject_reason=b'bad-cb-amount', reconnect=True) | reject_reason='bad-cb-amount', reconnect=True) | ||||
self.log.info( | self.log.info( | ||||
"Accept a block claiming the correct subsidy in the coinbase transaction") | "Accept a block claiming the correct subsidy in the coinbase transaction") | ||||
self.move_tip(65) | self.move_tip(65) | ||||
b69 = self.next_block(69, additional_coinbase_value=10) | b69 = self.next_block(69, additional_coinbase_value=10) | ||||
tx = self.create_and_sign_transaction( | tx = self.create_and_sign_transaction( | ||||
out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 10) | out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 10) | ||||
self.update_block(69, [tx]) | self.update_block(69, [tx]) | ||||
Show All 12 Lines | def run_test(self): | ||||
bogus_tx = CTransaction() | bogus_tx = CTransaction() | ||||
bogus_tx.sha256 = uint256_from_str( | bogus_tx.sha256 = uint256_from_str( | ||||
b"23c70ed7c0506e9178fc1a987f40a33946d4ad4c962b5ae3a52546da53af0c5c") | b"23c70ed7c0506e9178fc1a987f40a33946d4ad4c962b5ae3a52546da53af0c5c") | ||||
tx = CTransaction() | tx = CTransaction() | ||||
tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff)) | tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff)) | ||||
tx.vout.append(CTxOut(1, b"")) | tx.vout.append(CTxOut(1, b"")) | ||||
pad_tx(tx) | pad_tx(tx) | ||||
b70 = self.update_block(70, [tx]) | b70 = self.update_block(70, [tx]) | ||||
self.sync_blocks([b70], success=False, reject_code=16, | self.sync_blocks([b70], success=False, | ||||
reject_reason=b'bad-txns-inputs-missingorspent', reconnect=True) | reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | ||||
# 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) | # -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> 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. | ||||
Show All 11 Lines | def run_test(self): | ||||
self.block_heights[b71.sha256] = self.block_heights[b69.sha256] + 1 | self.block_heights[b71.sha256] = self.block_heights[b69.sha256] + 1 | ||||
self.blocks[71] = b71 | self.blocks[71] = b71 | ||||
assert_equal(len(b71.vtx), 4) | assert_equal(len(b71.vtx), 4) | ||||
assert_equal(len(b72.vtx), 3) | assert_equal(len(b72.vtx), 3) | ||||
assert_equal(b72.sha256, b71.sha256) | assert_equal(b72.sha256, b71.sha256) | ||||
self.move_tip(71) | self.move_tip(71) | ||||
self.sync_blocks([b71], success=False, reject_code=16, | self.sync_blocks([b71], success=False, | ||||
reject_reason=b'bad-txns-duplicate', reconnect=True) | reject_reason='bad-txns-duplicate', reconnect=True) | ||||
self.move_tip(72) | self.move_tip(72) | ||||
self.sync_blocks([b72], True) | self.sync_blocks([b72], True) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
# Test some invalid scripts and MAX_BLOCK_SIGOPS_PER_MB | # Test some invalid scripts and MAX_BLOCK_SIGOPS_PER_MB | ||||
# | # | ||||
# -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21) | # -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21) | ||||
Show All 23 Lines | def run_test(self): | ||||
a[MAX_BLOCK_SIGOPS_PER_MB + 1] = element_size // 256 | a[MAX_BLOCK_SIGOPS_PER_MB + 1] = element_size // 256 | ||||
a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0 | a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0 | ||||
a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0 | a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0 | ||||
tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a)) | tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a)) | ||||
b73 = self.update_block(73, [tx]) | b73 = self.update_block(73, [tx]) | ||||
assert_equal(get_legacy_sigopcount_block( | assert_equal(get_legacy_sigopcount_block( | ||||
b73), MAX_BLOCK_SIGOPS_PER_MB + 1) | b73), MAX_BLOCK_SIGOPS_PER_MB + 1) | ||||
self.sync_blocks([b73], success=False, reject_code=16, | self.sync_blocks([b73], success=False, | ||||
reject_reason=b'bad-blk-sigops', reconnect=True) | reject_reason='bad-blk-sigops', reconnect=True) | ||||
# b74/75 - if we push an invalid script element, all prevous sigops are counted, | # b74/75 - if we push an invalid script element, all prevous sigops are counted, | ||||
# but sigops after the element are not counted. | # but sigops after the element are not counted. | ||||
# | # | ||||
# The invalid script element is that the push_data indicates that | # The invalid script element is that the push_data indicates that | ||||
# there will be a large amount of data (0xffffff bytes), but we only | # there will be a large amount of data (0xffffff bytes), but we only | ||||
# provide a much smaller number. These bytes are CHECKSIGS so they would | # provide a much smaller number. These bytes are CHECKSIGS so they would | ||||
# cause b75 to fail for excessive sigops, if those bytes were counted. | # cause b75 to fail for excessive sigops, if those bytes were counted. | ||||
Show All 9 Lines | |||||
a = bytearray([OP_CHECKSIG] * size) | a = bytearray([OP_CHECKSIG] * size) | ||||
a[MAX_BLOCK_SIGOPS_PER_MB] = 0x4e | a[MAX_BLOCK_SIGOPS_PER_MB] = 0x4e | ||||
a[MAX_BLOCK_SIGOPS_PER_MB + 1] = 0xfe | a[MAX_BLOCK_SIGOPS_PER_MB + 1] = 0xfe | ||||
a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0xff | a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0xff | ||||
a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0xff | a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0xff | ||||
a[MAX_BLOCK_SIGOPS_PER_MB + 4] = 0xff | a[MAX_BLOCK_SIGOPS_PER_MB + 4] = 0xff | ||||
tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a)) | tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a)) | ||||
b74 = self.update_block(74, [tx]) | b74 = self.update_block(74, [tx]) | ||||
self.sync_blocks([b74], success=False, reject_code=16, | self.sync_blocks([b74], success=False, | ||||
reject_reason=b'bad-blk-sigops', reconnect=True) | reject_reason='bad-blk-sigops', reconnect=True) | ||||
self.move_tip(72) | self.move_tip(72) | ||||
b75 = self.next_block(75) | b75 = self.next_block(75) | ||||
size = MAX_BLOCK_SIGOPS_PER_MB - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 | size = MAX_BLOCK_SIGOPS_PER_MB - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 | ||||
a = bytearray([OP_CHECKSIG] * size) | a = bytearray([OP_CHECKSIG] * size) | ||||
a[MAX_BLOCK_SIGOPS_PER_MB - 1] = 0x4e | a[MAX_BLOCK_SIGOPS_PER_MB - 1] = 0x4e | ||||
a[MAX_BLOCK_SIGOPS_PER_MB] = 0xff | a[MAX_BLOCK_SIGOPS_PER_MB] = 0xff | ||||
a[MAX_BLOCK_SIGOPS_PER_MB + 1] = 0xff | a[MAX_BLOCK_SIGOPS_PER_MB + 1] = 0xff | ||||
▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | |||||
b88 = self.next_block(88, spend=out[31]) | b88 = self.next_block(88, spend=out[31]) | ||||
self.sync_blocks([b88], True) | self.sync_blocks([b88], True) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
# trying to spend the OP_RETURN output is rejected | # trying to spend the OP_RETURN output is rejected | ||||
b89a = self.next_block("89a", spend=out[32]) | b89a = self.next_block("89a", spend=out[32]) | ||||
tx = self.create_tx(tx1, 0, 0, CScript([OP_TRUE])) | tx = self.create_tx(tx1, 0, 0, CScript([OP_TRUE])) | ||||
b89a = self.update_block("89a", [tx]) | b89a = self.update_block("89a", [tx]) | ||||
self.sync_blocks([b89a], success=False, reject_code=16, | self.sync_blocks([b89a], success=False, | ||||
reject_reason=b'bad-txns-inputs-missingorspent', reconnect=True) | reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | ||||
self.log.info( | self.log.info( | ||||
"Test a re-org of one week's worth of blocks (1088 blocks)") | "Test a re-org of one week's worth of blocks (1088 blocks)") | ||||
self.move_tip(88) | self.move_tip(88) | ||||
LARGE_REORG_SIZE = 1088 | LARGE_REORG_SIZE = 1088 | ||||
blocks = [] | blocks = [] | ||||
spend = out[32] | spend = out[32] | ||||
▲ Show 20 Lines • Show All 142 Lines • ▼ Show 20 Lines | def reconnect_p2p(self): | ||||
"""Tear down and bootstrap the P2P connection to the node. | """Tear down and bootstrap the P2P connection to the node. | ||||
The node gets disconnected several times in this test. This helper | The node gets disconnected several times in this test. This helper | ||||
method reconnects the p2p and restarts the network thread.""" | method reconnects the p2p and restarts the network thread.""" | ||||
self.nodes[0].disconnect_p2ps() | self.nodes[0].disconnect_p2ps() | ||||
network_thread_join() | network_thread_join() | ||||
self.bootstrap_p2p() | self.bootstrap_p2p() | ||||
def sync_blocks(self, blocks, success=True, reject_code=None, reject_reason=None, request_block=True, reconnect=False, timeout=60): | def sync_blocks(self, blocks, success=True, reject_reason=None, request_block=True, reconnect=False, timeout=60): | ||||
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block. | """Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block. | ||||
Call with success = False if the tip shouldn't advance to the most recent block.""" | Call with success = False if the tip shouldn't advance to the most recent block.""" | ||||
self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, | self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, | ||||
reject_reason=reject_reason, request_block=request_block, timeout=timeout, expect_disconnect=reconnect) | reject_reason=reject_reason, request_block=request_block, timeout=timeout, expect_disconnect=reconnect) | ||||
if reconnect: | if reconnect: | ||||
self.reconnect_p2p() | self.reconnect_p2p() | ||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
FullBlockTest().main() | FullBlockTest().main() |