diff --git a/pybtc/__init__.py b/pybtc/__init__.py index 370c8a6..571d829 100644 --- a/pybtc/__init__.py +++ b/pybtc/__init__.py @@ -1,10 +1,8 @@ -from .tools import * +from .functions import * from .opcodes import * from .consensus import * from .transaction import * from .block import * from .address import * from .hdwallet import * -from .hash import * - version = "2.0.1" diff --git a/pybtc/address.py b/pybtc/address.py index 2eccdfe..1487342 100644 --- a/pybtc/address.py +++ b/pybtc/address.py @@ -1,4 +1,4 @@ -from .tools import * +from .functions import * class PrivateKey(): @@ -24,14 +24,14 @@ class PrivateKey(): #: private key in bytes (bytes) self.key = create_private_key(wif=False) #: private key in HEX (string) - self.hex = hexlify(self.key).decode() + self.hex = self.key.hex() #: private key in WIF format (string) self.wif = private_key_to_wif(self.key, compressed, testnet) else: if isinstance(key, str): try: - key = unhexlify(key) + key = bytes.fromhex(key) except: pass if isinstance(key, bytes): @@ -40,14 +40,14 @@ class PrivateKey(): self.key = key self.compressed = compressed self.testnet = testnet - self.hex = hexlify(self.key).decode() + self.hex = self.key.hex() self.wif = private_key_to_wif(self.key, compressed, testnet) return if not isinstance(key, str) or not is_wif_valid(key): raise TypeError("private key invalid") self.key = wif_to_private_key(key, hex=False) - self.hex = hexlify(self.key).decode() + self.hex = self.key.hex() if key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): self.compressed = False @@ -90,7 +90,7 @@ class PublicKey(): def __init__(self, key, compressed=True, testnet=False): if isinstance(key, str): try: - key = unhexlify(key) + key = bytes.fromhex(key) except: if is_wif_valid(key): key = PrivateKey(key) @@ -116,7 +116,7 @@ class PublicKey(): #: public key in bytes (bytes) self.key = public_key #: public key in HEX (string) - self.hex = hexlify(self.key).decode() + self.hex = self.key.hex() def __str__(self): return self.hex @@ -183,7 +183,7 @@ class Address(): #: redeeem script, only for P2SH_P2WPKH (bytes) self.redeem_script = public_key_to_p2sh_p2wpkh_script(self.public_key.key) #: redeeem script HEX, only for P2SH_P2WPKH (string) - self.redeem_script_hex = hexlify(self.redeem_script).decode() + self.redeem_script_hex = self.redeem_script.hex() #: address hash self.hash = hash160(self.redeem_script) self.witness_version = None @@ -191,7 +191,7 @@ class Address(): self.script_hash = False self.hash = hash160(self.public_key.key) #: address hash HEX (string) - self.hash_hex = hexlify(self.hash).decode() + self.hash_hex = self.hash.hex() #: address in base58 or bech32 encoding (string) self.address = hash_to_address(self.hash, script_hash=self.script_hash, @@ -208,9 +208,9 @@ class ScriptAddress(): self.witness_version = witness_version self.testnet = testnet if isinstance(script, str): - script = unhexlify(script) + script = bytes.fromhex(script) self.script = script - self.script_hex = hexlify(self.script).decode() + self.script_hex = self.script.hex() if witness_version is None: self.hash = hash160(self.script) else: @@ -248,7 +248,7 @@ class ScriptAddress(): for a in list(public_key_list): if isinstance(a, str): try: - a = unhexlify(a) + a = bytes.fromhex(a) except: if is_wif_valid(a): a = private_to_public_key(a, hex=False) diff --git a/pybtc/block.py b/pybtc/block.py index a810dd0..007a1d7 100644 --- a/pybtc/block.py +++ b/pybtc/block.py @@ -1,7 +1,6 @@ -from .tools import * from .transaction import Transaction from struct import pack, unpack - +from .functions import * class Block(dict): def __init__(self, raw_block=None, format="decoded", version=536870912, testnet=False): @@ -12,7 +11,7 @@ class Block(dict): self["header"] = None self["hash"] = None self["version"] = version - self["versionHex"] = hexlify(struct.pack(">L", version)).decode() + self["versionHex"] = struct.pack(">L", version).hex() self["previousBlockHash"] = None self["merkleRoot"] = None self["tx"] = dict() @@ -33,7 +32,7 @@ class Block(dict): s = self.get_stream(raw_block) self["format"] = "raw" self["version"] = unpack("L", self["version"])).decode() + self["versionHex"] = struct.pack(">L", self["version"]).hex() self["previousBlockHash"] = s.read(32) self["merkleRoot"] = s.read(32) self["time"] = unpack(" 1: + tx_hash_list = new_hash_list + else: + return new_hash_list[0] if not hex else hexlify(new_hash_list[0]).decode() + + +def merkle_branches(tx_hash_list, hex=True): + """ + Calculate merkle branches for coinbase transacton + + :param tx_hash_list: list of transaction hashes in bytes or HEX encoded string. + :param hex: (optional) If set to True return result in HEX format, by default is True. + :return: list of merkle branches in bytes or HEX encoded string corresponding hex flag. + """ + tx_hash_list = [h if isinstance(h, bytes) else s2rh(h) for h in tx_hash_list] + branches = [] + if len(tx_hash_list) == 1: + return [] + tx_hash_list.pop(0) + while True: + branches.append(tx_hash_list.pop(0)) + new_hash_list = list() + while tx_hash_list: + h1 = tx_hash_list.pop(0) + try: + h2 = tx_hash_list.pop(0) + except: + h2 = h1 + new_hash_list.append(double_sha256(h1 + h2)) + if len(new_hash_list) > 1: + tx_hash_list = new_hash_list + else: + if new_hash_list: + branches.append(new_hash_list.pop(0)) + return branches if not hex else [hexlify(h).decode() for h in branches] + + +def merkleroot_from_branches(merkle_branches, coinbase_hash, hex=True): + """ + Calculate merkle root from merkle branches and coinbase transacton hash + + :param merkle_branches: list merkle branches in bytes or HEX encoded string. + :param coinbase_hash: list coinbase transaction hash in bytes or HEX encoded string. + :param hex: (optional) If set to True return result in HEX format, by default is True. + :return: merkle root in bytes or HEX encoded string corresponding hex flag. + """ + merkle_root = coinbase_hash if not isinstance(coinbase_hash, str) else unhexlify(coinbase_hash) + for h in merkle_branches: + if type(h) == str: + h = unhexlify(h) + merkle_root = double_sha256(merkle_root + h) + return merkle_root if not hex else hexlify(merkle_root).decode() + + +# Difficulty + + +def bits_to_target(bits): + """ + Calculate target from bits + + :param bits: HEX string, bytes string or integer representation of bits. + :return: integer. + """ + if type(bits) == str: + bits = unhexlify(bits) + if type(bits) == bytes: + return int.from_bytes(bits[1:], 'big') * (2 ** (8 * (bits[0] - 3))) + else: + shift = bits >> 24 + target = (bits & 0xffffff) * (1 << (8 * (shift - 3))) + return target + + +def target_to_difficulty(target): + """ + Calculate difficulty from target + + :param target: integer. + :return: float. + """ + return 0x00000000FFFF0000000000000000000000000000000000000000000000000000 / target + + +def bits_to_difficulty(bits): + """ + Calculate difficulty from bits + + :param bits: HEX string, bytes string or integer representation of bits. + :return: integer. + """ + return target_to_difficulty(bits_to_target(bits)) + + +def difficulty_to_target(difficulty): + """ + Calculate target from difficulty + + :param target: integer. + :return: float. + """ + return int(0x00000000FFFF0000000000000000000000000000000000000000000000000000 / difficulty) + + diff --git a/pybtc/encode.py b/pybtc/functions/encode.py similarity index 93% rename from pybtc/encode.py rename to pybtc/functions/encode.py index 65e9e7f..06cac6e 100644 --- a/pybtc/encode.py +++ b/pybtc/functions/encode.py @@ -1,5 +1,3 @@ -from binascii import hexlify, unhexlify - b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' base32charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" base32charset_upcase = "QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L" @@ -23,7 +21,7 @@ def rebasebits(data, frombits, tobits, pad=True): max_acc = (1 << (frombits + tobits - 1)) - 1 for value in data: if value < 0 or (value >> frombits): - raise Exception("invalid bytes") + raise ValueError("invalid bytes") acc = ((acc << frombits) | value) & max_acc bits += frombits while bits >= tobits: @@ -33,7 +31,7 @@ def rebasebits(data, frombits, tobits, pad=True): if bits: ret.append((acc << (tobits - bits)) & maxv) elif bits >= frombits or ((acc << (tobits - bits)) & maxv): - raise Exception("invalid padding") + raise ValueError("invalid padding") return ret @@ -79,7 +77,9 @@ def bech32_polymod(values): def encode_base58(b): """Encode bytes to a base58-encoded string""" # Convert big-endian bytes to integer - n = int('0x0' + hexlify(b).decode('utf8'), 16) + + n= int('0x0' + b.hex(), 16) + # Divide that integer into bas58 res = [] while n > 0: @@ -113,7 +113,7 @@ def decode_base58(s): h = '%x' % n if len(h) % 2: h = '0' + h - res = unhexlify(h.encode('utf8')) + res = bytes.fromhex(h) # Add padding back. pad = 0 for c in s[:-1]: diff --git a/pybtc/hash.py b/pybtc/functions/hash.py similarity index 67% rename from pybtc/hash.py rename to pybtc/functions/hash.py index 6f45fc9..399347b 100644 --- a/pybtc/hash.py +++ b/pybtc/functions/hash.py @@ -1,42 +1,43 @@ import hashlib import hmac -from binascii import unhexlify -def sha256(h, hex = False): + +def sha256(h, hex=False): if type(h) == str: - h = unhexlify(h) + h = bytes.fromhex(h) if hex: return hashlib.sha256(h).hexdigest() return hashlib.sha256(h).digest() -def double_sha256(h, hex = False): + +def double_sha256(h, hex=False): if type(h) == str: - h = unhexlify(h) + h = bytes.fromhex(h) if hex: return sha256(sha256(h), 1) return sha256(sha256(h)) -def hmac_sha512(key, data, hex = False): + +def hmac_sha512(key, data, hex=False): if hex: return hmac.new(key, data, hashlib.sha512).hexdigest() return hmac.new(key, data, hashlib.sha512).digest() -def ripemd160(h, hex = False): +def ripemd160(h, hex=False): if type(h) == str: - h = unhexlify(h) + h = bytes.fromhex(h) a = hashlib.new('ripemd160') a.update(h) if hex: return a.hexdigest() return a.digest() -def hash160(h, hex = False): + +def hash160(h, hex=False): if type(h) == str: - h = unhexlify(h) + bytes.fromhex(h) if hex: return ripemd160(sha256(h), 1) return ripemd160(sha256(h)) - -# \ No newline at end of file diff --git a/pybtc/functions/key.py b/pybtc/functions/key.py new file mode 100644 index 0000000..f4086aa --- /dev/null +++ b/pybtc/functions/key.py @@ -0,0 +1,168 @@ +import os +import sys +import time +import random +from secp256k1 import ffi +parentPath = os.path.abspath("../..") +if parentPath not in sys.path: + sys.path.insert(0, parentPath) + +from pybtc.constants import * +from .hash import * +from .encode import * +from .hash import * + + +def create_private_key(compressed=True, testnet=False, wif=True, hex=False): + """ + Create private key + + :param compressed: (optional) Type of public key, by default set to compressed. + Using uncompressed public keys is deprecated in new SEGWIT addresses, + use this option only for backward compatibility. + :param testnet: (optional) flag for testnet network, by default is False. + :param wif: (optional) If set to True return key in WIF format, by default is True. + :param hex: (optional) If set to True return key in HEX format, by default is False. + :return: Private key in wif format (default), hex encoded byte string in case of hex flag or + raw bytes string in case wif and hex flags set to False. + + """ + a = random.SystemRandom().randint(0, MAX_INT_PRIVATE_KEY) + i = int((time.time() % 0.01 ) * 100000) + h = a.to_bytes(32, byteorder="big") + # more entropy from system timer and sha256 derivation + while i: + h = hashlib.sha256(h).digest() + i -= 1 + if not i and int.from_bytes(h, byteorder="big") > MAX_INT_PRIVATE_KEY: + i += 1 + if wif: + return private_key_to_wif(h, compressed=compressed, testnet=testnet) + elif hex: + return h.hex() + return h + + +def private_key_to_wif(h, compressed=True, testnet=False): + """ + Encode private key in HEX or RAW bytes format to WIF format. + + :param h: private key 32 byte string or HEX encoded string. + :param compressed: (optional) flag of public key compressed format, by default set to True. + :param testnet: (optional) flag for testnet network, by default is False. + :return: Private key in WIF format. + """ + # uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of previous 33 bytes], base58 encoded. + # compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash() previous 34 bytes], base58 encoded. + if isinstance(h, str): + h = bytes.fromhex(h) + if len(h) != 32 and isinstance(h, bytes): + raise TypeError("private key must be a 32 bytes or hex encoded string") + if testnet: + h = TESTNET_PRIVATE_KEY_BYTE_PREFIX + h + else: + h = MAINNET_PRIVATE_KEY_BYTE_PREFIX + h + if compressed: + h += b'\x01' + h += double_sha256(h)[:4] + return encode_base58(h) + + +def wif_to_private_key(h, hex=True): + """ + Decode WIF private key to bytes string or HEX encoded string + + :param hex: (optional) if set to True return key in HEX format, by default is True. + :return: Private key HEX encoded string or raw bytes string. + """ + if not is_wif_valid(h): + raise TypeError("invalid wif key") + h = decode_base58(h) + if hex: + return h[1:33].hex() + return h[1:33] + + +def is_wif_valid(wif): + """ + Check is private key in WIF format string is valid. + + :param wif: private key in WIF format string. + :return: boolean. + """ + if not isinstance(wif, str): + raise TypeError("invalid wif key") + if wif[0] not in PRIVATE_KEY_PREFIX_LIST: + return False + try: + h = decode_base58(wif) + except: + return False + checksum = h[-4:] + if wif[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, + TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): + if len(h) != 37: + return False + elif len(h) != 38: + return False + if double_sha256(h[:-4])[:4] != checksum: + return False + return True + + +def private_to_public_key(private_key, compressed=True, hex=True): + """ + Get public key from private key using ECDSA secp256k1 + + :param private_key: private key in WIF, HEX or bytes. + :param compressed: (optional) flag of public key compressed format, by default set to True. + In case private_key in WIF format, this flag is set in accordance with + the key format specified in WIF string. + :param hex: (optional) if set to True return key in HEX format, by default is True. + :return: 33/65 bytes public key in HEX or bytes string. + """ + if not isinstance(private_key, bytes): + if isinstance(private_key, bytearray): + private_key = bytes(private_key) + elif isinstance(private_key, str): + if not is_wif_valid(private_key): + private_key = bytes.fromhex(private_key) + else: + if private_key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, + TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): + compressed = False + private_key = wif_to_private_key(private_key, hex=0) + else: + raise TypeError("private key must be a bytes or WIF or hex encoded string") + pubkey_ptr = ffi.new('secp256k1_pubkey *') + r = secp256k1.secp256k1_ec_pubkey_create(ECDSA_CONTEXT_ALL, pubkey_ptr, private_key) + if not r: + raise RuntimeError("secp256k1 error") + len_key = 33 if compressed else 65 + pubkey = ffi.new('char [%d]' % len_key) + outlen = ffi.new('size_t *', len_key) + compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED + r = secp256k1.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag) + pub = bytes(ffi.buffer(pubkey, len_key)) + if not r: + raise RuntimeError("secp256k1 error") + return pub.hex() if hex else pub + + +def is_public_key_valid(key): + """ + Check public key is valid. + + :param key: public key in HEX or bytes string format. + :return: boolean. + """ + if isinstance(key, str): + key = bytes.fromhex(key) + if len(key) < 33: + return False + elif key[0] == 0x04 and len(key) != 65: + return False + elif key[0] == 0x02 or key[0] == 0x03: + if len(key) != 33: + return False + return True diff --git a/pybtc/functions/mnemonic.py b/pybtc/functions/mnemonic.py new file mode 100644 index 0000000..e69de29 diff --git a/pybtc/functions/script.py b/pybtc/functions/script.py new file mode 100644 index 0000000..40f84ac --- /dev/null +++ b/pybtc/functions/script.py @@ -0,0 +1,518 @@ +import os +import sys +import time +import random +import struct +from secp256k1 import ffi +parentPath = os.path.abspath("../..") +if parentPath not in sys.path: + sys.path.insert(0, parentPath) + +from pybtc.opcodes import * +from pybtc.constants import * +from .hash import * +from .encode import * +from .tools import * +from .hash import * + + +def public_key_to_pubkey_script(key, hex=True): + if isinstance(key, str): + key = bytes.from_hex(key) + s = b"%s%s%s" % (bytes([len(key)]), key, OP_CHECKSIG) + return s.hex() if hex else s + + +def parse_script(script, segwit=True): + """ + Parse script and return script type, script address and required signatures count. + + :param script: script in bytes string or HEX encoded string format. + :param segwit: (optional) If set to True recognize P2WPKH and P2WSH sripts, by default set to True. + + :return: dictionary: + + - nType - numeric script type + - type - script type + - addressHash - address hash in case address recognized + - script - script if no address recognized + - reqSigs - required signatures count + """ + if not script: + return {"nType": 7, "type": "NON_STANDARD", "reqSigs": 0, "script": b""} + if isinstance(script, str): + try: + script = bytes.fromhex(script) + except: + pass + assert isinstance(script, bytes) + l = len(script) + if segwit: + if l == 22 and script[0] == 0: + return {"nType": 5, "type": "P2WPKH", "reqSigs": 1, "addressHash": script[2:]} + if l == 34 and script[0] == 0: + return {"nType": 6, "type": "P2WSH", "reqSigs": None, "addressHash": script[2:]} + if l == 25 and \ + script[:2] == b"\x76\xa9" and \ + script[-2:] == b"\x88\xac": + return {"nType": 0, "type": "P2PKH", "reqSigs": 1, "addressHash": script[3:-2]} + if l == 23 and \ + script[0] == 169 and \ + script[-1] == 135: + return {"nType": 1, "type": "P2SH", "reqSigs": None, "addressHash": script[2:-1]} + if l == 67 and script[-1] == 172: + return {"nType": 2, "type": "PUBKEY", "reqSigs": 1, "addressHash": hash160(script[1:-1])} + if l == 35 and script[-1] == 172: + return {"nType": 2, "type": "PUBKEY", "reqSigs": 1, "addressHash": hash160(script[1:-1])} + if script[0] == 106 and l > 1 and l <= 82: + if script[1] == l - 2: + return {"nType": 3, "type": "NULL_DATA", "reqSigs": 0, "data": script[2:]} + if script[0] >= 81 and script[0] <= 96: + if script[-1] == 174: + if script[-2] >= 81 and script[-2] <= 96: + if script[-2] >= script[0]: + c, s = 0, 1 + while l - 2 - s > 0: + if script[s] < 0x4c: + s += script[s] + c += 1 + else: + c = 0 + break + s += 1 + if c == script[-2] - 80: + return {"nType": 4, "type": "MULTISIG", "reqSigs": script[0] - 80, "script": script} + + s, m, n, last, req_sigs = 0, 0, 0, 0, 0 + while l - s > 0: + if script[s] >= 81 and script[s] <= 96: + if not n: + n = script[s] - 80 + else: + if m == 0: + n, m = script[s] - 80, 0 + elif n > m: + n, m = script[s] - 80, 0 + elif m == script[s] - 80: + last = 0 if last else 2 + elif script[s] < 0x4c: + s += script[s] + m += 1 + if m > 16: + n, m = 0, 0 + elif script[s] == OPCODE["OP_PUSHDATA1"]: + try: + s += 1 + script[s + 1] + except: + break + elif script[s] == OPCODE["OP_PUSHDATA2"]: + try: + s += 2 + struct.unpack(' 0: + if script[s] < 0x4c and script[s]: + if asm: + append(script[s + 1:s + 1 + script[s]].hex()) + else: + append('[%s]' % script[s]) + s += script[s] + 1 + continue + + if script[s] == OPCODE["OP_PUSHDATA1"]: + ld = script[s + 1] + if asm: + append(script[s + 1:s + 1 + ld].hex()) + else: + append(RAW_OPCODE[script[s]]) + append('[%s]' % ld) + s += 1 + script[s + 1] + 1 + elif script[s] == OPCODE["OP_PUSHDATA2"]: + + ld = struct.unpack(' 0: + if script[s] < 0x4c and script[s]: + stack.append(script[s] + 1) + s += script[s] + 1 + elif script[s] == OPCODE["OP_PUSHDATA1"]: + stack.append(1 + script[s + 1]) + s += 1 + script[s + 1] + elif script[s] == OPCODE["OP_PUSHDATA2"]: + stack.append(2 + struct.unpack('= ls: + if script[k:s][:ls] == sub_script: + if s - k > ls: + result.append(script[k + ls:s]) + t = 0 + while t != s - k: + t += stack.pop(0) + k = s + else: + t = stack.pop(0) + result.append(script[k:k + t]) + k += t + if script[k:s][:ls] == sub_script: + if s - k > ls: + result.append(script[k + ls:s]) + else: + result.append(script[k:k + ls]) + + return b''.join(result) if not s_hex else b''.join(result).hex() + + +def script_to_hash(script, witness=False, hex=True): + """ + Encode script to hash HASH160 or SHA256 in dependency of the witness. + + :param script: script in bytes or HEX encoded string. + :param witness: (optional) If set to True return SHA256 hash for P2WSH, by default is False. + :param hex: (optional) If set to True return key in HEX format, by default is True. + :param sub_script: sub_script which is necessary to remove from target script in bytes or HEX encoded string. + :return: script in bytes or HEX encoded string corresponding to the format of target script. + """ + if isinstance(script, str): + s = bytes.fromhex(script) + if witness: + return sha256(script, hex) + else: + return hash160(script, hex) + + +def op_push_data(data): + if len(data) <= 0x4b: + return b''.join([bytes([len(data)]), data]) + elif len(data) <= 0xff: + return b''.join([OP_PUSHDATA1, bytes([len(data)]), data]) + elif len(data) <= 0xffff: + return b''.join([OP_PUSHDATA2, int_to_bytes(len(data), byteorder="little"), data]) + + else: + return b''.join([OP_PUSHDATA4, int_to_bytes(len(data), byteorder="little"), data]) + + +def get_multisig_public_keys(script): + pub_keys = [] + s = get_stream(script) + o, d = read_opcode(s) + while o: + o, d = read_opcode(s) + if d: + pub_keys.append(d) + return pub_keys + + +def read_opcode(stream): + b = stream.read(1) + if not b: + return None, None + if b[0] <= 0x4b: + return b, stream.read(b[0]) + elif b[0] == OP_PUSHDATA1: + return b, stream.read(stream.read(1)[0]) + elif b[0] == OP_PUSHDATA2: + return b, stream.read(struct.unpack(" 73): + return False + # A signature is of type 0x30 (compound). + if sig[0] != 0x30: + return False + # Make sure the length covers the entire signature. + if sig[1] != (length - 3): + return False + # Extract the length of the R element. + len_r = sig[3] + # Make sure the length of the S element is still inside the signature. + if (5 + len_r) >= length: + return False + # Extract the length of the S element. + len_s = sig[5 + len_r] + # Verify that the length of the signature matches the sum of the length + # of the elements. + if (len_r + len_s + 7) != length: + return False + # Check whether the R element is an integer. + if sig[2] != 0x02: + return False + # Zero-length integers are not allowed for R. + if len_r == 0: + return False + # Negative numbers are not allowed for R. + if sig[4] & 0x80: + return False + # Null bytes at the start of R are not allowed, unless R would + # otherwise be interpreted as a negative number. + if (len_r > 1) and (sig[4] == 0x00) and (not sig[5] & 0x80): + return False + # Check whether the S element is an integer. + if sig[len_r + 4] != 0x02: + return False + # Zero-length integers are not allowed for S. + if len_s == 0: + return False + # Negative numbers are not allowed for S. + if sig[len_r + 6] & 0x80: + return False + # Null bytes at the start of S are not allowed, unless S would otherwise be + # interpreted as a negative number. + if (len_s > 1) and (sig[len_r + 6] == 0x00) and (not sig[len_r + 7] & 0x80): + return False + return True + diff --git a/pybtc/functions/tools.py b/pybtc/functions/tools.py new file mode 100644 index 0000000..b68aa88 --- /dev/null +++ b/pybtc/functions/tools.py @@ -0,0 +1,347 @@ +import math +import io +import struct + + +def rh2s(raw_hash): + """ + Encode raw transaction hash to HEX string with bytes order change + + :param raw_hash: transaction hash in bytes string. + :return: HEX encoded string. + """ + return raw_hash[::-1].hex() + + +def s2rh(hash_string): + """ + Decode HEX transaction hash to bytes with byte order change + + :param raw_hash: transaction hash in bytes string. + :return: bytes string. + """ + return bytes.fromhex(hash_string)[::-1] + + +def s2rh_step4(hash_string): + h = bytes.fromhex(hash_string) + return reverse_hash(h) + + +def reverse_hash(raw_hash): + """ + Reverse hash order + + :param raw_hash: bytes string. + :return: bytes string. + """ + return struct.pack('>IIIIIIII', *struct.unpack('>IIIIIIII', raw_hash)[::-1])[::-1] + + +def bytes_needed(n): + """ + Calculate bytes needed to convert integer to bytes. + + :param n: integer. + :return: integer. + """ + if n == 0: + return 1 + return math.ceil(n.bit_length()/8) + + +def int_to_bytes(i, byteorder='big'): + """ + Convert integer to bytes. + + :param n: integer. + :param byteorder: (optional) byte order 'big' or 'little', by default 'big'. + :return: bytes. + """ + return i.to_bytes(bytes_needed(i), byteorder=byteorder, signed=False) + + +def bytes_to_int(i, byteorder='big'): + """ + Convert bytes to integer. + + :param i: bytes. + :param byteorder: (optional) byte order 'big' or 'little', by default 'big'. + :return: integer. + """ + return int.from_bytes(i, byteorder=byteorder, signed=False) + + +# variable integer + +def int_to_var_int(i): + """ + Convert integer to variable integer + + :param i: integer. + :return: bytes. + """ + if i < 0xfd: + return struct.pack(' 0: + s.append((v >> ((i - 1) * 8)) & 0xff) + i -= 1 + return s + + +def bin2bn(s): + l = 0 + for ch in s: + l = (l << 8) | ch + return l + + +def bn2mpi(v): + have_ext = False + if v.bit_length() > 0: + have_ext = (v.bit_length() & 0x07) == 0 + neg = False + if v < 0: + neg = True + v = -v + s = struct.pack(b">I", bn_bytes(v, have_ext)) + ext = bytearray() + if have_ext: + ext.append(0) + v_bin = bn2bin(v) + if neg: + if have_ext: + ext[0] |= 0x80 + else: + v_bin[0] |= 0x80 + return s + ext + v_bin + + +def mpi2bn(s): + if len(s) < 4: + return None + s_size = bytes(s[:4]) + v_len = struct.unpack(b">I", s_size)[0] + if len(s) != (v_len + 4): + return None + if v_len == 0: + return 0 + v_str = bytearray(s[4:]) + neg = False + i = v_str[0] + if i & 0x80: + neg = True + i &= ~0x80 + v_str[0] = i + v = bin2bn(v_str) + + if neg: + return -v + return v + +# bitcoin-specific little endian format, with implicit size + + +def mpi2vch(s): + r = s[4:] # strip size + # if r: + r = r[::-1] # reverse string, converting BE->LE + # else: r=b'\x00' + return r + + +def bn2vch(v): + return bytes(mpi2vch(bn2mpi(v))) + + +def vch2mpi(s): + r = struct.pack(b">I", len(s)) # size + r += s[::-1] # reverse string, converting LE->BE + return r + + +def vch2bn(s): + return mpi2bn(vch2mpi(s)) + + +def i2b(i): return bn2vch(i) + + +def b2i(b): return vch2bn(b) + + +def get_stream(stream): + if type(stream) != io.BytesIO: + if type(stream) == str: + stream = bytes.fromhex(stream) + if type(stream) == bytes: + stream = io.BytesIO(stream) + else: + raise TypeError + return stream + diff --git a/pybtc/hdwallet.py b/pybtc/hdwallet.py index 69c48df..a181fe4 100644 --- a/pybtc/hdwallet.py +++ b/pybtc/hdwallet.py @@ -6,8 +6,9 @@ from struct import pack, unpack from hashlib import pbkdf2_hmac from binascii import hexlify, unhexlify from .constants import * -from .tools import private_to_public_key, is_public_key_valid, encode_base58, decode_base58, private_key_to_wif -from .hash import hmac_sha512, hash160, double_sha256, sha256, double_sha256 +from .functions import * + + diff --git a/pybtc/opcodes.py b/pybtc/opcodes.py index b10c147..5a9d9b6 100644 --- a/pybtc/opcodes.py +++ b/pybtc/opcodes.py @@ -1,5 +1,3 @@ -from binascii import hexlify - OPCODE = dict() # push opcodes @@ -152,7 +150,7 @@ OPCODE["OP_INVALIDOPCODE"] = 0xff RAW_OPCODE = dict((OPCODE[i], i) for i in OPCODE) BYTE_OPCODE = dict((i, bytes([OPCODE[i]])) for i in OPCODE) -HEX_OPCODE = dict((i, hexlify(bytes([OPCODE[i]])).decode()) for i in OPCODE) +HEX_OPCODE = dict((i, bytes([OPCODE[i]]).hex()) for i in OPCODE) OP_FALSE = BYTE_OPCODE["OP_FALSE"] OP_0 = BYTE_OPCODE["OP_0"] diff --git a/pybtc/tools.py b/pybtc/tools.py deleted file mode 100644 index 914b291..0000000 --- a/pybtc/tools.py +++ /dev/null @@ -1,1413 +0,0 @@ -import time -import struct -from secp256k1 import ffi -from .constants import * -from .opcodes import * -from .hash import * -from .encode import * -import math -import io - - -# Key management - -def create_private_key(compressed=True, testnet=False, wif=True, hex=False): - """ - Create private key - - :param compressed: (optional) Type of public key, by default set to compressed. - Using uncompressed public keys is deprecated in new SEGWIT addresses, - use this option only for backward compatibility. - :param testnet: (optional) flag for testnet network, by default is False. - :param wif: (optional) If set to True return key in WIF format, by default is True. - :param hex: (optional) If set to True return key in HEX format, by default is False. - :return: Private key in wif format (default), hex encoded byte string in case of hex flag or - raw bytes string in case wif and hex flags set to False. - - """ - a = random.SystemRandom().randint(0, MAX_INT_PRIVATE_KEY) - i = int((time.time() % 0.01)*100000) - h = a.to_bytes(32, byteorder="big") - # more entropy from system timer and sha256 derivation - while i: - h = hashlib.sha256(h).digest() - i -= 1 - if not i and int.from_bytes(h, byteorder="big") > MAX_INT_PRIVATE_KEY: - i += 1 - if wif: - return private_key_to_wif(h, compressed=compressed, testnet=testnet) - elif hex: - return hexlify(h).decode() - return h - - -def private_key_to_wif(h, compressed=True, testnet=False): - """ - Encode private key in HEX or RAW bytes format to WIF format. - - :param h: private key 32 byte string or HEX encoded string. - :param compressed: (optional) flag of public key compressed format, by default set to True. - :param testnet: (optional) flag for testnet network, by default is False. - :return: Private key in WIF format. - """ - # uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of previous 33 bytes], base58 encoded. - # compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash() previous 34 bytes], base58 encoded. - if isinstance(h, str): - h = unhexlify(h) - if len(h) != 32 and isinstance(h, bytes): - raise TypeError("private key must be a 32 bytes or hex encoded string") - if testnet: - h = TESTNET_PRIVATE_KEY_BYTE_PREFIX + h - else: - h = MAINNET_PRIVATE_KEY_BYTE_PREFIX + h - if compressed: - h += b'\x01' - h += double_sha256(h)[:4] - return encode_base58(h) - - -def wif_to_private_key(h, hex=True): - """ - Decode WIF private key to bytes string or HEX encoded string - - :param hex: (optional) if set to True return key in HEX format, by default is True. - :return: Private key HEX encoded string or raw bytes string. - """ - if not is_wif_valid(h): - raise TypeError("invalid wif key") - h = decode_base58(h) - if hex: - return hexlify(h[1:33]).decode() - return h[1:33] - - -def is_wif_valid(wif): - """ - Check is private key in WIF format string is valid. - - :param wif: private key in WIF format string. - :return: boolean. - """ - if not isinstance(wif, str): - raise TypeError("invalid wif key") - if wif[0] not in PRIVATE_KEY_PREFIX_LIST: - return False - try: - h = decode_base58(wif) - except: - return False - checksum = h[-4:] - if wif[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, - TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): - if len(h) != 37: - return False - elif len(h) != 38: - return False - if double_sha256(h[:-4])[:4] != checksum: - return False - return True - - -def private_to_public_key(private_key, compressed=True, hex=True): - """ - Get public key from private key using ECDSA secp256k1 - - :param private_key: private key in WIF, HEX or bytes. - :param compressed: (optional) flag of public key compressed format, by default set to True. - In case private_key in WIF format, this flag is set in accordance with - the key format specified in WIF string. - :param hex: (optional) if set to True return key in HEX format, by default is True. - :return: 33/65 bytes public key in HEX or bytes string. - """ - if not isinstance(private_key, bytes): - if isinstance(private_key, bytearray): - private_key = bytes(private_key) - elif isinstance(private_key, str): - if not is_wif_valid(private_key): - private_key = unhexlify(private_key) - else: - if private_key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, - TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): - compressed = False - private_key = wif_to_private_key(private_key, hex=0) - else: - raise TypeError("private key must be a bytes or WIF or hex encoded string") - pubkey_ptr = ffi.new('secp256k1_pubkey *') - r = secp256k1.secp256k1_ec_pubkey_create(ECDSA_CONTEXT_ALL, pubkey_ptr, private_key) - if not r: - raise RuntimeError("secp256k1 error") - len_key = 33 if compressed else 65 - pubkey = ffi.new('char [%d]' % len_key) - outlen = ffi.new('size_t *', len_key) - compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED - r = secp256k1.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag) - pub = bytes(ffi.buffer(pubkey, len_key)) - if not r: - raise RuntimeError("secp256k1 error") - return hexlify(pub).decode() if hex else pub - - -def is_public_key_valid(key): - """ - Check public key is valid. - - :param key: public key in HEX or bytes string format. - :return: boolean. - """ - if isinstance(key, str): - key = unhexlify(key) - if len(key) < 33: - return False - elif key[0] == 0x04 and len(key) != 65: - return False - elif key[0] == 0x02 or key[0] == 0x03: - if len(key) != 33: - return False - return True - - -# Addresses - -def hash_to_address(address_hash, testnet=False, script_hash=False, witness_version=0): - """ - Get address from public key/script hash. In case PUBKEY, P2PKH, P2PKH public key/script hash is SHA256+RIPEMD160, - P2WSH script hash is SHA256. - - - :param address_hash: public key hash or script hash in HEX or bytes string format. - :param testnet: (optional) flag for testnet network, by default is False. - :param script_hash: (optional) flag for script hash (P2SH address), by default is False. - :param witness_version: (optional) witness program version, by default is 0, for legacy - address format use None. - :return: address in base58 or bech32 format. - """ - if isinstance(address_hash, str): - address_hash = unhexlify(address_hash) - if not isinstance(address_hash, bytes): - raise TypeError("address hash must be HEX encoded string or bytes") - - if not script_hash: - if witness_version is None: - if len(address_hash) != 20: - raise TypeError("address hash length incorrect") - if testnet: - prefix = TESTNET_ADDRESS_BYTE_PREFIX - else: - prefix = MAINNET_ADDRESS_BYTE_PREFIX - address_hash = prefix + address_hash - address_hash += double_sha256(address_hash)[:4] - return encode_base58(address_hash) - else: - if len(address_hash) not in (20, 32): - raise TypeError("address hash length incorrect") - - if witness_version is None: - if testnet: - prefix = TESTNET_SCRIPT_ADDRESS_BYTE_PREFIX - else: - prefix = MAINNET_SCRIPT_ADDRESS_BYTE_PREFIX - address_hash = prefix + address_hash - address_hash += double_sha256(address_hash)[:4] - return encode_base58(address_hash) - - if testnet: - prefix = TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX - hrp = TESTNET_SEGWIT_ADDRESS_PREFIX - else: - prefix = MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX - hrp = MAINNET_SEGWIT_ADDRESS_PREFIX - - address_hash = witness_version.to_bytes(1, "big") + rebase_8_to_5(address_hash) - checksum = bech32_polymod(prefix + address_hash + b"\x00" * 6) - checksum = rebase_8_to_5(checksum.to_bytes(5, "big"))[2:] - return "%s1%s" % (hrp, rebase_5_to_32(address_hash + checksum).decode()) - - -def public_key_to_address(pubkey, testnet=False, p2sh_p2wpkh=False, witness_version=0): - """ - Get address from public key/script hash. In case PUBKEY, P2PKH, P2PKH public key/script hash is SHA256+RIPEMD160, - P2WSH script hash is SHA256. - - :param pubkey: public key HEX or bytes string format. - :param testnet: (optional) flag for testnet network, by default is False. - :param p2sh_p2wpkh: (optional) flag for P2WPKH inside P2SH address, by default is False. - :param witness_version: (optional) witness program version, by default is 0, for legacy - address format use None. - :return: address in base58 or bech32 format. - """ - if isinstance(pubkey, str): - pubkey = unhexlify(pubkey) - if not isinstance(pubkey, bytes): - raise TypeError("public key invalid") - if p2sh_p2wpkh: - if len(pubkey) != 33: - raise TypeError("public key invalid") - h = hash160(b'\x00\x14' + hash160(pubkey)) - witness_version = None - else: - if witness_version is not None: - if len(pubkey) != 33: - raise TypeError("public key invalid") - h = hash160(pubkey) - return hash_to_address(h, testnet=testnet, - script_hash=p2sh_p2wpkh, - witness_version=witness_version) - - -def address_to_hash(address, hex=True): - """ - Get address hash from base58 or bech32 address format. - - :param address: address in base58 or bech32 format. - :param hex: (optional) If set to True return key in HEX format, by default is True. - :return: script in HEX or bytes string. - """ - if address[0] in ADDRESS_PREFIX_LIST: - h = decode_base58(address)[1:-4] - elif address[:2] in (MAINNET_SEGWIT_ADDRESS_PREFIX, - TESTNET_SEGWIT_ADDRESS_PREFIX): - address = address.split("1")[1] - h = rebase_5_to_8(rebase_32_to_5(address)[1:-6], False) - else: - return None - return h.hex() if hex else h - - -def address_type(address, num=False): - """ - Get address type. - - :param address: address in base58 or bech32 format. - :param num: (optional) If set to True return type in numeric format, by default is False. - :return: address type in string or numeric format. - """ - if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX, - MAINNET_SCRIPT_ADDRESS_PREFIX): - t = 'P2SH' - elif address[0] in (MAINNET_ADDRESS_PREFIX, - TESTNET_ADDRESS_PREFIX, - TESTNET_ADDRESS_PREFIX_2): - t = 'P2PKH' - elif address[:2] in (MAINNET_SEGWIT_ADDRESS_PREFIX, - TESTNET_SEGWIT_ADDRESS_PREFIX): - if len(address) == 42: - t = 'P2WPKH' - elif len(address) == 62: - t = 'P2WSH' - else: - return SCRIPT_TYPES['NON_STANDARD'] if num else 'UNKNOWN' - else: - return SCRIPT_TYPES['NON_STANDARD'] if num else 'UNKNOWN' - return SCRIPT_TYPES[t] if num else t - - -def address_net_type(address): - """ - Get address network type. - - :param address: address in base58 or bech32 format. - :return: address network type in string format or None. - """ - if address[0] in (MAINNET_SCRIPT_ADDRESS_PREFIX, - MAINNET_ADDRESS_PREFIX): - return "mainnet" - elif address[:2] == MAINNET_SEGWIT_ADDRESS_PREFIX: - return "mainnet" - elif address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX, - TESTNET_ADDRESS_PREFIX, - TESTNET_ADDRESS_PREFIX_2): - return "testnet" - elif address[:2] == TESTNET_SEGWIT_ADDRESS_PREFIX: - return "testnet" - return None - - -def address_to_script(address, hex=False): - """ - Get public key script from address. - - :param address: address in base58 or bech32 format. - :param hex: (optional) If set to True return key in HEX format, by default is True. - :return: public key script in HEX or bytes string. - """ - if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX, - MAINNET_SCRIPT_ADDRESS_PREFIX): - s = [BYTE_OPCODE["OP_HASH160"], - b'\x14', - address_to_hash(address, hex=False), - BYTE_OPCODE["OP_EQUAL"]] - elif address[0] in (MAINNET_ADDRESS_PREFIX, - TESTNET_ADDRESS_PREFIX, - TESTNET_ADDRESS_PREFIX_2): - s = [BYTE_OPCODE["OP_DUP"], - BYTE_OPCODE["OP_HASH160"], - b'\x14', - address_to_hash(address, hex=False), - BYTE_OPCODE["OP_EQUALVERIFY"], - BYTE_OPCODE["OP_CHECKSIG"]] - elif address[:2] in (TESTNET_SEGWIT_ADDRESS_PREFIX, - MAINNET_SEGWIT_ADDRESS_PREFIX): - h = address_to_hash(address, hex=False) - s = [BYTE_OPCODE["OP_0"], - bytes([len(h)]), - h] - else: - raise TypeError("address invalid") - s = b''.join(s) - return hexlify(s).decode() if hex else s - - -def public_key_to_p2sh_p2wpkh_script(pubkey): - if len(pubkey) != 33: - raise TypeError("public key len invalid") - return b'\x00\x14%s' % hash160(pubkey) - - -def is_address_valid(address, testnet=False): - """ - Check is address valid. - - :param address: address in base58 or bech32 format. - :param testnet: (optional) flag for testnet network, by default is False. - :return: boolean. - """ - if not address or type(address) != str: - return False - if address[0] in (MAINNET_ADDRESS_PREFIX, - MAINNET_SCRIPT_ADDRESS_PREFIX, - TESTNET_ADDRESS_PREFIX, - TESTNET_ADDRESS_PREFIX_2, - TESTNET_SCRIPT_ADDRESS_PREFIX): - if testnet: - if address[0] not in (TESTNET_ADDRESS_PREFIX, - TESTNET_ADDRESS_PREFIX_2, - TESTNET_SCRIPT_ADDRESS_PREFIX): - return False - else: - if address[0] not in (MAINNET_ADDRESS_PREFIX, - MAINNET_SCRIPT_ADDRESS_PREFIX): - return False - h = decode_base58(address) - if len(h) != 25: - return False - checksum = h[-4:] - if double_sha256(h[:-4])[:4] != checksum: - return False - return True - elif address[:2].lower() in (TESTNET_SEGWIT_ADDRESS_PREFIX, - MAINNET_SEGWIT_ADDRESS_PREFIX): - if len(address) not in (42, 62): - return False - try: - prefix, payload = address.split('1') - except: - return False - upp = True if prefix[0].isupper() else False - for i in payload[1:]: - if upp: - if not i.isupper() or i not in base32charset_upcase: - return False - else: - if i.isupper() or i not in base32charset: - return False - payload = payload.lower() - prefix = prefix.lower() - if testnet: - if prefix != TESTNET_SEGWIT_ADDRESS_PREFIX: - return False - stripped_prefix = TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX - else: - if prefix != MAINNET_SEGWIT_ADDRESS_PREFIX: - return False - stripped_prefix = MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX - d = rebase_32_to_5(payload) - address_hash = d[:-6] - checksum = d[-6:] - checksum2 = bech32_polymod(stripped_prefix + address_hash + b"\x00" * 6) - checksum2 = rebase_8_to_5(checksum2.to_bytes(5, "big"))[2:] - if checksum != checksum2: - return False - return True - - -def get_witness_version(address): - address = address.split("1")[1] - h = rebase_32_to_5(address) - return h[0] - - -# Script - -def public_key_to_pubkey_script(key, hex=True): - if isinstance(key, str): - key = unhexlify(key) - s = bytes([len(key)]) + key + OP_CHECKSIG - return hexlify(s).decode() if hex else s - - -def parse_script(script, segwit=True): - """ - Parse script and return script type, script address and required signatures count. - - :param script: script in bytes string or HEX encoded string format. - :param segwit: (optional) If set to True recognize P2WPKH and P2WSH sripts, by default set to True. - - :return: dictionary: - - - nType - numeric script type - - type - script type - - addressHash - address hash in case address recognized - - script - script if no address recognized - - reqSigs - required signatures count - """ - if not script: - return {"nType": 7, "type": "NON_STANDARD", "reqSigs": 0, "script": b""} - if type(script) == str: - try: - script = unhexlify(script) - except: - pass - assert type(script) == bytes - l = len(script) - if segwit: - if l == 22 and script[0] == 0: - return {"nType": 5, "type": "P2WPKH", "reqSigs": 1, "addressHash": script[2:]} - if l == 34 and script[0] == 0: - return {"nType": 6, "type": "P2WSH", "reqSigs": None, "addressHash": script[2:]} - if l == 25 and \ - script[:2] == b"\x76\xa9" and \ - script[-2:] == b"\x88\xac": - return {"nType": 0, "type": "P2PKH", "reqSigs": 1, "addressHash": script[3:-2]} - if l == 23 and \ - script[0] == 169 and \ - script[-1] == 135: - return {"nType": 1, "type": "P2SH", "reqSigs": None, "addressHash": script[2:-1]} - if l == 67 and script[-1] == 172: - return {"nType": 2, "type": "PUBKEY", "reqSigs": 1, "addressHash": hash160(script[1:-1])} - if l == 35 and script[-1] == 172: - return {"nType": 2, "type": "PUBKEY", "reqSigs": 1, "addressHash": hash160(script[1:-1])} - if script[0] == 106 and l > 1 and l <= 82: - if script[1] == l - 2: - return {"nType": 3, "type": "NULL_DATA", "reqSigs": 0, "data": script[2:]} - if script[0] >= 81 and script[0] <= 96: - if script[-1] == 174: - if script[-2] >= 81 and script[-2] <= 96: - if script[-2] >= script[0]: - c, s = 0, 1 - while l - 2 - s > 0: - if script[s] < 0x4c: - s += script[s] - c += 1 - else: - c = 0 - break - s += 1 - if c == script[-2] - 80: - return {"nType": 4, "type": "MULTISIG", "reqSigs": script[0] - 80, "script": script} - - s, m, n, last, req_sigs = 0, 0, 0, 0, 0 - while l - s > 0: - if script[s] >= 81 and script[s] <= 96: - if not n: - n = script[s] - 80 - else: - if m == 0: - n, m = script[s] - 80, 0 - elif n > m: - n, m = script[s] - 80, 0 - elif m == script[s] - 80: - last = 0 if last else 2 - elif script[s] < 0x4c: - s += script[s] - m += 1 - if m > 16: - n, m = 0, 0 - elif script[s] == OPCODE["OP_PUSHDATA1"]: - try: - s += 1 + script[s + 1] - except: - break - elif script[s] == OPCODE["OP_PUSHDATA2"]: - try: - s += 2 + struct.unpack(' 0: - if script[s] < 0x4c and script[s]: - if asm: - result.append(hexlify(script[s + 1:s + 1 + script[s]]).decode()) - else: - result.append('[%s]' % script[s]) - s += script[s] + 1 - continue - - if script[s] == OPCODE["OP_PUSHDATA1"]: - ld = script[s + 1] - if asm: - result.append(hexlify(script[s + 1:s + 1 + ld]).decode()) - else: - result.append(RAW_OPCODE[script[s]]) - result.append('[%s]' % ld) - s += 1 + script[s + 1] + 1 - elif script[s] == OPCODE["OP_PUSHDATA2"]: - - ld = struct.unpack(' 0: - if script[s] < 0x4c and script[s]: - stack.append(script[s] + 1) - s += script[s] + 1 - elif script[s] == OPCODE["OP_PUSHDATA1"]: - stack.append(1 + script[s + 1]) - s += 1 + script[s + 1] - elif script[s] == OPCODE["OP_PUSHDATA2"]: - stack.append(2 + struct.unpack('= ls: - if script[k:s][:ls] == sub_script: - if s - k > ls: - result.append(script[k + ls:s]) - t = 0 - while t != s - k: - t += stack.pop(0) - k = s - else: - t = stack.pop(0) - result.append(script[k:k + t]) - k += t - if script[k:s][:ls] == sub_script: - if s - k > ls: - result.append(script[k + ls:s]) - else: - result.append(script[k:k + ls]) - - return b''.join(result) if not s_hex else hexlify(b''.join(result)).decode() - - -def script_to_hash(script, witness=False, hex=True): - """ - Encode script to hash HASH160 or SHA256 in dependency of the witness. - - :param script: script in bytes or HEX encoded string. - :param witness: (optional) If set to True return SHA256 hash for P2WSH, by default is False. - :param hex: (optional) If set to True return key in HEX format, by default is True. - :param sub_script: sub_script which is necessary to remove from target script in bytes or HEX encoded string. - :return: script in bytes or HEX encoded string corresponding to the format of target script. - """ - if isinstance(script, str): - s = unhexlify(script) - if witness: - return sha256(script, hex) - else: - return hash160(script, hex) - - -def op_push_data(data): - if len(data) <= 0x4b: - return b''.join([bytes([len(data)]),data]) - elif len(data) <= 0xff: - return b''.join([OP_PUSHDATA1, bytes([len(data)]), data]) - elif len(data) <= 0xffff: - return b''.join([OP_PUSHDATA2, int_to_bytes(len(data), byteorder="little"), data]) - - else: - return b''.join([OP_PUSHDATA4, int_to_bytes(len(data), byteorder="little"), data]) - - -def get_multisig_public_keys(script): - pub_keys = [] - s = get_stream(script) - o, d = read_opcode(s) - while o: - o, d = read_opcode(s) - if d: - pub_keys.append(d) - return pub_keys - - -def read_opcode(stream): - b = stream.read(1) - if not b: - return None, None - if b[0] <= 0x4b: - return b, stream.read(b[0]) - elif b[0] == OP_PUSHDATA1: - return b, stream.read(stream.read(1)[0]) - elif b[0] == OP_PUSHDATA2: - return b, stream.read(struct.unpack(" 73): - return False - # A signature is of type 0x30 (compound). - if sig[0] != 0x30: - return False - # Make sure the length covers the entire signature. - if sig[1] != (length - 3): - return False - # Extract the length of the R element. - len_r = sig[3] - # Make sure the length of the S element is still inside the signature. - if (5 + len_r) >= length: - return False - # Extract the length of the S element. - len_s = sig[5 + len_r] - # Verify that the length of the signature matches the sum of the length - # of the elements. - if (len_r + len_s + 7) != length: - return False - # Check whether the R element is an integer. - if sig[2] != 0x02: - return False - # Zero-length integers are not allowed for R. - if len_r == 0: - return False - # Negative numbers are not allowed for R. - if sig[4] & 0x80: - return False - # Null bytes at the start of R are not allowed, unless R would - # otherwise be interpreted as a negative number. - if (len_r > 1) and (sig[4] == 0x00) and (not sig[5] & 0x80): - return False - # Check whether the S element is an integer. - if sig[len_r + 4] != 0x02: - return False - # Zero-length integers are not allowed for S. - if len_s == 0: - return False - # Negative numbers are not allowed for S. - if sig[len_r + 6] & 0x80: - return False - # Null bytes at the start of S are not allowed, unless S would otherwise be - # interpreted as a negative number. - if (len_s > 1) and (sig[len_r + 6] == 0x00) and (not sig[len_r + 7] & 0x80): - return False - return True - - -# Hash encoding - -def rh2s(raw_hash): - """ - Encode raw transaction hash to HEX string with bytes order change - - :param raw_hash: transaction hash in bytes string. - :return: HEX encoded string. - """ - return hexlify(raw_hash[::-1]).decode() - - -def s2rh(hash_string): - """ - Decode HEX transaction hash to bytes with byte order change - - :param raw_hash: transaction hash in bytes string. - :return: bytes string. - """ - return unhexlify(hash_string)[::-1] - - -def s2rh_step4(hash_string): - h = unhexlify(hash_string) - return reverse_hash(h) - - -def reverse_hash(raw_hash): - """ - Reverse hash order - - :param raw_hash: bytes string. - :return: bytes string. - """ - return struct.pack('>IIIIIIII', *struct.unpack('>IIIIIIII', raw_hash)[::-1])[::-1] - - -# Merkle root - -def merkle_root(tx_hash_list, hex=True): - """ - Calculate merkle root from transaction hash list - - :param tx_hash_list: list of transaction hashes in bytes or HEX encoded string. - :param hex: (optional) If set to True return result in HEX format, by default is True. - :return: merkle root in bytes or HEX encoded string corresponding hex flag. - """ - tx_hash_list = [h if isinstance(h, bytes) else s2rh(h) for h in tx_hash_list] - if len(tx_hash_list) == 1: - return tx_hash_list[0] - while True: - new_hash_list = list() - while tx_hash_list: - h1 = tx_hash_list.pop(0) - try: - h2 = tx_hash_list.pop(0) - except: - h2 = h1 - new_hash_list.append(double_sha256(h1 + h2)) - if len(new_hash_list) > 1: - tx_hash_list = new_hash_list - else: - return new_hash_list[0] if not hex else hexlify(new_hash_list[0]).decode() - - -def merkle_branches(tx_hash_list, hex=True): - """ - Calculate merkle branches for coinbase transacton - - :param tx_hash_list: list of transaction hashes in bytes or HEX encoded string. - :param hex: (optional) If set to True return result in HEX format, by default is True. - :return: list of merkle branches in bytes or HEX encoded string corresponding hex flag. - """ - tx_hash_list = [h if isinstance(h, bytes) else s2rh(h) for h in tx_hash_list] - branches = [] - if len(tx_hash_list) == 1: - return [] - tx_hash_list.pop(0) - while True: - branches.append(tx_hash_list.pop(0)) - new_hash_list = list() - while tx_hash_list: - h1 = tx_hash_list.pop(0) - try: - h2 = tx_hash_list.pop(0) - except: - h2 = h1 - new_hash_list.append(double_sha256(h1 + h2)) - if len(new_hash_list) > 1: - tx_hash_list = new_hash_list - else: - if new_hash_list: - branches.append(new_hash_list.pop(0)) - return branches if not hex else [hexlify(h).decode() for h in branches] - - -def merkleroot_from_branches(merkle_branches, coinbase_hash, hex=True): - """ - Calculate merkle root from merkle branches and coinbase transacton hash - - :param merkle_branches: list merkle branches in bytes or HEX encoded string. - :param coinbase_hash: list coinbase transaction hash in bytes or HEX encoded string. - :param hex: (optional) If set to True return result in HEX format, by default is True. - :return: merkle root in bytes or HEX encoded string corresponding hex flag. - """ - merkle_root = coinbase_hash if not isinstance(coinbase_hash, str) else unhexlify(coinbase_hash) - for h in merkle_branches: - if type(h) == str: - h = unhexlify(h) - merkle_root = double_sha256(merkle_root + h) - return merkle_root if not hex else hexlify(merkle_root).decode() - - -# Difficulty - - -def bits_to_target(bits): - """ - Calculate target from bits - - :param bits: HEX string, bytes string or integer representation of bits. - :return: integer. - """ - if type(bits) == str: - bits = unhexlify(bits) - if type(bits) == bytes: - return int.from_bytes(bits[1:], 'big') * (2 ** (8 * (bits[0] - 3))) - else: - shift = bits >> 24 - target = (bits & 0xffffff) * (1 << (8 * (shift - 3))) - return target - - -def target_to_difficulty(target): - """ - Calculate difficulty from target - - :param target: integer. - :return: float. - """ - return 0x00000000FFFF0000000000000000000000000000000000000000000000000000 / target - - -def bits_to_difficulty(bits): - """ - Calculate difficulty from bits - - :param bits: HEX string, bytes string or integer representation of bits. - :return: integer. - """ - return target_to_difficulty(bits_to_target(bits)) - - -def difficulty_to_target(difficulty): - """ - Calculate target from difficulty - - :param target: integer. - :return: float. - """ - return int(0x00000000FFFF0000000000000000000000000000000000000000000000000000 / difficulty) - - -# Tools - - -def bytes_needed(n): - """ - Calculate bytes needed to convert integer to bytes. - - :param n: integer. - :return: integer. - """ - if n == 0: - return 1 - return math.ceil(n.bit_length()/8) - - -def int_to_bytes(i, byteorder='big'): - """ - Convert integer to bytes. - - :param n: integer. - :param byteorder: (optional) byte order 'big' or 'little', by default 'big'. - :return: bytes. - """ - return i.to_bytes(bytes_needed(i), byteorder=byteorder, signed=False) - - -def bytes_to_int(i, byteorder='big'): - """ - Convert bytes to integer. - - :param i: bytes. - :param byteorder: (optional) byte order 'big' or 'little', by default 'big'. - :return: integer. - """ - return int.from_bytes(i, byteorder=byteorder, signed=False) - - -# variable integer - -def int_to_var_int(i): - """ - Convert integer to variable integer - - :param i: integer. - :return: bytes. - """ - if i < 0xfd: - return struct.pack(' 0: - s.append((v >> ((i - 1) * 8)) & 0xff) - i -= 1 - return s - - -def bin2bn(s): - l = 0 - for ch in s: - l = (l << 8) | ch - return l - - -def bn2mpi(v): - have_ext = False - if v.bit_length() > 0: - have_ext = (v.bit_length() & 0x07) == 0 - neg = False - if v < 0: - neg = True - v = -v - s = struct.pack(b">I", bn_bytes(v, have_ext)) - ext = bytearray() - if have_ext: - ext.append(0) - v_bin = bn2bin(v) - if neg: - if have_ext: - ext[0] |= 0x80 - else: - v_bin[0] |= 0x80 - return s + ext + v_bin - - -def mpi2bn(s): - if len(s) < 4: - return None - s_size = bytes(s[:4]) - v_len = struct.unpack(b">I", s_size)[0] - if len(s) != (v_len + 4): - return None - if v_len == 0: - return 0 - v_str = bytearray(s[4:]) - neg = False - i = v_str[0] - if i & 0x80: - neg = True - i &= ~0x80 - v_str[0] = i - v = bin2bn(v_str) - - if neg: - return -v - return v - -# bitcoin-specific little endian format, with implicit size - - -def mpi2vch(s): - r = s[4:] # strip size - # if r: - r = r[::-1] # reverse string, converting BE->LE - # else: r=b'\x00' - return r - - -def bn2vch(v): - return bytes(mpi2vch(bn2mpi(v))) - - -def vch2mpi(s): - r = struct.pack(b">I", len(s)) # size - r += s[::-1] # reverse string, converting LE->BE - return r - - -def vch2bn(s): - return mpi2bn(vch2mpi(s)) - - -def i2b(i): return bn2vch(i) - - -def b2i(b): return vch2bn(b) - - -def get_stream(stream): - if type(stream) != io.BytesIO: - if type(stream) == str: - stream = unhexlify(stream) - if type(stream) == bytes: - stream = io.BytesIO(stream) - else: - raise TypeError - return stream - diff --git a/pybtc/transaction.py b/pybtc/transaction.py index 72b29f5..8f5b72e 100644 --- a/pybtc/transaction.py +++ b/pybtc/transaction.py @@ -1,9 +1,8 @@ from struct import unpack import json -from .tools import * +from .functions import * from .address import PrivateKey, Address, PublicKey, ScriptAddress -from binascii import hexlify, unhexlify class Transaction(dict): @@ -70,7 +69,7 @@ class Transaction(dict): self["vIn"][k]["txId"] = stream.read(32) self["vIn"][k]["vOut"] = unpack('= 0x4c: l += 1 @@ -412,13 +412,13 @@ class Transaction(dict): # script_pub_key if script_pub_key: if isinstance(script_pub_key, str): - script_pub_key = unhexlify(script_pub_key) + script_pub_key = bytes.fromhex(script_pub_key) if not isinstance(script_pub_key, bytes): raise TypeError("script_pub_key tx invalid") if redeem_script: if isinstance(redeem_script, str): - redeem_script = unhexlify(redeem_script) + redeem_script = bytes.fromhex(redeem_script) if not isinstance(redeem_script, bytes): raise TypeError("redeem_script tx invalid") @@ -448,15 +448,15 @@ class Transaction(dict): self["vIn"][k]["redeemScript"] = redeem_script else: self["vIn"][k]["txId"] = rh2s(tx_id) - self["vIn"][k]["scriptSig"] = hexlify(script_sig).decode() + self["vIn"][k]["scriptSig"] = script_sig.hex() self["vIn"][k]["scriptSigOpcodes"] = decode_script(script_sig) self["vIn"][k]["scriptSigAsm"] = decode_script(script_sig, 1) if script_pub_key: - self["vIn"][k]["scriptPubKey"] = hexlify(script_pub_key).decode() + self["vIn"][k]["scriptPubKey"] = script_pub_key.hex() self["vIn"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key) self["vIn"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1) if redeem_script: - self["vIn"][k]["redeemScript"] = hexlify(redeem_script).decode() + self["vIn"][k]["redeemScript"] = redeem_script.hex() self["vIn"][k]["redeemScriptOpcodes"] = decode_script(redeem_script) self["vIn"][k]["redeemScriptAsm"] = decode_script(script_pub_key, 1) if tx_in_witness: @@ -476,7 +476,7 @@ class Transaction(dict): assert amount >= 0 and amount <= MAX_AMOUNT if script_pub_key: if type(script_pub_key) == str: - script_pub_key = unhexlify(script_pub_key) + script_pub_key = bytes.fromhex(script_pub_key) assert type(script_pub_key) == bytes else: if type(address) == Address: @@ -500,12 +500,12 @@ class Transaction(dict): self["vOut"][k]["addressHash"] = s["addressHash"] self["vOut"][k]["reqSigs"] = s["reqSigs"] else: - self["vOut"][k]["scriptPubKey"] = hexlify(script_pub_key).decode() + self["vOut"][k]["scriptPubKey"] = script_pub_key.hex() if self["data"] is None: if s["nType"] == 3: - self["data"] = hexlify(s["data"]).decode() + self["data"] = s["data"].hex() if s["nType"] not in (3, 4, 7): - self["vOut"][k]["addressHash"] = hexlify(s["addressHash"]).decode() + self["vOut"][k]["addressHash"] = s["addressHash"].hex() self["vOut"][k]["reqSigs"] = s["reqSigs"] self["vOut"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key) self["vOut"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1) @@ -595,7 +595,7 @@ class Transaction(dict): else: st = parse_script(script_pub_key) if isinstance(script_pub_key, str): - script_pub_key = unhexlify(script_pub_key) + script_pub_key = bytes.fromhex(script_pub_key) # sign input if st["type"] == "PUBKEY": @@ -615,7 +615,7 @@ class Transaction(dict): if self["format"] == "raw": self["vIn"][n]["scriptSig"] = script_sig else: - self["vIn"][n]["scriptSig"] = hexlify(script_sig).decode() + self["vIn"][n]["scriptSig"] = script_sig.hex() self["vIn"][n]["scriptSigOpcodes"] = decode_script(script_sig) self["vIn"][n]["scriptSigAsm"] = decode_script(script_sig, 1) self.__refresh__() @@ -644,7 +644,7 @@ class Transaction(dict): else: raise RuntimeError("no redeem script") if isinstance(redeem_script, str): - redeem_script = unhexlify(redeem_script) + redeem_script = bytes.fromhex(redeem_script) rst = parse_script(redeem_script) if p2sh_p2wsh: @@ -677,14 +677,14 @@ class Transaction(dict): except: raise RuntimeError("no input amount") sighash = self.sig_hash_segwit(n, amount, script_pub_key=b"".join(s), sighash_type=sighash_type) - sighash = unhexlify(sighash) if isinstance(sighash, str) else sighash + sighash = bytes.fromhex(sighash) if isinstance(sighash, str) else sighash signature = sign_message(sighash, private_key[0], 0) + bytes([sighash_type]) self["segwit"] = True if self["format"] == "raw": self["vIn"][n]['txInWitness'] = [signature, public_key[0]] else: - self["vIn"][n]['txInWitness'] = [hexlify(signature).decode(), hexlify(public_key[0]).decode()] + self["vIn"][n]['txInWitness'] = [signature.hex(), public_key[0].hex()] return op_push_data(redeem_script) def __sign_p2sh_p2wsh(self, n, private_key, public_key, @@ -708,15 +708,15 @@ class Transaction(dict): except: raise RuntimeError("no input amount") sighash = self.sig_hash_segwit(n, amount, script_pub_key=b"".join(s), sighash_type=sighash_type) - sighash = unhexlify(sighash) if isinstance(sighash, str) else sighash + sighash = bytes.fromhex(sighash) if isinstance(sighash, str) else sighash signature = sign_message(sighash, private_key[0], 0) + bytes([sighash_type]) self["segwit"] = True if self["format"] == "raw": self["vIn"][n]['txInWitness'] = [signature, public_key[0]] else: - self["vIn"][n]['txInWitness'] = [hexlify(signature).decode(), - hexlify(public_key[0]).decode()] + self["vIn"][n]['txInWitness'] = [signature.hex(), + public_key[0].hex()] return b"" def __sign_p2wsh(self, n, private_key, public_key, script_pub_key, redeem_script, sighash_type, amount): @@ -727,7 +727,7 @@ class Transaction(dict): else: raise RuntimeError("no redeem script") if isinstance(redeem_script, str): - redeem_script = unhexlify(redeem_script) + redeem_script = bytes.fromhex(redeem_script) rst = parse_script(redeem_script) if amount is None: try: @@ -744,7 +744,7 @@ class Transaction(dict): def __sign_p2wsh_multisig(self, n, private_key, public_key, script_pub_key, redeem_script, sighash_type, amount): script_code = int_to_var_int(len(redeem_script)) + redeem_script sighash = self.sig_hash_segwit(n, amount, script_pub_key=script_code, sighash_type=sighash_type) - sighash = unhexlify(sighash) if isinstance(sighash, str) else sighash + sighash = bytes.fromhex(sighash) if isinstance(sighash, str) else sighash sig = [sign_message(sighash, p, 0) + bytes([sighash_type]) for p in private_key] if "txInWitness" not in self["vIn"][n]: self["vIn"][n]["txInWitness"] = [] @@ -753,7 +753,7 @@ class Transaction(dict): if self["format"] == "raw": self["vIn"][n]['txInWitness'] = list(witness) else: - self["vIn"][n]["txInWitness"] = list([hexlify(w).decode() for w in witness]) + self["vIn"][n]["txInWitness"] = list([w.hex() for w in witness]) return b"" def __sign_p2wsh_custom(self, n, private_key, public_key, script_pub_key, redeem_script, sighash_type, amount): @@ -764,7 +764,7 @@ class Transaction(dict): self["segwit"] = True script_code = int_to_var_int(len(redeem_script)) + redeem_script sighash = self.sig_hash_segwit(n, amount, script_pub_key=script_code, sighash_type=sighash_type) - sighash = unhexlify(sighash) if isinstance(sighash, str) else sighash + sighash = bytes.fromhex(sighash) if isinstance(sighash, str) else sighash sig = [sign_message(sighash, p, 0) + bytes([sighash_type]) for p in private_key] if "txInWitness" not in self["vIn"][n]: self["vIn"][n]["txInWitness"] = [] @@ -778,7 +778,7 @@ class Transaction(dict): if self["format"] == "raw": self["vIn"][n]['txInWitness'] = list(witness) else: - self["vIn"][n]["txInWitness"] = list([hexlify(w).decode() for w in witness]) + self["vIn"][n]["txInWitness"] = list([w.hex() for w in witness]) # calculate P2SH redeem script from P2WSH redeem script return op_push_data(b"\x00" + op_push_data(sha256(redeem_script))) @@ -814,7 +814,7 @@ class Transaction(dict): else: for w in script_sig: if isinstance(w, str): - w = unhexlify(w) + w = bytes.fromhex(w) if w and is_valid_signature_encoding(w): d = w[:-1] for i in range(4): @@ -822,8 +822,6 @@ class Transaction(dict): script_pub_key=script_code, sighash_type=w[-1]) pk = public_key_recovery(d, sighash, i, hex=0) - if pk is not None: - l = hexlify(pk) if pk in pub_keys: sig_map[pk] = w break @@ -854,7 +852,7 @@ class Transaction(dict): assert "scriptPubKey" in self["vIn"][n] script_code = self["vIn"][n]["scriptPubKey"] if type(script_code) == str: - script_code = unhexlify(script_code) + script_code = bytes.fromhex(script_code) assert type(script_code) == bytes # remove opcode separators @@ -902,7 +900,7 @@ class Transaction(dict): for i in self["vOut"]: script_pub_key = self["vOut"][i]["scriptPubKey"] if type(self["vOut"][i]["scriptPubKey"]) == str: - script_pub_key = unhexlify(script_pub_key) + script_pub_key = bytes.fromhex(script_pub_key) if i > n and (sighash_type & 31) == SIGHASH_SINGLE: continue if (sighash_type & 31) == SIGHASH_SINGLE and (n != i): @@ -933,7 +931,7 @@ class Transaction(dict): assert "scriptPubKey" in self["vIn"][n] script_code = self["vIn"][n]["scriptPubKey"] if type(script_code) == str: - script_code = unhexlify(script_code) + script_code = bytes.fromhex(script_code) assert type(script_code) == bytes # remove opcode separators @@ -967,7 +965,7 @@ class Transaction(dict): for o in self["vOut"]: script_pub_key = self["vOut"][o]["scriptPubKey"] if type(self["vOut"][o]["scriptPubKey"]) == str: - script_pub_key = unhexlify(script_pub_key) + script_pub_key = bytes.fromhex(script_pub_key) if (sighash_type & 31) != SIGHASH_SINGLE and (sighash_type & 31) != SIGHASH_NONE: ho += self["vOut"][o]["value"].to_bytes(8, 'little') ho += int_to_var_int(len(script_pub_key)) + script_pub_key @@ -981,7 +979,7 @@ class Transaction(dict): preimage += struct.pack('>>",block.bits) # print(">>>",block.hash) # print(">>>",block.timestamp) diff --git a/tests/test/hash_functions.py b/tests/test/hash_functions.py index 8d4fcd5..c08e110 100644 --- a/tests/test/hash_functions.py +++ b/tests/test/hash_functions.py @@ -3,7 +3,7 @@ import os, sys parentPath = os.path.abspath("..") if parentPath not in sys.path: sys.path.insert(0, parentPath) -from pybtc import tools +from pybtc.functions import hash as tools from binascii import unhexlify, hexlify diff --git a/tests/test/transaction_constructor.py b/tests/test/transaction_constructor.py index c60810a..bdcc729 100644 --- a/tests/test/transaction_constructor.py +++ b/tests/test/transaction_constructor.py @@ -7,7 +7,7 @@ if parentPath not in sys.path: from secp256k1 import ffi import secp256k1 -from pybtc.tools import * +from pybtc.functions import * from pybtc.opcodes import * from pybtc.transaction import * from pybtc.address import * diff --git a/tests/test/transaction_deserialize.py b/tests/test/transaction_deserialize.py index 6b60dd5..b21fa46 100644 --- a/tests/test/transaction_deserialize.py +++ b/tests/test/transaction_deserialize.py @@ -6,8 +6,7 @@ parentPath = os.path.abspath("..") if parentPath not in sys.path: sys.path.insert(0, parentPath) -from pybtc.tools import * -from pybtc.hash import * +from pybtc.functions import * from pybtc.transaction import * from binascii import unhexlify from pybtc import address_to_hash as address2hash160 diff --git a/tests/test_bip0032.py b/tests/test_bip0032.py index 50b658f..3e317a9 100644 --- a/tests/test_bip0032.py +++ b/tests/test_bip0032.py @@ -1,9 +1,16 @@ import os +import sys import random import hashlib import hmac from binascii import hexlify, unhexlify + + +parentPath = os.path.abspath("..") +if parentPath not in sys.path: + sys.path.insert(0, parentPath) + from pybtc.hdwallet import * from pybtc.tools import encode_base58, decode_base58