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