class HDKey(Key):
    """
    Class for Hierarchical Deterministic keys as defined in BIP0032

    Besides a private or public key a HD Key has a chain code, allowing to create
    a structure of related keys.

    The structure and key-path are defined in BIP0043 and BIP0044.
    """

    @staticmethod
    def from_seed(import_seed, key_type='bip32', network=DEFAULT_NETWORK, compressed=True,
                  encoding=None, witness_type=DEFAULT_WITNESS_TYPE, multisig=False):
        """
        Used by class init function, import key from seed

        :param import_seed: Private key seed as bytes or hexstring
        :type import_seed: str, bytes
        :param key_type: Specify type of key, default is BIP32
        :type key_type: str
        :param network: Network to use
        :type network: str, Network
        :param compressed: Is key compressed or not, default is True
        :type compressed: bool
        :param encoding: Encoding used for address, i.e.: base58 or bech32. Default is base58 or derive from witness type
        :type encoding: str
        :param witness_type: Witness type used when creating scripts: legacy, p2sh-segwit or segwit.
        :type witness_type: str
        :param multisig: Specify if key is part of multisig wallet, used when creating key representations such as WIF and addresses
        :type multisig: bool

        :return HDKey:
        """
        seed = to_bytes(import_seed)
        i = hmac.new(b"Bitcoin seed", seed, hashlib.sha512).digest()
        key = i[:32]
        chain = i[32:]
        key_int = int.from_bytes(key, 'big')
        if key_int >= secp256k1_n:
            raise BKeyError("Key int value cannot be greater than secp256k1_n")
        return HDKey(key=key, chain=chain, network=network, key_type=key_type, compressed=compressed,
                     encoding=encoding, witness_type=witness_type, multisig=multisig)

    @staticmethod
    def from_passphrase(passphrase, password='', network=DEFAULT_NETWORK, key_type='bip32', compressed=True,
                        encoding=None, witness_type=DEFAULT_WITNESS_TYPE, multisig=False):
        """
        Create key from Mnemonic passphrase

        :param passphrase: Mnemonic passphrase, list of words as string seperated with a space character
        :type passphrase: str
        :param password: Password to protect passphrase
        :type password: str
        :param network: Network to use
        :type network: str, Network
        :param key_type: HD BIP32 or normal Private Key. Default is 'bip32'
        :type key_type: str
        :param compressed: Is key compressed or not, default is True
        :type compressed: bool
        :param encoding: Encoding used for address, i.e.: base58 or bech32. Default is base58 or derive from witness type
        :type encoding: str
        :param witness_type: Witness type used when creating scripts: legacy, p2sh-segwit or segwit.
        :type witness_type: str
        :param multisig: Specify if key is part of multisig wallet, used when creating key representations such as WIF and addreses
        :type multisig: bool

        :return HDKey:
        """
        return HDKey.from_seed(Mnemonic().to_seed(passphrase, password), network=network, key_type=key_type,
                               compressed=compressed, encoding=encoding, witness_type=witness_type, multisig=multisig)

    @staticmethod
    def from_wif(wif, network=None, compressed=True, multisig=None):
        """
        Create HDKey from BIP32 WIF

        :param wif: HDKey WIF
        :type wif: str
        :param network: Network to use as string
        :type network: str
        :param compressed: Is key compressed or not, default is True
        :type compressed: bool
        :param multisig: Specify if key is part of multisig wallet, used when creating key representations such as WIF and addresses
        :type multisig: bool

        :return HDKey:
        """
        bkey = change_base(wif, 58, 256)
        if len(bkey) != 82:
            raise BKeyError("Invalid BIP32 HDkey WIF. Length must be 82 characters")

        if ord(bkey[45:46]):
            is_private = False
            key = bkey[45:78]
        else:
            is_private = True
            key = bkey[46:78]
        depth = ord(bkey[4:5])
        parent_fingerprint = bkey[5:9]
        child_index = int.from_bytes(bkey[9:13], 'big')
        chain = bkey[13:45]

        key_hex = bkey.hex()
        prefix_data = wif_prefix_search(key_hex[:8], network=network, multisig=multisig)
        if not prefix_data:
            raise BKeyError("Invalid BIP32 HDkey WIF. Cannot find prefix in network definitions")

        networks = list(dict.fromkeys([n['network'] for n in prefix_data]))
        if not network and networks:
            network = networks[0]
        elif network not in networks:
            raise BKeyError("Network %s not found in list of derived networks %s" % (network, networks))

        witness_type = next(iter(list(dict.fromkeys([n['witness_type'] for n in prefix_data]))), None)
        multisig = multisig or next(iter(list(dict.fromkeys([n['multisig'] for n in prefix_data]))), None)

        return HDKey(key=key, chain=chain, depth=depth, parent_fingerprint=parent_fingerprint,
                     child_index=child_index, is_private=is_private, network=network, witness_type=witness_type,
                     multisig=multisig, compressed=compressed)

    def __init__(self, import_key=None, key=None, chain=None, depth=0, parent_fingerprint=b'\0\0\0\0',
                 child_index=0, is_private=True, network=None, key_type='bip32', password='', compressed=True,
                 encoding=None, witness_type=None, multisig=False):
        """
        Hierarchical Deterministic Key class init function.

        If no import_key is specified a key will be generated with systems cryptographically random function.
        Import key can be any format normal or HD key (extended key) accepted by get_key_format.
        If a normal key with no chain part is provided, an chain with only 32 0-bytes will be used.

        >>> private_hex = '221ff330268a9bb5549a02c801764cffbc79d5c26f4041b26293a425fd5b557c'
        >>> k = HDKey(private_hex)
        >>> k
        <HDKey(public_hex=0363c152144dcd5253c1216b733fdc6eb8a94ab2cd5caa8ead5e59ab456ff99927, wif_public=xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6SmypHzZG2cYrwpGkWJqRxS6EAW77gd7CHFoXNpBd3LN8xjAyCW, network=bitcoin)>

        :param import_key: HD Key to import in WIF format or as byte with key (32 bytes) and chain (32 bytes)
        :type import_key: str, bytes, int
        :param key: Private or public key (length 32)
        :type key: bytes
        :param chain: A chain code (length 32)
        :type chain: bytes
        :param depth: Level of depth in BIP32 key path
        :type depth: int
        :param parent_fingerprint: 4-byte fingerprint of parent
        :type parent_fingerprint: bytes
        :param child_index: Index number of child as integer
        :type child_index: int
        :param is_private: True for private, False for public key. Default is True
        :type is_private: bool
        :param network: Network name. Derived from import_key if possible
        :type network: str, Network
        :param key_type: HD BIP32 or normal Private Key. Default is 'bip32'
        :type key_type: str
        :param password: Optional password if imported key is password protected
        :type password: str
        :param compressed: Is key compressed or not, default is True
        :type compressed: bool
        :param encoding: Encoding used for address, i.e.: base58 or bech32. Default is base58 or derive from witness type
        :type encoding: str
        :param witness_type: Witness type used when creating scripts: legacy, p2sh-segwit or segwit.
        :type witness_type: str
        :param multisig: Specify if key is part of multisig wallet, used when creating key representations such as WIF and addreses
        :type multisig: bool

        :return HDKey:
        """

        if not encoding and witness_type:
            encoding = get_encoding_from_witness(witness_type)
        self.script_type = script_type_default(witness_type, multisig)

        # if (key and not chain) or (not key and chain):
        #     raise BKeyError("Please specify both key and chain, use import_key attribute "
        #                     "or use simple Key class instead")
        if not key:
            if not import_key:
                # Generate new Master Key
                seed = os.urandom(64)
                key, chain = self._key_derivation(seed)
            # If key is 64 bytes long assume a HD Key with key and chain part
            elif isinstance(import_key, bytes) and len(import_key) == 64:
                key = import_key[:32]
                chain = import_key[32:]
            elif isinstance(import_key, Key):
                if not import_key.compressed:
                    _logger.warning("Uncompressed private keys are not standard for BIP32 keys, use at your own risk!")
                    compressed = False
                chain = chain if chain else b'\0' * 32
                if not import_key.private_byte:
                    raise BKeyError('Cannot import public Key in HDKey')
                key = import_key.private_byte
                key_type = 'private'
            else:
                kf = get_key_format(import_key)
                if kf['format'] == 'address':
                    raise BKeyError("Can not create HDKey object from address")
                if len(kf['script_types']) == 1:
                    self.script_type = kf['script_types'][0]
                if len(kf['witness_types']) == 1 and not witness_type:
                    witness_type = kf['witness_types'][0]
                    encoding = get_encoding_from_witness(witness_type)
                if len(kf['multisig']) == 1:
                    multisig = kf['multisig'][0]
                network = Network(check_network_and_key(import_key, network, kf["networks"]))
                if kf['format'] in ['hdkey_private', 'hdkey_public']:
                    bkey = change_base(import_key, 58, 256)
                    # Derive key, chain, depth, child_index and fingerprint part from extended key WIF
                    if ord(bkey[45:46]):
                        is_private = False
                        key = bkey[45:78]
                    else:
                        key = bkey[46:78]
                    depth = ord(bkey[4:5])
                    parent_fingerprint = bkey[5:9]
                    child_index = int.from_bytes(bkey[9:13], 'big')
                    chain = bkey[13:45]
                elif kf['format'] == 'mnemonic':
                    raise BKeyError("Use HDKey.from_passphrase() method to parse a passphrase")
                elif kf['format'] == 'wif_protected':
                    key, compressed = self._bip38_decrypt(import_key, password, network.name, witness_type)
                    chain = chain if chain else b'\0' * 32
                    key_type = 'private'
                else:
                    key = import_key
                    chain = chain if chain else b'\0' * 32
                    is_private = kf['is_private']
                    key_type = 'private' if is_private else 'public'

        if witness_type is None:
            witness_type = DEFAULT_WITNESS_TYPE

        Key.__init__(self, key, network, compressed, password, is_private)

        self.encoding = encoding
        self.witness_type = witness_type
        self.multisig = multisig

        self.chain = chain
        self.depth = depth
        self.parent_fingerprint = parent_fingerprint
        self.child_index = child_index
        self.key_type = key_type

    def __repr__(self):
        return "<HDKey(public_hex=%s, wif_public=%s, network=%s)>" % \
               (self.public_hex, self.wif_public(), self.network.name)

    def info(self):
        """
        Prints key information to standard output

        """
        super(HDKey, self).info()

        print("EXTENDED KEY")
        print(" Key Type                    %s" % self.key_type)
        print(" Chain code (hex)            %s" % self.chain.hex())
        print(" Child Index                 %s" % self.child_index)
        print(" Parent Fingerprint (hex)    %s" % self.parent_fingerprint.hex())
        print(" Depth                       %s" % self.depth)
        print(" Extended Public Key (wif)   %s" % self.wif_public())
        print(" Witness type                %s" % self.witness_type)
        print(" Script type                 %s" % self.script_type)
        print(" Multisig                    %s" % self.multisig)
        if self.is_private:
            print(" Extended Private Key (wif)  %s" % self.wif(is_private=True))
        print("\n")

    def as_dict(self, include_private=False):
        """
        Get current HDKey class as dictionary. Byte values are represented by hexadecimal strings.

        :param include_private: Include private key information in dictionary
        :type include_private: bool

        :return collections.OrderedDict:
        """

        key_dict = super(HDKey, self).as_dict()
        if include_private:
            key_dict['fingerprint'] = self.fingerprint.hex()
            key_dict['chain_code'] = self.chain.hex()
            key_dict['fingerprint_parent'] = self.parent_fingerprint.hex()
        key_dict['child_index'] = self.child_index
        key_dict['depth'] = self.depth
        key_dict['extended_wif_public'] = self.wif_public()
        if include_private:
            key_dict['extended_wif_private'] = self.wif(is_private=True)
        return key_dict

    def as_json(self, include_private=False):
        """
        Get current key as json formatted string

        :param include_private: Include private key information in dictionary
        :type include_private: bool
        
        :return str:
        """
        return json.dumps(self.as_dict(include_private=include_private), indent=4)

    def _key_derivation(self, seed):
        """
        Derive extended private key with key and chain part from seed

        :param seed:
        :type seed: bytes

        :return tuple: key and chain bytes
        """
        chain = hasattr(self, 'chain') and self.chain or b"Bitcoin seed"
        i = hmac.new(chain, seed, hashlib.sha512).digest()
        key = i[:32]
        chain = i[32:]
        key_int = int.from_bytes(key, 'big')
        if key_int >= secp256k1_n:
            raise BKeyError("Key cannot be greater than secp256k1_n. Try another index number.")
        return key, chain

    @property
    def fingerprint(self):
        """
        Get key fingerprint: the last for bytes of the hash160 of this key.

        :return bytes:
        """

        return self.hash160[:4]

    @staticmethod
    def _bip38_decrypt(encrypted_privkey, password, network=DEFAULT_NETWORK, witness_type=DEFAULT_WITNESS_TYPE):
        """
        BIP0038 non-ec-multiply decryption. Returns WIF private key.
        Based on code from https://github.com/nomorecoin/python-bip38-testing
        This method is called by Key class init function when importing BIP0038 key.

        :param encrypted_privkey: Encrypted private key using WIF protected key format
        :type encrypted_privkey: str
        :param password: Required password for decryption
        :type password: str

        :return str: Private Key WIF
        """
        priv, addresshash, compressed = bip38_decrypt(encrypted_privkey, password)
        # compressed = True if priv[-1:] == b'\1' else False

        # Verify addresshash
        k = HDKey(priv, compressed=compressed, network=network, witness_type=witness_type)
        addr = k.address()
        if isinstance(addr, str):
            addr = addr.encode('utf-8')
        if double_sha256(addr)[0:4] != addresshash:
            raise BKeyError('Addresshash verification failed! Password or '
                            'specified network %s might be incorrect' % network)
        return priv, compressed

    def wif(self, is_private=None, child_index=None, prefix=None, witness_type=None, multisig=None):
        """
        Get Extended WIF of current key

        >>> private_hex = '221ff330268a9bb5549a02c801764cffbc79d5c26f4041b26293a425fd5b557c'
        >>> k = HDKey(private_hex)
        >>> k.wif()
        'xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6SmypHzZG2cYrwpGkWJqRxS6EAW77gd7CHFoXNpBd3LN8xjAyCW'

        :param is_private: Return public or private key
        :type is_private: bool
        :param child_index: Change child index of output WIF key
        :type child_index: int
        :param prefix: Specify version prefix in hexstring or bytes. Normally doesn't need to be specified, method uses default prefix from network settings
        :type prefix: str, bytes
        :param witness_type: Specify witness type, default is legacy. Use 'segwit' for segregated witness.
        :type witness_type: str
        :param multisig: Key is part of a multisignature wallet?
        :type multisig: bool

        :return str: Base58 encoded WIF key
        """

        if not witness_type:
            witness_type = DEFAULT_WITNESS_TYPE if not self.witness_type else self.witness_type
        if not multisig:
            multisig = False if not self.multisig else self.multisig

        rkey = self.private_byte or self.public_compressed_byte
        if prefix and not isinstance(prefix, bytes):
            prefix = bytes.fromhex(prefix)
        if self.is_private and is_private:
            if not prefix:
                prefix = self.network.wif_prefix(is_private=True, witness_type=witness_type, multisig=multisig)
            typebyte = b'\x00'
        else:
            if not prefix:
                prefix = self.network.wif_prefix(witness_type=witness_type, multisig=multisig)
            typebyte = b''
            if not is_private:
                rkey = self.public_byte
        if child_index:
            self.child_index = child_index
        raw = prefix + self.depth.to_bytes(1, 'big') + self.parent_fingerprint + \
              self.child_index.to_bytes(4, 'big') + self.chain + typebyte + rkey
        chk = double_sha256(raw)[:4]
        ret = raw + chk
        return change_base(ret, 256, 58, 111)

    def wif_key(self, prefix=None):
        """
        Get WIF of Key object. Call to parent object Key.wif()

        :param prefix: Specify versionbyte prefix in hexstring or bytes. Normally doesn't need to be specified, method uses default prefix from network settings
        :type prefix: str, bytes
        :return str: Base58Check encoded Private Key WIF
        """
        return super(HDKey, self).wif(prefix)

    def wif_public(self, prefix=None, witness_type=None, multisig=None):
        """
        Get Extended WIF public key. Wrapper for the :func:`wif` method

        :param prefix: Specify version prefix in hexstring or bytes. Normally doesn't need to be specified, method uses default prefix from network settings
        :type prefix: str, bytes
        :param witness_type: Specify witness type, default is legacy. Use 'segwit' for segregated witness.
        :type witness_type: str
        :param multisig: Key is part of a multisignature wallet?
        :type multisig: bool

        :return str: Base58 encoded WIF key
        """
        return self.wif(is_private=False, prefix=prefix, witness_type=witness_type, multisig=multisig)

    def wif_private(self, prefix=None, witness_type=None, multisig=None):
        """
        Get Extended WIF private key. Wrapper for the :func:`wif` method

        :param prefix: Specify version prefix in hexstring or bytes. Normally doesn't need to be specified, method uses default prefix from network settings
        :type prefix: str, bytes
        :param witness_type: Specify witness type, default is legacy. Use 'segwit' for segregated witness.
        :type witness_type: str
        :param multisig: Key is part of a multi signature wallet?
        :type multisig: bool

        :return str: Base58 encoded WIF key
        """
        return self.wif(is_private=True, prefix=prefix, witness_type=witness_type, multisig=multisig)

    def address(self, compressed=None, prefix=None, script_type=None, encoding=None):
        """
        Get address derived from public key

        >>> wif = 'xpub661MyMwAqRbcFcXi3aM3fVdd42FGDSdufhrr5tdobiPjMrPUykFMTdaFEr7yoy1xxeifDY8kh2k4h9N77MY6rk18nfgg5rPtbFDF2YHzLfA'
        >>> k = HDKey.from_wif(wif)
        >>> k.address()
        '15CacK61qnzJKpSpx9PFiC8X1ajeQxhq8a'

        :param compressed: Always return compressed address
        :type compressed: bool
        :param prefix: Specify versionbyte prefix in hexstring or bytes. Normally doesn't need to be specified, method uses default prefix from network settings
        :type prefix: str, bytes
        :param script_type: Type of script, i.e. p2sh or p2pkh.
        :type script_type: str
        :param encoding: Address encoding. Default is base58 encoding, for segwit you can specify bech32 encoding
        :type encoding: str

        :return str: Base58 or Bech32 encoded address
        """
        if compressed is None:
            compressed = self.compressed
        if script_type is None:
            script_type = self.script_type
        if encoding is None:
            encoding = self.encoding
        return super(HDKey, self).address(compressed, prefix, script_type, encoding)

    def subkey_for_path(self, path, network=None):
        """
        Determine subkey for HD Key for given path.
        Path format: m / purpose' / coin_type' / account' / change / address_index

        See BIP0044 bitcoin proposal for more explanation.

        >>> wif = 'xprv9s21ZrQH143K4LvcS93AHEZh7gBiYND6zMoRiZQGL5wqbpCU2KJDY87Txuv9dduk9hAcsL76F8b5JKzDREf8EmXjbUwN1c4nR9GEx56QGg2'
        >>> k = HDKey.from_wif(wif)
        >>> k.subkey_for_path("m/44'/0'/0'/0/2")
        <HDKey(public_hex=03004331ca7f0dcdd925abc4d0800a0d4a0562a02c257fa39185c55abdfc4f0c0c, wif_public=xpub6GyQoEbMUNwu1LnbiCSaD8wLrcjyRCEQA8tNsFCH4pnvCbuWSZkSB6LUNe89YsCBTg1Ncs7vHJBjMvw2Q7siy3A4g1srAq7Lv3CtEXghv44, network=bitcoin)>

        :param path: BIP0044 key path
        :type path: str, list
        :param network: Network name.
        :type network: str

        :return HDKey: HD Key class object of subkey
        """

        if isinstance(path, TYPE_TEXT):
            path = path.split("/")
        if self.key_type == 'single':
            raise BKeyError("Key derivation cannot be used for 'single' type keys")
        key = self
        first_public = False
        if path[0] == 'm':  # Use Private master key
            path = path[1:]
        elif path[0] == 'M':  # Use Public master key
            path = path[1:]
            first_public = True
        if path:
            if len(path) > 1:
                _logger.info("Path length > 1 can be slow for larger paths, use Wallet Class to generate keys paths")
            for item in path:
                if not item:
                    raise BKeyError("Could not parse path. Index is empty.")
                hardened = item[-1] in "'HhPp"
                if hardened:
                    item = item[:-1]
                index = int(item)
                if index < 0:
                    raise BKeyError("Could not parse path. Index must be a positive integer.")
                if first_public or not key.is_private:
                    key = key.child_public(index=index, network=network)  # TODO hardened=hardened key?
                    first_public = False
                else:
                    key = key.child_private(index=index, hardened=hardened, network=network)
        return key

    def public_master(self, account_id=0, purpose=None, multisig=None, witness_type=None, as_private=False):
        """
        Derives a public master key for current HDKey. A public master key can be shared with other software
        administration tools to create readonly wallets or can be used to create multisignature wallets.

        >>> private_hex = 'b66ed9778029d32ebede042c79f448da8f7ab9efba19c63b7d3cdf6925203b71'
        >>> k = HDKey(private_hex)
        >>> pm = k.public_master()
        >>> pm.wif()
        'xpub6CjFexgdDZEtHdW7V4LT8wS9rtG3m187pM9qhTpoZdViFhSv3tW9sWonQNtFN1TCkRGAQGKj1UC2ViHTqb7vJV3X67xSKuCDzv14tBHR3Y7'

        :param account_id: Account ID. Leave empty for account 0
        :type account_id: int
        :param purpose: BIP standard used, i.e. 44 for default, 45 for multisig, 84 for segwit. Derived from witness_type and multisig arguments if not provided
        :type purpose: int
        :param multisig: Key is part of a multisignature wallet?
        :type multisig: bool
        :param witness_type: Specify witness type, default is legacy. Use 'segwit' or 'p2sh-segwit' for segregated witness.
        :type witness_type: str
        :param as_private: Return private key if available. Default is to return public key

        :return HDKey:
        """
        if multisig:
            self.multisig = multisig
        if witness_type:
            self.witness_type = witness_type
        ks = [k for k in WALLET_KEY_STRUCTURES if
              k['witness_type'] == self.witness_type and k['multisig'] == self.multisig and k['purpose'] is not None]
        if len(ks) > 1:
            raise BKeyError("Please check definitions in WALLET_KEY_STRUCTURES. Multiple options found for "
                            "witness_type - multisig combination")
        if ks and not purpose:
            purpose = ks[0]['purpose']
        path_template = ks[0]['key_path']

        # Use last hardened key as public master root
        pm_depth = path_template.index([x for x in path_template if x[-1:] == "'"][-1]) + 1
        path = path_expand(path_template[:pm_depth], path_template, account_id=account_id, purpose=purpose,
                           witness_type=self.witness_type, network=self.network.name)
        if as_private:
            return self.subkey_for_path(path)
        else:
            return self.subkey_for_path(path).public()

    def public_master_multisig(self, account_id=0, purpose=None, witness_type=None, as_private=False):
        """
        Derives a public master key for current HDKey for use with multi signature wallets. Wrapper for the
        :func:`public_master` method.

        :param account_id: Account ID. Leave empty for account 0
        :type account_id: int
        :param purpose: BIP standard used, i.e. 44 for default, 45 for multisig, 84 for segwit.
        :type purpose: int
        :param witness_type: Specify witness type, default is legacy. Use 'segwit' or 'p2sh-segwit' for segregated witness.
        :type witness_type: str
        :param as_private: Return private key if available. Default is to return public key

        :return HDKey:
        """

        return self.public_master(account_id, purpose, True, witness_type, as_private)

    def network_change(self, new_network):
        """
        Change network for current key

        :param new_network: Name of new network
        :type new_network: str

        :return bool: True
        """
        self.network = Network(new_network)
        return True

    def child_private(self, index=0, hardened=False, network=None):
        """
        Use Child Key Derivation (CDK) to derive child private key of current HD Key object.

        Used by :func:`subkey_for_path` to create key paths for instance to use in HD wallets. You can use this method to create your own key structures.

        This method create private child keys, use :func:`child_public` to create public child keys.

        >>> private_hex = 'd02220828cad5e0e0f25057071f4dae9bf38720913e46a596fd7eb8f83ad045d'
        >>> k = HDKey(private_hex)
        >>> ck = k.child_private(10)
        >>> ck.address()
        '1FgHK5JUa87ASxz5mz3ypeaUV23z9yW654'
        >>> ck.depth
        1
        >>> ck.child_index
        10

        :param index: Key index number
        :type index: int
        :param hardened: Specify if key must be hardened (True) or normal (False)
        :type hardened: bool
        :param network: Network name.
        :type network: str

        :return HDKey: HD Key class object
        """

        if network is None:
            network = self.network.name
        if not self.is_private:
            raise BKeyError("Need a private key to create child private key")
        if hardened:
            index |= 0x80000000
            data = b'\0' + self.private_byte + index.to_bytes(4, 'big')
        else:
            data = self.public_byte + index.to_bytes(4, 'big')
        key, chain = self._key_derivation(data)

        key = int.from_bytes(key, 'big')
        if key >= secp256k1_n:
            raise BKeyError("Key cannot be greater than secp256k1_n. Try another index number.")
        newkey = (key + self.secret) % secp256k1_n
        if newkey == 0:
            raise BKeyError("Key cannot be zero. Try another index number.")
        newkey = int.to_bytes(newkey, 32, 'big')

        return HDKey(key=newkey, chain=chain, depth=self.depth + 1, parent_fingerprint=self.fingerprint,
                     child_index=index, witness_type=self.witness_type, multisig=self.multisig,
                     encoding=self.encoding, network=network)

    def child_public(self, index=0, network=None):
        """
        Use Child Key Derivation to derive child public key of current HD Key object.

        Used by :func:`subkey_for_path` to create key paths for instance to use in HD wallets. You can use this method to create your own key structures.

        This method create public child keys, use :func:`child_private` to create private child keys.

        >>> private_hex = 'd02220828cad5e0e0f25057071f4dae9bf38720913e46a596fd7eb8f83ad045d'
        >>> k = HDKey(private_hex)
        >>> ck = k.child_public(15)
        >>> ck.address()
        '1PfLJJgKs8nUbMPpaQUucbGmr8qyNSMGeK'
        >>> ck.depth
        1
        >>> ck.child_index
        15

        :param index: Key index number
        :type index: int
        :param network: Network name.
        :type network: str

        :return HDKey: HD Key class object
        """
        if network is None:
            network = self.network.name
        if index > 0x80000000:
            raise BKeyError("Cannot derive hardened key from public private key. Index must be less than 0x80000000")
        data = self.public_byte + index.to_bytes(4, 'big')
        key, chain = self._key_derivation(data)
        key = int.from_bytes(key, 'big')
        if key >= secp256k1_n:
            raise BKeyError("Key cannot be greater than secp256k1_n. Try another index number.")

        x, y = self.public_point()
        if USE_FASTECDSA:
            ki = ec_point(key) + fastecdsa_point.Point(x, y, fastecdsa_secp256k1)
            ki_x = ki.x
            ki_y = ki.y
        else:
            ki = ec_point(key) + ecdsa.ellipticcurve.Point(secp256k1_curve, x, y, secp256k1_n)
            ki_x = ki.x()
            ki_y = ki.y()

        if ki_y % 2:
            prefix = '03'
        else:
            prefix = '02'
        xhex = change_base(ki_x, 10, 16, 64)
        secret = bytes.fromhex(prefix + xhex)
        return HDKey(key=secret, chain=chain, depth=self.depth + 1, parent_fingerprint=self.fingerprint,
                     child_index=index, is_private=False, witness_type=self.witness_type, multisig=self.multisig,
                     encoding=self.encoding, network=network)

    def public(self):
        """
        Public version of current private key. Strips all private information from HDKey object, returns deepcopy
        version of current object

        :return HDKey:
        """

        hdkey = deepcopy(self)
        hdkey.is_private = False
        hdkey.secret = None
        hdkey.private_hex = None
        hdkey.private_byte = None
        hdkey.key_hex = hdkey.public_hex
        # hdkey.key = self.key.public()
        return hdkey class Key(object):
    """
    Class to generate, import and convert public cryptographic key pairs used for bitcoin.

    If no key is specified when creating class a cryptographically secure Private Key is
    generated using the os.urandom() function.
    """

    @staticmethod
    def from_wif(wif, network=None):
        """
        Import private key in WIF format.

        :param wif: Private key in WIF format
        :type wif: str
        :param network: Bitcoin, testnet, litecoin or other network
        :type network: str, Network

        :return Key:
        """
        key_hex = change_base(wif, 58, 16)
        networks = network_by_value('prefix_wif', key_hex[:2])
        compressed = False
        if networks:
            if key_hex[-10:-8] == '01':
                compressed = True
            network = network or next(iter(networks), DEFAULT_NETWORK)
        else:
            raise BKeyError("Could not create key, wif format not recognised")
        return Key(wif, network, compressed, is_private=True)

    def __init__(self, import_key=None, network=None, compressed=True, password='', is_private=None, strict=True):
        """
        Initialize a Key object. Import key can be in WIF, bytes, hexstring, etc. If import_key is empty a new
        private key will be generated.

        If a private key is imported a public key will be derived. If a public is imported the private key data will
        be empty.

        Both compressed and uncompressed key version is available, the compressed boolean attribute tells if the
        original imported key was compressed or not.

        >>> k = Key('cNUpWJbC1hVJtyxyV4bVAnb4uJ7FPhr82geo1vnoA29XWkeiiCQn')
        >>> k.secret
        12127227708610754620337553985245292396444216111803695028419544944213442390363

        Can also be used to import BIP-38 password protected keys

        >>> k2 = Key('6PYM8wAnnmAK5mHYoF7zqj88y5HtK7eiPeqPdu4WnYEFkYKEEoMFEVfuDg', password='test', network='testnet')
        >>> k2.secret
        12127227708610754620337553985245292396444216111803695028419544944213442390363

        :param import_key: If specified import given private or public key. If not specified a new private key is generated.
        :type import_key: str, int, bytes
        :param network: Bitcoin, testnet, litecoin or other network
        :type network: str, Network
        :param compressed: Is key compressed or not, default is True
        :type compressed: bool
        :param password: Optional password if imported key is password protected
        :type password: str
        :param is_private: Specify if imported key is private or public. Default is None: derive from provided key
        :type is_private: bool
        :param strict: Raise BKeyError if key is invalid. Default is True. Set to False if you're parsing blockchain transactions, as some may contain invalid keys, but the transaction is/was still valid.
        :type strict: bool

        :return: Key object
        """
        self.public_hex = None
        self.public_uncompressed_hex = None
        self.public_compressed_hex = None
        self.public_byte = None
        self.public_uncompressed_byte = None
        self.public_compressed_byte = None
        self.private_byte = None
        self.private_hex = None
        self._x = None
        self._y = None
        self.x_hex = None
        self.y_hex = None
        self.secret = None
        self.compressed = compressed
        self._hash160 = None
        self.key_format = None
        self.is_private = None

        if not import_key:
            import_key = random.SystemRandom().randint(1, secp256k1_n - 1)
            self.key_format = 'decimal'
            networks_extracted = network
            assert is_private is True or is_private is None
            self.is_private = True  # Ignore provided attribute
        else:
            try:
                kf = get_key_format(import_key)
            except BKeyError:
                if strict:
                    raise BKeyError("Unrecognised key format")
                else:
                    networks_extracted = []
            else:
                if kf['format'] == 'address':
                    raise BKeyError("Can not create Key object from address")
                self.key_format = kf["format"]
                networks_extracted = kf["networks"]
                self.is_private = is_private if is_private else kf['is_private']
                if self.is_private is None:
                    raise BKeyError("Could not determine if key is private or public")

        if network is not None:
            self.network = network
            if not isinstance(network, Network):
                self.network = Network(network)
        elif networks_extracted:
            self.network = Network(check_network_and_key(import_key, None, networks_extracted))
        else:
            self.network = Network(DEFAULT_NETWORK)

        if self.key_format == "wif_protected":
            import_key, self.compressed = self._bip38_decrypt(import_key, password, network)
            self.key_format = 'bin_compressed' if self.compressed else 'bin'

        if not self.is_private:
            self.secret = None
            pub_key = to_hexstring(import_key)
            if len(pub_key) == 130:
                self.public_uncompressed_hex = pub_key
                self.x_hex = pub_key[2:66]
                self.y_hex = pub_key[66:130]
                self._y = int(self.y_hex, 16)
                self.compressed = False
                if self._y % 2:
                    prefix = '03'
                else:
                    prefix = '02'
                self.public_hex = pub_key
                self.public_compressed_hex = prefix + self.x_hex
            else:
                self.public_hex = pub_key
                self.x_hex = pub_key[2:66]
                self.compressed = True
                # Calculate y from x with y=x^3 + 7 function
                sign = pub_key[:2] == '03'
                self._x = int(self.x_hex, 16)
                ys = pow(self._x, 3, secp256k1_p) + 7 % secp256k1_p
                self._y = mod_sqrt(ys)
                if self._y & 1 != sign:
                    self._y = secp256k1_p - self._y
                self.y_hex = change_base(self._y, 10, 16, 64)
                self.public_uncompressed_hex = '04' + self.x_hex + self.y_hex
                self.public_compressed_hex = pub_key
            self.public_compressed_byte = bytes.fromhex(self.public_compressed_hex)
            self.public_uncompressed_byte = bytes.fromhex(self.public_uncompressed_hex)
            if self.compressed:
                self.public_byte = self.public_compressed_byte
            else:
                self.public_byte = self.public_uncompressed_byte
        elif self.is_private and self.key_format == 'decimal':
            self.secret = int(import_key)
            self.private_hex = change_base(self.secret, 10, 16, 64)
            self.private_byte = bytes.fromhex(self.private_hex)
        elif self.is_private:
            if self.key_format == 'hex':
                key_hex = import_key
                key_byte = bytes.fromhex(key_hex)
            elif self.key_format == 'hex_compressed':
                key_hex = import_key[:-2]
                key_byte = bytes.fromhex(key_hex)
                self.compressed = True
            elif self.key_format == 'bin':
                key_byte = import_key
                key_hex = key_byte.hex()
            elif self.key_format == 'bin_compressed':
                key_byte = import_key
                if len(import_key) in [33, 65, 129] and import_key[-1:] == b'\1':
                    key_byte = import_key[:-1]
                key_hex = key_byte.hex()
                self.compressed = True
            elif self.is_private and self.key_format in ['wif', 'wif_compressed']:
                # Check and remove Checksum, prefix and postfix tags
                key = change_base(import_key, 58, 256)
                checksum = key[-4:]
                key = key[:-4]
                if checksum != double_sha256(key)[:4]:
                    raise BKeyError("Invalid checksum, not a valid WIF key")
                found_networks = network_by_value('prefix_wif', key[0:1].hex())
                if not len(found_networks):
                    raise BKeyError("Unrecognised WIF private key, version byte unknown. Versionbyte: %s" % key[0:1])
                self._wif = import_key
                self._wif_prefix = key[0:1]
                # if self.network.name not in found_networks:
                #     if len(found_networks) > 1:
                #         raise BKeyError("More then one network found with this versionbyte, please specify network. "
                #                         "Networks found: %s" % found_networks)
                #     else:
                #         _logger.warning("Current network %s is different then the one found in key: %s" %
                #                         (network, found_networks[0]))
                #         self.network = Network(found_networks[0])
                if key[-1:] == b'\x01':
                    self.compressed = True
                    key = key[:-1]
                else:
                    self.compressed = False
                key_byte = key[1:]
                key_hex = key_byte.hex()
            else:
                raise BKeyError("Unknown key format %s" % self.key_format)

            if not (key_byte or key_hex):
                raise BKeyError("Cannot format key in hex or byte format")
            self.private_hex = key_hex
            self.private_byte = key_byte
            self.secret = int(key_hex, 16)
        else:
            raise BKeyError("Cannot import key. Public key format unknown")

        if self.is_private and not (self.public_byte or self.public_hex):
            if not self.is_private:
                raise BKeyError("Private key has no known secret number")
            p = ec_point(self.secret)
            if USE_FASTECDSA:
                self._x = p.x
                self._y = p.y
            else:
                self._x = p.x()
                self._y = p.y()
            self.x_hex = change_base(self._x, 10, 16, 64)
            self.y_hex = change_base(self._y, 10, 16, 64)
            if self._y % 2:
                prefix = '03'
            else:
                prefix = '02'

            self.public_compressed_hex = prefix + self.x_hex
            self.public_uncompressed_hex = '04' + self.x_hex + self.y_hex
            self.public_hex = self.public_compressed_hex if self.compressed else self.public_uncompressed_hex

            self.public_compressed_byte = bytes.fromhex(self.public_compressed_hex)
            self.public_uncompressed_byte = bytes.fromhex(self.public_uncompressed_hex)
            self.public_byte = self.public_compressed_byte if self.compressed else self.public_uncompressed_byte
        self._address_obj = None
        self._wif = None
        self._wif_prefix = None

    def __repr__(self):
        return "<Key(public_hex=%s, network=%s)>" % (self.public_hex, self.network.name)

    def __str__(self):
        return self.public_hex

    def __bytes__(self):
        return self.public_byte

    def __add__(self, other):
        return self.public_byte + other

    def __radd__(self, other):
        return other + self.public_byte

    def __len__(self):
        return len(self.public_byte)

    def __eq__(self, other):
        if other is None or not isinstance(other, Key):
            return False
        if self.is_private and other.is_private:
            return self.private_hex == other.private_hex
        else:
            return self.public_hex == other.public_hex

    def __hash__(self):
        if self.is_private:
            return hash(self.private_byte)
        else:
            return hash(self.public_byte)

    def __int__(self):
        if self.is_private:
            return self.secret
        else:
            return None

    @property
    def x(self):
        if not self._x and self.x_hex:
            self._x = int(self.x_hex, 16)
        return self._x

    @property
    def y(self):
        if not self._y and self.y_hex:
            self._y = int(self.y_hex, 16)
        return self._y

    def hex(self):
        return self.public_hex

    def as_dict(self, include_private=False):
        """
        Get current Key class as dictionary. Byte values are represented by hexadecimal strings.

        :param include_private: Include private key information in dictionary
        :type include_private: bool

        :return collections.OrderedDict:
        """

        key_dict = collections.OrderedDict()
        key_dict['network'] = self.network.name
        key_dict['key_format'] = self.key_format
        key_dict['compressed'] = self.compressed
        key_dict['is_private'] = self.is_private
        if include_private:
            key_dict['private_hex'] = self.private_hex
            key_dict['secret'] = self.secret
            key_dict['wif'] = self.wif()
        key_dict['public_hex'] = self.public_hex
        key_dict['public_uncompressed_hex'] = self.public_uncompressed_hex
        key_dict['hash160'] = self.hash160.hex()
        key_dict['address'] = self.address()
        x, y = self.public_point()
        key_dict['point_x'] = x
        key_dict['point_y'] = y
        return key_dict

    def as_json(self, include_private=False):
        """
        Get current key as json formatted string

        :param include_private: Include private key information in dictionary
        :type include_private: bool

        :return str:
        """
        return json.dumps(self.as_dict(include_private=include_private), indent=4)

    @staticmethod
    def _bip38_decrypt(encrypted_privkey, password, network=DEFAULT_NETWORK):
        """
        BIP0038 non-ec-multiply decryption. Returns WIF private key.
        Based on code from https://github.com/nomorecoin/python-bip38-testing
        This method is called by Key class init function when importing BIP0038 key.

        :param encrypted_privkey: Encrypted private key using WIF protected key format
        :type encrypted_privkey: str
        :param password: Required password for decryption
        :type password: str

        :return str: Private Key WIF
        """
        priv, addresshash, compressed = bip38_decrypt(encrypted_privkey, password)

        # Verify addresshash
        k = Key(priv, compressed=compressed, network=network)
        addr = k.address()
        if isinstance(addr, str):
            addr = addr.encode('utf-8')
        if double_sha256(addr)[0:4] != addresshash:
            raise BKeyError('Addresshash verification failed! Password or '
                            'specified network %s might be incorrect' % network)
        return priv, compressed

    def encrypt(self, password):
        """
        BIP0038 non-ec-multiply encryption. Returns BIP0038 encrypted private key
        Based on code from https://github.com/nomorecoin/python-bip38-testing

        >>> k = Key('cNUpWJbC1hVJtyxyV4bVAnb4uJ7FPhr82geo1vnoA29XWkeiiCQn')
        >>> k.encrypt('test')
        '6PYM8wAnnmAK5mHYoF7zqj88y5HtK7eiPeqPdu4WnYEFkYKEEoMFEVfuDg'

        :param password: Required password for encryption
        :type password: str

        :return str: BIP38 password encrypted private key
        """
        flagbyte = b'\xe0' if self.compressed else b'\xc0'
        return bip38_encrypt(self.private_hex, self.address(), password, flagbyte)

    @deprecated
    def bip38_encrypt(self, password):
        return self.encrypt(password)

    def wif(self, prefix=None):
        """
        Get private Key in Wallet Import Format, steps:
        # Convert to Binary and add 0x80 hex
        # Calculate Double SHA256 and add as checksum to end of key

        :param prefix: Specify versionbyte prefix in hexstring or bytes. Normally doesn't need to be specified, method uses default prefix from network settings
        :type prefix: str, bytes

        :return str: Base58Check encoded Private Key WIF
        """
        if not self.secret:
            raise BKeyError("WIF format not supported for public key")
        if prefix is None:
            versionbyte = self.network.prefix_wif
        else:
            if not isinstance(prefix, bytes):
                versionbyte = bytes.fromhex(prefix)
            else:
                versionbyte = prefix

        if self._wif and self._wif_prefix == versionbyte:
            return self._wif

        key = versionbyte + self.secret.to_bytes(32, byteorder='big')
        if self.compressed:
            key += b'\1'
        key += double_sha256(key)[:4]
        self._wif = base58encode(key)
        self._wif_prefix = versionbyte
        return self._wif

    def public(self):
        """
        Get public version of current key. Removes all private information from current key

        :return Key: Public key
        """
        key = deepcopy(self)
        key.is_private = False
        key.private_byte = None
        key.private_hex = None
        key.secret = None
        return key

    def public_point(self):
        """
        Get public key point on Elliptic curve

        :return tuple: (x, y) point
        """
        return (self.x, self.y)

    @property
    def hash160(self):
        """
        Get public key in RIPEMD-160 + SHA256 format

        :return bytes:
        """
        if not self._hash160:
            self._hash160 = hash160(self.public_byte if self.compressed else self.public_uncompressed_byte)
        return self._hash160

    @property
    def address_obj(self):
        """
        Get address object property. Create standard address object if not defined already.

        :return Address:
        """
        if not self._address_obj:
            self.address()
        return self._address_obj

    def address(self, compressed=None, prefix=None, script_type=None, encoding=None):
        """
        Get address derived from public key

        :param compressed: Always return compressed address
        :type compressed: bool
        :param prefix: Specify versionbyte prefix in hexstring or bytes. Normally doesn't need to be specified, method uses default prefix from network settings
        :type prefix: str, bytes
        :param script_type: Type of script, i.e. p2sh or p2pkh.
        :type script_type: str
        :param encoding: Address encoding. Default is base58 encoding, for segwit you can specify bech32 encoding
        :type encoding: str

        :return str: Base58 or Bech32 encoded address
        """
        if (self.compressed and compressed is None) or compressed:
            data = self.public_byte
            self.compressed = True
        else:
            data = self.public_uncompressed_byte
            self.compressed = False
        if encoding is None:
            if self._address_obj:
                encoding = self._address_obj.encoding
            else:
                encoding = 'base58'
        if not self.compressed and encoding == 'bech32':
            raise BKeyError("Uncompressed keys are non-standard for segwit/bech32 encoded addresses")
        if self._address_obj and script_type is None:
            script_type = self._address_obj.script_type
        if not (self._address_obj and self._address_obj.prefix == prefix and self._address_obj.encoding == encoding):
            self._address_obj = Address(data, prefix=prefix, network=self.network, script_type=script_type,
                                        encoding=encoding, compressed=compressed)
        return self._address_obj.address

    def address_uncompressed(self, prefix=None, script_type=None, encoding=None):
        """
        Get uncompressed address from public key

        :param prefix: Specify versionbyte prefix in hexstring or bytes. Normally doesn't need to be specified, method uses default prefix from network settings
        :type prefix: str, bytes
        :param script_type: Type of script, i.e. p2sh or p2pkh.
        :type script_type: str
        :param encoding: Address encoding. Default is base58 encoding, for segwit you can specify bech32 encoding
        :type encoding: str

        :return str: Base58 encoded address
        """
        return self.address(compressed=False, prefix=prefix, script_type=script_type, encoding=encoding)

    def info(self):
        """
        Prints key information to standard output

        """

        print("KEY INFO")
        print(" Network                     %s" % self.network.name)
        print(" Compressed                  %s" % self.compressed)
        if self.secret:
            print("SECRET EXPONENT")
            print(" Private Key (hex)              %s" % self.private_hex)
            print(" Private Key (long)             %s" % self.secret)
            if isinstance(self, HDKey):
                print(" Private Key (wif)              %s" % self.wif_key())
            else:
                print(" Private Key (wif)              %s" % self.wif())
        else:
            print("PUBLIC KEY ONLY, NO SECRET EXPONENT")
        print("PUBLIC KEY")
        print(" Public Key (hex)            %s" % self.public_hex)
        print(" Public Key uncompr. (hex)   %s" % self.public_uncompressed_hex)
        print(" Public Key Hash160          %s" % self.hash160.hex())
        print(" Address (b58)               %s" % self.address())
        point_x, point_y = self.public_point()
        print(" Point x                     %s" % point_x)
        print(" Point y                     %s" % point_y) def deserialize_address(address, encoding=None, network=None):
    """
    Deserialize address. Calculate public key hash and try to determine script type and network.

    The 'network' dictionary item with contains the network with highest priority if multiple networks are found. Same applies for the script type.

    Specify the network argument if network is known to avoid unexpected results.

    If more networks and or script types are found you can find these in the 'networks' field.

    >>> deserialize_address('1Khyc5eUddbhYZ8bEZi9wiN8TrmQ8uND4j')
    {'address': '1Khyc5eUddbhYZ8bEZi9wiN8TrmQ8uND4j', 'encoding': 'base58', 'public_key_hash': 'cd322766c02e7c37c3e3f9b825cd41ffbdcd17d7', 'public_key_hash_bytes': b"\\xcd2'f\\xc0.|7\\xc3\\xe3\\xf9\\xb8%\\xcdA\\xff\\xbd\\xcd\\x17\\xd7", 'prefix': b'\\x00', 'network': 'bitcoin', 'script_type': 'p2pkh', 'witness_type': 'legacy', 'networks': ['bitcoin', 'regtest']}

    :param address: A base58 or bech32 encoded address
    :type address: str
    :param encoding: Encoding scheme used for address encoding. Attempts to guess encoding if not specified.
    :type encoding: str
    :param network: Specify network filter, i.e.: bitcoin, testnet, litecoin, etc. Wil trigger check if address is valid for this network
    :type network: str

    :return dict: with information about this address
    """

    if encoding is None or encoding == 'base58':
        try:
            address_bytes = change_base(address, 58, 256, 25)
        except EncodingError:
            pass
        else:
            check = address_bytes[-4:]
            key_hash = address_bytes[:-4]
            checksum = double_sha256(key_hash)[0:4]
            if check != checksum and encoding == 'base58':
                raise BKeyError("Invalid address %s, checksum incorrect" % address)
            elif check == checksum:
                address_prefix = key_hash[0:1]
                networks_p2pkh = network_by_value('prefix_address', address_prefix.hex())
                networks_p2sh = network_by_value('prefix_address_p2sh', address_prefix.hex())
                public_key_hash = key_hash[1:]
                script_type = ''
                witness_type = ''
                networks = []
                if networks_p2pkh and not networks_p2sh:
                    script_type = 'p2pkh'
                    witness_type = 'legacy'
                    networks = networks_p2pkh
                elif networks_p2sh:
                    script_type = 'p2sh'
                    networks = networks_p2sh
                if network:
                    if network not in networks:
                        raise BKeyError("Network %s not found in extracted networks: %s" % (network, networks))
                elif len(networks) >= 1:
                    network = networks[0]
                return {
                    'address': address,
                    'encoding': 'base58',
                    'public_key_hash': '' if not public_key_hash else public_key_hash.hex(),
                    'public_key_hash_bytes': public_key_hash,
                    'prefix': address_prefix,
                    'network': network,
                    'script_type': script_type,
                    'witness_type': witness_type,
                    'networks': networks,
                }
    if encoding == 'bech32' or encoding is None:
        try:
            public_key_hash = addr_bech32_to_pubkeyhash(address)
            prefix = address[:address.rfind('1')]
            networks = network_by_value('prefix_bech32', prefix)
            witness_type = 'segwit'
            if len(public_key_hash) == 20:
                script_type = 'p2wpkh'
            else:
                script_type = 'p2wsh'
            return {
                'address': address,
                'encoding': 'bech32',
                'public_key_hash': '' if not public_key_hash else public_key_hash.hex(),
                'public_key_hash_bytes': public_key_hash,
                'prefix': prefix,
                'network': '' if not networks else networks[0],
                'script_type': script_type,
                'witness_type': witness_type,
                'networks': networks,
            }
        except EncodingError as err:
            raise EncodingError("Invalid address %s: %s" % (address, err))
    else:
        raise EncodingError("Address %s is not in specified encoding %s" % (address, encoding)) class Address(object):
    """
    Class to store, convert and analyse various address types as representation of public keys or scripts hashes
    """

    @classmethod
    @deprecated
    def import_address(cls, address, compressed=None, encoding=None, depth=None, change=None,
                       address_index=None, network=None, network_overrides=None):
        """
        Import an address to the Address class. Specify network if available, otherwise it will be
        derived form the address.

        :param address: Address to import
        :type address: str
        :param compressed: Is key compressed or not, default is None
        :type compressed: bool
        :param encoding: Address encoding. Default is base58 encoding, for native segwit addresses specify bech32 encoding. Leave empty to derive from address
        :type encoding: str
        :param depth: Level of depth in BIP32 key path
        :type depth: int
        :param change: Use 0 for normal address/key, and 1 for change address (for returned/change payments)
        :type change: int
        :param address_index: Index of address. Used in BIP32 key paths
        :type address_index: int
        :param network: Specify network filter, i.e.: bitcoin, testnet, litecoin, etc. Wil trigger check if address is valid for this network
        :type network: str
        :param network_overrides: Override network settings for specific prefixes, i.e.: {"prefix_address_p2sh": "32"}. Used by settings in providers.json
        :type network_overrides: dict

        :return Address:
        """
        return cls.parse(address, compressed, encoding, depth, change, address_index, network, network_overrides)

    @classmethod
    def parse(cls, address, compressed=None, encoding=None, depth=None, change=None,
              address_index=None, network=None, network_overrides=None):
        """
        Import an address to the Address class. Specify network if available, otherwise it will be
        derived form the address.

        >>> addr = Address.parse('bc1qyftqrh3hm2yapnhh0ukaht83d02a7pda8l5uhkxk9ftzqsmyu7pst6rke3')
        >>> addr.as_dict()
        {'network': 'bitcoin', '_data': None, 'script_type': 'p2wsh', 'encoding': 'bech32', 'compressed': None, 'witness_type': 'segwit', 'depth': None, 'change': None, 'address_index': None, 'prefix': 'bc', 'redeemscript': '', '_hashed_data': None, 'address': 'bc1qyftqrh3hm2yapnhh0ukaht83d02a7pda8l5uhkxk9ftzqsmyu7pst6rke3', 'address_orig': 'bc1qyftqrh3hm2yapnhh0ukaht83d02a7pda8l5uhkxk9ftzqsmyu7pst6rke3'}

        :param address: Address to import
        :type address: str
        :param compressed: Is key compressed or not, default is None
        :type compressed: bool
        :param encoding: Address encoding. Default is base58 encoding, for native segwit addresses specify bech32 encoding. Leave empty to derive from address
        :type encoding: str
        :param depth: Level of depth in BIP32 key path
        :type depth: int
        :param change: Use 0 for normal address/key, and 1 for change address (for returned/change payments)
        :type change: int
        :param address_index: Index of address. Used in BIP32 key paths
        :type address_index: int
        :param network: Specify network filter, i.e.: bitcoin, testnet, litecoin, etc. Wil trigger check if address is valid for this network
        :type network: str
        :param network_overrides: Override network settings for specific prefixes, i.e.: {"prefix_address_p2sh": "32"}. Used by settings in providers.json
        :type network_overrides: dict

        :return Address:
        """
        if encoding is None and address[:3].split("1")[0] in ENCODING_BECH32_PREFIXES:
            encoding = 'bech32'
        addr_dict = deserialize_address(address, encoding=encoding, network=network)
        public_key_hash_bytes = addr_dict['public_key_hash_bytes']
        prefix = addr_dict['prefix']
        if network is None:
            network = addr_dict['network']
        script_type = addr_dict['script_type']
        return Address(hashed_data=public_key_hash_bytes, prefix=prefix, script_type=script_type,
                       compressed=compressed, encoding=addr_dict['encoding'], depth=depth, change=change,
                       address_index=address_index, network=network, network_overrides=network_overrides)

    def __init__(self, data='', hashed_data='', prefix=None, script_type=None,
                 compressed=None, encoding=None, witness_type=None, depth=None, change=None,
                 address_index=None, network=DEFAULT_NETWORK, network_overrides=None):
        """
        Initialize an Address object. Specify a public key, redeemscript or a hash.

        >>> addr = Address('03715219f51a2681b7642d1e0e35f61e5288ff59b87d275be9eaf1a5f481dcdeb6', encoding='bech32', script_type='p2wsh')
        >>> addr.address
        'bc1qaehsuffn0stxmugx3z69z9hm6gnjd9qzeqlfv92cpf5adw63x4tsfl7vwl'

        :param data: Public key, redeem script or other type of script.
        :type data: str, bytes
        :param hashed_data: Hash of a public key or script. Will be generated if 'data' parameter is provided
        :type hashed_data: str, bytes
        :param prefix: Address prefix. Use default network / script_type prefix if not provided
        :type prefix: str, bytes
        :param script_type: Type of script, i.e. p2sh or p2pkh.
        :type script_type: str
        :param encoding: Address encoding. Default is base58 encoding, for native segwit addresses specify bech32 encoding
        :type encoding: str
        :param witness_type: Specify 'legacy', 'segwit' or 'p2sh-segwit'. Legacy for old-style bitcoin addresses, segwit for native segwit addresses and p2sh-segwit for segwit embedded in a p2sh script. Leave empty to derive automatically from script type if possible
        :type witness_type: str
        :param network: Bitcoin, testnet, litecoin or other network
        :type network: str, Network
        :param network_overrides: Override network settings for specific prefixes, i.e.: {"prefix_address_p2sh": "32"}. Used by settings in providers.json
        :type network_overrides: dict

        """
        self.network = network
        if not (data or hashed_data):
            raise BKeyError("Please specify data (public key or script) or hashed_data argument")
        if not isinstance(network, Network):
            self.network = Network(network)
        self.data_bytes = to_bytes(data)
        self._data = None
        self.script_type = script_type
        self.encoding = encoding
        self.compressed = compressed
        if witness_type is None:
            if self.script_type in ['p2wpkh', 'p2wsh']:
                witness_type = 'segwit'
            elif self.script_type in ['p2sh_p2wpkh', 'p2sh_p2wsh']:
                witness_type = 'p2sh-segwit'
        self.witness_type = witness_type
        self.depth = depth
        self.change = change
        self.address_index = address_index

        if self.encoding is None:
            if self.script_type in ['p2wpkh', 'p2wsh'] or self.witness_type == 'segwit':
                self.encoding = 'bech32'
            else:
                self.encoding = 'base58'
        self.hash_bytes = to_bytes(hashed_data)
        self.prefix = prefix
        self.redeemscript = b''
        if not self.hash_bytes:
            if (self.encoding == 'bech32' and self.script_type in ['p2sh', 'p2sh_multisig']) or \
                    self.script_type in ['p2wsh', 'p2sh_p2wsh']:
                self.hash_bytes = hashlib.sha256(self.data_bytes).digest()
            else:
                self.hash_bytes = hash160(self.data_bytes)
        self._hashed_data = None
        if self.encoding == 'base58':
            if self.script_type is None:
                self.script_type = 'p2pkh'
            if self.witness_type == 'p2sh-segwit':
                self.redeemscript = b'\0' + varstr(self.hash_bytes)
                # overwrite hash_bytes with hash of redeemscript
                self.hash_bytes = hash160(self.redeemscript)
            if self.prefix is None:
                if self.script_type in ['p2sh', 'p2sh_p2wpkh', 'p2sh_p2wsh', 'p2sh_multisig'] or \
                        self.witness_type == 'p2sh-segwit':
                    self.prefix = self.network.prefix_address_p2sh
                else:
                    self.prefix = self.network.prefix_address
            else:
                self.prefix = to_bytes(prefix)
        elif self.encoding == 'bech32':
            if self.script_type is None:
                self.script_type = 'p2wpkh'
            if self.prefix is None:
                self.prefix = self.network.prefix_bech32
        else:
            raise BKeyError("Encoding %s not supported" % self.encoding)
        self.address = pubkeyhash_to_addr(self.hash_bytes, prefix=self.prefix, encoding=self.encoding)
        self.address_orig = None
        provider_prefix = None
        if network_overrides and 'prefix_address_p2sh' in network_overrides and self.script_type == 'p2sh':
            provider_prefix = network_overrides['prefix_address_p2sh']
        self.address_orig = self.address
        if provider_prefix:
            self.address = addr_convert(self.address, provider_prefix)

    def __repr__(self):
        return "<Address(address=%s)>" % self.address

    @property
    def hashed_data(self):
        if not self._hashed_data:
            self._hashed_data = self.hash_bytes.hex()
        return self._hashed_data

    @property
    def data(self):
        if not self._data:
            self._data = self.data_bytes.hex()
        return self._data

    def as_dict(self):
        """
        Get current Address class as dictionary. Byte values are represented by hexadecimal strings

        :return dict:
        """
        addr_dict = deepcopy(self.__dict__)
        del (addr_dict['data_bytes'])
        del (addr_dict['hash_bytes'])
        if isinstance(addr_dict['network'], Network):
            addr_dict['network'] = addr_dict['network'].name
        addr_dict['redeemscript'] = addr_dict['redeemscript'].hex()
        addr_dict['prefix'] = addr_dict['prefix']
        return addr_dict

    def as_json(self):
        """
        Get current key as json formatted string

        :return str:
        """
        adict = self.as_dict()
        return json.dumps(adict, indent=4)

    def with_prefix(self, prefix):
        """
        Convert address using another prefix

        :param prefix: Address prefix
        :type prefix: str, bytes

        :return str: Converted address
        """
        return addr_convert(self.address, prefix) def sign(txid, private, use_rfc6979=True, k=None):
    """
    Sign transaction hash or message with secret private key. Creates a signature object.
    
    Sign a transaction hash with a private key and show DER encoded signature

    >>> sk = HDKey('728afb86a98a0b60cc81faadaa2c12bc17d5da61b8deaf1c08fc07caf424d493')
    >>> txid = 'c77545c8084b6178366d4e9a06cf99a28d7b5ff94ba8bd76bbbce66ba8cdef70'
    >>> signature = sign(txid, sk)
    >>> signature.as_der_encoded().hex()
    '30440220792f04c5ba654e27eb636ceb7804c5590051dd77da8b80244f1fa8dfbff369b302204ba03b039c808a0403d067f3d75fbe9c65831444c35d64d4192b408d2a7410a101'

    :param txid: Transaction signature or transaction hash. If unhashed transaction or message is provided the double_sha256 hash of message will be calculated.
    :type txid: bytes, str
    :param private: Private key as HDKey or Key object, or any other string accepted by HDKey object
    :type private: HDKey, Key, str, hexstring, bytes
    :param use_rfc6979: Use deterministic value for k nonce to derive k from txid/message according to RFC6979 standard. Default is True, set to False to use random k
    :type use_rfc6979: bool
    :param k: Provide own k. Only use for testing or if you known what you are doing. Providing wrong value for k can result in leaking your private key!
    :type k: int
        
    :return Signature: 
    """
    return Signature.create(txid, private, use_rfc6979, k) def verify(self, txid=None, public_key=None):
    """
    Verify this signature. Provide txid or public_key if not already known

    >>> k = 'b2da575054fb5daba0efde613b0b8e37159b8110e4be50f73cbe6479f6038f5b'
    >>> pub_key = HDKey(k).public()
    >>> txid = '0d12fdc4aac9eaaab9730999e0ce84c3bd5bb38dfd1f4c90c613ee177987429c'
    >>> sig = '48e994862e2cdb372149bad9d9894cf3a5562b4565035943efe0acc502769d351cb88752b5fe8d70d85f3541046df617f8459e991d06a7c0db13b5d4531cd6d4'
    >>> sig = Signature.parse_hex(sig)
    >>> sig.verify(txid, pub_key)
    True

    :param txid: Transaction hash
    :type txid: bytes, hexstring
    :param public_key: Public key P
    :type public_key: HDKey, Key, str, hexstring, bytes
                
    :return bool: 
    """
    if txid is not None:
        self.txid = to_hexstring(txid)
    if public_key is not None:
        self.public_key = public_key

    if not self.txid or not self.public_key:
        raise BKeyError("Please provide txid and public_key to verify signature")

    if USE_FASTECDSA:
        return _ecdsa.verify(
            str(self.r),
            str(self.s),
            self.txid,
            str(self.x),
            str(self.y),
            str(secp256k1_p),
            str(secp256k1_a),
            str(secp256k1_b),
            str(secp256k1_n),
            str(secp256k1_Gx),
            str(secp256k1_Gy)
        )
    else:
        transaction_to_sign = to_bytes(self.txid)
        signature = self.bytes()
        if len(transaction_to_sign) != 32:
            transaction_to_sign = double_sha256(transaction_to_sign)
        ver_key = ecdsa.VerifyingKey.from_string(self.public_key.public_uncompressed_byte[1:],
                                                 curve=ecdsa.SECP256k1)
        try:
            if len(signature) > 64 and signature.startswith(b'\x30'):
                try:
                    signature = convert_der_sig(signature[:-1], as_hex=False)
                except Exception:
                    pass
            ver_key.verify_digest(signature, transaction_to_sign)
        except ecdsa.keys.BadSignatureError:
            return False
        except ecdsa.keys.BadDigestError as e:
            _logger.info("Bad Digest %s (error %s)" % (signature.hex(), e))
            return False
        return True class Signature(object):
    """
    Signature class for transactions. Used to create signatures to sign transaction and verification
    
    Sign a transaction hash with a private key and show DER encoded signature:

    >>> sk = HDKey('f2620684cef2b677dc2f043be8f0873b61e79b274c7e7feeb434477c082e0dc2')
    >>> txid = 'c77545c8084b6178366d4e9a06cf99a28d7b5ff94ba8bd76bbbce66ba8cdef70'
    >>> signature = sign(txid, sk)
    >>> signature.as_der_encoded().hex()
    '3044022015f9d39d8b53c68c7549d5dc4cbdafe1c71bae3656b93a02d2209e413d9bbcd00220615cf626da0a81945a707f42814cc51ecde499442eb31913a870b9401af6a4ba01'
    
    """

    @classmethod
    def parse(cls, signature, public_key=None):
        if isinstance(signature, bytes):
            return cls.parse_bytes(signature, public_key)
        elif isinstance(signature, str):
            return cls.parse_hex(signature, public_key)

    @classmethod
    def parse_hex(cls, signature, public_key=None):
        return cls.parse_bytes(bytes.fromhex(signature), public_key)

    @staticmethod
    def parse_bytes(signature, public_key=None):
        """
        Create a signature from signature string with r and s part. Signature length must be 64 bytes or 128
        character hexstring

        :param signature: Signature string
        :type signature: bytes
        :param public_key: Public key as HDKey or Key object or any other string accepted by HDKey object
        :type public_key: HDKey, Key, str, hexstring, bytes

        :return Signature:
        """

        der_signature = ''
        hash_type = SIGHASH_ALL
        if len(signature) > 64 and signature.startswith(b'\x30'):
            der_signature = signature[:-1]
            hash_type = int.from_bytes(signature[-1:], 'big')
            signature = convert_der_sig(signature[:-1], as_hex=False)
        if len(signature) != 64:
            raise BKeyError("Signature length must be 64 bytes or 128 character hexstring")
        r = int.from_bytes(signature[:32], 'big')
        s = int.from_bytes(signature[32:], 'big')
        return Signature(r, s, signature=signature, der_signature=der_signature, public_key=public_key,
                         hash_type=hash_type)

    @staticmethod
    @deprecated
    def from_str(signature, public_key=None):
        """
        Create a signature from signature string with r and s part. Signature length must be 64 bytes or 128 
        character hexstring 
        
        :param signature: Signature string
        :type signature: bytes, str
        :param public_key: Public key as HDKey or Key object or any other string accepted by HDKey object
        :type public_key: HDKey, Key, str, hexstring, bytes
        
        :return Signature: 
        """

        signature = to_bytes(signature)
        return Signature(signature, public_key)

    @staticmethod
    def create(txid, private, use_rfc6979=True, k=None):
        """
        Sign a transaction hash and create a signature with provided private key.

        >>> k = 'b2da575054fb5daba0efde613b0b8e37159b8110e4be50f73cbe6479f6038f5b'
        >>> txid = '0d12fdc4aac9eaaab9730999e0ce84c3bd5bb38dfd1f4c90c613ee177987429c'
        >>> sig = Signature.create(txid, k)
        >>> sig.hex()
        '48e994862e2cdb372149bad9d9894cf3a5562b4565035943efe0acc502769d351cb88752b5fe8d70d85f3541046df617f8459e991d06a7c0db13b5d4531cd6d4'
        >>> sig.r
        32979225540043540145671192266052053680452913207619328973512110841045982813493
        >>> sig.s
        12990793585889366641563976043319195006380846016310271470330687369836458989268

        :param txid: Transaction signature or transaction hash. If unhashed transaction or message is provided the double_sha256 hash of message will be calculated.
        :type txid: bytes, str
        :param private: Private key as HDKey or Key object, or any other string accepted by HDKey object
        :type private: HDKey, Key, str, hexstring, bytes
        :param use_rfc6979: Use deterministic value for k nonce to derive k from txid/message according to RFC6979 standard. Default is True, set to False to use random k
        :type use_rfc6979: bool
        :param k: Provide own k. Only use for testing or if you known what you are doing. Providing wrong value for k can result in leaking your private key!
        :type k: int
        
        :return Signature: 
        """
        if isinstance(txid, bytes):
            txid = txid.hex()
        if len(txid) > 64:
            txid = double_sha256(bytes.fromhex(txid), as_hex=True)
        if not isinstance(private, (Key, HDKey)):
            private = HDKey(private)
        pub_key = private.public()
        secret = private.secret

        if not k:
            if use_rfc6979 and USE_FASTECDSA:
                rfc6979 = RFC6979(txid, secret, secp256k1_n, hashlib.sha256)
                k = rfc6979.gen_nonce()
            else:
                global rfc6979_warning_given
                if not USE_FASTECDSA and not rfc6979_warning_given:
                    _logger.warning("RFC6979 only supported when fastecdsa library is used")
                    rfc6979_warning_given = True
                k = random.SystemRandom().randint(1, secp256k1_n - 1)

        if USE_FASTECDSA:
            r, s = _ecdsa.sign(
                txid,
                str(secret),
                str(k),
                str(secp256k1_p),
                str(secp256k1_a),
                str(secp256k1_b),
                str(secp256k1_n),
                str(secp256k1_Gx),
                str(secp256k1_Gy)
            )
            if int(s) > secp256k1_n / 2:
                s = secp256k1_n - int(s)
            return Signature(r, s, txid, secret, public_key=pub_key, k=k)
        else:
            sk = ecdsa.SigningKey.from_string(private.private_byte, curve=ecdsa.SECP256k1)
            txid_bytes = to_bytes(txid)
            sig_der = sk.sign_digest(txid_bytes, sigencode=ecdsa.util.sigencode_der, k=k)
            signature = convert_der_sig(sig_der)
            r = int(signature[:64], 16)
            s = int(signature[64:], 16)
            if s > secp256k1_n / 2:
                s = secp256k1_n - s
            return Signature(r, s, txid, secret, public_key=pub_key, der_signature=sig_der, signature=signature, k=k)

    def __init__(self, r, s, txid=None, secret=None, signature=None, der_signature=None, public_key=None, k=None,
                 hash_type=SIGHASH_ALL):
        """
        Initialize Signature object with provided r and r value

        >>> r = 32979225540043540145671192266052053680452913207619328973512110841045982813493
        >>> s = 12990793585889366641563976043319195006380846016310271470330687369836458989268
        >>> sig = Signature(r, s)
        >>> sig.hex()
        '48e994862e2cdb372149bad9d9894cf3a5562b4565035943efe0acc502769d351cb88752b5fe8d70d85f3541046df617f8459e991d06a7c0db13b5d4531cd6d4'
        
        :param r: r value of signature
        :type r: int
        :param s: s value of signature
        :type s: int
        :param txid: Transaction hash z to sign if known
        :type txid: bytes, hexstring
        :param secret: Private key secret number
        :type secret: int
        :param signature: r and s value of signature as string
        :type signature: str, bytes
        :param der_signature: DER encoded signature
        :type der_signature: str, bytes
        :param public_key: Provide public key P if known
        :type public_key: HDKey, Key, str, hexstring, bytes
        :param k: k value used for signature
        :type k: int
        """

        self.r = int(r)
        self.s = int(s)
        self.x = None
        self.y = None
        if self.r < 1 or self.r >= secp256k1_n:
            raise BKeyError('Invalid Signature: r is not a positive integer smaller than the curve order')
        elif self.s < 1 or self.s >= secp256k1_n:
            raise BKeyError('Invalid Signature: s is not a positive integer smaller than the curve order')
        self._txid = None
        self.txid = txid
        self.secret = None if not secret else int(secret)
        if isinstance(signature, bytes):
            self._signature = signature
            signature = signature.hex()
        else:
            self._signature = to_bytes(signature)
        if signature and len(signature) != 128:
            raise BKeyError('Invalid Signature: length must be 64 bytes')
        self._public_key = None
        self.public_key = public_key
        self.k = k
        self.hash_type = hash_type
        self.hash_type_byte = self.hash_type.to_bytes(1, 'big')
        self.der_signature = der_signature
        if not der_signature:
            self.der_signature = der_encode_sig(self.r, self.s)

        self._der_encoded = to_bytes(der_signature) + self.hash_type_byte

    def __repr__(self):
        der_sig = '' if not self._der_encoded else self._der_encoded.hex()
        return "<Signature(r=%d, s=%d, signature=%s, der_signature=%s)>" % \
               (self.r, self.s, self.hex(), der_sig)

    def __str__(self):
        return self.as_der_encoded(as_hex=True)

    def __bytes__(self):
        return self.as_der_encoded()

    def __add__(self, other):
        return self.as_der_encoded() + other

    def __radd__(self, other):
        return other + self.as_der_encoded()

    def __len__(self):
        return len(self.as_der_encoded())

    @property
    def txid(self):
        return self._txid

    @txid.setter
    def txid(self, value):
        if value is not None:
            self._txid = value
            if isinstance(value, bytes):
                self._txid = value.hex()

    @property
    def public_key(self):
        """
        Return public key as HDKey object
        
        :return HDKey: 
        """
        return self._public_key

    @public_key.setter
    def public_key(self, value):
        if value is None:
            return
        if isinstance(value, bytes):
            value = HDKey(value)
        if value.is_private:
            value = value.public()
        self.x, self.y = value.public_point()

        if USE_FASTECDSA:
            if not fastecdsa_secp256k1.is_point_on_curve((self.x, self.y)):
                raise BKeyError('Invalid public key, point is not on secp256k1 curve')
        self._public_key = value

    def hex(self):
        """
        Signature r and s value as single hexadecimal string

        :return hexstring:
        """
        return self.bytes().hex()

    def __index__(self):
        return self.bytes()

    def bytes(self):
        """
        Signature r and s value as single bytes string

        :return bytes:
        """

        if not self._signature:
            self._signature = self.r.to_bytes(32, 'big') + self.s.to_bytes(32, 'big')
        return self._signature

    def as_der_encoded(self, as_hex=False, include_hash_type=True):
        """
        Get DER encoded signature

        :param as_hex: Output as hexstring
        :type as_hex: bool
        :param include_hash_type: Include hash_type byte at end of signatures as used in raw scripts. Default is True
        :type include_hash_type: bool

        :return bytes: 
        """
        if not self._der_encoded or len(self._der_encoded) < 2:
            self._der_encoded = der_encode_sig(self.r, self.s) + self.hash_type_byte

        if include_hash_type:
            return self._der_encoded.hex() if as_hex else self._der_encoded
        else:
            return der_encode_sig(self.r, self.s).hex() if as_hex else der_encode_sig(self.r, self.s)

    def verify(self, txid=None, public_key=None):
        """
        Verify this signature. Provide txid or public_key if not already known

        >>> k = 'b2da575054fb5daba0efde613b0b8e37159b8110e4be50f73cbe6479f6038f5b'
        >>> pub_key = HDKey(k).public()
        >>> txid = '0d12fdc4aac9eaaab9730999e0ce84c3bd5bb38dfd1f4c90c613ee177987429c'
        >>> sig = '48e994862e2cdb372149bad9d9894cf3a5562b4565035943efe0acc502769d351cb88752b5fe8d70d85f3541046df617f8459e991d06a7c0db13b5d4531cd6d4'
        >>> sig = Signature.parse_hex(sig)
        >>> sig.verify(txid, pub_key)
        True

        :param txid: Transaction hash
        :type txid: bytes, hexstring
        :param public_key: Public key P
        :type public_key: HDKey, Key, str, hexstring, bytes
                
        :return bool: 
        """
        if txid is not None:
            self.txid = to_hexstring(txid)
        if public_key is not None:
            self.public_key = public_key

        if not self.txid or not self.public_key:
            raise BKeyError("Please provide txid and public_key to verify signature")

        if USE_FASTECDSA:
            return _ecdsa.verify(
                str(self.r),
                str(self.s),
                self.txid,
                str(self.x),
                str(self.y),
                str(secp256k1_p),
                str(secp256k1_a),
                str(secp256k1_b),
                str(secp256k1_n),
                str(secp256k1_Gx),
                str(secp256k1_Gy)
            )
        else:
            transaction_to_sign = to_bytes(self.txid)
            signature = self.bytes()
            if len(transaction_to_sign) != 32:
                transaction_to_sign = double_sha256(transaction_to_sign)
            ver_key = ecdsa.VerifyingKey.from_string(self.public_key.public_uncompressed_byte[1:],
                                                     curve=ecdsa.SECP256k1)
            try:
                if len(signature) > 64 and signature.startswith(b'\x30'):
                    try:
                        signature = convert_der_sig(signature[:-1], as_hex=False)
                    except Exception:
                        pass
                ver_key.verify_digest(signature, transaction_to_sign)
            except ecdsa.keys.BadSignatureError:
                return False
            except ecdsa.keys.BadDigestError as e:
                _logger.info("Bad Digest %s (error %s)" % (signature.hex(), e))
                return False
            return True class Network(object):
    """
    Network class with all network definitions. 
    
    Prefixes for WIF, P2SH keys, HD public and private keys, addresses. A currency symbol and type, the 
    denominator (such as satoshi) and a BIP0044 cointype.
    
    """

    def __init__(self, network_name=DEFAULT_NETWORK):
        if network_name not in NETWORK_DEFINITIONS:
            raise NetworkError("Network %s not found in network definitions" % network_name)
        self.name = network_name

        self.currency_name = NETWORK_DEFINITIONS[network_name]['currency_name']
        self.currency_name_plural = NETWORK_DEFINITIONS[network_name]['currency_name_plural']
        self.currency_code = NETWORK_DEFINITIONS[network_name]['currency_code']
        self.currency_symbol = NETWORK_DEFINITIONS[network_name]['currency_symbol']
        self.description = NETWORK_DEFINITIONS[network_name]['description']
        self.prefix_address_p2sh = bytes.fromhex(NETWORK_DEFINITIONS[network_name]['prefix_address_p2sh'])
        self.prefix_address = bytes.fromhex(NETWORK_DEFINITIONS[network_name]['prefix_address'])
        self.prefix_bech32 = NETWORK_DEFINITIONS[network_name]['prefix_bech32']
        self.prefix_wif = bytes.fromhex(NETWORK_DEFINITIONS[network_name]['prefix_wif'])
        self.denominator = NETWORK_DEFINITIONS[network_name]['denominator']
        self.bip44_cointype = NETWORK_DEFINITIONS[network_name]['bip44_cointype']
        self.dust_amount = NETWORK_DEFINITIONS[network_name]['dust_amount']  # Dust amount in satoshi
        self.fee_default = NETWORK_DEFINITIONS[network_name]['fee_default']  # Default fee in satoshi per kilobyte
        self.fee_min = NETWORK_DEFINITIONS[network_name]['fee_min']  # Minimum transaction fee in satoshi per kilobyte
        self.fee_max = NETWORK_DEFINITIONS[network_name]['fee_max']  # Maximum transaction fee in satoshi per kilobyte
        self.priority = NETWORK_DEFINITIONS[network_name]['priority']
        self.prefixes_wif = NETWORK_DEFINITIONS[network_name]['prefixes_wif']

        # This could be more shorter and more flexible with this code, but this gives 'Unresolved attributes' warnings
        # for f in list(NETWORK_DEFINITIONS[network_name].keys()):
        #     exec("self.%s = NETWORK_DEFINITIONS[network_name]['%s']" % (f, f))

    def __repr__(self):
        return "<Network: %s>" % self.name

    def __eq__(self, other):
        if isinstance(other, str):
            return self.name == other
        return self.name == other.name

    def __hash__(self):
        return hash(self.name)

    # Replaced by Value class
    @deprecated
    def print_value(self, value, rep='string', denominator=1, decimals=None):
        """
        Return the value as string with currency symbol

        Print value for 100000 satoshi as string in human readable format

        >>> Network('bitcoin').print_value(100000)
        '0.00100000 BTC'

        :param value: Value in smallest denominator such as Satoshi
        :type value: int, float
        :param rep: Currency representation: 'string', 'symbol', 'none' or your own custom name
        :type rep: str
        :param denominator: Unit to use in representation. Default is 1. I.e. 1 = 1 BTC, 0.001 = milli BTC / mBTC
        :type denominator: float
        :param decimals: Number of digits after the decimal point, leave empty for automatic determination based on value. Use integer value between 0 and 8
        :type decimals: int

        :return str: 
        """
        if denominator not in NETWORK_DENOMINATORS:
            raise NetworkError("Denominator not found in definitions, use one of the following values: %s" %
                               NETWORK_DENOMINATORS.keys())
        if value is None:
            return ""
        symb = rep
        if rep == 'string':
            symb = NETWORK_DENOMINATORS[denominator] + self.currency_code
        elif rep == 'symbol':
            symb = NETWORK_DENOMINATORS[denominator] + self.currency_symbol
        elif rep == 'none':
            symb = ''
        decimals = decimals if decimals is not None else -int(math.log10(self.denominator / denominator))
        decimals = 0 if decimals < 0 else decimals
        decimals = 8 if decimals > 8 else decimals
        balance = round(float(value) * self.denominator / denominator, decimals)
        format_str = "%%.%df %%s" % decimals
        return (format_str % (balance, symb)).rstrip()

    def wif_prefix(self, is_private=False, witness_type='legacy', multisig=False):
        """
        Get WIF prefix for this network and specifications in arguments

        >>> Network('bitcoin').wif_prefix()  # xpub
        b'\\x04\\x88\\xb2\\x1e'
        >>> Network('bitcoin').wif_prefix(is_private=True, witness_type='segwit', multisig=True)  # Zprv
        b'\\x02\\xaaz\\x99'

        :param is_private: Private or public key, default is True
        :type is_private: bool
        :param witness_type: Legacy, segwit or p2sh-segwit
        :type witness_type: str
        :param multisig: Multisignature or single signature wallet. Default is False: no multisig
        :type multisig: bool

        :return bytes:
        """
        script_type = script_type_default(witness_type, multisig, locking_script=True)
        if script_type == 'p2sh' and witness_type in ['p2sh-segwit', 'segwit']:
            script_type = 'p2sh_p2wsh' if multisig else 'p2sh_p2wpkh'
        if is_private:
            ip = 'private'
        else:
            ip = 'public'
        found_prefixes = [bytes.fromhex(pf[0]) for pf in self.prefixes_wif if pf[2] == ip and script_type == pf[5]]
        if found_prefixes:
            return found_prefixes[0]
        else:
            raise NetworkError("WIF Prefix for script type %s not found" % script_type) class Value:
    """
    Class to represent and convert cryptocurrency values
    """

    @classmethod
    def from_satoshi(cls, value, denominator=None, network=DEFAULT_NETWORK):
        """
        Initialize Value class with smallest denominator as input. Such as represented in script and transactions cryptocurrency values.

        :param value: Amount of Satoshi's / smallest denominator for this network
        :type value: int
        :param denominator: Denominator as integer or string. Such as 0.001 or m for milli, 1000 or k for kilo, etc. See NETWORK_DENOMINATORS for list of available denominator symbols.
        :type denominator: int, float, str
        :param network: Specify network if not supplied already in the value string
        :type network: str, Network

        :return Value:
        """
        if not isinstance(network, Network):
            network = Network(network)
        if denominator is None:
            denominator = network.denominator
        else:
            if isinstance(denominator, str):
                dens = [den for den, symb in NETWORK_DENOMINATORS.items() if symb == denominator]
                if dens:
                    denominator = dens[0]
            value = value * (network.denominator / denominator)
        return cls(value or 0, denominator, network)

    def __init__(self, value, denominator=None, network=DEFAULT_NETWORK):
        """
        Create a new Value class. Specify value as integer, float or string. If a string is provided
        the amount, denominator and currency will be extracted if provided

        Examples: Initialize value class
        >>> Value(10)
        Value(value=10.00000000000000, denominator=1.00000000, network='bitcoin')

        >>> Value('15 mBTC')
        Value(value=0.01500000000000, denominator=0.00100000, network='bitcoin')

        >>> Value('10 sat')
        Value(value=0.00000010000000, denominator=0.00000001, network='bitcoin')

        >>> Value('1 doge')
        Value(value=1.00000000000000, denominator=1.00000000, network='dogecoin')

        >>> Value(500, 'm')
        Value(value=0.50000000000000, denominator=0.00100000, network='bitcoin')

        >>> Value(500, 0.001)
        Value(value=0.50000000000000, denominator=0.00100000, network='bitcoin')

        All frequently used arithmetic, comparision and logical operators can be used on the Value object. So you can compare Value object, add them together, divide or multiply them, etc.

        Values need to use the same network / currency if you work with multiple Value objects. I.e. Value('1 BTC') + Value('1 LTC') raises an error.

        # Examples: Value operators
        >>> Value('50000 sat') == Value('5000 fin')  # 1 Satoshi equals 10 Finney, see https://en.bitcoin.it/wiki/Units
        True

        >>> Value('1 btc') > Value('2 btc')
        False

        >>> Value('1000 LTC') / 5
        Value(value=200.00000000000000, denominator=1.00000000, network='litecoin')

        >>> Value('0.002 BTC') + 0.02
        Value(value=0.02200000000000, denominator=1.00000000, network='bitcoin')

        The Value class can be represented in several formats.

        # Examples: Format Value class
        >>> int(Value("10.1 BTC"))
        10

        >>> float(Value("10.1 BTC"))
        10.1

        >>> round(Value("10.123 BTC"), 2).str()
        '10.12000000 BTC'

        >>> hex(Value("10.1 BTC"))
        '0x3c336080'

        :param value: Value as integer, float or string. Numeric values must be supllied in smallest denominator such as Satoshi's. String values must be in the format: <value> [<denominator>][<currency_symbol>]
        :type value: int, float, str
        :param denominator: Denominator as integer or string. Such as 0.001 or m for milli, 1000 or k for kilo, etc. See NETWORK_DENOMINATORS for list of available denominator symbols.
        :type denominator: int, float, str
        :param network: Specify network if not supplied already in the value string
        :type network: str, Network

        """
        self.network = network
        if not isinstance(network, Network):
            self.network = Network(network)
        if isinstance(denominator, str):
            dens = [den for den, symb in NETWORK_DENOMINATORS.items() if symb == denominator]
            if dens:
                denominator = dens[0]
        den_arg = denominator

        if isinstance(value, str):
            value_items = value.split()
            value = value_items[0]
            cur_code = self.network.currency_code
            den_input = 1
            if len(value_items) > 1:
                cur_code = value_items[1]
            network_names = [n for n in NETWORK_DEFINITIONS if
                             NETWORK_DEFINITIONS[n]['currency_code'].upper() == cur_code.upper()]
            if network_names:
                self.network = Network(network_names[0])
                self.currency = cur_code
            else:
                for den, symb in NETWORK_DENOMINATORS.items():
                    if len(symb) and cur_code[:len(symb)] == symb:
                        cur_code = cur_code[len(symb):]
                        network_names = [n for n in NETWORK_DEFINITIONS if
                                         NETWORK_DEFINITIONS[n]['currency_code'].upper() == cur_code.upper()]
                        if network_names:
                            self.network = Network(network_names[0])
                            self.currency = cur_code
                        elif len(cur_code):
                            raise ValueError("Currency symbol not recognised")
                        den_input = den
                        break
            self.value = float(value) * den_input
            self.denominator = den_input if den_arg is None else den_arg
        else:
            self.denominator = den_arg or 1.0
            self.value = float(value) * self.denominator

    def __str__(self):
        return self.str()

    def __repr__(self):
        return "Value(value=%.14f, denominator=%.8f, network='%s')" % \
               (self.value, self.denominator, self.network.name)

    def __int__(self):
        return int(self.value)

    def __float__(self):
        if self.value > self.network.denominator:
            return round(self.value, -int(math.log10(self.network.denominator)))
        else:
            return self.value

    def __lt__(self, other):
        if self.network != other.network:
            raise ValueError("Cannot compare values from different networks")
        return self.value < other.value

    def __le__(self, other):
        if self.network != other.network:
            raise ValueError("Cannot compare values from different networks")
        return self.value <= other.value

    def __eq__(self, other):
        if isinstance(other, Value):
            if self.network != other.network:
                raise ValueError("Cannot compare values from different networks")
            return self.value == other.value
        else:
            other = Value(other)
            return self.value == other.value and self.network == other.network

    def __ne__(self, other):
        return not self.__eq__(other)

    def __ge__(self, other):
        if self.network != other.network:
            raise ValueError("Cannot compare values from different networks")
        return self.value >= other.value

    def __gt__(self, other):
        if self.network != other.network:
            raise ValueError("Cannot compare values from different networks")
        return self.value > other.value

    def __add__(self, other):
        if isinstance(other, Value):
            if self.network != other.network:
                raise ValueError("Cannot calculate with values from different networks")
            other = other.value
        return Value((self.value + other) / self.denominator, self.denominator, self.network)

    def __iadd__(self, other):
        if isinstance(other, Value):
            if self.network != other.network:
                raise ValueError("Cannot calculate with values from different networks")
            other = other.value
        return Value((self.value + other) / self.denominator, self.denominator, self.network)

    def __isub__(self, other):
        if isinstance(other, Value):
            if self.network != other.network:
                raise ValueError("Cannot calculate with values from different networks")
            other = other.value
        return Value((self.value - other) / self.denominator, self.denominator, self.network)

    def __sub__(self, other):
        if isinstance(other, Value):
            if self.network != other.network:
                raise ValueError("Cannot calculate with values from different networks")
            other = other.value
        return Value((self.value - other) / self.denominator, self.denominator, self.network)

    def __mul__(self, other):
        return Value((self.value * other) / self.denominator, self.denominator, self.network)

    def __truediv__(self, other):
        return Value((self.value / other) / self.denominator, self.denominator, self.network)

    def __floordiv__(self, other):
        return Value(((self.value / self.denominator) // other), self.denominator, self.network)

    def __round__(self, n=0):
        val = round(self.value / self.denominator, n) * self.denominator
        return Value(val, self.denominator, self.network)

    def __index__(self):
        return self.value_sat

    def str(self, denominator=None, decimals=None, currency_repr='code'):
        """
        Get string representation of Value with requested denominator and number of decimals.

        >>> Value(1200000, 'sat').str('m')  # milli Bitcoin
        '12.00000 mBTC'

        >>> Value(12000.3, 'sat').str(1)  # Use denominator = 1 for Bitcoin
        '0.00012000 BTC'

        >>> Value(12000, 'sat').str('auto')
        '120.00 µBTC'

        >>> Value(0.005).str('m')
        '5.00000 mBTC'

        >>> Value(12000, 'sat').str('auto', decimals=0)
        '120 µBTC'

        >>> Value('13000000 Doge').str('auto')  # Yeah, mega Dogecoins...
        '13.00000000 MDOGE'

        >>> Value('2100000000').str('auto')
        '2.10000000 GBTC'

        >>> Value('1.5 BTC').str(currency_repr='symbol')
        '1.50000000 ₿'

        >>> Value('1.5 BTC').str(currency_repr='name')
        '1.50000000 bitcoins'

        :param denominator: Denominator as integer or string. Such as 0.001 or m for milli, 1000 or k for kilo, etc. See NETWORK_DENOMINATORS for list of available denominator symbols. If not provided the default self.denominator value is used. Use value 'auto' to automatically determine best denominator for human readability.
        :type denominator: int, float, str
        :param decimals: Number of decimals to use
        :type decimals: float
        :param currency_repr: Representation of currency. I.e. code: BTC, name: bitcoins, symbol: ₿
        :type currency_repr: str

        :return str:
        """
        if denominator is None:
            denominator = self.denominator
        elif denominator == 'auto':
            # First try denominator=1 and smallest denominator (satoshi)
            if 0.001 <= self.value < 1000:
                denominator = 1
            elif 1 <= self.value / self.network.denominator < 1000:
                denominator = self.network.denominator
            else:  # Try other frequently used denominators
                for den, symb in NETWORK_DENOMINATORS.items():
                    if symb in ['n', 'fin', 'da', 'c', 'd', 'h']:
                        continue
                    if 1 <= self.value / den < 1000:
                        denominator = den
        elif isinstance(denominator, str):
            dens = [den for den, symb in NETWORK_DENOMINATORS.items() if symb == denominator[:len(symb)] and len(symb)]
            if len(dens) > 1:
                dens = [den for den, symb in NETWORK_DENOMINATORS.items() if symb == denominator]
            if dens:
                denominator = dens[0]
        if denominator in NETWORK_DENOMINATORS:
            den_symb = NETWORK_DENOMINATORS[denominator]
        else:
            raise ValueError("Denominator not found in NETWORK_DENOMINATORS definition")

        if decimals is None:
            decimals = -int(math.log10(self.network.denominator / denominator))
            if decimals > 8:
                decimals = 8
        if decimals < 0:
            decimals = 0
        balance = round(self.value / denominator, decimals)
        cur_code = self.network.currency_code
        if currency_repr == 'symbol':
            cur_code = self.network.currency_symbol
        if currency_repr == 'name':
            cur_code = self.network.currency_name_plural
        if 'sat' in den_symb and self.network.name == 'bitcoin':
            cur_code = ''
        return ("%%.%df %%s%%s" % decimals) % (balance, den_symb, cur_code)

    def str_unit(self, decimals=None, currency_repr='code'):
        """
        String representation of this Value. Wrapper for the :func:`str` method, but always uses 1 as denominator, meaning main denominator such as BTC, LTC.

        >>> Value('12000 sat').str_unit()
        '0.00012000 BTC'

        :param decimals: Number of decimals to use
        :type decimals: float
        :param currency_repr: Representation of currency. I.e. code: BTC, name: Bitcoin, symbol: ₿
        :type currency_repr: str
        :return str:
        """
        return self.str(1, decimals, currency_repr)

    def str_auto(self, decimals=None, currency_repr='code'):
        """
        String representation of this Value. Wrapper for the :func:`str` method, but automatically determines the denominator depending on the value.

        >>> Value('0.0000012 BTC').str_auto()
        '120 sat'

        >>> Value('0.0005 BTC').str_auto()
        '500.00 µBTC'

        :param decimals: Number of decimals to use
        :type decimals: float
        :param currency_repr: Representation of currency. I.e. code: BTC, name: Bitcoin, symbol: ₿
        :type currency_repr: str
        :return str:
        """

        return self.str('auto', decimals, currency_repr)

    @property
    def value_sat(self):
        """
        Value in smallest denominator, i.e. Satoshi for the Bitcoin network

        :return int:
        """
        return round(self.value / self.network.denominator)

    def to_bytes(self, length=8, byteorder='little'):
        """
        Representation of value_sat (value in smallest denominator: satoshi's) as bytes string. Used for script or transaction serialization.

        >>> Value('1 sat').to_bytes()
        b'\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00'

        :param length: Length of bytes string to return, default is 8 bytes
        :type length: int
        :param byteorder: Order of bytes: little or big endian. Default is 'little'
        :type byteorder: str

        :return bytes:
        """
        return self.value_sat.to_bytes(length, byteorder)

    def to_hex(self, length=16, byteorder='little'):
        """
        Representation of value_sat (value in smallest denominator: satoshi's) as hexadecimal string.

        >>> Value('15 sat').to_hex()
        '0f00000000000000'

        :param length: Length of hexadecimal string to return, default is 16 characters
        :type length: int
        :param byteorder: Order of bytes: little or big endian. Default is 'little'
        :type byteorder: str
        :return:
        """
        return self.value_sat.to_bytes(length // 2, byteorder).hex() def value_to_satoshi(value, network=None):
    """
    Convert Value object or value string to smallest denominator amount as integer

    :param value: Value object, value string as accepted by Value class or numeric value amount
    :type value: str, int, float, Value
    :param network: Specify network to validate value string
    :type network: str, Network

    :return int:
    """
    if isinstance(value, str):
        value = Value(value)
    if isinstance(value, Value):
        if network and value.network != network:
            raise ValueError("Value uses different network (%s) then supplied network: %s" % (value.network.name, network))
        value = value.value_sat
    return value class Script(object):

    def __init__(self, commands=None, message=None, script_types='', is_locking=True, keys=None, signatures=None,
                 blueprint=None, tx_data=None, public_hash=b'', sigs_required=None, redeemscript=b'',
                 hash_type=SIGHASH_ALL):
        """
        Create a Script object with specified parameters. Use parse() method to create a Script from raw hex

        >>> s = Script([op.op_2, op.op_4, op.op_add])
        >>> s
        <Script([op.op_2, op.op_4, op.op_add])>
        >>> s.blueprint
        [82, 84, 147]
        >>> s.evaluate()
        True

        Stack is empty now, because evaluate pops last item from stack and check if is non-zero
        >>> s.stack
        []

        :param commands: List of script language commands
        :type commands: list
        :param message: Signed message to verify, normally a transaction hash. Used to validate script
        :type message: bytes
        :param script_types: List of script_types as defined in SCRIPT_TYPES
        :type script_types: list of str
        :param is_locking: Is this a locking script (Output), otherwise unlocking (Input)
        :type is_locking: bool
        :param keys: Provide list of keys to create script
        :type keys: list of Key
        :param signatures: Provide list of signatures to create script
        :type signatures: list of Signature
        :param blueprint: Simplified version of script, normally generated by Script object
        :type blueprint: list of str
        :param tx_data: Dictionary with extra information needed to verify script. Such as 'redeemscript' for multisignature scripts and 'blockcount' for time locked scripts
        :type tx_data: dict
        :param public_hash: Public hash of key or redeemscript used to create scripts
        :type public_hash: bytes
        :param sigs_required: Nubmer of signatures required to create multisig script
        :type sigs_required: int
        :param redeemscript: Provide redeemscript to create a new (multisig) script
        :type redeemscript: bytes
        :param hash_type: Specific script hash type, default is SIGHASH_ALL
        :type hash_type: int
        """
        self.commands = commands if commands else []
        self._raw = b''
        self.stack = []
        self.message = message
        self.script_types = script_types if script_types else []
        self.is_locking = is_locking
        self.keys = keys if keys else []
        self.signatures = signatures if signatures else []
        self._blueprint = blueprint if blueprint else []
        self.tx_data = {} if not tx_data else tx_data
        self.sigs_required = sigs_required if sigs_required else len(self.keys) if len(self.keys) else 1
        self.redeemscript = redeemscript
        self.public_hash = public_hash
        self.hash_type = hash_type

        if not self.commands and self.script_types and (self.keys or self.signatures or self.public_hash):
            for st in self.script_types:
                st_values = SCRIPT_TYPES[st]
                script_template = st_values[1]
                self.is_locking = True if st_values[0] == 'locking' else False
                sig_n_and_m = [len(self.keys), self.sigs_required]
                for tc in script_template:
                    command = [tc]
                    if tc == 'data':
                        command = [self.public_hash] if self.public_hash else []
                    elif tc == 'signature':
                        command = self.signatures
                    elif tc == 'key':
                        command = self.keys
                    elif tc == 'op_n':
                        command = [sig_n_and_m.pop() + 80]
                    elif tc == 'redeemscript':
                        command = [self.redeemscript]
                    if not command or command == [b'']:
                        raise ScriptError("Cannot create script, please supply %s" % (tc if tc != 'data' else
                                          'public key hash'))
                    self.commands += command
        if not (self.keys and self.signatures and self.blueprint):
            self._blueprint = []
            for c in self.commands:
                if isinstance(c, int):
                    self._blueprint.append(c)
                else:
                    data_type = get_data_type(c)
                    if data_type in ['key', 'signature', 'key_object', 'signature_object']:
                        if data_type == 'key_object':
                            data_type = 'key'
                        elif data_type == 'signature_object':
                            data_type = 'signature'
                        self._blueprint.append(data_type)
                    else:
                        self._blueprint.append('data-%d' % len(c))

    @classmethod
    def parse(cls, script, message=None, tx_data=None, strict=True, _level=0):
        """
        Parse raw script and return Script object. Extracts script commands, keys, signatures and other data.

        Wrapper for the :func:`parse_bytesio` method. Convert hexadecimal string or bytes script to BytesIO.

        >>> Script.parse('76a914af8e14a2cecd715c363b3a72b55b59a31e2acac988ac')
        <Script([op.op_dup, op.op_hash160, data-20, op.op_equalverify, op.op_checksig])>

        :param script: Raw script to parse in bytes, BytesIO or hexadecimal string format
        :type script: BytesIO, bytes, str
        :param message: Signed message to verify, normally a transaction hash
        :type message: bytes
        :param tx_data: Dictionary with extra information needed to verify script. Such as 'redeemscript' for multisignature scripts and 'blockcount' for time locked scripts
        :type tx_data: dict
        :param strict: Raise exception when script is malformed, incomplete or not understood. Default is True
        :type strict: bool
        :param _level: Internal argument used to avoid recursive depth
        :type _level: int

        :return Script:
        """
        if isinstance(script, bytes):
            script = BytesIO(script)
        elif isinstance(script, str):
            script = BytesIO(bytes.fromhex(script))
        return cls.parse_bytesio(script, message, tx_data, strict, _level)

    @classmethod
    def parse_bytesio(cls, script, message=None, tx_data=None, strict=True, _level=0):
        """
        Parse raw script and return Script object. Extracts script commands, keys, signatures and other data.

        :param script: Raw script to parse in bytes, BytesIO or hexadecimal string format
        :type script: BytesIO
        :param message: Signed message to verify, normally a transaction hash
        :type message: bytes
        :param tx_data: Dictionary with extra information needed to verify script. Such as 'redeemscript' for multisignature scripts and 'blockcount' for time locked scripts
        :type tx_data: dict
        :param strict: Raise exception when script is malformed, incomplete or not understood. Default is True
        :type strict: bool
        :param _level: Internal argument used to avoid recursive depth
        :type _level: int

        :return Script:
        """
        commands = []
        signatures = []
        keys = []
        blueprint = []
        redeemscript = b''
        sigs_required = None
        # hash_type = SIGHASH_ALL  # todo: check
        hash_type = None
        if not tx_data:
            tx_data = {}

        while script:
            chb = script.read(1)
            if not chb:
                break
            ch = int.from_bytes(chb, 'big')
            data = None
            data_length = 0
            if 1 <= ch <= 75:  # Data`
                data_length = ch
            elif ch == op.op_pushdata1:
                data_length = int.from_bytes(script.read(1), 'little')
            elif ch == op.op_pushdata2:
                data_length = int.from_bytes(script.read(2), 'little')
            if data_length:
                data = script.read(data_length)
                if len(data) != data_length:
                    msg = "Malformed script, not enough data found"
                    if strict:
                        raise ScriptError(msg)
                    else:
                        _logger.warning(msg)

            if data:
                data_type = get_data_type(data)
                commands.append(data)
                if data_type == 'signature':
                    try:
                        sig = Signature.parse_bytes(data)
                        signatures.append(sig)
                        hash_type = sig.hash_type
                        blueprint.append('signature')
                    except Exception as e:
                        if strict:
                            raise ScriptError(str(e))
                        else:
                            _logger.warning(str(e))
                elif data_type == 'signature_object':
                    signatures.append(data)
                    hash_type = data.hash_type
                    blueprint.append('signature')
                elif data_type == 'key':
                    keys.append(Key(data))
                    blueprint.append('key')
                elif data_type == 'key_object':
                    keys.append(data)
                    blueprint.append('key')
                elif data_type[:4] == 'data':
                    # FIXME: This is arbitrary
                    blueprint.append('data-%d' % len(data))
                elif len(commands) >= 2 and commands[-2] == op.op_return:
                    blueprint.append('data-%d' % len(data))
                else:
                    # FIXME: Only parse sub-scripts if script is expected
                    try:
                        if _level >= 1:
                            blueprint.append('data-%d' % len(data))
                        else:
                            s2 = Script.parse_bytes(data, _level=_level+1, strict=strict)
                            commands.pop()
                            commands += s2.commands
                            blueprint += s2.blueprint
                            keys += s2.keys
                            signatures += s2.signatures
                            redeemscript = s2.redeemscript
                            sigs_required = s2.sigs_required
                    except (ScriptError, IndexError):
                        blueprint.append('data-%d' % len(data))
            else:  # Other opcode
                commands.append(ch)
                blueprint.append(ch)

        s = cls(commands, message, keys=keys, signatures=signatures, blueprint=blueprint, tx_data=tx_data,
                hash_type=hash_type)
        script.seek(0)
        s._raw = script.read()

        s.script_types = _get_script_types(blueprint)
        if 'unknown' in s.script_types:
            s.script_types = ['unknown']

        # Extract extra information from script data
        for st in s.script_types[:1]:
            if st == 'multisig':
                s.redeemscript = s.raw
                s.sigs_required = s.commands[0] - 80
                if s.sigs_required > len(keys):
                    raise ScriptError("Number of signatures required (%d) is higher then number of keys (%d)" %
                                      (s.sigs_required, len(keys)))
                if len(s.keys) != s.commands[-2] - 80:
                    raise ScriptError("%d keys found but %d keys expected" %
                                      (len(s.keys), s.commands[-2] - 80))
            elif st in ['p2wpkh', 'p2wsh', 'p2sh'] and len(s.commands) > 1:
                s.public_hash = s.commands[1]
            elif st == 'p2pkh' and len(s.commands) > 2:
                s.public_hash = s.commands[2]
        s.redeemscript = redeemscript if redeemscript else s.redeemscript
        if s.redeemscript and 'redeemscript' not in s.tx_data:
            s.tx_data['redeemscript'] = s.redeemscript

        s.sigs_required = sigs_required if sigs_required else s.sigs_required

        return s

    @classmethod
    def parse_hex(cls, script, message=None, tx_data=None, strict=True, _level=0):
        """
        Parse raw script and return Script object. Extracts script commands, keys, signatures and other data.

        Wrapper for the :func:`parse_bytesio` method. Convert hexadecimal string script to BytesIO.

        >>> Script.parse_hex('76a914af8e14a2cecd715c363b3a72b55b59a31e2acac988ac')
        <Script([op.op_dup, op.op_hash160, data-20, op.op_equalverify, op.op_checksig])>

        :param script: Raw script to parse in hexadecimal string format
        :type script: str
        :param message: Signed message to verify, normally a transaction hash
        :type message: bytes
        :param tx_data: Dictionary with extra information needed to verify script. Such as 'redeemscript' for multisignature scripts and 'blockcount' for time locked scripts
        :type tx_data: dict
        :param strict: Raise exception when script is malformed, incomplete or not understood. Default is True
        :type strict: bool
        :param _level: Internal argument used to avoid recursive depth
        :type _level: int

        :return Script:
        """
        return cls.parse_bytesio(BytesIO(bytes.fromhex(script)), message, tx_data, strict, _level)

    @classmethod
    def parse_bytes(cls, script, message=None, tx_data=None, strict=True, _level=0):
        """
        Parse raw script and return Script object. Extracts script commands, keys, signatures and other data.

        Wrapper for the :func:`parse_bytesio` method. Convert bytes script to BytesIO.

        :param script: Raw script to parse in bytes format
        :type script: bytes
        :param message: Signed message to verify, normally a transaction hash
        :type message: bytes
        :param tx_data: Dictionary with extra information needed to verify script. Such as 'redeemscript' for multisignature scripts and 'blockcount' for time locked scripts
        :type tx_data: dict
        :param strict: Raise exception when script is malformed or incomplete
        :type strict: bool
        :param _level: Internal argument used to avoid recursive depth
        :type _level: int

        :return Script:
        """
        return cls.parse_bytesio(BytesIO(script), message, tx_data, strict, _level)

    def __repr__(self):
        s_items = []
        for command in self.blueprint:
            if isinstance(command, int):
                s_items.append('op.' + opcodenames.get(command, 'unknown-op-%s' % command).lower())
            else:
                s_items.append(command)
        return '<Script([' + ', '.join(s_items) + '])>'

    def __str__(self):
        s_items = []
        for command in self.blueprint:
            if isinstance(command, int):
                s_items.append(opcodenames.get(command, 'unknown-op-%s' % command))
            else:
                s_items.append(command)
        return ' '.join(s_items)

    def __add__(self, other):
        self.commands += other.commands
        self._raw += other.raw
        if other.message and not self.message:
            self.message = other.message
        self.is_locking = None
        self.keys += other.keys
        self.signatures += other.signatures
        self._blueprint += other._blueprint
        self.script_types = _get_script_types(self._blueprint)
        if other.tx_data and not self.tx_data:
            self.tx_data = other.tx_data
        if other.redeemscript and not self.redeemscript:
            self.redeemscript = other.redeemscript
        return self

    def __bool__(self):
        return bool(self.commands)

    def __hash__(self):
        return hash160(self.raw)

    @property
    def blueprint(self):
        # TODO: create blueprint from commands if empty
        return self._blueprint

    @property
    def raw(self):
        if not self._raw:
            self._raw = self.serialize()
        return self._raw

    def serialize(self):
        """
        Serialize script. Return all commands and data as bytes

        >>> s = Script.parse_hex('76a914af8e14a2cecd715c363b3a72b55b59a31e2acac988ac')
        >>> s.serialize().hex()
        '76a914af8e14a2cecd715c363b3a72b55b59a31e2acac988ac'

        :return bytes:
        """
        raw = b''
        for cmd in self.commands:
            if isinstance(cmd, int):
                raw += bytes([cmd])
            else:
                raw += data_pack(cmd)
        self._raw = raw
        return raw

    def serialize_list(self):
        """
        Serialize script and return commands and data as list

        >>> s = Script.parse_hex('76a9')
        >>> s.serialize_list()
        [b'v', b'\\xa9']

        :return list of bytes:
        """
        clist = []
        for cmd in self.commands:
            if isinstance(cmd, int):
                clist.append(bytes([cmd]))
            else:
                clist.append(bytes(cmd))
        return clist

    def evaluate(self, message=None, tx_data=None):
        """
        Evaluate script, run all commands and check if it is valid

        >>> s = Script([op.op_2, op.op_4, op.op_add])
        >>> s
        <Script([op.op_2, op.op_4, op.op_add])>
        >>> s.blueprint
        [82, 84, 147]
        >>> s.evaluate()
        True

        >>> lock_script = bytes.fromhex('76a914f9cc73824051cc82d64a716c836c54467a21e22c88ac')
        >>> unlock_script = bytes.fromhex('483045022100ba2ec7c40257b3d22864c9558738eea4d8771ab97888368124e176fdd6d7cd8602200f47c8d0c437df1ea8f9819d344e05b9c93e38e88df1fc46abb6194506c50ce1012103e481f20561573cfd800e64efda61405917cb29e4bd20bed168c52b674937f535')
        >>> s = Script.parse_bytes(unlock_script + lock_script)
        >>> transaction_hash = bytes.fromhex('12824db63e7856d00ee5e109fd1c26ac8a6a015858c26f4b336274f6b52da1c3')
        >>> s.evaluate(message=transaction_hash)
        True

        :param message: Signed message to verify, normally a transaction hash. Leave empty to use Script.message. If supplied Script.message will be ignored.
        :type message: bytes
        :param tx_data: Dictionary with extra information needed to verify script. Such as 'redeemscript' for multisignature scripts and 'blockcount' for time locked scripts. Leave emtpy to use Script.tx_data. If supplied Script.tx_data will be ignored

        :return bool: Valid or not valid
        """
        self.message = self.message if message is None else message
        self.tx_data = self.tx_data if tx_data is None else tx_data
        self.stack = Stack()

        commands = self.commands[:]
        while len(commands):
            command = commands.pop(0)
            if isinstance(command, int):
                if command == op.op_0:  # OP_0
                    self.stack.append(encode_num(0))
                elif command == op.op_1negate:  # OP_1NEGATE
                    self.stack.append(encode_num(-1))
                elif op.op_1 <= command <= op.op_16:   # OP_1 to OP_16
                    self.stack.append(encode_num(command-80))
                elif command == op.op_if or command == op.op_notif:
                    method = opcodenames[command].lower()
                    method = getattr(self.stack, method)
                    if not method(commands):
                        return False
                else:
                    method_name = opcodenames[command].lower()
                    if method_name not in dir(self.stack):
                        raise ScriptError("Method %s not found" % method_name)
                    try:
                        method = getattr(self.stack, method_name)
                        if method_name == 'op_checksig' or method_name == 'op_checksigverify':
                            res = method(self.message)
                        elif method_name == 'op_checkmultisig' or method_name == 'op_checkmultisigverify':
                            res = method(self.message, self.tx_data)
                        elif method_name == 'op_checklocktimeverify':
                            res = self.stack.op_checklocktimeverify(
                                self.tx_data['sequence'], self.tx_data.get('locktime'))
                        elif method_name == 'op_checksequenceverify':
                            res = self.stack.op_checksequenceverify(self.tx_data['sequence'], self.tx_data['version'])
                        else:
                            res = method()
                        if res is False:
                            return False
                    except Exception as e:
                        _logger.warning("Stack evaluate error: %s" % e)
                        return False
            else:
                self.stack.append(command)

        if len(self.stack) == 0:
            return False
        if self.stack.pop() == b'':
            return False

        return True
from datetime import datetime
from io import BytesIO
from bitcoinlib.encoding import *
from bitcoinlib.config.opcodes import *
from bitcoinlib.keys import HDKey, Key, deserialize_address, Address, sign, verify, Signature
from bitcoinlib.networks import Network
from bitcoinlib.values import Value, value_to_satoshi
from bitcoinlib.scripts import Script
import json
import pickle
import random
        :type encoding: str
        :param strict: Raise exception when input is malformed, incomplete or not understood
        :type strict: bool
        :param network: Network, leave empty for default
        :type network: str, Network
        """

        self.prev_txid = to_bytes(prev_txid)
        self.output_n = output_n
        if isinstance(output_n, int):
            self.output_n_int = output_n
            self.output_n = output_n.to_bytes(4, 'big')
        else:
            self.output_n_int = int.from_bytes(output_n, 'big')
            self.output_n = output_n
        self.unlocking_script = b'' if unlocking_script is None else to_bytes(unlocking_script)
        self.unlocking_script_unsigned = b'' if unlocking_script_unsigned is None \
            else to_bytes(unlocking_script_unsigned)
        self.script = None
        if isinstance(sequence, numbers.Number):
            self.sequence = sequence
        else:
            self.sequence = int.from_bytes(sequence, 'little')
        self.compressed = compressed
        self.network = network
        if not isinstance(network, Network):
            self.network = Network(network)
        self.index_n = index_n
        self.value = value_to_satoshi(value, network=network)
        if not keys:
            keys = []
        self.keys = []
        if not isinstance(keys, list):
            keys = [keys]
        self.public_hash = public_hash
        if not signatures:
            signatures = []
        if not isinstance(signatures, list):
            signatures = [signatures]
        self.sort = sort
        if isinstance(address, Address):
            self.address = address.address
            self.encoding = address.encoding
            self.network = address.network
        else:
            self.address = address
        self.signatures = []
        self.redeemscript = b''
        self.script_type = script_type
        if self.prev_txid == b'\0' * 32:
            self.script_type = 'coinbase'
        self.double_spend = double_spend
        self.locktime_cltv = locktime_cltv
        self.locktime_csv = locktime_csv
        self.witness_type = witness_type
        if encoding is None:
            self.encoding = 'base58'
            if self.witness_type == 'segwit':
                self.encoding = 'bech32'
        else:
            self.encoding = encoding
        self.valid = None
        self.key_path = key_path

        self.witnesses = []
        if isinstance(witnesses, bytes):
            n_items, cursor = varbyteint_to_int(witnesses[0:9])
            for m in range(0, n_items):
                witness = b'\0'
                item_size, size = varbyteint_to_int(witnesses[cursor:cursor + 9])
                if item_size:
                    witness = witnesses[cursor + size:cursor + item_size + size]
                cursor += item_size + size
                self.witnesses.append(witness)
        elif witnesses:
            self.witnesses = [bytes.fromhex(w) if isinstance(w, str) else w for w in witnesses]
        self.script_code = b''
        self.script = script

        # If unlocking script is specified extract keys, signatures, type from script
        if self.unlocking_script and self.script_type != 'coinbase' and not (signatures and keys) and not script:
            self.script = Script.parse_bytes(self.unlocking_script, strict=strict)
            self.keys = self.script.keys
            self.signatures = self.script.signatures
            sigs_required = self.script.sigs_required
            self.redeemscript = self.script.redeemscript if self.script.redeemscript else self.redeemscript
            if len(self.script.script_types) == 1 and not self.script_type:
                self.script_type = self.script.script_types[0]
            elif self.script.script_types == ['signature_multisig', 'multisig']:
                self.script_type = 'p2sh_multisig'
            # TODO: Check if this if is necessary
            if 'p2wpkh' in self.script.script_types:
                self.script_type = 'p2sh_p2wpkh'
                self.witness_type = 'p2sh-segwit'
            elif 'p2wsh' in self.script.script_types:
                self.script_type = 'p2sh_p2wsh'
                self.witness_type = 'p2sh-segwit'
        if self.unlocking_script_unsigned and not self.signatures:
            ls = Script.parse_bytes(self.unlocking_script_unsigned, strict=strict)
            self.public_hash = self.public_hash if not ls.public_hash else ls.public_hash
            if ls.script_types[0] in ['p2wpkh', 'p2wsh']:
                self.witness_type = 'segwit'
        self.sigs_required = sigs_required if sigs_required else 1

        if self.script_type is None and self.witness_type is None and self.witnesses:
            self.witness_type = 'segwit'
        if self.witness_type is None or self.witness_type == 'legacy':
            # if self.script_type in ['p2wpkh', 'p2wsh', 'p2sh_p2wpkh', 'p2sh_p2wsh']:
            if self.script_type in ['p2wpkh', 'p2wsh']:
                self.witness_type = 'segwit'
            elif self.script_type in ['p2sh_p2wpkh', 'p2sh_p2wsh']:
                self.witness_type = 'p2sh-segwit'
            else:
                self.witness_type = 'legacy'
        elif self.witness_type == 'segwit' and self.script_type == 'sig_pubkey' and encoding is None:
            self.encoding = 'bech32'
        if not self.script_type:
            self.script_type = 'sig_pubkey'

        for key in keys:

            if not isinstance(key, Key):