/ 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.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__()