invalid_txs.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2015-present The Bitcoin Core 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 CTxInWitness,
30 CTxOut,
31 MAX_MONEY,
32 SEQUENCE_FINAL,
33 )
34 from test_framework.blocktools import (
35 create_tx_with_script,
36 MAX_BLOCK_SIGOPS,
37 MAX_STANDARD_TX_SIGOPS,
38 )
39 from test_framework.script import (
40 OP_TRUE,
41 CScript,
42 OP_0,
43 OP_2DIV,
44 OP_2MUL,
45 OP_AND,
46 OP_CAT,
47 OP_CHECKSIG,
48 OP_DIV,
49 OP_INVERT,
50 OP_LEFT,
51 OP_LSHIFT,
52 OP_MOD,
53 OP_MUL,
54 OP_OR,
55 OP_RETURN,
56 OP_RIGHT,
57 OP_RSHIFT,
58 OP_SUBSTR,
59 OP_XOR,
60 )
61 from test_framework.util import assert_equal
62 from test_framework.script_util import (
63 MIN_PADDING,
64 MIN_STANDARD_TX_NONWITNESS_SIZE,
65 script_to_p2sh_script,
66 )
67 basic_p2sh = script_to_p2sh_script(CScript([OP_0]))
68
69 class BadTxTemplate:
70 """Allows simple construction of a certain kind of invalid tx. Base class to be subclassed."""
71 __metaclass__ = abc.ABCMeta
72
73 # The expected error code given by bitcoind upon submission of the tx.
74 reject_reason: Optional[str] = ""
75
76 # Only specified if it differs from mempool acceptance error.
77 block_reject_reason = ""
78
79 # Is this tx considered valid when included in a block, but not for acceptance into
80 # the mempool (i.e. does it violate policy but not consensus)?
81 valid_in_block = False
82
83 def __init__(self, *, spend_tx=None, spend_block=None):
84 self.spend_tx = spend_block.vtx[0] if spend_block else spend_tx
85 self.spend_avail = sum(o.nValue for o in self.spend_tx.vout)
86 self.valid_txin = CTxIn(COutPoint(self.spend_tx.txid_int, 0), b"", SEQUENCE_FINAL)
87
88 @abc.abstractmethod
89 def get_tx(self, *args, **kwargs):
90 """Return a CTransaction that is invalid per the subclass."""
91 pass
92
93
94 class OutputMissing(BadTxTemplate):
95 reject_reason = "bad-txns-vout-empty"
96
97 def get_tx(self):
98 tx = CTransaction()
99 tx.vin.append(self.valid_txin)
100 return tx
101
102
103 class InputMissing(BadTxTemplate):
104 reject_reason = "bad-txns-vin-empty"
105
106 # We use a blank transaction here to make sure
107 # it is interpreted as a non-witness transaction.
108 # Otherwise the transaction will fail the
109 # "surpufluous witness" check during deserialization
110 # rather than the input count check.
111 def get_tx(self):
112 tx = CTransaction()
113 return tx
114
115
116 # The following check prevents exploit of lack of merkle
117 # tree depth commitment (CVE-2017-12842)
118 class SizeTooSmall(BadTxTemplate):
119 reject_reason = "tx-size-small"
120 valid_in_block = True
121
122 def get_tx(self):
123 tx = CTransaction()
124 tx.vin.append(self.valid_txin)
125 tx.vout.append(CTxOut(0, CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 2)))))
126 assert_equal(len(tx.serialize_without_witness()), 64)
127 assert_equal(MIN_STANDARD_TX_NONWITNESS_SIZE - 1, 64)
128 return tx
129
130 # reject a transaction that contains a witness
131 # but doesn't spend a segwit output
132 class ExtraWitness(BadTxTemplate):
133 reject_reason = "tx-size-small"
134 block_reject_reason = "block-script-verify-flag-failed (Witness provided for non-witness script)"
135
136 def get_tx(self):
137 tx = CTransaction()
138 tx.vin.append(self.valid_txin)
139 tx.vout.append(CTxOut(0, CScript()))
140 tx.wit.vtxinwit = [CTxInWitness()]
141 tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
142 return tx
143
144
145 class BadInputOutpointIndex(BadTxTemplate):
146 # Won't be rejected - nonexistent outpoint index is treated as an orphan since the coins
147 # database can't distinguish between spent outpoints and outpoints which never existed.
148 reject_reason = None
149 # But fails in block
150 block_reject_reason = "bad-txns-inputs-missingorspent"
151
152 def get_tx(self):
153 num_indices = len(self.spend_tx.vin)
154 bad_idx = num_indices + 100
155
156 tx = CTransaction()
157 tx.vin.append(CTxIn(COutPoint(self.spend_tx.txid_int, bad_idx), b"", SEQUENCE_FINAL))
158 tx.vout.append(CTxOut(0, basic_p2sh))
159 return tx
160
161
162 class DuplicateInput(BadTxTemplate):
163 reject_reason = 'bad-txns-inputs-duplicate'
164
165 def get_tx(self):
166 tx = CTransaction()
167 tx.vin.append(self.valid_txin)
168 tx.vin.append(self.valid_txin)
169 tx.vout.append(CTxOut(1, basic_p2sh))
170 return tx
171
172
173 class PrevoutNullInput(BadTxTemplate):
174 reject_reason = 'bad-txns-prevout-null'
175
176 def get_tx(self):
177 tx = CTransaction()
178 tx.vin.append(self.valid_txin)
179 tx.vin.append(CTxIn(COutPoint(hash=0, n=0xffffffff)))
180 tx.vout.append(CTxOut(1, basic_p2sh))
181 return tx
182
183
184 class NonexistentInput(BadTxTemplate):
185 reject_reason = None # Added as an orphan tx.
186 # But fails in block
187 block_reject_reason = "bad-txns-inputs-missingorspent"
188
189 def get_tx(self):
190 tx = CTransaction()
191 tx.vin.append(CTxIn(COutPoint(self.spend_tx.txid_int + 1, 0), b"", SEQUENCE_FINAL))
192 tx.vin.append(self.valid_txin)
193 tx.vout.append(CTxOut(1, basic_p2sh))
194 return tx
195
196
197 class SpendTooMuch(BadTxTemplate):
198 reject_reason = 'bad-txns-in-belowout'
199
200 def get_tx(self):
201 return create_tx_with_script(
202 self.spend_tx, 0, output_script=basic_p2sh, amount=(self.spend_avail + 1))
203
204
205 class CreateNegative(BadTxTemplate):
206 reject_reason = 'bad-txns-vout-negative'
207
208 def get_tx(self):
209 return create_tx_with_script(self.spend_tx, 0, amount=-1)
210
211
212 class CreateTooLarge(BadTxTemplate):
213 reject_reason = 'bad-txns-vout-toolarge'
214
215 def get_tx(self):
216 return create_tx_with_script(self.spend_tx, 0, amount=MAX_MONEY + 1)
217
218
219 class CreateSumTooLarge(BadTxTemplate):
220 reject_reason = 'bad-txns-txouttotal-toolarge'
221
222 def get_tx(self):
223 tx = create_tx_with_script(self.spend_tx, 0, amount=MAX_MONEY)
224 tx.vout = [tx.vout[0]] * 2
225 return tx
226
227
228 class InvalidOPIFConstruction(BadTxTemplate):
229 reject_reason = "mempool-script-verify-flag-failed (Invalid OP_IF construction)"
230
231 def get_tx(self):
232 return create_tx_with_script(
233 self.spend_tx, 0, script_sig=b'\x68' * 35,
234 amount=(self.spend_avail // 2))
235
236
237 class TooManySigopsPerBlock(BadTxTemplate):
238 reject_reason = "bad-txns-too-many-sigops"
239 block_reject_reason = "bad-blk-sigops, out-of-bounds SigOpCount"
240
241 def get_tx(self):
242 lotsa_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS))
243 return create_tx_with_script(
244 self.spend_tx, 0,
245 output_script=lotsa_checksigs,
246 amount=1)
247
248
249 class TooManySigopsPerTransaction(BadTxTemplate):
250 reject_reason = "bad-txns-too-many-sigops"
251 valid_in_block = True
252
253 def get_tx(self):
254 lotsa_checksigs = CScript([OP_CHECKSIG] * (MAX_STANDARD_TX_SIGOPS + 1))
255 return create_tx_with_script(
256 self.spend_tx, 0,
257 output_script=lotsa_checksigs,
258 amount=1)
259
260
261 def getDisabledOpcodeTemplate(opcode):
262 """ Creates disabled opcode tx template class"""
263 def get_tx(self):
264 tx = CTransaction()
265 vin = self.valid_txin
266 vin.scriptSig = CScript([opcode])
267 tx.vin.append(vin)
268 tx.vout.append(CTxOut(1, basic_p2sh))
269 return tx
270
271 return type('DisabledOpcode_' + str(opcode), (BadTxTemplate,), {
272 'reject_reason': "disabled opcode",
273 'get_tx': get_tx,
274 'valid_in_block' : False
275 })
276
277 class NonStandardAndInvalid(BadTxTemplate):
278 """A non-standard transaction which is also consensus-invalid should return the first error."""
279 reject_reason = "mempool-script-verify-flag-failed (Using OP_CODESEPARATOR in non-witness script)"
280 block_reject_reason = "block-script-verify-flag-failed (OP_RETURN was encountered)"
281 valid_in_block = False
282
283 def get_tx(self):
284 return create_tx_with_script(
285 self.spend_tx, 0, script_sig=b'\x00' * 3 + b'\xab\x6a',
286 amount=(self.spend_avail // 2))
287
288 # Disabled opcode tx templates (CVE-2010-5137)
289 DisabledOpcodeTemplates = [getDisabledOpcodeTemplate(opcode) for opcode in [
290 OP_CAT,
291 OP_SUBSTR,
292 OP_LEFT,
293 OP_RIGHT,
294 OP_INVERT,
295 OP_AND,
296 OP_OR,
297 OP_XOR,
298 OP_2MUL,
299 OP_2DIV,
300 OP_MUL,
301 OP_DIV,
302 OP_MOD,
303 OP_LSHIFT,
304 OP_RSHIFT]]
305
306
307 def iter_all_templates():
308 """Iterate through all bad transaction template types."""
309 return BadTxTemplate.__subclasses__()
310