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