invalid_txs.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2015-2022 The Limenka developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """
6 Templates for constructing various sorts of invalid transactions.
7
8 These templates (or an iterator over all of them) can be reused in different
9 contexts to test using a number of invalid transaction types.
10
11 Hopefully this makes it easier to get coverage of a full variety of tx
12 validation checks through different interfaces (AcceptBlock, AcceptToMemPool,
13 etc.) without repeating ourselves.
14
15 Invalid tx cases not covered here can be found by running:
16
17 $ diff \
18 <(grep -IREho "bad-txns[a-zA-Z-]+" src | sort -u) \
19 <(grep -IEho "bad-txns[a-zA-Z-]+" test/functional/data/invalid_txs.py | sort -u)
20
21 """
22 import abc
23
24 from typing import Optional
25 from test_framework.messages import (
26 COutPoint,
27 CTransaction,
28 CTxIn,
29 CTxOut,
30 MAX_MONEY,
31 SEQUENCE_FINAL,
32 )
33 from test_framework.blocktools import create_tx_with_script, MAX_BLOCK_SIGOPS
34 from test_framework.script import (
35 CScript,
36 OP_0,
37 OP_2DIV,
38 OP_2MUL,
39 OP_AND,
40 OP_CAT,
41 OP_CHECKSIG,
42 OP_DIV,
43 OP_INVERT,
44 OP_LEFT,
45 OP_LSHIFT,
46 OP_MOD,
47 OP_MUL,
48 OP_OR,
49 OP_RETURN,
50 OP_RIGHT,
51 OP_RSHIFT,
52 OP_SUBSTR,
53 OP_XOR,
54 )
55 from test_framework.script_util import (
56 MIN_PADDING,
57 MIN_STANDARD_TX_NONWITNESS_SIZE,
58 script_to_p2sh_script,
59 )
60 basic_p2sh = script_to_p2sh_script(CScript([OP_0]))
61
62 class BadTxTemplate:
63 """Allows simple construction of a certain kind of invalid tx. Base class to be subclassed."""
64 __metaclass__ = abc.ABCMeta
65
66 # The expected error code given by limenkad upon submission of the tx.
67 reject_reason: Optional[str] = ""
68
69 # Only specified if it differs from mempool acceptance error.
70 block_reject_reason = ""
71
72 # Is this tx considered valid when included in a block, but not for acceptance into
73 # the mempool (i.e. does it violate policy but not consensus)?
74 valid_in_block = False
75
76 def __init__(self, *, spend_tx=None, spend_block=None):
77 self.spend_tx = spend_block.vtx[0] if spend_block else spend_tx
78 self.spend_avail = sum(o.nValue for o in self.spend_tx.vout)
79 self.valid_txin = CTxIn(COutPoint(self.spend_tx.sha256, 0), b"", SEQUENCE_FINAL)
80
81 @abc.abstractmethod
82 def get_tx(self, *args, **kwargs):
83 """Return a CTransaction that is invalid per the subclass."""
84 pass
85
86
87 class OutputMissing(BadTxTemplate):
88 reject_reason = "bad-txns-vout-empty"
89
90 def get_tx(self):
91 tx = CTransaction()
92 tx.vin.append(self.valid_txin)
93 tx.calc_sha256()
94 return tx
95
96
97 class InputMissing(BadTxTemplate):
98 reject_reason = "bad-txns-vin-empty"
99
100 # We use a blank transaction here to make sure
101 # it is interpreted as a non-witness transaction.
102 # Otherwise the transaction will fail the
103 # "surpufluous witness" check during deserialization
104 # rather than the input count check.
105 def get_tx(self):
106 tx = CTransaction()
107 tx.calc_sha256()
108 return tx
109
110
111 # The following check prevents exploit of lack of merkle
112 # tree depth commitment (CVE-2017-12842)
113 class SizeTooSmall(BadTxTemplate):
114 reject_reason = "tx-size-small"
115 valid_in_block = True
116
117 def get_tx(self):
118 tx = CTransaction()
119 tx.vin.append(self.valid_txin)
120 tx.vout.append(CTxOut(0, CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 2)))))
121 assert len(tx.serialize_without_witness()) == 64
122 assert MIN_STANDARD_TX_NONWITNESS_SIZE - 1 == 64
123 tx.calc_sha256()
124 return tx
125
126
127 class BadInputOutpointIndex(BadTxTemplate):
128 # Won't be rejected - nonexistent outpoint index is treated as an orphan since the coins
129 # database can't distinguish between spent outpoints and outpoints which never existed.
130 reject_reason = None
131
132 def get_tx(self):
133 num_indices = len(self.spend_tx.vin)
134 bad_idx = num_indices + 100
135
136 tx = CTransaction()
137 tx.vin.append(CTxIn(COutPoint(self.spend_tx.sha256, bad_idx), b"", SEQUENCE_FINAL))
138 tx.vout.append(CTxOut(0, basic_p2sh))
139 tx.calc_sha256()
140 return tx
141
142
143 class DuplicateInput(BadTxTemplate):
144 reject_reason = 'bad-txns-inputs-duplicate'
145
146 def get_tx(self):
147 tx = CTransaction()
148 tx.vin.append(self.valid_txin)
149 tx.vin.append(self.valid_txin)
150 tx.vout.append(CTxOut(1, basic_p2sh))
151 tx.calc_sha256()
152 return tx
153
154
155 class PrevoutNullInput(BadTxTemplate):
156 reject_reason = 'bad-txns-prevout-null'
157
158 def get_tx(self):
159 tx = CTransaction()
160 tx.vin.append(self.valid_txin)
161 tx.vin.append(CTxIn(COutPoint(hash=0, n=0xffffffff)))
162 tx.vout.append(CTxOut(1, basic_p2sh))
163 tx.calc_sha256()
164 return tx
165
166
167 class NonexistentInput(BadTxTemplate):
168 reject_reason = None # Added as an orphan tx.
169
170 def get_tx(self):
171 tx = CTransaction()
172 tx.vin.append(CTxIn(COutPoint(self.spend_tx.sha256 + 1, 0), b"", SEQUENCE_FINAL))
173 tx.vin.append(self.valid_txin)
174 tx.vout.append(CTxOut(1, basic_p2sh))
175 tx.calc_sha256()
176 return tx
177
178
179 class SpendTooMuch(BadTxTemplate):
180 reject_reason = 'bad-txns-in-belowout'
181
182 def get_tx(self):
183 return create_tx_with_script(
184 self.spend_tx, 0, output_script=basic_p2sh, amount=(self.spend_avail + 1))
185
186
187 class CreateNegative(BadTxTemplate):
188 reject_reason = 'bad-txns-vout-negative'
189
190 def get_tx(self):
191 return create_tx_with_script(self.spend_tx, 0, amount=-1)
192
193
194 class CreateTooLarge(BadTxTemplate):
195 reject_reason = 'bad-txns-vout-toolarge'
196
197 def get_tx(self):
198 return create_tx_with_script(self.spend_tx, 0, amount=MAX_MONEY + 1)
199
200
201 class CreateSumTooLarge(BadTxTemplate):
202 reject_reason = 'bad-txns-txouttotal-toolarge'
203
204 def get_tx(self):
205 tx = create_tx_with_script(self.spend_tx, 0, amount=MAX_MONEY)
206 tx.vout = [tx.vout[0]] * 2
207 tx.calc_sha256()
208 return tx
209
210
211 class InvalidOPIFConstruction(BadTxTemplate):
212 reject_reason = "mempool-script-verify-flag-failed (Invalid OP_IF construction)"
213 valid_in_block = True
214
215 def get_tx(self):
216 return create_tx_with_script(
217 self.spend_tx, 0, script_sig=b'\x64' * 35,
218 amount=(self.spend_avail // 2))
219
220
221 class TooManySigops(BadTxTemplate):
222 reject_reason = "bad-txns-too-many-sigops"
223 block_reject_reason = "bad-blk-sigops, out-of-bounds SigOpCount"
224
225 def get_tx(self):
226 # Put OP_CHECKSIGs in scriptSig (input) instead of scriptPubKey (output)
227 # to avoid violating MAX_OUTPUT_SCRIPT_SIZE=34 consensus limit.
228 # Sigops are counted from both input and output scripts.
229 lotsa_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS))
230 return create_tx_with_script(
231 self.spend_tx, 0,
232 script_sig=lotsa_checksigs,
233 output_script=basic_p2sh, # 23-byte P2SH, well under 34-byte limit
234 amount=1)
235
236 def getDisabledOpcodeTemplate(opcode):
237 """ Creates disabled opcode tx template class"""
238 def get_tx(self):
239 tx = CTransaction()
240 vin = self.valid_txin
241 vin.scriptSig = CScript([opcode])
242 tx.vin.append(vin)
243 tx.vout.append(CTxOut(1, basic_p2sh))
244 tx.calc_sha256()
245 return tx
246
247 return type('DisabledOpcode_' + str(opcode), (BadTxTemplate,), {
248 'reject_reason': "disabled opcode",
249 'get_tx': get_tx,
250 'valid_in_block' : True
251 })
252
253 class NonStandardAndInvalid(BadTxTemplate):
254 """A non-standard transaction which is also consensus-invalid should return the first error."""
255 reject_reason = "mempool-script-verify-flag-failed (Using OP_CODESEPARATOR in non-witness script)"
256 block_reject_reason = "mandatory-script-verify-flag-failed (OP_RETURN was encountered)"
257 valid_in_block = False
258
259 def get_tx(self):
260 return create_tx_with_script(
261 self.spend_tx, 0, script_sig=b'\x00' * 3 + b'\xab\x6a',
262 amount=(self.spend_avail // 2))
263
264 # Disabled opcode tx templates (CVE-2010-5137)
265 DisabledOpcodeTemplates = [getDisabledOpcodeTemplate(opcode) for opcode in [
266 OP_CAT,
267 OP_SUBSTR,
268 OP_LEFT,
269 OP_RIGHT,
270 OP_INVERT,
271 OP_AND,
272 OP_OR,
273 OP_XOR,
274 OP_2MUL,
275 OP_2DIV,
276 OP_MUL,
277 OP_DIV,
278 OP_MOD,
279 OP_LSHIFT,
280 OP_RSHIFT]]
281
282
283 def iter_all_templates():
284 """Iterate through all bad transaction template types."""
285 return BadTxTemplate.__subclasses__()
286