Changeset View
Changeset View
Standalone View
Standalone View
test/functional/feature_block.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2015-2017 The Bitcoin Core developers | # Copyright (c) 2015-2017 The Bitcoin Core developers | ||||
# Copyright (c) 2017 The Bitcoin developers | # Copyright (c) 2017 The Bitcoin developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Test block processing.""" | """Test block processing.""" | ||||
import copy | import copy | ||||
import struct | import struct | ||||
import time | import time | ||||
from test_framework.blocktools import ( | from test_framework.blocktools import ( | ||||
create_block, | create_block, | ||||
create_coinbase, | create_coinbase, | ||||
create_tx_with_script, | create_tx_with_script, | ||||
get_legacy_sigopcount_block, | |||||
make_conform_to_ctor, | make_conform_to_ctor, | ||||
) | ) | ||||
from test_framework.cdefs import LEGACY_MAX_BLOCK_SIZE, MAX_BLOCK_SIGOPS_PER_MB | from test_framework.cdefs import LEGACY_MAX_BLOCK_SIZE | ||||
from test_framework.key import CECKey | from test_framework.key import CECKey | ||||
from test_framework.messages import ( | from test_framework.messages import ( | ||||
CBlock, | CBlock, | ||||
COIN, | COIN, | ||||
COutPoint, | COutPoint, | ||||
CTransaction, | CTransaction, | ||||
CTxIn, | CTxIn, | ||||
CTxOut, | CTxOut, | ||||
uint256_from_compact, | uint256_from_compact, | ||||
uint256_from_str, | uint256_from_str, | ||||
) | ) | ||||
from test_framework.mininode import P2PDataStore | from test_framework.mininode import P2PDataStore | ||||
from test_framework.script import ( | from test_framework.script import ( | ||||
CScript, | CScript, | ||||
hash160, | |||||
MAX_SCRIPT_ELEMENT_SIZE, | |||||
OP_2DUP, | |||||
OP_CHECKMULTISIG, | |||||
OP_CHECKMULTISIGVERIFY, | |||||
OP_CHECKSIG, | |||||
OP_CHECKSIGVERIFY, | |||||
OP_ELSE, | OP_ELSE, | ||||
OP_ENDIF, | OP_ENDIF, | ||||
OP_EQUAL, | |||||
OP_FALSE, | OP_FALSE, | ||||
OP_HASH160, | |||||
OP_IF, | OP_IF, | ||||
OP_INVALIDOPCODE, | OP_INVALIDOPCODE, | ||||
OP_RETURN, | OP_RETURN, | ||||
OP_TRUE, | OP_TRUE, | ||||
SIGHASH_ALL, | SIGHASH_ALL, | ||||
SIGHASH_FORKID, | SIGHASH_FORKID, | ||||
SignatureHashForkId, | SignatureHashForkId, | ||||
) | ) | ||||
▲ Show 20 Lines • Show All 155 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
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, | self.sync_blocks([b12, b13, b14], success=False, | ||||
reject_reason='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 | self.log.info("Skipped sigops tests") | ||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) | # tests were moved to feature_block_sigops.py | ||||
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6) | |||||
# \-> b3 (1) -> b4 (2) | |||||
self.log.info("Accept a block with lots of checksigs") | |||||
lots_of_checksigs = CScript( | |||||
[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) | ||||
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") | |||||
too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB)) | |||||
b16 = self.next_block(16, spend=out[6], script=too_many_checksigs) | |||||
self.sync_blocks([b16], success=False, | |||||
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, | self.sync_blocks([b17], success=False, | ||||
▲ Show 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
self.move_tip(23) | self.move_tip(23) | ||||
b30 = self.next_block(30) | b30 = self.next_block(30) | ||||
b30.vtx[0].vin[0].scriptSig = b'\x00' * 100 | b30.vtx[0].vin[0].scriptSig = b'\x00' * 100 | ||||
b30.vtx[0].rehash() | b30.vtx[0].rehash() | ||||
b30 = self.update_block(30, []) | b30 = self.update_block(30, []) | ||||
self.sync_blocks([b30], True) | self.sync_blocks([b30], True) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
# b31 - b35 - check sigops of OP_CHECKMULTISIG / OP_CHECKMULTISIGVERIFY / OP_CHECKSIGVERIFY | self.log.info("Skipped sigops tests") | ||||
# | # tests were moved to feature_block_sigops.py | ||||
# genesis -> ... -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) | b31 = self.next_block(31) | ||||
# \-> b36 (11) | |||||
# \-> b34 (10) | |||||
# \-> b32 (9) | |||||
# | |||||
# MULTISIG: each op code counts as 20 sigops. To create the edge case, pack another 19 sigops at the end. | |||||
self.log.info( | |||||
"Accept a block with the max number of OP_CHECKMULTISIG sigops") | |||||
lots_of_multisigs = CScript( | |||||
[OP_CHECKMULTISIG] * ((MAX_BLOCK_SIGOPS_PER_MB - 1) // 20) + [OP_CHECKSIG] * 19) | |||||
b31 = self.next_block(31, spend=out[8], script=lots_of_multisigs) | |||||
assert_equal(get_legacy_sigopcount_block(b31), MAX_BLOCK_SIGOPS_PER_MB) | |||||
self.sync_blocks([b31], True) | |||||
self.save_spendable_output() | |||||
# this goes over the limit because the coinbase has one sigop | |||||
self.log.info("Reject a block with too many OP_CHECKMULTISIG sigops") | |||||
too_many_multisigs = CScript( | |||||
[OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS_PER_MB // 20)) | |||||
b32 = self.next_block(32, spend=out[9], script=too_many_multisigs) | |||||
assert_equal(get_legacy_sigopcount_block( | |||||
b32), MAX_BLOCK_SIGOPS_PER_MB + 1) | |||||
self.sync_blocks([b32], success=False, | |||||
reject_reason='bad-blk-sigops', reconnect=True) | |||||
# CHECKMULTISIGVERIFY | |||||
self.log.info( | |||||
"Accept a block with the max number of OP_CHECKMULTISIGVERIFY sigops") | |||||
self.move_tip(31) | |||||
lots_of_multisigs = CScript( | |||||
[OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS_PER_MB - 1) // 20) + [OP_CHECKSIG] * 19) | |||||
b33 = self.next_block(33, spend=out[9], script=lots_of_multisigs) | |||||
self.sync_blocks([b33], True) | |||||
self.save_spendable_output() | self.save_spendable_output() | ||||
b33 = self.next_block(33) | |||||
self.log.info( | self.save_spendable_output() | ||||
"Reject a block with too many OP_CHECKMULTISIGVERIFY sigops") | b35 = self.next_block(35) | ||||
too_many_multisigs = CScript( | self.save_spendable_output() | ||||
[OP_CHECKMULTISIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB // 20)) | self.sync_blocks([b31, b33, b35], True) | ||||
b34 = self.next_block(34, spend=out[10], script=too_many_multisigs) | |||||
self.sync_blocks([b34], success=False, | |||||
reject_reason='bad-blk-sigops', reconnect=True) | |||||
# CHECKSIGVERIFY | |||||
self.log.info( | |||||
"Accept a block with the max number of OP_CHECKSIGVERIFY sigops") | |||||
self.move_tip(33) | |||||
lots_of_checksigs = CScript( | |||||
[OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB - 1)) | |||||
b35 = self.next_block(35, spend=out[10], script=lots_of_checksigs) | |||||
self.sync_blocks([b35], True) | |||||
self.save_spendable_output() | |||||
self.log.info("Reject a block with too many OP_CHECKSIGVERIFY sigops") | |||||
too_many_checksigs = CScript( | |||||
[OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB)) | |||||
b36 = self.next_block(36, spend=out[11], script=too_many_checksigs) | |||||
self.sync_blocks([b36], success=False, | |||||
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) | ||||
# | # | ||||
Show All 10 Lines | def run_test(self): | ||||
reject_reason='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, | self.sync_blocks([b38], success=False, | ||||
reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | ||||
# Check P2SH SigOp counting | self.log.info("Skipped sigops tests") | ||||
# | # tests were moved to feature_block_sigops.py | ||||
# | |||||
# 13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b41 (12) | |||||
# \-> b40 (12) | |||||
# | |||||
# b39 - create some P2SH outputs that will require 6 sigops to spend: | |||||
# | |||||
# redeem_script = COINBASE_PUBKEY, (OP_2DUP+OP_CHECKSIGVERIFY) * 5, OP_CHECKSIG | |||||
# p2sh_script = OP_HASH160, ripemd160(sha256(script)), OP_EQUAL | |||||
# | |||||
self.log.info("Check P2SH SIGOPS are correctly counted") | |||||
self.move_tip(35) | self.move_tip(35) | ||||
b39 = self.next_block(39) | b39 = self.next_block(39) | ||||
b39_outputs = 0 | |||||
b39_sigops_per_output = 6 | |||||
# Build the redeem script, hash it, use hash to create the p2sh script | |||||
redeem_script = CScript([self.coinbase_pubkey] + [ | |||||
OP_2DUP, OP_CHECKSIGVERIFY] * 5 + [OP_CHECKSIG]) | |||||
redeem_script_hash = hash160(redeem_script) | |||||
p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL]) | |||||
# Create a transaction that spends one satoshi to the p2sh_script, the rest to OP_TRUE | |||||
# This must be signed because it is spending a coinbase | |||||
spend = out[11] | |||||
tx = self.create_tx(spend, 0, 1, p2sh_script) | |||||
tx.vout.append(CTxOut(spend.vout[0].nValue - 1, CScript([OP_TRUE]))) | |||||
self.sign_tx(tx, spend) | |||||
tx.rehash() | |||||
b39 = self.update_block(39, [tx]) | |||||
b39_outputs += 1 | |||||
# Until block is full, add tx's with 1 satoshi to p2sh_script, the rest | |||||
# to OP_TRUE | |||||
tx_new = None | |||||
tx_last = tx | |||||
tx_last_n = len(tx.vout) - 1 | |||||
total_size = len(b39.serialize()) | |||||
while(total_size < LEGACY_MAX_BLOCK_SIZE): | |||||
tx_new = self.create_tx(tx_last, tx_last_n, 1, p2sh_script) | |||||
tx_new.vout.append( | |||||
CTxOut(tx_last.vout[tx_last_n].nValue - 1, CScript([OP_TRUE]))) | |||||
tx_new.rehash() | |||||
total_size += len(tx_new.serialize()) | |||||
if total_size >= LEGACY_MAX_BLOCK_SIZE: | |||||
break | |||||
b39.vtx.append(tx_new) # add tx to block | |||||
tx_last = tx_new | |||||
tx_last_n = len(tx_new.vout) - 1 | |||||
b39_outputs += 1 | |||||
b39 = self.update_block(39, []) | |||||
self.sync_blocks([b39], True) | |||||
self.save_spendable_output() | self.save_spendable_output() | ||||
b41 = self.next_block(41) | |||||
# Test sigops in P2SH redeem scripts | self.sync_blocks([b39, b41], True) | ||||
# | |||||
# b40 creates 3333 tx's spending the 6-sigop P2SH outputs from b39 for a total of 19998 sigops. | |||||
# The first tx has one sigop and then at the end we add 2 more to put us just over the max. | |||||
# | |||||
# b41 does the same, less one, so it has the maximum sigops permitted. | |||||
# | |||||
self.log.info("Reject a block with too many P2SH sigops") | |||||
self.move_tip(39) | |||||
b40 = self.next_block(40, spend=out[12]) | |||||
sigops = get_legacy_sigopcount_block(b40) | |||||
numTxs = (MAX_BLOCK_SIGOPS_PER_MB - sigops) // b39_sigops_per_output | |||||
assert_equal(numTxs <= b39_outputs, True) | |||||
lastOutpoint = COutPoint(b40.vtx[1].sha256, 0) | |||||
lastAmount = b40.vtx[1].vout[0].nValue | |||||
new_txs = [] | |||||
for i in range(1, numTxs + 1): | |||||
tx = CTransaction() | |||||
tx.vout.append(CTxOut(1, CScript([OP_TRUE]))) | |||||
tx.vin.append(CTxIn(lastOutpoint, b'')) | |||||
# second input is corresponding P2SH output from b39 | |||||
tx.vin.append(CTxIn(COutPoint(b39.vtx[i].sha256, 0), b'')) | |||||
# Note: must pass the redeem_script (not p2sh_script) to the | |||||
# signature hash function | |||||
sighash = SignatureHashForkId( | |||||
redeem_script, tx, 1, SIGHASH_ALL | SIGHASH_FORKID, | |||||
lastAmount) | |||||
sig = self.coinbase_key.sign( | |||||
sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) | |||||
scriptSig = CScript([sig, redeem_script]) | |||||
tx.vin[1].scriptSig = scriptSig | |||||
pad_tx(tx) | |||||
tx.rehash() | |||||
new_txs.append(tx) | |||||
lastOutpoint = COutPoint(tx.sha256, 0) | |||||
lastAmount = tx.vout[0].nValue | |||||
b40_sigops_to_fill = MAX_BLOCK_SIGOPS_PER_MB - \ | |||||
(numTxs * b39_sigops_per_output + sigops) + 1 | |||||
tx = CTransaction() | |||||
tx.vin.append(CTxIn(lastOutpoint, b'')) | |||||
tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b40_sigops_to_fill))) | |||||
pad_tx(tx) | |||||
tx.rehash() | |||||
new_txs.append(tx) | |||||
self.update_block(40, new_txs) | |||||
self.sync_blocks([b40], success=False, | |||||
reject_reason='bad-blk-sigops', reconnect=True) | |||||
# same as b40, but one less sigop | |||||
self.log.info("Accept a block with the max number of P2SH sigops") | |||||
self.move_tip(39) | |||||
b41 = self.next_block(41, spend=None) | |||||
self.update_block(41, [b40tx for b40tx in b40.vtx[1:] if b40tx != tx]) | |||||
b41_sigops_to_fill = b40_sigops_to_fill - 1 | |||||
tx = CTransaction() | |||||
tx.vin.append(CTxIn(lastOutpoint, b'')) | |||||
tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b41_sigops_to_fill))) | |||||
pad_tx(tx) | |||||
self.update_block(41, [tx]) | |||||
self.sync_blocks([b41], True) | |||||
# Fork off of b39 to create a constant base again | # Fork off of b39 to create a constant base again | ||||
# | # | ||||
# b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) | # b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) | ||||
# \-> b41 (12) | # \-> b41 (12) | ||||
# | # | ||||
self.move_tip(39) | self.move_tip(39) | ||||
b42 = self.next_block(42, spend=out[12]) | b42 = self.next_block(42, spend=out[12]) | ||||
▲ Show 20 Lines • Show All 463 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
self.move_tip(71) | self.move_tip(71) | ||||
self.sync_blocks([b71], success=False, | self.sync_blocks([b71], success=False, | ||||
reject_reason='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 | self.log.info("Skipped sigops tests") | ||||
# | # tests were moved to feature_block_sigops.py | ||||
# -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21) | |||||
# \-> b** (22) | |||||
# | |||||
# b73 - tx with excessive sigops that are placed after an excessively large script element. | |||||
# The purpose of the test is to make sure those sigops are counted. | |||||
# | |||||
# script is a bytearray of size 20,526 | |||||
# | |||||
# bytearray[0-19,998] : OP_CHECKSIG | |||||
# bytearray[19,999] : OP_PUSHDATA4 | |||||
# bytearray[20,000-20,003]: 521 (max_script_element_size+1, in little-endian format) | |||||
# bytearray[20,004-20,525]: unread data (script_element) | |||||
# bytearray[20,526] : OP_CHECKSIG (this puts us over the limit) | |||||
self.log.info( | |||||
"Reject a block containing too many sigops after a large script element") | |||||
self.move_tip(72) | |||||
b73 = self.next_block(73) | |||||
size = MAX_BLOCK_SIGOPS_PER_MB - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 + 1 | |||||
a = bytearray([OP_CHECKSIG] * size) | |||||
a[MAX_BLOCK_SIGOPS_PER_MB - 1] = int("4e", 16) # OP_PUSHDATA4 | |||||
element_size = MAX_SCRIPT_ELEMENT_SIZE + 1 | |||||
a[MAX_BLOCK_SIGOPS_PER_MB] = 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 + 3] = 0 | |||||
tx = self.create_and_sign_transaction(out[22], 1, CScript(a)) | |||||
b73 = self.update_block(73, [tx]) | |||||
assert_equal(get_legacy_sigopcount_block( | |||||
b73), MAX_BLOCK_SIGOPS_PER_MB + 1) | |||||
self.sync_blocks([b73], success=False, | |||||
reject_reason='bad-blk-sigops', reconnect=True) | |||||
# b74/75 - if we push an invalid script element, all prevous sigops are counted, | |||||
# but sigops after the element are not counted. | |||||
# | |||||
# The invalid script element is that the push_data indicates that | |||||
# 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 | |||||
# cause b75 to fail for excessive sigops, if those bytes were counted. | |||||
# | |||||
# b74 fails because we put MAX_BLOCK_SIGOPS_PER_MB+1 before the element | |||||
# b75 succeeds because we put MAX_BLOCK_SIGOPS_PER_MB before the element | |||||
self.log.info( | |||||
"Check sigops are counted correctly after an invalid script element") | |||||
self.move_tip(72) | |||||
b74 = self.next_block(74) | |||||
size = MAX_BLOCK_SIGOPS_PER_MB - 1 + \ | |||||
MAX_SCRIPT_ELEMENT_SIZE + 42 # total = 20,561 | |||||
a = bytearray([OP_CHECKSIG] * size) | |||||
a[MAX_BLOCK_SIGOPS_PER_MB] = 0x4e | |||||
a[MAX_BLOCK_SIGOPS_PER_MB + 1] = 0xfe | |||||
a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0xff | |||||
a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0xff | |||||
a[MAX_BLOCK_SIGOPS_PER_MB + 4] = 0xff | |||||
tx = self.create_and_sign_transaction(out[22], 1, CScript(a)) | |||||
b74 = self.update_block(74, [tx]) | |||||
self.sync_blocks([b74], success=False, | |||||
reject_reason='bad-blk-sigops', reconnect=True) | |||||
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 | |||||
a = bytearray([OP_CHECKSIG] * size) | |||||
a[MAX_BLOCK_SIGOPS_PER_MB - 1] = 0x4e | |||||
a[MAX_BLOCK_SIGOPS_PER_MB] = 0xff | |||||
a[MAX_BLOCK_SIGOPS_PER_MB + 1] = 0xff | |||||
a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0xff | |||||
a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0xff | |||||
tx = self.create_and_sign_transaction(out[22], 1, CScript(a)) | |||||
b75 = self.update_block(75, [tx]) | |||||
self.sync_blocks([b75], True) | |||||
self.save_spendable_output() | self.save_spendable_output() | ||||
# Check that if we push an element filled with CHECKSIGs, they are not counted | |||||
self.move_tip(75) | |||||
b76 = self.next_block(76) | b76 = self.next_block(76) | ||||
size = MAX_BLOCK_SIGOPS_PER_MB - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 | |||||
a = bytearray([OP_CHECKSIG] * size) | |||||
# PUSHDATA4, but leave the following bytes as just checksigs | |||||
a[MAX_BLOCK_SIGOPS_PER_MB - 1] = 0x4e | |||||
tx = self.create_and_sign_transaction(out[23], 1, CScript(a)) | |||||
b76 = self.update_block(76, [tx]) | |||||
self.sync_blocks([b76], True) | |||||
self.save_spendable_output() | self.save_spendable_output() | ||||
self.sync_blocks([b75, b76], True) | |||||
# Test transaction resurrection | # Test transaction resurrection | ||||
# | # | ||||
# -> b77 (24) -> b78 (25) -> b79 (26) | # -> b77 (24) -> b78 (25) -> b79 (26) | ||||
# \-> b80 (25) -> b81 (26) -> b82 (27) | # \-> b80 (25) -> b81 (26) -> b82 (27) | ||||
# | # | ||||
# b78 creates a tx, which is spent in b79. After b82, both should be in mempool | # b78 creates a tx, which is spent in b79. After b82, both should be in mempool | ||||
# | # | ||||
▲ Show 20 Lines • Show All 292 Lines • Show Last 20 Lines |