33# Distributed under the MIT software license, see the accompanying
44# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55"""Test sigop limit mempool policy (`-bytespersigop` parameter)"""
6+ from copy import deepcopy
67from decimal import Decimal
78from math import ceil
89
1718)
1819from test_framework .script import (
1920 CScript ,
21+ OP_2DUP ,
2022 OP_CHECKMULTISIG ,
2123 OP_CHECKSIG ,
24+ OP_DROP ,
2225 OP_ENDIF ,
2326 OP_FALSE ,
2427 OP_IF ,
28+ OP_NOT ,
2529 OP_RETURN ,
2630 OP_TRUE ,
2731)
2832from test_framework .script_util import (
2933 keys_to_multisig_script ,
3034 script_to_p2wsh_script ,
35+ script_to_p2sh_script ,
36+ MAX_STD_LEGACY_SIGOPS ,
37+ MAX_STD_P2SH_SIGOPS ,
3138)
3239from test_framework .test_framework import BitcoinTestFramework
3340from test_framework .util import (
3441 assert_equal ,
3542 assert_greater_than ,
3643 assert_greater_than_or_equal ,
44+ assert_raises_rpc_error ,
3745)
3846from test_framework .wallet import MiniWallet
3947from test_framework .wallet_util import generate_keypair
@@ -174,6 +182,42 @@ def create_bare_multisig_tx(utxo_to_spend=None):
174182 # Transactions are tiny in weight
175183 assert_greater_than (2000 , tx_parent .get_weight () + tx_child .get_weight ())
176184
185+ def test_legacy_sigops_stdness (self ):
186+ self .log .info ("Test a transaction with too many legacy sigops in its inputs is non-standard." )
187+
188+ # Restart with the default settings
189+ self .restart_node (0 )
190+
191+ # Create a P2SH script with 15 sigops.
192+ _ , dummy_pubkey = generate_keypair ()
193+ packed_redeem_script = [dummy_pubkey ]
194+ for _ in range (MAX_STD_P2SH_SIGOPS - 1 ):
195+ packed_redeem_script += [OP_2DUP , OP_CHECKSIG , OP_DROP ]
196+ packed_redeem_script = CScript (packed_redeem_script + [OP_CHECKSIG , OP_NOT ])
197+ packed_p2sh_script = script_to_p2sh_script (packed_redeem_script )
198+
199+ # Create enough outputs to reach the sigops limit when spending them all at once.
200+ outpoints = []
201+ for _ in range (int (MAX_STD_LEGACY_SIGOPS / MAX_STD_P2SH_SIGOPS ) + 1 ):
202+ res = self .wallet .send_to (from_node = self .nodes [0 ], scriptPubKey = packed_p2sh_script , amount = 1_000 )
203+ txid = int .from_bytes (bytes .fromhex (res ["txid" ]), byteorder = "big" )
204+ outpoints .append (COutPoint (txid , res ["sent_vout" ]))
205+ self .generate (self .nodes [0 ], 1 )
206+
207+ # Spending all these outputs at once accounts for 2505 legacy sigops and is non-standard.
208+ nonstd_tx = CTransaction ()
209+ nonstd_tx .vin = [CTxIn (op , CScript ([b"" , packed_redeem_script ])) for op in outpoints ]
210+ nonstd_tx .vout = [CTxOut (0 , CScript ([OP_RETURN , b"" ]))]
211+ assert_raises_rpc_error (- 26 , "bad-txns-nonstandard-inputs" , self .nodes [0 ].sendrawtransaction , nonstd_tx .serialize ().hex ())
212+
213+ # Spending one less accounts for 2490 legacy sigops and is standard.
214+ std_tx = deepcopy (nonstd_tx )
215+ std_tx .vin .pop ()
216+ self .nodes [0 ].sendrawtransaction (std_tx .serialize ().hex ())
217+
218+ # Make sure the original, non-standard, transaction can be mined.
219+ self .generateblock (self .nodes [0 ], output = "raw(42)" , transactions = [nonstd_tx .serialize ().hex ()])
220+
177221 def run_test (self ):
178222 self .wallet = MiniWallet (self .nodes [0 ])
179223
@@ -191,6 +235,7 @@ def run_test(self):
191235 self .generate (self .wallet , 1 )
192236
193237 self .test_sigops_package ()
238+ self .test_legacy_sigops_stdness ()
194239
195240
196241if __name__ == '__main__' :
0 commit comments