diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 2dfa568c5b6..5a8d72bc9e1 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -30,6 +30,7 @@ from test_framework.p2p import P2PDataStore from test_framework.script import ( CScript, MAX_SCRIPT_ELEMENT_SIZE, + MAX_SCRIPT_SIZE, OP_2DUP, OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY, @@ -52,6 +53,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_greater_than, + assert_raises_rpc_error, ) from test_framework.wallet_util import generate_keypair from data import invalid_txs @@ -112,10 +114,20 @@ class FullBlockTest(BitcoinTestFramework): b_dup_cb = self.update_block('dup_cb', []) self.send_blocks([b_dup_cb]) - b0 = self.next_block(0) + # Add gigantic boundary scripts that respect all other limits + max_valid_script = CScript([b'\x01' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [b'\x01' * 62]) + assert_equal(len(max_valid_script), MAX_SCRIPT_SIZE) + min_invalid_script = CScript([b'\x01' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [b'\x01' * 63]) + assert_equal(len(min_invalid_script), MAX_SCRIPT_SIZE + 1) + + b0 = self.next_block(0, additional_output_scripts=[max_valid_script, min_invalid_script]) self.save_spendable_output() self.send_blocks([b0]) + # Will test spending once possibly-mature + max_size_spendable_output = CTxIn(COutPoint(b0.vtx[0].sha256, 1)) + min_size_unspendable_output = CTxIn(COutPoint(b0.vtx[0].sha256, 2)) + # These constants chosen specifically to trigger an immature coinbase spend # at a certain time below. NUM_BUFFER_BLOCKS_TO_GENERATE = 99 @@ -128,6 +140,19 @@ class FullBlockTest(BitcoinTestFramework): self.save_spendable_output() self.send_blocks(blocks) + # MAX_SCRIPT_SIZE testing now that coins are mature + tx = CTransaction() + tx.vin.append(max_size_spendable_output) + tx.vout.append(CTxOut(0, CScript([]))) + block = self.generateblock(self.nodes[0], output="raw(55)", transactions=[tx.serialize().hex()]) + assert_equal(block["hash"], self.nodes[0].getbestblockhash()) + self.nodes[0].invalidateblock(block["hash"]) + assert_equal(self.nodes[0].getrawmempool(), []) + + # MAX_SCRIPT_SIZE + 1 wasn't added to the utxo set + tx.vin[0] = min_size_unspendable_output + assert_raises_rpc_error(-25, f'TestBlockValidity failed: bad-txns-inputs-missingorspent, CheckTxInputs: inputs missing/spent in transaction {tx.rehash()}', self.generateblock, self.nodes[0], output="raw(55)", transactions=[tx.serialize().hex()]) + # collect spendable outputs now to avoid cluttering the code later on out = [] for _ in range(NUM_OUTPUTS_TO_COLLECT): @@ -1349,9 +1374,11 @@ class FullBlockTest(BitcoinTestFramework): tx.rehash() return tx - def next_block(self, number, spend=None, additional_coinbase_value=0, *, script=None, version=4): + def next_block(self, number, spend=None, additional_coinbase_value=0, *, script=None, version=4, additional_output_scripts=None): if script is None: script = CScript([OP_TRUE]) + if additional_output_scripts is None: + additional_output_scripts = [] if self.tip is None: base_block_hash = self.genesis_hash block_time = int(time.time()) + 1 @@ -1362,6 +1389,8 @@ class FullBlockTest(BitcoinTestFramework): height = self.block_heights[base_block_hash] + 1 coinbase = create_coinbase(height, self.coinbase_pubkey) coinbase.vout[0].nValue += additional_coinbase_value + for additional_script in additional_output_scripts: + coinbase.vout.append(CTxOut(0, additional_script)) coinbase.rehash() if spend is None: block = create_block(base_block_hash, coinbase, block_time, version=version) diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 12bfee7c776..b7783eb3a8c 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -23,6 +23,7 @@ from .messages import ( from .crypto.ripemd160 import ripemd160 MAX_SCRIPT_ELEMENT_SIZE = 520 +MAX_SCRIPT_SIZE = 10000 MAX_PUBKEYS_PER_MULTI_A = 999 LOCKTIME_THRESHOLD = 500000000 ANNEX_TAG = 0x50