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