# Copyright (c) 2018, John L. Jegutanis # # All rights reserved. # # See the file "LICENCE" for information about the copyright # and warranty status of this software. import json import os from binascii import unhexlify import pytest from electrumx.lib.coins import Coin, Namecoin from electrumx.lib.hash import hash_to_hex_str from electrumx.lib.script import OpCodes, Script TRANSACTION_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'transactions') # Find testcases transactions_bitcoin = [] transactions_alts = [] for name in os.listdir(TRANSACTION_DIR): name_parts = name.split("_") tx_list = transactions_bitcoin if name_parts[0] == "bitcoin" else transactions_alts try: coinFound = Coin.lookup_coin_class(name_parts[0], name_parts[1]) with open(os.path.join(TRANSACTION_DIR, name)) as f: tx_list.append((coinFound, json.load(f))) except Exception as e: tx_list.append(pytest.fail(name)) @pytest.fixture(params=transactions_bitcoin) def transaction_details_bitcoin(request): return request.param def test_transaction_bitcoin(transaction_details_bitcoin): coin, tx_info = transaction_details_bitcoin raw_tx = unhexlify(tx_info['hex']) tx, vsize = coin.DESERIALIZER(raw_tx, 0).read_tx_and_vsize() tx_hash = tx.txid_rev assert tx_info['txid'] == hash_to_hex_str(tx_hash) assert tx_info['hash'] == hash_to_hex_str(tx.wtxid_rev) assert tx_info['version'] == tx.version assert tx_info['vsize'] == vsize vin = tx_info['vin'] for i in range(len(vin)): assert vin[i]['txid'] == hash_to_hex_str(tx.inputs[i].prev_txid_rev) assert vin[i]['vout'] == tx.inputs[i].prev_idx if "txinwitness" in vin[i] or (hasattr(tx, "witness") and tx.witness[i]): assert vin[i]["txinwitness"] == [x.hex() for x in tx.witness[i]] vout = tx_info['vout'] for i in range(len(vout)): # value pk_script assert vout[i]['value'] == tx.outputs[i].value / 10**8 spk = vout[i]['scriptPubKey'] tx_pks = tx.outputs[i].pk_script assert spk['hex'] == tx_pks.hex() ########## # Non-Bitcoin stuff goes below this line. @pytest.fixture(params=transactions_alts) def transaction_details_altcoin(request): return request.param def test_transaction_alts(transaction_details_altcoin): coin, tx_info = transaction_details_altcoin raw_tx = unhexlify(tx_info['hex']) tx = coin.DESERIALIZER(raw_tx, 0).read_tx() tx_hash = tx.txid_rev assert tx_info['txid'] == hash_to_hex_str(tx_hash) if expected_wtxid := tx_info.get('hash'): assert expected_wtxid == hash_to_hex_str(tx.wtxid_rev) vin = tx_info['vin'] for i in range(len(vin)): assert vin[i]['txid'] == hash_to_hex_str(tx.inputs[i].prev_txid_rev) assert vin[i]['vout'] == tx.inputs[i].prev_idx vout = tx_info['vout'] for i in range(len(vout)): # value pk_script assert vout[i]['value'] == tx.outputs[i].value spk = vout[i]['scriptPubKey'] tx_pks = tx.outputs[i].pk_script assert spk['hex'] == tx_pks.hex() if "addresses" in spk: assert len(spk["addresses"]) == 1 address = spk["addresses"][0] else: address = spk["address"] if spk.get("type", "").startswith("witness_"): continue # bech32 addresses are not supported by Coin.address_to_hashX assert coin.address_to_hashX(address) == coin.hashX_from_script(tx_pks) if issubclass(coin, Namecoin): if "nameOp" not in spk or "name" not in spk["nameOp"]: assert coin.name_hashX_from_script(tx_pks) is None else: OP_NAME_UPDATE = OpCodes.OP_3 normalized_name_op_script = bytearray() normalized_name_op_script.append(OP_NAME_UPDATE) normalized_name_op_script.extend(Script.push_data(spk["nameOp"]["name"].encode("ascii"))) normalized_name_op_script.extend(Script.push_data(bytes([]))) normalized_name_op_script.append(OpCodes.OP_2DROP) normalized_name_op_script.append(OpCodes.OP_DROP) normalized_name_op_script.append(OpCodes.OP_RETURN) assert coin.name_hashX_from_script(tx_pks) == Coin.hashX_from_script(normalized_name_op_script)