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