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 decimal import Decimal
67from math import ceil
78
89from test_framework .messages import (
2526 OP_TRUE ,
2627)
2728from test_framework .script_util import (
29+ keys_to_multisig_script ,
2830 script_to_p2wsh_script ,
2931)
3032from test_framework .test_framework import BitcoinTestFramework
3133from test_framework .util import (
3234 assert_equal ,
3335 assert_greater_than ,
3436 assert_greater_than_or_equal ,
37+ assert_raises_rpc_error ,
3538)
3639from test_framework .wallet import MiniWallet
37-
40+ from test_framework . wallet_util import generate_keypair
3841
3942DEFAULT_BYTES_PER_SIGOP = 20 # default setting
4043
@@ -133,6 +136,45 @@ def test_sigops_limit(self, bytes_per_sigop, num_sigops):
133136 assert_equal (entry_parent ['descendantcount' ], 2 )
134137 assert_equal (entry_parent ['descendantsize' ], parent_tx .get_vsize () + sigop_equivalent_vsize )
135138
139+ def test_sigops_package (self ):
140+ self .log .info ("Test a overly-large sigops-vbyte hits package limits" )
141+ # Make a 2-transaction package which fails vbyte checks even though
142+ # separately they would work.
143+ self .restart_node (0 , extra_args = ["-bytespersigop=5000" ] + self .extra_args [0 ])
144+
145+ def create_bare_multisig_tx (utxo_to_spend = None ):
146+ _ , pubkey = generate_keypair ()
147+ amount_for_bare = 50000
148+ tx_dict = self .wallet .create_self_transfer (fee = Decimal ("3" ), utxo_to_spend = utxo_to_spend )
149+ tx_utxo = tx_dict ["new_utxo" ]
150+ tx = tx_dict ["tx" ]
151+ tx .vout .append (CTxOut (amount_for_bare , keys_to_multisig_script ([pubkey ], k = 1 )))
152+ tx .vout [0 ].nValue -= amount_for_bare
153+ tx_utxo ["txid" ] = tx .rehash ()
154+ tx_utxo ["value" ] -= Decimal ("0.00005000" )
155+ return (tx_utxo , tx )
156+
157+ tx_parent_utxo , tx_parent = create_bare_multisig_tx ()
158+ tx_child_utxo , tx_child = create_bare_multisig_tx (tx_parent_utxo )
159+
160+ # Separately, the parent tx is ok
161+ parent_individual_testres = self .nodes [0 ].testmempoolaccept ([tx_parent .serialize ().hex ()])[0 ]
162+ assert parent_individual_testres ["allowed" ]
163+ # Multisig is counted as MAX_PUBKEYS_PER_MULTISIG = 20 sigops
164+ assert_equal (parent_individual_testres ["vsize" ], 5000 * 20 )
165+
166+ # But together, it's exceeding limits in the *package* context. If sigops adjusted vsize wasn't being checked
167+ # here, it would get further in validation and give too-long-mempool-chain error instead.
168+ packet_test = self .nodes [0 ].testmempoolaccept ([tx_parent .serialize ().hex (), tx_child .serialize ().hex ()])
169+ assert_equal ([x ["package-error" ] for x in packet_test ], ["package-mempool-limits" , "package-mempool-limits" ])
170+
171+ # When we actually try to submit, the parent makes it into the mempool, but the child would exceed ancestor vsize limits
172+ assert_raises_rpc_error (- 26 , "too-long-mempool-chain" , self .nodes [0 ].submitpackage , [tx_parent .serialize ().hex (), tx_child .serialize ().hex ()])
173+ assert tx_parent .rehash () in self .nodes [0 ].getrawmempool ()
174+
175+ # Transactions are tiny in weight
176+ assert_greater_than (2000 , tx_parent .get_weight () + tx_child .get_weight ())
177+
136178 def run_test (self ):
137179 self .wallet = MiniWallet (self .nodes [0 ])
138180
@@ -149,6 +191,8 @@ def run_test(self):
149191
150192 self .generate (self .wallet , 1 )
151193
194+ self .test_sigops_package ()
195+
152196
153197if __name__ == '__main__' :
154198 BytesPerSigOpTest ().main ()
0 commit comments