Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc-p2p-fullblocktest.py
- This file was copied to test/functional/abc-p2p-fullblocktest-sigops.py.
Show All 16 Lines | |||||
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, | ||||
make_conform_to_ctor, | make_conform_to_ctor, | ||||
) | ) | ||||
from test_framework.cdefs import ( | from test_framework.cdefs import ( | ||||
MAX_BLOCK_SIGOPS_PER_MB, | |||||
MAX_TX_SIGOPS_COUNT, | |||||
ONE_MEGABYTE, | ONE_MEGABYTE, | ||||
) | ) | ||||
from test_framework.key import CECKey | |||||
from test_framework.messages import ( | from test_framework.messages import ( | ||||
COutPoint, | COutPoint, | ||||
CTransaction, | CTransaction, | ||||
CTxIn, | CTxIn, | ||||
CTxOut, | CTxOut, | ||||
ser_compact_size, | ser_compact_size, | ||||
ToHex, | ToHex, | ||||
) | ) | ||||
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, | |||||
OP_2DUP, | |||||
OP_CHECKSIG, | |||||
OP_CHECKSIGVERIFY, | |||||
OP_EQUAL, | |||||
OP_HASH160, | |||||
OP_RETURN, | OP_RETURN, | ||||
OP_TRUE, | OP_TRUE, | ||||
SIGHASH_ALL, | |||||
SIGHASH_FORKID, | |||||
SignatureHashForkId, | |||||
) | ) | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.util import assert_equal | from test_framework.util import assert_equal | ||||
class PreviousSpendableOutput(): | class PreviousSpendableOutput(): | ||||
def __init__(self, tx=CTransaction(), n=-1): | def __init__(self, tx=CTransaction(), n=-1): | ||||
Show All 23 Lines | def add_transactions_to_block(self, block, tx_list): | ||||
[tx.rehash() for tx in tx_list] | [tx.rehash() for tx in tx_list] | ||||
block.vtx.extend(tx_list) | block.vtx.extend(tx_list) | ||||
# this is a little handier to use than the version in blocktools.py | # this is a little handier to use than the version in blocktools.py | ||||
def create_tx(self, spend, value, script=CScript([OP_TRUE])): | def create_tx(self, spend, value, script=CScript([OP_TRUE])): | ||||
tx = create_tx_with_script(spend.tx, spend.n, b"", value, script) | tx = create_tx_with_script(spend.tx, spend.n, b"", value, script) | ||||
return tx | return tx | ||||
def next_block(self, number, spend=None, script=CScript([OP_TRUE]), block_size=0, extra_sigops=0): | def next_block(self, number, spend=None, script=CScript([OP_TRUE]), block_size=0): | ||||
if self.tip == None: | if self.tip == None: | ||||
base_block_hash = self.genesis_hash | base_block_hash = self.genesis_hash | ||||
block_time = int(time.time()) + 1 | block_time = int(time.time()) + 1 | ||||
else: | else: | ||||
base_block_hash = self.tip.sha256 | base_block_hash = self.tip.sha256 | ||||
block_time = self.tip.nTime + 1 | block_time = self.tip.nTime + 1 | ||||
# First create the coinbase | # First create the coinbase | ||||
height = self.block_heights[base_block_hash] + 1 | height = self.block_heights[base_block_hash] + 1 | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | def next_block(self, number, spend=None, script=CScript([OP_TRUE]), block_size=0): | ||||
script_length = block_size - current_block_size - base_tx_size | script_length = block_size - current_block_size - base_tx_size | ||||
if script_length > 510000: | if script_length > 510000: | ||||
if script_length < 1000000: | if script_length < 1000000: | ||||
# Make sure we don't find ourselves in a position where we | # Make sure we don't find ourselves in a position where we | ||||
# need to generate a transaction smaller than what we expected. | # need to generate a transaction smaller than what we expected. | ||||
script_length = script_length // 2 | script_length = script_length // 2 | ||||
else: | else: | ||||
script_length = 500000 | script_length = 500000 | ||||
tx_sigops = min(extra_sigops, script_length, | script_pad_len = script_length | ||||
MAX_TX_SIGOPS_COUNT) | script_output = CScript([b'\x00' * script_pad_len]) | ||||
extra_sigops -= tx_sigops | |||||
script_pad_len = script_length - tx_sigops | |||||
script_output = CScript( | |||||
[b'\x00' * script_pad_len] + [OP_CHECKSIG] * tx_sigops) | |||||
tx.vout.append(CTxOut(0, script_output)) | tx.vout.append(CTxOut(0, script_output)) | ||||
# Add the tx to the list of transactions to be included | # Add the tx to the list of transactions to be included | ||||
# in the block. | # in the block. | ||||
self.add_transactions_to_block(block, [tx]) | self.add_transactions_to_block(block, [tx]) | ||||
current_block_size += len(tx.serialize()) | current_block_size += len(tx.serialize()) | ||||
# Now that we added a bunch of transaction, we need to recompute | # Now that we added a bunch of transaction, we need to recompute | ||||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
# Reject oversized blocks with bad-blk-length error | # Reject oversized blocks with bad-blk-length error | ||||
block(18, spend=out[17], block_size=self.excessive_block_size + 1) | block(18, spend=out[17], block_size=self.excessive_block_size + 1) | ||||
node.p2p.send_blocks_and_test( | node.p2p.send_blocks_and_test( | ||||
[self.tip], node, success=False, reject_reason='bad-blk-length') | [self.tip], node, success=False, reject_reason='bad-blk-length') | ||||
# Rewind bad block. | # Rewind bad block. | ||||
tip(17) | tip(17) | ||||
# Accept many sigops | |||||
lots_of_checksigs = CScript( | |||||
[OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB) | |||||
block(19, spend=out[17], script=lots_of_checksigs, | |||||
block_size=ONE_MEGABYTE) | |||||
node.p2p.send_blocks_and_test([self.tip], node) | |||||
block(20, spend=out[18], script=lots_of_checksigs, | |||||
block_size=ONE_MEGABYTE, extra_sigops=1) | |||||
node.p2p.send_blocks_and_test( | |||||
[self.tip], node, success=False, reject_reason='bad-blk-sigops') | |||||
# Rewind bad block | |||||
tip(19) | |||||
# Accept 40k sigops per block > 1MB and <= 2MB | |||||
block(21, spend=out[18], script=lots_of_checksigs, | |||||
extra_sigops=MAX_BLOCK_SIGOPS_PER_MB, block_size=ONE_MEGABYTE + 1) | |||||
node.p2p.send_blocks_and_test([self.tip], node) | |||||
# Accept 40k sigops per block > 1MB and <= 2MB | |||||
block(22, spend=out[19], script=lots_of_checksigs, | |||||
extra_sigops=MAX_BLOCK_SIGOPS_PER_MB, block_size=2 * ONE_MEGABYTE) | |||||
node.p2p.send_blocks_and_test([self.tip], node) | |||||
# Reject more than 40k sigops per block > 1MB and <= 2MB. | |||||
block(23, spend=out[20], script=lots_of_checksigs, | |||||
extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=ONE_MEGABYTE + 1) | |||||
node.p2p.send_blocks_and_test( | |||||
[self.tip], node, success=False, reject_reason='bad-blk-sigops') | |||||
# Rewind bad block | |||||
tip(22) | |||||
# Reject more than 40k sigops per block > 1MB and <= 2MB. | |||||
block(24, spend=out[20], script=lots_of_checksigs, | |||||
extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=2 * ONE_MEGABYTE) | |||||
node.p2p.send_blocks_and_test( | |||||
[self.tip], node, success=False, reject_reason='bad-blk-sigops') | |||||
# Rewind bad block | |||||
tip(22) | |||||
# Accept 60k sigops per block > 2MB and <= 3MB | |||||
block(25, spend=out[20], script=lots_of_checksigs, extra_sigops=2 * | |||||
MAX_BLOCK_SIGOPS_PER_MB, block_size=2 * ONE_MEGABYTE + 1) | |||||
node.p2p.send_blocks_and_test([self.tip], node) | |||||
# Accept 60k sigops per block > 2MB and <= 3MB | |||||
block(26, spend=out[21], script=lots_of_checksigs, | |||||
extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB, block_size=3 * ONE_MEGABYTE) | |||||
node.p2p.send_blocks_and_test([self.tip], node) | |||||
# Reject more than 40k sigops per block > 1MB and <= 2MB. | |||||
block(27, spend=out[22], script=lots_of_checksigs, extra_sigops=2 * | |||||
MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=2 * ONE_MEGABYTE + 1) | |||||
node.p2p.send_blocks_and_test( | |||||
[self.tip], node, success=False, reject_reason='bad-blk-sigops') | |||||
# Rewind bad block | |||||
tip(26) | |||||
# Reject more than 40k sigops per block > 1MB and <= 2MB. | |||||
block(28, spend=out[22], script=lots_of_checksigs, extra_sigops=2 * | |||||
MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=3 * ONE_MEGABYTE) | |||||
node.p2p.send_blocks_and_test( | |||||
[self.tip], node, success=False, reject_reason='bad-blk-sigops') | |||||
# Rewind bad block | |||||
tip(26) | |||||
# Too many sigops in one txn | |||||
too_many_tx_checksigs = CScript( | |||||
[OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB + 1)) | |||||
block( | |||||
29, spend=out[22], script=too_many_tx_checksigs, block_size=ONE_MEGABYTE + 1) | |||||
node.p2p.send_blocks_and_test( | |||||
[self.tip], node, success=False, reject_reason='bad-txn-sigops') | |||||
# Rewind bad block | |||||
tip(26) | |||||
# Generate a key pair to test P2SH sigops count | |||||
private_key = CECKey() | |||||
private_key.set_secretbytes(b"fatstacks") | |||||
public_key = private_key.get_pubkey() | |||||
# P2SH | |||||
# Build the redeem script, hash it, use hash to create the p2sh script | |||||
redeem_script = CScript( | |||||
[public_key] + [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 p2sh transaction | |||||
p2sh_tx = self.create_tx(out[22], 1, p2sh_script) | |||||
# Add the transaction to the block | |||||
block(30) | |||||
update_block(30, [p2sh_tx]) | |||||
node.p2p.send_blocks_and_test([self.tip], node) | |||||
# Creates a new transaction using the p2sh transaction included in the | |||||
# last block | |||||
def spend_p2sh_tx(output_script=CScript([OP_TRUE])): | |||||
# Create the transaction | |||||
spent_p2sh_tx = CTransaction() | |||||
spent_p2sh_tx.vin.append(CTxIn(COutPoint(p2sh_tx.sha256, 0), b'')) | |||||
spent_p2sh_tx.vout.append(CTxOut(1, output_script)) | |||||
# Sign the transaction using the redeem script | |||||
sighash = SignatureHashForkId( | |||||
redeem_script, spent_p2sh_tx, 0, SIGHASH_ALL | SIGHASH_FORKID, p2sh_tx.vout[0].nValue) | |||||
sig = private_key.sign(sighash) + \ | |||||
bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) | |||||
spent_p2sh_tx.vin[0].scriptSig = CScript([sig, redeem_script]) | |||||
spent_p2sh_tx.rehash() | |||||
return spent_p2sh_tx | |||||
# Sigops p2sh limit | |||||
p2sh_sigops_limit = MAX_BLOCK_SIGOPS_PER_MB - \ | |||||
redeem_script.GetSigOpCount(True) | |||||
# Too many sigops in one p2sh txn | |||||
too_many_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit + 1)) | |||||
block(31, spend=out[23], block_size=ONE_MEGABYTE + 1) | |||||
update_block(31, [spend_p2sh_tx(too_many_p2sh_sigops)]) | |||||
node.p2p.send_blocks_and_test( | |||||
[self.tip], node, success=False, reject_reason='bad-txn-sigops') | |||||
# Rewind bad block | |||||
tip(30) | |||||
# Max sigops in one p2sh txn | |||||
max_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit)) | |||||
block(32, spend=out[23], block_size=ONE_MEGABYTE + 1) | |||||
update_block(32, [spend_p2sh_tx(max_p2sh_sigops)]) | |||||
node.p2p.send_blocks_and_test([self.tip], node) | |||||
# Submit a very large block via RPC | # Submit a very large block via RPC | ||||
large_block = block( | large_block = block( | ||||
33, spend=out[24], block_size=self.excessive_block_size) | 33, spend=out[17], block_size=self.excessive_block_size) | ||||
node.submitblock(ToHex(large_block)) | assert_equal(node.submitblock(ToHex(large_block)), None) | ||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
FullBlockTest().main() | FullBlockTest().main() |