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