1import unittest 2import json 3import os 4 5from pycoin.encoding.hexbytes import h2b_rev 6from pycoin.symbols.btc import network 7 8 9flags = network.validator.flags 10ValidationFailureError = network.validator.ValidationFailureError 11 12DEBUG_TX_ID_LIST = [] 13 14 15TX_VALID_JSON = os.path.dirname(__file__) + '/data/tx_valid.json' 16TX_INVALID_JSON = os.path.dirname(__file__) + '/data/tx_invalid.json' 17 18 19def parse_flags(flag_string): 20 v = 0 21 if len(flag_string) > 0: 22 for f in flag_string.split(","): 23 v |= getattr(flags, "VERIFY_%s" % f) 24 return v 25 26 27def txs_from_json(path): 28 """ 29 Read tests from ./data/tx_??valid.json 30 Format is an array of arrays 31 Inner arrays are either [ "comment" ] 32 or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...], serializedTransaction, verifyFlags] 33 ... where all scripts are stringified scripts. 34 35 verifyFlags is a comma separated list of script verification flags to apply, or "NONE" 36 """ 37 comments = None 38 with open(path, 'r') as f: 39 for tvec in json.load(f): 40 if len(tvec) == 1: 41 comments = tvec[0] 42 continue 43 assert len(tvec) == 3 44 prevouts = tvec[0] 45 for prevout in prevouts: 46 assert len(prevout) in (3, 4) 47 48 tx_hex = tvec[1] 49 50 flag_mask = parse_flags(tvec[2]) 51 try: 52 tx = network.tx.from_hex(tx_hex) 53 except Exception: 54 print("Cannot parse tx_hex: %s" % tx_hex) 55 raise 56 57 spendable_db = {} 58 blank_spendable = network.tx.Spendable(0, b'', b'\0' * 32, 0) 59 for prevout in prevouts: 60 coin_value = 1000000 61 if len(prevout) == 4: 62 coin_value = prevout[3] 63 spendable = network.tx.Spendable( 64 coin_value=coin_value, script=network.script.compile(prevout[2]), 65 tx_hash=h2b_rev(prevout[0]), tx_out_index=prevout[1]) 66 spendable_db[(spendable.tx_hash, spendable.tx_out_index)] = spendable 67 unspents = [ 68 spendable_db.get((tx_in.previous_hash, tx_in.previous_index), blank_spendable) for tx_in in tx.txs_in] 69 tx.set_unspents(unspents) 70 yield (tx, flag_mask, comments) 71 72 73class TestTx(unittest.TestCase): 74 pass 75 76 77def make_f(tx, flag_mask, comments, expect_ok=True): 78 tx_hex = tx.as_hex(include_unspents=True) 79 80 def test_f(self): 81 why = None 82 try: 83 tx.check() 84 except ValidationFailureError as ex: 85 why = str(ex) 86 bs = 0 87 for tx_in_idx in range(len(tx.txs_in)): 88 try: 89 if DEBUG_TX_ID_LIST: 90 import pdb 91 pdb.set_trace() 92 tx.check_solution(tx_in_idx=tx_in_idx, flags=flag_mask) 93 except tx.SolutionChecker.ScriptError as se: 94 bs += 1 95 if bs > 0: 96 why = "bad sig count = %d" % bs 97 if (why is not None) == expect_ok: 98 why = why or "tx unexpectedly validated" 99 f = open("tx-%s-%x-%s.hex" % (tx.w_id(), flag_mask, "valid" if expect_ok else "invalid"), "w") 100 f.write(tx_hex) 101 f.close() 102 self.fail("fail on %s because of %s with hex %s: %s" % (tx.w_id(), why, tx_hex, comments)) 103 if DEBUG_TX_ID_LIST and tx.w_id() not in DEBUG_TX_ID_LIST: 104 return lambda self: 0 105 return test_f 106 107 108def inject(): 109 for idx, (tx, flag_mask, comments) in enumerate(txs_from_json(TX_VALID_JSON)): 110 name_of_f = "test_valid_%02d_%s" % (idx, tx.w_id()) 111 setattr(TestTx, name_of_f, make_f(tx, flag_mask, comments)) 112 print("adding %s" % name_of_f) 113 114 for idx, (tx, flag_mask, comments) in enumerate(txs_from_json(TX_INVALID_JSON)): 115 name_of_f = "test_invalid_%02d_%s" % (idx, tx.w_id()) 116 setattr(TestTx, name_of_f, make_f(tx, flag_mask, comments, expect_ok=False)) 117 print("adding %s" % name_of_f) 118 119 120inject() 121 122 123if __name__ == '__main__': 124 unittest.main() 125 126 127""" 128Test suite for pycoin library: check validity of txs in files 129tx_valid.json and tx_invalid.json. Adapted from Bitcoin Core 130transaction_tests.cpp test suite. 131 132The MIT License (MIT) 133 134Copyright (c) 2015 by Marek Miller 135Copyright (c) 2015 by Richard Kiss 136Copyright (c) 2015 by The Bitcoin Core Developers 137 138Permission is hereby granted, free of charge, to any person obtaining a copy 139of this software and associated documentation files (the "Software"), to deal 140in the Software without restriction, including without limitation the rights 141to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 142copies of the Software, and to permit persons to whom the Software is 143furnished to do so, subject to the following conditions: 144 145The above copyright notice and this permission notice shall be included in 146all copies or substantial portions of the Software. 147 148THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 149IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 150FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 151AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 152LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 153OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 154THE SOFTWARE. 155""" 156