Skip to content

Commit 96da68a

Browse files
committed
qa: functional test a transaction running into the legacy sigop limit
It's useful to have an end-to-end test in addition to the unit test to sanity check the RPC error as well as making sure the transaction is otherwise fully standard.
1 parent 3671479 commit 96da68a

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

test/functional/mempool_sigoplimit.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
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
67
from decimal import Decimal
78
from math import ceil
89

@@ -17,23 +18,30 @@
1718
)
1819
from 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
)
2832
from 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
)
3239
from test_framework.test_framework import BitcoinTestFramework
3340
from test_framework.util import (
3441
assert_equal,
3542
assert_greater_than,
3643
assert_greater_than_or_equal,
44+
assert_raises_rpc_error,
3745
)
3846
from test_framework.wallet import MiniWallet
3947
from 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

196241
if __name__ == '__main__':

test/functional/test_framework/script_util.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
sha256,
2323
)
2424

25+
# Maximum number of potentially executed legacy signature operations in validating a transaction.
26+
MAX_STD_LEGACY_SIGOPS = 2_500
27+
28+
# Maximum number of sigops per standard P2SH redeemScript.
29+
MAX_STD_P2SH_SIGOPS = 15
30+
2531
# To prevent a "tx-size-small" policy rule error, a transaction has to have a
2632
# non-witness size of at least 65 bytes (MIN_STANDARD_TX_NONWITNESS_SIZE in
2733
# src/policy/policy.h). Considering a Tx with the smallest possible single

0 commit comments

Comments
 (0)