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) class BKeyError(Exception):
    """
    Handle Key class Exceptions

    """

    def __init__(self, msg=''):
        self.msg = msg
        _logger.error(msg)

    def __str__(self):
        return self.msg 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 def check_network_and_key(key, network=None, kf_networks=None, default_network=DEFAULT_NETWORK):
    """
    Check if given key corresponds with given network and return network if it does. If no network is specified
    this method tries to extract the network from the key. If no network can be extracted from the key the
    default network will be returned.

    >>> check_network_and_key('L4dTuJf2ceEdWDvCPsLhYf8GiiuYqXtqfbcKdC21BPDvEM1ykJRC')
    'bitcoin'
    
    A BKeyError will be raised if key does not correspond with network or if multiple network are found.
    
    :param key: Key in any format recognized by get_key_format function
    :type key: str, int, bytes
    :param network: Optional network. Method raises BKeyError if keys belongs to another network
    :type network: str, None
    :param kf_networks: Optional list of networks which is returned by get_key_format. If left empty the get_key_format function will be called.
    :type kf_networks: list, None
    :param default_network: Specify different default network, leave empty for default (bitcoin)
    :type default_network: str, None
    
    :return str: Network name
    """
    if not kf_networks:
        kf = get_key_format(key)
        if kf['networks']:
            kf_networks = kf['networks']
    if kf_networks:
        if network is not None and network not in kf_networks:
            raise BKeyError("Specified key %s is from different network then specified: %s" % (kf_networks, network))
        elif network is None and len(kf_networks) == 1:
            return kf_networks[0]
        elif network is None and len(kf_networks) > 1:
            if default_network in kf_networks:
                return default_network
            elif 'testnet' in kf_networks:
                return 'testnet'
            raise BKeyError("Could not determine network of specified key, multiple networks found: %s" % kf_networks)
    if network is None:
        return default_network
    else:
        return network def path_expand(path, path_template=None, level_offset=None, account_id=0, cosigner_id=0, purpose=44,
                address_index=0, change=0, witness_type=DEFAULT_WITNESS_TYPE, multisig=False, network=DEFAULT_NETWORK):
    """
    Create key path. Specify part of key path and path settings

    >>> path_expand([10, 20], witness_type='segwit')
    ['m', "84'", "0'", "0'", '10', '20']

    :param path: Part of path, for example [0, 2] for change=0 and address_index=2
    :type path: list, str
    :param path_template: Template for path to create, default is BIP 44: ["m", "purpose'", "coin_type'",  "account'", "change", "address_index"]
    :type path_template: list
    :param level_offset: Just create part of path. For example -2 means create path with the last 2 items (change, address_index) or 1 will return the master key 'm'
    :type level_offset: int
    :param account_id: Account ID
    :type account_id: int
    :param cosigner_id: ID of cosigner
    :type cosigner_id: int
    :param purpose: Purpose value
    :type purpose: int
    :param address_index: Index of key, normally provided to 'path' argument
    :type address_index: int
    :param change: Change key = 1 or normal = 0, normally provided to 'path' argument
    :type change: int
    :param witness_type: Witness type for paths with a script ID, specify 'p2sh-segwit' or 'segwit'
    :type witness_type: str
    :param multisig: Is path for multisig keys?
    :type multisig: bool
    :param network: Network name. Leave empty for default network
    :type network: str

    :return list:
    """
    if isinstance(path, TYPE_TEXT):
        path = path.split('/')
    if not path_template:
        ks = [k for k in WALLET_KEY_STRUCTURES if
              k['witness_type'] == witness_type and k['multisig'] == multisig and k['purpose'] is not None]
        if ks:
            purpose = ks[0]['purpose']
            path_template = ks[0]['key_path']
    if not isinstance(path, list):
        raise BKeyError("Please provide path as list with at least 1 item. Wallet key path format is %s" %
                        path_template)
    if len(path) > len(path_template):
        raise BKeyError("Invalid path provided. Path should be shorter than %d items. "
                        "Wallet key path format is %s" % (len(path_template), path_template))

    # If path doesn't start with m/M complement path
    poppath = deepcopy(path)
    if path == [] or path[0] not in ['m', 'M']:
        wallet_key_path = path_template
        if level_offset:
            wallet_key_path = wallet_key_path[:level_offset]
        new_path = []
        for pi in wallet_key_path[::-1]:
            if not len(poppath):
                new_path.append(pi)
            else:
                new_path.append(poppath.pop())
        new_path = new_path[::-1]
    else:
        new_path = deepcopy(path)

    # Replace variable names in path with corresponding values
    # network, account_id, _ = self._get_account_defaults(network, account_id)
    script_type_id = 1 if witness_type == 'p2sh-segwit' else 2
    var_defaults = {
        'network': network,
        'account': account_id,
        'purpose': purpose,
        'coin_type': Network(network).bip44_cointype,
        'script_type': script_type_id,
        'cosigner_index': cosigner_id,
        'change': change,
        'address_index': address_index
    }
    npath = new_path
    for i, pi in enumerate(new_path):
        if not isinstance(pi, TYPE_TEXT):
            pi = str(pi)
        if pi in "mM":
            continue
        hardened = False
        varname = pi
        if pi[-1:] == "'" or (pi[-1:] in "HhPp" and pi[:-1].isdigit()):
            varname = pi[:-1]
            hardened = True
        if path_template[i][-1:] == "'":
            hardened = True
        new_varname = (str(var_defaults[varname]) if varname in var_defaults else varname)
        if new_varname == varname and not new_varname.isdigit():
            raise BKeyError("Variable %s not found in Key structure definitions in main.py" % varname)
        if varname == 'address_index' and address_index is None:
            raise BKeyError("Please provide value for 'address_index' or 'path'")
        npath[i] = new_varname + ("'" if hardened else '')
    if "None'" in npath or "None" in npath:
        raise BKeyError("Could not parse all variables in path %s" % npath)
    return npath class Mnemonic(object):
    """
    Class to convert, generate and parse Mnemonic sentences
    
    Implementation of BIP0039 for Mnemonics passphrases 

    Took some parts from Pavol Rusnak Trezors implementation, see https://github.com/trezor/python-mnemonic
    """

    def __init__(self, language=DEFAULT_LANGUAGE):
        """
        Init Mnemonic class and read wordlist of specified language
        
        :param language: use specific wordlist, i.e. chinese, dutch (in development), english, french, italian, japanese or spanish. Leave empty for default 'english'
        :type language: str
        
        """
        self._wordlist = []
        with Path(BCL_INSTALL_DIR, 'wordlist', '%s.txt' % language).open() as f:
            self._wordlist = [w.strip() for w in f.readlines()]

    @staticmethod
    def checksum(data):
        """
        Calculates checksum for given data key

        :param data: key string
        :type data: bytes, hexstring
        
        :return str: Checksum of key in bits
        """
        data = to_bytes(data)
        if len(data) % 4 > 0:
            raise ValueError('Data length in bits should be divisible by 32, but it is not (%d bytes = %d bits).' %
                             (len(data), len(data) * 8))
        key_hash = hashlib.sha256(data).digest()
        return change_base(key_hash, 256, 2, 256)[:len(data) * 8 // 32]

    def to_seed(self, words, password='', validate=True):
        """
        Use Mnemonic words and optionally a password to create a PBKDF2 seed (Password-Based Key Derivation Function 2)
        
        First use 'sanitize_mnemonic' to determine language and validate and check words

        >>> Mnemonic().to_seed('chunk gun celery million wood kite tackle twenty story episode raccoon dutch').hex()
        '6969ed4666db67fc74fae7869e2acf3c766b5ef95f5e31eb2fcebd93d76069c6de971225f700042b0b513f0ad6c8562277fc4b5ee1344b720f1686dc2dccc220'

        :param words: Mnemonic passphrase as string with space separated words
        :type words: str
        :param password: A password to protect key, leave empty to disable
        :type password: str
        :param validate: Validate checksum for given word phrase, default is True
        :type validate: bool
        
        :return bytes: PBKDF2 seed
        """
        words = self.sanitize_mnemonic(words)
        # Check if passphrase is valid
        if validate:
            self.to_entropy(words)
        mnemonic = to_bytes(words)
        password = to_bytes(password)
        return hashlib.pbkdf2_hmac(hash_name='sha512', password=mnemonic, salt=b'mnemonic' + password,
                                   iterations=2048)

    def word(self, index):
        """
        Get word from wordlist
        
        :param index: word index ID
        :type index: int
        
        :return str: A word from the dictionary 
        """
        return self._wordlist[index]

    def wordlist(self):
        """
        Get full selected wordlist. A wordlist is selected when initializing Mnemonic class
        
        :return list: Full list with 2048 words 
        """
        return self._wordlist

    def generate(self, strength=128, add_checksum=True):
        """
        Generate a random Mnemonic key
        
        Uses cryptographically secure os.urandom() function to generate data. Then creates a Mnemonic sentence with
        the 'to_mnemonic' method.

        :param strength: Key strength in number of bits as multiply of 32, default is 128 bits. It advised to specify 128 bits or more, i.e.: 128, 256, 512 or 1024
        :type strength: int
        :param add_checksum: Included a checksum? Default is True
        :type add_checksum: bool
        
        :return str: Mnemonic passphrase consisting of a space seperated list of words
        """
        if strength % 32 > 0:
            raise ValueError("Strength should be divisible by 32")
        data = os.urandom(strength // 8)
        return self.to_mnemonic(data, add_checksum=add_checksum)

    def to_mnemonic(self, data, add_checksum=True, check_on_curve=True):
        """
        Convert key data entropy to Mnemonic sentence

        >>> Mnemonic().to_mnemonic('28acfc94465fd2f6774759d6897ec122')
        'chunk gun celery million wood kite tackle twenty story episode raccoon dutch'

        :param data: Key data entropy
        :type data: bytes, hexstring
        :param add_checksum: Included a checksum? Default is True
        :type add_checksum: bool
        :param check_on_curve: Check if data integer value is on secp256k1 curve. Should be enabled when not testing and working with crypto
        :type check_on_curve: bool
        
        :return str: Mnemonic passphrase consisting of a space seperated list of words
        """
        data = to_bytes(data)
        data_int = int.from_bytes(data, 'big')
        if check_on_curve and not 0 < data_int < secp256k1_n:
            raise ValueError("Integer value of data should be in secp256k1 domain between 1 and secp256k1_n-1")
        if add_checksum:
            binresult = change_base(data_int, 10, 2, len(data) * 8) + self.checksum(data)
            wi = change_base(binresult, 2, 2048)
        else:
            wi = change_base(data_int, 10, 2048, len(data) // 1.375 + len(data) % 1.375 > 0)
        return normalize_string(' '.join([self._wordlist[i] for i in wi]))

    def to_entropy(self, words, includes_checksum=True):
        """
        Convert Mnemonic words back to key data entropy

        >>> Mnemonic().to_entropy('chunk gun celery million wood kite tackle twenty story episode raccoon dutch').hex()
        '28acfc94465fd2f6774759d6897ec122'


        :param words: Mnemonic words as string of list of words
        :type words: str
        :param includes_checksum: Boolean to specify if checksum is used. Default is True
        :type includes_checksum: bool
        
        :return bytes: Entropy seed
        """
        words = self.sanitize_mnemonic(words)
        if isinstance(words, TYPE_TEXT):
            words = words.split(' ')
        wi = []
        for word in words:
            wi.append(self._wordlist.index(word))
        ent_length = int(len(words) * 4/3)
        ent = change_base(wi, 2048, 256, ent_length, output_even=False)
        if includes_checksum:
            binresult = change_base(ent, 256, 2, len(ent) * 4)
            ent = change_base(binresult[:-len(binresult) // 33], 2, 256, ent_length)

            # Check checksum
            checksum = binresult[-len(binresult) // 33:]
            if checksum != self.checksum(ent):
                raise ValueError("Invalid checksum %s for entropy %s" % (checksum, ent))

        return ent

    @staticmethod
    def detect_language(words):
        """
        Detect language of given phrase

        >>> Mnemonic().detect_language('chunk gun celery million wood kite tackle twenty story episode raccoon dutch')
        'english'
        
        :param words: List of space separated words
        :type words: str
        
        :return str: Language 
        """
        words = normalize_string(words)
        if isinstance(words, TYPE_TEXT):
            words = words.split(' ')

        wlcount = {}
        for fn in Path(BCL_INSTALL_DIR, 'wordlist').iterdir():
            if fn.suffix == ".txt":
                with open(os.path.join(str(BCL_INSTALL_DIR), 'wordlist', fn)) as f:
                    wordlist = [w.strip() for w in f.readlines()]
                    language = fn.name.split('.')[0]
                    wlcount[language] = 0
                    for word in words:
                        if word in wordlist:
                            wlcount[language] += 1
        detlang = max(wlcount.keys(), key=(lambda key: wlcount[key]))
        if not wlcount[detlang]:
            raise Warning("Could not detect language of Mnemonic sentence %s" % words)
        return detlang

    def sanitize_mnemonic(self, words):
        """
        Check and convert list of words to utf-8 encoding.
        
        Raises an error if unrecognised word is found
        
        :param words: List of space separated words
        :type words: str
        
        :return str: Sanitized list of words
        """
        words = normalize_string(words)
        language = self.detect_language(words)
        if isinstance(words, TYPE_TEXT):
            words = words.split(' ')
        with Path(BCL_INSTALL_DIR, 'wordlist', '%s.txt' % language).open() as f:
            wordlist = [w.strip() for w in f.readlines()]
            for word in words:
                if word not in wordlist:
                    raise Warning("Unrecognised word %s in mnemonic sentence" % word.encode('utf8'))
        return ' '.join(words) 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 Service(object):
    """
    Class to connect to various cryptocurrency service providers. Use to receive network and blockchain information,
    get specific transaction information, current network fees or push a raw transaction.

    The Service class connects to 1 or more service providers at random to retrieve or send information. If a service
    providers fails to correctly respond the Service class will try another available provider.

    """

    def __init__(self, network=DEFAULT_NETWORK, min_providers=1, max_providers=1, providers=None,
                 timeout=TIMEOUT_REQUESTS, cache_uri=None, ignore_priority=False, exclude_providers=None,
                 max_errors=SERVICE_MAX_ERRORS, strict=True):
        """
        Create a service object for the specified network. By default, the object connect to 1 service provider, but you
        can specify a list of providers or a minimum or maximum number of providers.

        :param network: Specify network used
        :type network: str, Network
        :param min_providers: Minimum number of providers to connect to. Default is 1. Use for instance to receive fee information from a number of providers and calculate the average fee.
        :type min_providers: int
        :param max_providers: Maximum number of providers to connect to. Default is 1.
        :type max_providers: int
        :param providers: List of providers to connect to. Default is all providers and select a provider at random.
        :type providers: list of str
        :param timeout: Timeout for web requests. Leave empty to use default from config settings
        :type timeout: int
        :param cache_uri: Database to use for caching
        :type cache_uri: str
        :param ignore_priority: Ignores provider priority if set to True. Could be used for unit testing, so no providers are missed when testing. Default is False
        :type ignore_priority: bool
        :param exclude_providers: Exclude providers in this list, can be used when problems with certain providers arise.
        :type exclude_providers: list of str
        :param strict: Strict checks of valid signatures, scripts and transactions. Normally use strict=True for wallets, transaction validations etcetera. For blockchain parsing strict=False should be used, but be sure to check warnings in the log file. Default is True.
        :type strict: bool

        """

        self.network = network
        if not isinstance(network, Network):
            self.network = Network(network)
        if min_providers > max_providers:
            max_providers = min_providers
        fn = Path(BCL_DATA_DIR, 'providers.json')
        f = fn.open("r")

        try:
            self.providers_defined = json.loads(f.read())
        except json.decoder.JSONDecodeError as e:  # pragma: no cover
            errstr = "Error reading provider definitions from %s: %s" % (fn, e)
            _logger.warning(errstr)
            raise ServiceError(errstr)
        f.close()

        provider_list = list([self.providers_defined[x]['provider'] for x in self.providers_defined])
        if providers is None:
            providers = []
        if exclude_providers is None:
            exclude_providers = []
        if not isinstance(providers, list):
            providers = [providers]
        for p in providers:
            if p not in provider_list:
                raise ServiceError("Provider '%s' not found in provider definitions" % p)

        self.providers = {}
        for p in self.providers_defined:
            if (self.providers_defined[p]['network'] == network or self.providers_defined[p]['network'] == '') and \
                    (not providers or self.providers_defined[p]['provider'] in providers):
                self.providers.update({p: self.providers_defined[p]})
        for nop in exclude_providers:
            if nop in self.providers:
                del(self.providers[nop])

        if not self.providers:
            raise ServiceError("No providers found for network %s" % network)
        self.min_providers = min_providers
        self.max_providers = max_providers
        self.results = {}
        self.errors = {}
        self.resultcount = 0
        self.max_errors = max_errors
        self.complete = None
        self.timeout = timeout
        self._blockcount_update = 0
        self._blockcount = None
        self.cache = None
        self.cache_uri = cache_uri
        try:
            self.cache = Cache(self.network, db_uri=cache_uri)
        except Exception as e:
            self.cache = Cache(self.network, db_uri='')
            _logger.warning("Could not connect to cache database. Error: %s" % e)
        self.results_cache_n = 0
        self.ignore_priority = ignore_priority
        self.strict = strict
        if self.min_providers > 1:
            self._blockcount = Service(network=network, cache_uri=cache_uri).blockcount()
        else:
            self._blockcount = self.blockcount()

    def __exit__(self):
        try:
            self.cache.session.close()
        except Exception:
            pass

    def _reset_results(self):
        self.results = {}
        self.errors = {}
        self.complete = None
        self.resultcount = 0

    def _provider_execute(self, method, *arguments):
        self._reset_results()
        provider_lst = [p[0] for p in sorted([(x, self.providers[x]['priority']) for x in self.providers],
                        key=lambda x: (x[1], random.random()), reverse=True)]
        if self.ignore_priority:
            random.shuffle(provider_lst)

        for sp in provider_lst:
            if self.resultcount >= self.max_providers:
                break
            try:
                if sp not in ['bitcoind', 'litecoind', 'dashd', 'dogecoind', 'caching'] and not self.providers[sp]['url'] and \
                        self.network.name != 'bitcoinlib_test':
                    continue
                client = getattr(services, self.providers[sp]['provider'])
                providerclient = getattr(client, self.providers[sp]['client_class'])
                pc_instance = providerclient(
                    self.network, self.providers[sp]['url'], self.providers[sp]['denominator'],
                    self.providers[sp]['api_key'], self.providers[sp]['provider_coin_id'],
                    self.providers[sp]['network_overrides'], self.timeout, self._blockcount, self.strict)
                if not hasattr(pc_instance, method):
                    continue
                providermethod = getattr(pc_instance, method)
                res = providermethod(*arguments)
                if res is False:  # pragma: no cover
                    self.errors.update(
                        {sp: 'Received empty response'}
                    )
                    _logger.info("Empty response from %s when calling %s" % (sp, method))
                    continue
                self.results.update(
                    {sp: res}
                )
                _logger.debug("Executed method %s from provider %s" % (method, sp))
                self.resultcount += 1
            except Exception as e:
                if not isinstance(e, AttributeError):
                    try:
                        err = e.msg
                    except AttributeError:
                        err = e
                    self.errors.update(
                        {sp: err}
                    )
                    _logger.debug("Error %s on provider %s" % (e, sp))
                    # -- Use this to debug specific Services errors --
                    # from pprint import pprint
                    # pprint(self.errors)

                if len(self.errors) >= self.max_errors:
                    _logger.warning("No successful response from serviceproviders, max errors exceeded: %s" %
                                    list(self.errors.keys()))
                    return False

            if self.resultcount >= self.max_providers:
                break

        if not self.resultcount:
            raise ServiceError("No successful response from any serviceprovider: %s" % list(self.providers.keys()))
        return list(self.results.values())[0]

    def getbalance(self, addresslist, addresses_per_request=5):
        """
        Get total balance for address or list of addresses

        :param addresslist: Address or list of addresses
        :type addresslist: list, str
        :param addresses_per_request: Maximum number of addresses per request. Default is 5. Use lower setting when you experience timeouts or service request errors, or higher when possible.
        :type addresses_per_request: int

        :return dict: Balance per address
        """
        if isinstance(addresslist, TYPE_TEXT):
            addresslist = [addresslist]

        tot_balance = 0
        while addresslist:
            for address in addresslist:
                db_addr = self.cache.getaddress(address)
                if db_addr and db_addr.last_block and db_addr.last_block >= self.blockcount() and db_addr.balance:
                    tot_balance += db_addr.balance
                    addresslist.remove(address)

            balance = self._provider_execute('getbalance', addresslist[:addresses_per_request])
            if balance:
                tot_balance += balance
            if len(addresslist) == 1:
                self.cache.store_address(addresslist[0], balance=balance)
            addresslist = addresslist[addresses_per_request:]
        return tot_balance

    def getutxos(self, address, after_txid='', limit=MAX_TRANSACTIONS):
        """
        Get list of unspent outputs (UTXO's) for specified address.

        Sorted from old to new, so highest number of confirmations first.

        :param address: Address string
        :type address: str
        :param after_txid: Transaction ID of last known transaction. Only check for utxos after given tx id. Default: Leave empty to return all utxos.
        :type after_txid: str
        :param limit: Maximum number of utxo's to return
        :type limit: int

        :return dict: UTXO's per address
        """
        if not isinstance(address, TYPE_TEXT):
            raise ServiceError("Address parameter must be of type text")
        self.results_cache_n = 0
        self.complete = True

        utxos_cache = []
        if self.min_providers <= 1:
            utxos_cache = self.cache.getutxos(address, bytes.fromhex(after_txid)) or []
        db_addr = self.cache.getaddress(address)
        if utxos_cache:
            self.results_cache_n = len(utxos_cache)

            if db_addr and db_addr.last_block and db_addr.last_block >= self.blockcount():
                return utxos_cache
            else:
                utxos_cache = []
                # after_txid = utxos_cache[-1:][0]['txid']
        # if db_addr and db_addr.last_txid:
        #     after_txid = db_addr.last_txid

        utxos = self._provider_execute('getutxos', address, after_txid, limit)
        if utxos is False:
            raise ServiceError("Error when retrieving UTXO's")
        else:
            # TODO: Update cache_transactions_node
            if utxos and len(utxos) >= limit:
                self.complete = False
            elif not after_txid:
                balance = sum(u['value'] for u in utxos)
                self.cache.store_address(address, balance=balance, n_utxos=len(utxos))

        return utxos_cache + utxos

    def gettransaction(self, txid):
        """
        Get a transaction by its transaction hash. Convert to Bitcoinlib Transaction object.

        :param txid: Transaction identification hash
        :type txid: str

        :return Transaction: A single transaction object
        """
        tx = None
        self.results_cache_n = 0

        if self.min_providers <= 1:
            tx = self.cache.gettransaction(bytes.fromhex(txid))
            if tx:
                self.results_cache_n = 1
        if not tx:
            tx = self._provider_execute('gettransaction', txid)
            if tx and tx.txid != txid:
                _logger.warning("Incorrect txid after parsing")
                tx.txid = txid
            if len(self.results) and self.min_providers <= 1:
                self.cache.store_transaction(tx)
        return tx

    def gettransactions(self, address, after_txid='', limit=MAX_TRANSACTIONS):
        """
        Get all transactions for specified address.

        Sorted from old to new, so transactions with highest number of confirmations first.

        :param address: Address string
        :type address: str
        :param after_txid: Transaction ID of last known transaction. Only check for transactions after given tx id. Default: Leave empty to return all transaction. If used only provide a single address
        :type after_txid: str
        :param limit: Maximum number of transactions to return
        :type limit: int

        :return list: List of Transaction objects
        """
        self._reset_results()
        self.results_cache_n = 0
        if not address:
            return []
        if not isinstance(address, TYPE_TEXT):
            raise ServiceError("Address parameter must be of type text")
        if after_txid is None:
            after_txid = ''
        db_addr = self.cache.getaddress(address)
        txs_cache = []
        qry_after_txid = bytes.fromhex(after_txid)

        # Retrieve transactions from cache
        caching_enabled = True
        if self.min_providers > 1:  # Disable cache if comparing providers
            caching_enabled = False

        if caching_enabled:
            txs_cache = self.cache.gettransactions(address, qry_after_txid, limit) or []
            if txs_cache:
                self.results_cache_n = len(txs_cache)
                if len(txs_cache) == limit:
                    return txs_cache
                limit = limit - len(txs_cache)
                qry_after_txid = bytes.fromhex(txs_cache[-1:][0].txid)

        # Get (extra) transactions from service providers
        txs = []
        if not(db_addr and db_addr.last_block and db_addr.last_block >= self.blockcount()) or not caching_enabled:
            txs = self._provider_execute('gettransactions', address, qry_after_txid.hex(),  limit)
            if txs is False:
                raise ServiceError("Error when retrieving transactions from service provider")

        # Store transactions and address in cache
        # - disable cache if comparing providers or if after_txid is used and no cache is available
        last_block = None
        last_txid = None
        if self.min_providers <= 1 and not(after_txid and not db_addr) and caching_enabled:
            last_block = self.blockcount()
            last_txid = qry_after_txid
            self.complete = True
            if len(txs) == limit:
                self.complete = False
                last_block = txs[-1:][0].block_height
            if len(txs):
                last_txid = bytes.fromhex(txs[-1:][0].txid)
            if len(self.results):
                order_n = 0
                for t in txs:
                    if t.confirmations != 0:
                        res = self.cache.store_transaction(t, order_n, commit=False)
                        order_n += 1
                        # Failure to store transaction: stop caching transaction and store last tx block height - 1
                        if res is False:
                            if t.block_height:
                                last_block = t.block_height - 1
                            break
                self.cache.commit()
                self.cache.store_address(address, last_block, last_txid=last_txid, txs_complete=self.complete)

        all_txs = txs_cache + txs
        # If we have txs for this address update spent and balance information in cache
        if self.complete:
            all_txs = transaction_update_spents(all_txs, address)
            if caching_enabled:
                self.cache.store_address(address, last_block, last_txid=last_txid, txs_complete=True)
                for t in all_txs:
                    self.cache.store_transaction(t, commit=False)
                self.cache.commit()
        return all_txs

    def getrawtransaction(self, txid):
        """
        Get a raw transaction by its transaction hash

        :param txid: Transaction identification hash
        :type txid: str

        :return str: Raw transaction as hexstring
        """
        self.results_cache_n = 0
        rawtx = self.cache.getrawtransaction(bytes.fromhex(txid))
        if rawtx:
            self.results_cache_n = 1
            return rawtx
        return self._provider_execute('getrawtransaction', txid)

    def sendrawtransaction(self, rawtx):
        """
        Push a raw transaction to the network

        :param rawtx: Raw transaction as hexstring or bytes
        :type rawtx: str

        :return dict: Send transaction result
        """
        return self._provider_execute('sendrawtransaction', rawtx)

    def estimatefee(self, blocks=3, priority=''):
        """
        Estimate fee per kilobyte for a transaction for this network with expected confirmation within a certain
        amount of blocks

        :param blocks: Expected confirmation time in blocks. Default is 3.
        :type blocks: int
        :param priority: Priority for transaction: can be 'low', 'medium' or 'high'. Overwrites value supplied in 'blocks' argument
        :type priority: str

        :return int: Fee in the smallest network denominator (satoshi)
        """
        self.results_cache_n = 0
        if priority:
            if priority == 'low':
                blocks = 10
            elif priority == 'high':
                blocks = 1
        if self.min_providers <= 1:  # Disable cache if comparing providers
            fee = self.cache.estimatefee(blocks)
            if fee:
                self.results_cache_n = 1
                return fee
        fee = self._provider_execute('estimatefee', blocks)
        if not fee:  # pragma: no cover
            if self.network.fee_default:
                fee = self.network.fee_default
            else:
                raise ServiceError("Could not estimate fees, please define default fees in network settings")
        if fee < self.network.fee_min:
            fee = self.network.fee_min
        elif fee > self.network.fee_max:
            fee = self.network.fee_max
        self.cache.store_estimated_fee(blocks, fee)
        return fee

    def blockcount(self):
        """
        Get latest block number: The block number of last block in longest chain on the Blockchain.

        Block count is cashed for BLOCK_COUNT_CACHE_TIME seconds to avoid to many calls to service providers.

        :return int:
        """

        blockcount = self.cache.blockcount()
        last_cache_blockcount = self.cache.blockcount(never_expires=True)
        if blockcount:
            self._blockcount = blockcount
            return blockcount

        current_timestamp = time.time()
        if self._blockcount_update < current_timestamp - BLOCK_COUNT_CACHE_TIME:
            new_count = self._provider_execute('blockcount')
            if not self._blockcount or (new_count and new_count > self._blockcount):
                self._blockcount = new_count
                self._blockcount_update = current_timestamp
            if last_cache_blockcount > self._blockcount:
                return last_cache_blockcount
            # Store result in cache
            if len(self.results) and list(self.results.keys())[0] != 'caching':
                self.cache.store_blockcount(self._blockcount)
        return self._blockcount

    def getblock(self, blockid, parse_transactions=True, page=1, limit=None):
        """
        Get block with specified block height or block hash from service providers.

        If parse_transaction is set to True a list of Transaction object will be returned otherwise a
        list of transaction ID's.

        Some providers require 1 or 2 extra request per transaction, so to avoid timeouts or rate limiting errors
        you can specify a page and limit for the transaction. For instance with page=2, limit=4 only transaction
        5 to 8 are returned in the Blocks's 'transaction' attribute.

        If you only use a local bcoin or bitcoind provider, make sure you set the limit to maximum (i.e. 9999)
        because all transactions are already downloaded when fetching the block.

        >>> from bitcoinlib.services.services import Service
        >>> srv = Service()
        >>> b = srv.getblock(0)
        >>> b
        <Block(000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f, 0, transactions: 1)>

        :param blockid: Hash or block height of block
        :type blockid: str, int
        :param parse_transactions: Return Transaction objects or just transaction ID's. Default is return txids.
        :type parse_transactions: bool
        :param page: Page number of transaction paging. Default is start from the beginning: 1
        :type page: int
        :param limit: Maximum amount of transaction to return. Default is 10 is parse transaction is enabled, otherwise returns all txid's (9999)
        :type limit: int

        :return Block:
        """
        if limit is None:
            limit = 10 if parse_transactions else 99999

        block = self.cache.getblock(blockid)
        is_last_page = False
        if block:
            # Block found get transactions from cache
            block.transactions = self.cache.getblocktransactions(block.height, page, limit)
            if block.transactions:
                self.results_cache_n = 1
            is_last_page = page*limit > block.tx_count
        if not block or (not len(block.transactions) and limit != 0) or (not is_last_page and len(block.transactions) < limit) or \
                (is_last_page and ((page-1)*limit - block.tx_count + len(block.transactions)) < 0):
            self.results_cache_n = 0
            bd = self._provider_execute('getblock', blockid, parse_transactions, page, limit)
            if not bd or isinstance(bd, bool):
                return False
            block = Block(bd['block_hash'], bd['version'], bd['prev_block'], bd['merkle_root'], bd['time'], bd['bits'],
                          bd['nonce'], bd['txs'], bd['height'], bd['depth'], self.network)
            block.tx_count = bd['tx_count']
            block.limit = limit
            block.page = page

            if parse_transactions and self.min_providers <= 1:
                order_n = (page-1)*limit
                for tx in block.transactions:
                    if isinstance(tx, Transaction):
                        self.cache.store_transaction(tx, order_n, commit=False)
                    order_n += 1
                self.cache.commit()
            self.complete = True if len(block.transactions) == block.tx_count else False
            self.cache.store_block(block)
        return block

    def getrawblock(self, blockid):
        """
        Get raw block as hexadecimal string for block with specified hash or block height.

        Not many providers offer this option, and it can be slow, so it is advised to use a local client such
        as bitcoind.

        :param blockid: Block hash or block height
        :type blockid: str, int

        :return str:
        """
        return self._provider_execute('getrawblock', blockid)

    def mempool(self, txid=''):
        """
        Get list of all transaction IDs in the current mempool

        A full list of transactions ID's will only be returned if a bcoin or bitcoind client is available. Otherwise
        specify the txid option to verify if a transaction is added to the mempool.

        :param txid: Check if transaction with this hash exists in memory pool
        :type txid: str

        :return list:
        """
        return self._provider_execute('mempool', txid)

    def getcacheaddressinfo(self, address):
        """
        Get address information from cache. I.e. balance, number of transactions, number of utox's, etc

        Cache will only be filled after all transactions for a specific address are retrieved (with gettransactions ie)

        :param address: address string
        :type address: str

        :return dict:
        """
        addr_dict = {'address': address}
        addr_rec = self.cache.getaddress(address)
        if isinstance(addr_rec, DbCacheAddress):
            addr_dict['balance'] = addr_rec.balance
            addr_dict['last_block'] = addr_rec.last_block
            addr_dict['n_txs'] = addr_rec.n_txs
            addr_dict['n_utxos'] = addr_rec.n_utxos
        return addr_dict

    def isspent(self, txid, output_n):
        """
        Check if the output with provided transaction ID and output number is spent.

        :param txid: Transaction ID hex
        :type txid: str
        :param output_n: Output number
        :type output_n: int

        :return bool:
        """
        t = self.cache.gettransaction(bytes.fromhex(txid))
        if t and len(t.outputs) > output_n and t.outputs[output_n].spent is not None:
            return t.outputs[output_n].spent
        else:
            return bool(self._provider_execute('isspent', txid, output_n))

    def getinfo(self):
        """
        Returns info about current network. Such as difficulty, latest block, mempool size and network hashrate.

        :return dict:
        """
        return self._provider_execute('getinfo')

    def getinputvalues(self, t):
        """
        Retrieve values for transaction inputs for given Transaction.

        Raw transactions as stored on the blockchain do not contain the input values but only the previous
        transaction hash and index number. This method retrieves the previous transaction and reads the value.

        :param t: Transaction
        :type t: Transaction

        :return Transaction:
        """
        prev_txs = []
        for i in t.inputs:
            if not i.value:
                if i.prev_txid not in prev_txs:
                    prev_t = self.gettransaction(i.prev_txid.hex())
                else:
                    prev_t = [t for t in prev_txs if t.txid == i.prev_txid][0]
                i.value = prev_t.outputs[i.output_n_int].value
        return t class Input(object):
    """
    Transaction Input class, used by Transaction class
    
    An Input contains a reference to an UTXO or Unspent Transaction Output (prev_txid + output_n).
    To spent the UTXO an unlocking script can be included to prove ownership.
    
    Inputs are verified by the Transaction class.
    """

    def __init__(self, prev_txid, output_n, keys=None, signatures=None, public_hash=b'', unlocking_script=b'',
                 unlocking_script_unsigned=None, script=None, script_type=None, address='',
                 sequence=0xffffffff, compressed=None, sigs_required=None, sort=False, index_n=0,
                 value=0, double_spend=False, locktime_cltv=None, locktime_csv=None, key_path='', witness_type=None,
                 witnesses=None, encoding=None, strict=True, network=DEFAULT_NETWORK):
        """
        Create a new transaction input
        
        :param prev_txid: Transaction hash of the UTXO (previous output) which will be spent.
        :type prev_txid: bytes, str
        :param output_n: Output number in previous transaction.
        :type output_n: bytes, int
        :param keys: A list of Key objects or public / private key string in various formats. If no list is provided but a bytes or string variable, a list with one item will be created. Optional
        :type keys: list (bytes, str, Key)
        :param signatures: Specify optional signatures
        :type signatures: list (bytes, str, Signature)
        :param public_hash: Public key hash or script hash. Specify if key is not available
        :type public_hash: bytes
        :param unlocking_script: Unlocking script (scriptSig) to prove ownership. Optional
        :type unlocking_script: bytes, hexstring
        :param unlocking_script_unsigned: Unlocking script for signing transaction
        :type unlocking_script_unsigned: bytes, hexstring
        :param script_type: Type of unlocking script used, i.e. p2pkh or p2sh_multisig. Default is p2pkh
        :type script_type: str
        :param address: Address string or object for input
        :type address: str, Address
        :param sequence: Sequence part of input, you normally do not have to touch this
        :type sequence: bytes, int
        :param compressed: Use compressed or uncompressed public keys. Default is compressed
        :type compressed: bool
        :param sigs_required: Number of signatures required for a p2sh_multisig unlocking script
        :type sigs_required: int
        :param sort: Sort public keys according to BIP0045 standard. Default is False to avoid unexpected change of key order.
        :type sort: boolean
        :param index_n: Index of input in transaction. Used by Transaction class.
        :type index_n: int
        :param value: Value of input in smallest denominator integers (Satoshi's) or as Value object or string
        :type value: int, Value, str
        :param double_spend: Is this input also spend in another transaction
        :type double_spend: bool
        :param locktime_cltv: Check Lock Time Verify value. Script level absolute time lock for this input
        :type locktime_cltv: int
        :param locktime_csv: Check Sequence Verify value
        :type locktime_csv: int
        :param key_path: Key path of input key as BIP32 string or list
        :type key_path: str, list
        :param witness_type: Specify witness/signature position: 'segwit' or 'legacy'. Determine from script, address or encoding if not specified.
        :type witness_type: str
        :param witnesses: List of witnesses for inputs, used for segwit transactions for instance. Argument can be list of bytes or string or a single bytes string with concatenated witnesses as found in a raw transaction.
        :type witnesses: list of bytes, list of str, bytes
        :param encoding: Address encoding used. For example bech32/base32 or base58. Leave empty for default
        :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):
                kobj = Key(key, network=network, strict=strict)
            else:
                kobj = key
            if kobj not in self.keys:
                # if self.compressed is not None and kobj.compressed != self.compressed:
                #     _logger.warning("Key compressed is %s but Input class compressed argument is %s " %
                #                     (kobj.compressed, self.compressed))
                self.compressed = kobj.compressed
                self.keys.append(kobj)
        if self.compressed is None:
            self.compressed = True
        if self.sort:
            self.keys.sort(key=lambda k: k.public_byte)
        self.hash_type = SIGHASH_ALL
        self.strict = strict

        for sig in signatures:
            if not isinstance(sig, Signature):
                try:
                    sig = Signature.parse(sig)
                except Exception as e:
                    _logger.error("Could not parse signature %s in Input. Error: %s" % (to_hexstring(sig), e))
                    continue
            if sig.as_der_encoded() not in [x.as_der_encoded() for x in self.signatures]:
                self.signatures.append(sig)
                if sig.hash_type:
                    self.hash_type = sig.hash_type

        # fixme: p2wpkh == p2sh_p2wpkh
        if self.script_type in ['sig_pubkey', 'p2sh_p2wpkh', 'p2wpkh'] and self.witnesses and not self.signatures and \
                self.script_type in ['sig_pubkey', 'p2sh_p2wpkh'] and len(self.witnesses) == 2 and \
                b'\0' not in self.witnesses:
            self.signatures = [Signature.parse_bytes(self.witnesses[0])]
            self.keys = [Key(self.witnesses[1], network=self.network)]

        self.update_scripts(hash_type=self.hash_type)

    @classmethod
    def parse(cls, raw, witness_type='segwit', index_n=0, strict=True, network=DEFAULT_NETWORK):
        """
        Parse raw BytesIO string and return Input object

        :param raw: Input
        :type raw: BytesIO
        :param witness_type: Specify witness/signature position: 'segwit' or 'legacy'. Derived from script if not specified.
        :type witness_type: str
        :param index_n: Index number of input
        :type index_n: int
        :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

        :return Input:
        """
        prev_hash = raw.read(32)[::-1]
        if len(prev_hash) != 32:
            raise TransactionError("Input transaction hash not found. Probably malformed raw transaction")
        output_n = raw.read(4)[::-1]
        unlocking_script_size = read_varbyteint(raw)
        unlocking_script = raw.read(unlocking_script_size)
        inp_type = 'legacy'
        if witness_type == 'segwit' and not unlocking_script_size:
            inp_type = 'segwit'
        sequence_number = raw.read(4)

        return Input(prev_txid=prev_hash, output_n=output_n, unlocking_script=unlocking_script,
                     witness_type=inp_type, sequence=sequence_number, index_n=index_n, strict=strict, network=network)

    def update_scripts(self, hash_type=SIGHASH_ALL):
        """
        Method to update Input scripts.

        Creates or updates unlocking script, witness script for segwit inputs, multisig redeemscripts and
        locktime scripts. This method is called when initializing a Input class or when signing an input.

        :param hash_type: Specific hash type, default is SIGHASH_ALL
        :type hash_type: int

        :return bool: Always returns True when method is completed
        """

        addr_data = b''
        unlock_script = b''
        if self.script_type in ['sig_pubkey', 'p2sh_p2wpkh', 'p2wpkh']:  # fixme: p2wpkh == p2sh_p2wpkh
            if not self.public_hash and self.keys:
                self.public_hash = self.keys[0].hash160
            if not self.keys and not self.public_hash:
                return
            self.script_code = b'\x76\xa9\x14' + self.public_hash + b'\x88\xac'
            self.unlocking_script_unsigned = self.script_code
            addr_data = self.public_hash
            if self.signatures and self.keys:
                self.witnesses = [self.signatures[0].as_der_encoded() if hash_type else b'', self.keys[0].public_byte]
                unlock_script = b''.join([bytes(varstr(w)) for w in self.witnesses])
            if not self.unlocking_script or self.strict:
                if self.witness_type == 'p2sh-segwit':
                    self.unlocking_script = varstr(b'\0' + varstr(self.public_hash))
                elif self.witness_type == 'segwit':
                    self.unlocking_script = b''
                elif unlock_script != b'':
                    self.unlocking_script = unlock_script
        elif self.script_type in ['p2sh_multisig', 'p2sh_p2wsh', 'p2wsh']:  # fixme: p2sh_p2wsh == p2wsh
            if not self.redeemscript and self.keys:
                self.redeemscript = Script(script_types=['multisig'], keys=self.keys,
                                           sigs_required=self.sigs_required).serialize()
            if self.redeemscript:
                if self.witness_type == 'segwit' or self.witness_type == 'p2sh-segwit':
                    self.public_hash = hashlib.sha256(self.redeemscript).digest()
                else:
                    self.public_hash = hash160(self.redeemscript)
            addr_data = self.public_hash
            self.unlocking_script_unsigned = self.redeemscript

            if self.redeemscript and self.keys:
                n_tag = self.redeemscript[0:1]
                if not isinstance(n_tag, int):
                    n_tag = int.from_bytes(n_tag, 'big')
                self.sigs_required = n_tag - 80
                signatures = [s.as_der_encoded() for s in self.signatures[:self.sigs_required]]
                if b'' in signatures:
                    raise TransactionError("Empty signature found in signature list when signing. "
                                           "Is DER encoded version of signature defined?")
                if len(signatures) == self.sigs_required:  # and not self.unlocking_script
                    unlock_script_obj = Script(script_types=['p2sh_multisig'], keys=[k.public_byte for k in self.keys],
                                               signatures=self.signatures[:self.sigs_required],
                                               sigs_required=self.sigs_required, redeemscript=self.redeemscript)
                    if self.witness_type in ['segwit', 'p2sh-segwit']:
                        unlock_script = unlock_script_obj.serialize_list()
                    else:
                        unlock_script = unlock_script_obj.serialize()
                if self.witness_type == 'segwit':
                    script_code = b''
                    for k in self.keys:
                        script_code += varstr(k.public_byte) + b'\xad\xab'
                    if len(script_code) > 3:
                        script_code = script_code[:-2] + b'\xac'
                    self.script_code = script_code
                    if signatures:
                        self.witnesses = unlock_script
                elif self.witness_type == 'p2sh-segwit':
                    self.unlocking_script = varstr(b'\0' + varstr(self.public_hash))
                    self.script_code = self.unlocking_script
                    if signatures:
                        self.witnesses = unlock_script
                elif unlock_script != b'' and self.strict:
                    self.unlocking_script = unlock_script
        elif self.script_type == 'signature':
            if self.keys:
                self.script_code = varstr(self.keys[0].public_byte) + b'\xac'
                self.unlocking_script_unsigned = self.script_code
                addr_data = self.keys[0].public_byte
            if self.signatures and not self.unlocking_script:
                self.unlocking_script = varstr(self.signatures[0].as_der_encoded())
        elif self.script_type not in ['coinbase', 'unknown'] and self.strict:
            raise TransactionError("Unknown unlocking script type %s for input %d" % (self.script_type, self.index_n))
        if addr_data and not self.address:
            self.address = Address(hashed_data=addr_data, encoding=self.encoding, network=self.network,
                                   script_type=self.script_type, witness_type=self.witness_type).address

        if self.locktime_cltv:
            self.unlocking_script_unsigned = script_add_locktime_cltv(self.locktime_cltv,
                                                                      self.unlocking_script_unsigned)
            self.unlocking_script = script_add_locktime_cltv(self.locktime_cltv, self.unlocking_script)
        elif self.locktime_csv:
            self.unlocking_script_unsigned = script_add_locktime_csv(self.locktime_csv, self.unlocking_script_unsigned)
            self.unlocking_script = script_add_locktime_csv(self.locktime_csv, self.unlocking_script)
        return True

    def verify(self, transaction_hash):
        """
        Verify input with provided transaction hash, check if signatures matches public key.

        Does not check if UTXO is valid or has already been spent

        :param transaction_hash: Double SHA256 Hash of Transaction signature
        :type transaction_hash: bytes

        :return bool: True if enough signatures provided and if all signatures are valid
        """

        if self.script_type == 'coinbase':
            self.valid = True
            return True
        if not self.signatures:
            _logger.info("No signatures found for transaction input %d" % self.index_n)
            return False

        sig_n = 0
        key_n = 0
        sigs_verified = 0
        while sigs_verified < self.sigs_required:
            if key_n >= len(self.keys):
                _logger.info(
                    "Not enough valid signatures provided for input %d. Found %d signatures but %d needed" %
                    (self.index_n, sigs_verified, self.sigs_required))
                return False
            if sig_n >= len(self.signatures):
                _logger.info("No valid signatures found")
                return False
            key = self.keys[key_n]
            sig = self.signatures[sig_n]
            if verify(transaction_hash, sig, key):
                sigs_verified += 1
                sig_n += 1
            elif sig_n > 0:
                # try previous signature
                prev_sig = deepcopy(self.signatures[sig_n - 1])
                if verify(transaction_hash, prev_sig, key):
                    sigs_verified += 1
            key_n += 1
        self.valid = True
        return True

    def as_dict(self):
        """
        Get transaction input information in json format
        
        :return dict: Json with output_n, prev_txid, output_n, type, address, public_key, public_hash, unlocking_script and sequence
        """

        pks = []
        for k in self.keys:
            pks.append(k.public_hex)
        if len(self.keys) == 1:
            pks = pks[0]
        return {
            'index_n': self.index_n,
            'prev_txid': self.prev_txid.hex(),
            'output_n': self.output_n_int,
            'script_type': self.script_type,
            'address': self.address,
            'value': self.value,
            'public_keys': pks,
            'compressed': self.compressed,
            'encoding': self.encoding,
            'double_spend': self.double_spend,
            'script': self.unlocking_script.hex(),
            'redeemscript': self.redeemscript.hex(),
            'sequence': self.sequence,
            'signatures': [s.hex() for s in self.signatures],
            'sigs_required': self.sigs_required,
            'locktime_cltv': self.locktime_cltv,
            'locktime_csv': self.locktime_csv,
            'public_hash': self.public_hash.hex(),
            'script_code': self.script_code.hex(),
            'unlocking_script': self.unlocking_script.hex(),
            'unlocking_script_unsigned': self.unlocking_script_unsigned.hex(),
            'witness_type': self.witness_type,
            'witness': b''.join(self.witnesses).hex(),
            'sort': self.sort,
            'valid': self.valid,
        }

    def __repr__(self):
        return "<Input(prev_txid='%s', output_n=%d, address='%s', index_n=%s, type='%s')>" % \
               (self.prev_txid.hex(), self.output_n_int, self.address, self.index_n, self.script_type) class Output(object):
    """
    Transaction Output class, normally part of Transaction class.
    
    Contains the amount and destination of a transaction.
    """

    def __init__(self, value, address='', public_hash=b'', public_key=b'', lock_script=b'', spent=False,
                 output_n=0, script_type=None, encoding=None, spending_txid='', spending_index_n=None, strict=True,
                 network=DEFAULT_NETWORK):
        """
        Create a new transaction output
        
        An transaction outputs locks the specified amount to a public key. Anyone with the private key can unlock
        this output.
        
        The transaction output class contains an amount and the destination which can be provided either as address, 
        public key, public key hash or a locking script. Only one needs to be provided as the they all can be derived 
        from each other, but you can provide as much attributes as you know to improve speed.
        
        :param value: Amount of output in smallest denominator integers (Satoshi's) or as Value object or string
        :type value: int, Value, str
        :param address: Destination address of output. Leave empty to derive from other attributes you provide. An instance of an Address or HDKey class is allowed as argument.
        :type address: str, Address, HDKey
        :param public_hash: Hash of public key or script
        :type public_hash: bytes, str
        :param public_key: Destination public key
        :type public_key: bytes, str
        :param lock_script: Locking script of output. If not provided a default unlocking script will be provided with a public key hash.
        :type lock_script: bytes, str
        :param spent: Is output already spent? Default is False
        :type spent: bool
        :param output_n: Output index number, default is 0. Index number has to be unique per transaction and 0 for first output, 1 for second, etc
        :type output_n: int
        :param script_type: Script type of output (p2pkh, p2sh, segwit p2wpkh, etc). Extracted from lock_script if provided.
        :type script_type: str
        :param encoding: Address encoding used. For example bech32/base32 or base58. Leave empty to derive from address or default base58 encoding
        :type encoding: str
        :param spending_txid: Transaction hash of input spending this transaction output
        :type spending_txid: str
        :param spending_index_n: Index number of input spending this transaction output
        :type spending_index_n: int
        :param strict: Raise exception when output is malformed, incomplete or not understood
        :type strict: bool
        :param network: Network, leave empty for default
        :type network: str, Network
        """

        if strict and not (address or public_hash or public_key or lock_script):
            raise TransactionError("Please specify address, lock_script, public key or public key hash when "
                                   "creating output")

        self.network = network
        if not isinstance(network, Network):
            self.network = Network(network)
        self.value = value_to_satoshi(value, network=network)
        self.lock_script = b'' if lock_script is None else to_bytes(lock_script)
        self.public_hash = to_bytes(public_hash)
        if isinstance(address, Address):
            self.address = address.address
            self.address_obj = address
        elif isinstance(address, HDKey):
            self.address = address.address()
            self.address_obj = address.address_obj
            public_key = address.public_byte
            if not script_type:
                script_type = script_type_default(address.witness_type, address.multisig, True)
            self.public_hash = address.hash160
        else:
            self.address = address
            self.address_obj = None
        self.public_key = to_bytes(public_key)
        self.compressed = True
        self.k = None
        self.versionbyte = self.network.prefix_address
        self.script_type = script_type
        self.encoding = encoding
        if not self.address and self.encoding is None:
            self.encoding = 'base58'
        self.spent = spent
        self.output_n = output_n
        self.script = Script.parse_bytes(self.lock_script, strict=strict)

        if self.address_obj:
            self.script_type = self.address_obj.script_type if script_type is None else script_type
            self.public_hash = self.address_obj.hash_bytes
            self.network = self.address_obj.network
            self.encoding = self.address_obj.encoding

        if self.script:
            self.script_type = self.script_type if not self.script.script_types else self.script.script_types[0]
            if self.script_type in ['p2wpkh', 'p2wsh']:
                self.encoding = 'bech32'
            self.public_hash = self.script.public_hash
            if self.script.keys:
                self.public_key = self.script.keys[0].public_hex

        if self.public_key and not self.public_hash:
            k = Key(self.public_key, is_private=False, network=network)
            self.public_hash = k.hash160
        elif self.address and (not self.public_hash or not self.script_type or not self.encoding):
            address_dict = deserialize_address(self.address, self.encoding, self.network.name)
            if address_dict['script_type'] and not script_type:
                self.script_type = address_dict['script_type']
            if not self.script_type:
                raise TransactionError("Could not determine script type of address %s" % self.address)
            self.encoding = address_dict['encoding']
            network_guesses = address_dict['networks']
            if address_dict['network'] and self.network.name != address_dict['network']:
                raise TransactionError("Address %s is from %s network and transaction from %s network" %
                                       (self.address, address_dict['network'], self.network.name))
            elif self.network.name not in network_guesses:
                raise TransactionError("Network for output address %s is different from transaction network. %s not "
                                       "in %s" % (self.address, self.network.name, network_guesses))
            self.public_hash = address_dict['public_key_hash_bytes']
        if not self.encoding:
            self.encoding = 'base58'
            if self.script_type in ['p2wpkh', 'p2wsh']:
                self.encoding = 'bech32'

        if self.script_type is None:
            self.script_type = 'p2pkh'
            if self.encoding == 'bech32':
                self.script_type = 'p2wpkh'
        if self.public_hash and not self.address:
            self.address_obj = Address(hashed_data=self.public_hash, script_type=self.script_type,
                                       encoding=self.encoding, network=self.network)
            self.address = self.address_obj.address
            self.versionbyte = self.address_obj.prefix
        if not self.script and strict and (self.public_hash or self.public_key):
            self.script = Script(script_types=[self.script_type], public_hash=self.public_hash, keys=[self.public_key])
            self.lock_script = self.script.serialize()
            if not self.script:
                raise TransactionError("Unknown output script type %s, please provide locking script" %
                                       self.script_type)
        self.spending_txid = spending_txid
        self.spending_index_n = spending_index_n
        # if self.script_type != 'nulldata' and value < self.network.dust_amount:
        #     raise TransactionError("Output to %s must be more then dust amount %d" %
        #                            (self.address, self.network.dust_amount))

    @classmethod
    def parse(cls, raw, output_n=0, strict=True, network=DEFAULT_NETWORK):
        """
        Parse raw BytesIO string and return Output object

        :param raw: raw output stream
        :type raw: BytesIO
        :param output_n: Output number of Transaction output
        :type output_n: int
        :param strict: Raise exception when output is malformed, incomplete or not understood
        :type strict: bool
        :param network: Network, leave empty for default network
        :type network: str, Network

        :return Output:
        """
        value = int.from_bytes(raw.read(8)[::-1], 'big')
        lock_script_size = read_varbyteint(raw)
        lock_script = raw.read(lock_script_size)
        return Output(value=value, lock_script=lock_script, output_n=output_n, strict=strict, network=network)

    # TODO: Write and rewrite locktime methods
    # def set_locktime - CLTV (BIP65)
    # def set_locktime_blocks
    # def set_locktime_time

    def set_locktime_relative(self, locktime):
        """
        Relative timelocks with CHECKSEQUENCEVERIFY (CSV) as defined in BIP112
        :param locktime:
        :return:
        """
        pass

    def set_locktime_relative_blocks(self, blocks):
        """
        Set nSequence relative locktime for this transaction input. The transaction will only be valid if the specified number of blocks has been mined since the previous UTXO is confirmed.

        Maximum number of blocks is 65535 as defined in BIP-0068, which is around 455 days.

        When setting an relative timelock, the transaction version must be at least 2. The transaction will be updated so existing signatures for this input will be removed.

        :param blocks: The blocks value is the number of blocks since the previous transaction output has been confirmed.
        :type blocks: int

        :return None:
        """
        # if blocks == 0 or blocks == 0xffffffff:
        #     self.sequence = 0xffffffff
        #     return
        # if blocks > SEQUENCE_LOCKTIME_MASK:
        #     raise TransactionError("Number of nSequence timelock blocks exceeds %d" % SEQUENCE_LOCKTIME_MASK)
        # self.sequence = blocks
        # self.signatures = []

    def set_locktime_relative_time(self, seconds):
        """
        Set nSequence relative locktime for this transaction input. The transaction will only be valid if the specified amount of seconds have been passed since the previous UTXO is confirmed.

        Number of seconds will be rounded to the nearest 512 seconds. Any value below 512 will be interpreted as 512 seconds.

        Maximum number of seconds is 33553920 (512 * 65535), which equals 384 days. See BIP-0068 definition.

        When setting an relative timelock, the transaction version must be at least 2. The transaction will be updated so existing signatures for this input will be removed.

        :param seconds: Number of seconds since the related previous transaction output has been confirmed.
        :return:
        """
        # if seconds == 0 or seconds == 0xffffffff:
        #     self.sequence = 0xffffffff
        #     return
        # if seconds < 512:
        #     seconds = 512
        # if (seconds // 512) > SEQUENCE_LOCKTIME_MASK:
        #     raise TransactionError("Number of relative nSeqence timelock seconds exceeds %d" % SEQUENCE_LOCKTIME_MASK)
        # self.sequence = seconds // 512 + SEQUENCE_LOCKTIME_TYPE_FLAG
        # self.signatures = []

    def as_dict(self):
        """
        Get transaction output information in json format

        :return dict: Json with amount, locking script, public key, public key hash and address
        """

        return {
            'value': self.value,
            'script': self.lock_script.hex(),
            'script_type': self.script_type,
            'public_key': self.public_key.hex(),
            'public_hash': self.public_hash.hex(),
            'address': self.address,
            'output_n': self.output_n,
            'spent': self.spent,
            'spending_txid': self.spending_txid,
            'spending_index_n': self.spending_index_n,
        }

    def __repr__(self):
        return "<Output(value=%d, address=%s, type=%s)>" % (self.value, self.address, self.script_type) class Transaction(object):
    """
    Transaction Class

    Contains 1 or more Input class object with UTXO's to spent and 1 or more Output class objects with destinations.
    Besides the transaction class contains a locktime and version.

    Inputs and outputs can be included when creating the transaction, or can be add later with add_input and
    add_output respectively.

    A verify method is available to check if the transaction Inputs have valid unlocking scripts.

    Each input in the transaction can be signed with the sign method provided a valid private key.
    """

    @staticmethod
    @deprecated  # Replaced by Transaction.parse() in version 0.6
    def import_raw(rawtx, network=DEFAULT_NETWORK, check_size=True):  # pragma: no cover
        """
        Import a raw transaction and create a Transaction object

        Uses the transaction_deserialize method to parse the raw transaction and then calls the init method of
        this transaction class to create the transaction object

        REPLACED BY THE PARSE() METHOD

        :param rawtx: Raw transaction string
        :type rawtx: bytes, str
        :param network: Network, leave empty for default
        :type network: str, Network
        :param check_size: Check if no bytes are left when parsing is finished. Disable when parsing list of transactions, such as the transactions in a raw block. Default is True
        :type check_size: bool

        :return Transaction:
        """

        return transaction_deserialize(rawtx, network=network, check_size=check_size)

    @classmethod
    def parse(cls, rawtx, strict=True, network=DEFAULT_NETWORK):
        """
        Parse a raw transaction and create a Transaction object

        :param rawtx: Raw transaction string
        :type rawtx: BytesIO, bytes, str
        :param strict: Raise exception when transaction is malformed, incomplete or not understood
        :type strict: bool
        :param network: Network, leave empty for default network
        :type network: str, Network

        :return Transaction:
        """
        if isinstance(rawtx, bytes):
            rawtx = BytesIO(rawtx)
        elif isinstance(rawtx, str):
            rawtx = BytesIO(bytes.fromhex(rawtx))

        return cls.parse_bytesio(rawtx, strict, network)

    @classmethod
    def parse_bytesio(cls, rawtx, strict=True, network=DEFAULT_NETWORK):
        """
        Parse a raw transaction and create a Transaction object

        :param rawtx: Raw transaction string
        :type rawtx: BytesIO
        :param strict: Raise exception when transaction is malformed, incomplete or not understood
        :type strict: bool
        :param network: Network, leave empty for default network
        :type network: str, Network

        :return Transaction:
        """
        coinbase = False
        flag = None
        witness_type = 'legacy'
        network = network
        if not isinstance(network, Network):
            cls.network = Network(network)
        raw_bytes = b''

        try:
            pos_start = rawtx.tell()
        except AttributeError:
            raise TransactionError("Provide raw transaction as BytesIO. Use parse, parse_bytes, parse_hex to parse "
                                   "other data types")

        version = rawtx.read(4)[::-1]
        if rawtx.read(1) == b'\0':
            flag = rawtx.read(1)
            if flag == b'\1':
                witness_type = 'segwit'
        else:
            rawtx.seek(-1, 1)

        n_inputs = read_varbyteint(rawtx)
        inputs = []
        for n in range(0, n_inputs):
            inp = Input.parse(rawtx, index_n=n, witness_type=witness_type, strict=strict, network=network)
            if inp.prev_txid == 32 * b'\0':
                coinbase = True
            inputs.append(inp)

        outputs = []
        output_total = 0
        n_outputs = read_varbyteint(rawtx)
        for n in range(0, n_outputs):
            o = Output.parse(rawtx, output_n=n, strict=strict, network=network)
            outputs.append(o)
            output_total += o.value
        if not outputs:
            raise TransactionError("Error no outputs found in this transaction")

        if witness_type == 'segwit':
            for n in range(0, len(inputs)):
                n_items = read_varbyteint(rawtx)
                if not n_items:
                    continue
                script = Script()
                for m in range(0, n_items):
                    item_size = read_varbyteint(rawtx)
                    witness = rawtx.read(item_size)
                    inputs[n].witnesses.append(witness)
                    s = Script.parse_bytes(varstr(witness), strict=strict)
                    script += s

                inputs[n].script = script if not inputs[n].script else inputs[n].script + script
                inputs[n].keys = script.keys
                inputs[n].signatures = script.signatures
                if script.script_types[0][:13] == 'p2sh_multisig' or script.script_types[0] == 'signature_multisig':  # , 'p2sh_p2wsh'
                    inputs[n].script_type = 'p2sh_multisig'
                    inputs[n].redeemscript = inputs[n].witnesses[-1]
                elif inputs[n].script_type == 'p2wpkh':
                    inputs[n].script_type = 'p2sh_p2wpkh'
                    inputs[n].witness_type = 'p2sh-segwit'
                elif inputs[n].script_type == 'p2wpkh' or inputs[n].script_type == 'p2wsh':
                    inputs[n].script_type = 'p2sh_p2wsh'
                    inputs[n].witness_type = 'p2sh-segwit'

                inputs[n].update_scripts()

        locktime = int.from_bytes(rawtx.read(4)[::-1], 'big')
        raw_len = len(raw_bytes)
        if not raw_bytes:
            pos_end = rawtx.tell()
            raw_len = pos_end - pos_start
            rawtx.seek(pos_start)
            raw_bytes = rawtx.read(raw_len)

        return Transaction(inputs, outputs, locktime, version, network, size=raw_len, output_total=output_total,
                           coinbase=coinbase, flag=flag, witness_type=witness_type, rawtx=raw_bytes)

    @classmethod
    def parse_hex(cls, rawtx, strict=True, network=DEFAULT_NETWORK):
        """
        Parse a raw hexadecimal transaction and create a Transaction object. Wrapper for the :func:`parse_bytesio`
        method

        :param rawtx: Raw transaction hexadecimal string
        :type rawtx: str
        :param strict: Raise exception when transaction is malformed, incomplete or not understood
        :type strict: bool
        :param network: Network, leave empty for default network
        :type network: str, Network

        :return Transaction:
        """

        return cls.parse_bytesio(BytesIO(bytes.fromhex(rawtx)), strict, network)

    @classmethod
    def parse_bytes(cls, rawtx, strict=True, network=DEFAULT_NETWORK):
        """
        Parse a raw bytes transaction and create a Transaction object.  Wrapper for the :func:`parse_bytesio`
        method

        :param rawtx: Raw transaction hexadecimal string
        :type rawtx: bytes
        :param strict: Raise exception when transaction is malformed, incomplete or not understood
        :type strict: bool
        :param network: Network, leave empty for default network
        :type network: str, Network

        :return Transaction:
        """

        return cls.parse(BytesIO(rawtx), strict, network)

    @staticmethod
    def load(txid=None, filename=None):
        """
        Load transaction object from file which has been stored with the :func:`save` method.

        Specify transaction ID or filename.

        :param txid: Transaction ID. Transaction object will be read from .bitcoinlib datadir
        :type txid: str
        :param filename: Name of transaction object file
        :type filename: str

        :return Transaction:
        """
        if not filename and not txid:
            raise TransactionError("Please supply filename or txid")
        elif not filename and txid:
            p = Path(BCL_DATA_DIR, '%s.tx' % txid)
        else:
            p = Path(filename)
            if not p.parent or str(p.parent) == '.':
                p = Path(BCL_DATA_DIR, filename)
        f = p.open('rb')
        t = pickle.load(f)
        f.close()
        return t

    def __init__(self, inputs=None, outputs=None, locktime=0, version=None,
                 network=DEFAULT_NETWORK, fee=None, fee_per_kb=None, size=None, txid='', txhash='', date=None,
                 confirmations=None, block_height=None, block_hash=None, input_total=0, output_total=0, rawtx=b'',
                 status='new', coinbase=False, verified=False, witness_type='legacy', flag=None):
        """
        Create a new transaction class with provided inputs and outputs.

        You can also create a empty transaction and add input and outputs later.

        To verify and sign transactions all inputs and outputs need to be included in transaction. Any modification
        after signing makes the transaction invalid.

        :param inputs: Array of Input objects. Leave empty to add later
        :type inputs: list (Input)
        :param outputs: Array of Output object. Leave empty to add later
        :type outputs: list (Output)
        :param locktime: Transaction level locktime. Locks the transaction until a specified block (value from 1 to 5 million) or until a certain time (Timestamp in seconds after 1-jan-1970). Default value is 0 for transactions without locktime
        :type locktime: int
        :param version: Version rules. Defaults to 1 in bytes
        :type version: bytes, int
        :param network: Network, leave empty for default network
        :type network: str, Network
        :param fee: Fee in smallest denominator (ie Satoshi) for complete transaction
        :type fee: int
        :param fee_per_kb: Fee in smallest denominator per kilobyte. Specify when exact transaction size is not known.
        :type fee_per_kb: int
        :param size: Transaction size in bytes
        :type size: int
        :param txid: The transaction id (same for legacy/segwit) based on [nVersion][txins][txouts][nLockTime as hexadecimal string
        :type txid: str
        :param txhash: The transaction hash (differs from txid for witness transactions), based on [nVersion][marker][flag][txins][txouts][witness][nLockTime] in Segwit (as hexadecimal string). Unused at the moment
        :type txhash: str
        :param date: Confirmation date of transaction
        :type date: datetime
        :param confirmations: Number of confirmations
        :type confirmations: int
        :param block_height: Block number which includes transaction
        :type block_height: int
        :param block_hash: Hash of block for this transaction
        :type block_hash: str
        :param input_total: Total value of inputs
        :type input_total: int
        :param output_total: Total value of outputs
        :type output_total: int
        :param rawtx: Bytes representation of complete transaction
        :type rawtx: bytes
        :param status: Transaction status, for example: 'new', 'unconfirmed', 'confirmed'
        :type status: str
        :param coinbase: Coinbase transaction or not?
        :type coinbase: bool
        :param verified: Is transaction successfully verified? Updated when verified() method is called
        :type verified: bool
        :param witness_type: Specify witness/signature position: 'segwit' or 'legacy'. Determine from script, address or encoding if not specified.
        :type witness_type: str
        :param flag: Transaction flag to indicate version, for example for SegWit
        :type flag: bytes, str

        """

        self.coinbase = coinbase
        self.inputs = []
        if inputs is not None:
            for inp in inputs:
                self.inputs.append(inp)
            if not input_total:
                input_total = sum([i.value for i in inputs])
        id_list = [i.index_n for i in self.inputs]
        if list(dict.fromkeys(id_list)) != id_list:
            _logger.info("Identical transaction indexes (tid) found in inputs, please specify unique index. "
                         "Indexes will be automatically recreated")
            index_n = 0
            for inp in self.inputs:
                inp.index_n = index_n
                index_n += 1
        if outputs is None:
            self.outputs = []
        else:
            self.outputs = outputs
            if not output_total:
                output_total = sum([o.value for o in outputs])
        if fee is None and output_total and input_total:
            fee = input_total - output_total
            if fee < 0 or fee == 0 and not self.coinbase:
                raise TransactionError("Transaction inputs total value must be greater then total value of "
                                       "transaction outputs")
        if not version:
            version = b'\x00\x00\x00\x01'
        if isinstance(version, int):
            self.version = version.to_bytes(4, 'big')
            self.version_int = version
        else:
            self.version = version
            self.version_int = int.from_bytes(version, 'big')
        self.locktime = locktime
        self.network = network
        if not isinstance(network, Network):
            self.network = Network(network)
        self.flag = flag
        self.fee = fee
        self.fee_per_kb = fee_per_kb
        self.size = size
        self.vsize = size
        self.txid = txid
        self.txhash = txhash
        self.date = date
        self.confirmations = confirmations
        self.block_height = block_height
        self.block_hash = block_hash
        self.input_total = input_total
        self.output_total = output_total
        self.rawtx = rawtx
        self.status = status
        self.verified = verified
        self.witness_type = witness_type
        self.change = 0
        self.calc_weight_units()
        if self.witness_type not in ['legacy', 'segwit']:
            raise TransactionError("Please specify a valid witness type: legacy or segwit")
        if not self.txid:
            self.txid = self.signature_hash()[::-1].hex()

    def __repr__(self):
        return "<Transaction(id=%s, inputs=%d, outputs=%d, status=%s, network=%s)>" % \
               (self.txid, len(self.inputs), len(self.outputs), self.status, self.network.name)

    def __str__(self):
        return self.txid

    def __add__(self, other):
        """
        Merge this transaction with another transaction keeping the original transaction intact.

        :return Transaction:
        """
        t = deepcopy(self)
        t.merge_transaction(other)
        return t

    def __hash__(self):
        return self.txid

    def __eq__(self, other):
        """
        Compare two transaction, must have same transaction ID

        :param other: Other transaction object
        :type other: Transaction

        :return bool:
        """
        if not isinstance(other, Transaction):
            raise TransactionError("Can only compare with other Transaction object")
        return self.txid == other.txid

    def as_dict(self):
        """
        Return Json dictionary with transaction information: Inputs, outputs, version and locktime

        :return dict:
        """

        inputs = []
        outputs = []
        for i in self.inputs:
            inputs.append(i.as_dict())
        for o in self.outputs:
            outputs.append(o.as_dict())
        return {
            'txid': self.txid,
            'date': self.date,
            'network': self.network.name,
            'witness_type': self.witness_type,
            'coinbase': self.coinbase,
            'flag': None if not self.flag else ord(self.flag),
            'txhash': self.txhash,
            'confirmations': self.confirmations,
            'block_height': self.block_height,
            'block_hash': self.block_hash,
            'fee': self.fee,
            'fee_per_kb': self.fee_per_kb,
            'inputs': inputs,
            'outputs': outputs,
            'input_total': self.input_total,
            'output_total': self.output_total,
            'version': self.version_int,
            'locktime': self.locktime,
            'raw': self.raw_hex(),
            'size': self.size,
            'vsize': self.vsize,
            'verified': self.verified,
            'status': self.status
        }

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

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

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

        print("Transaction %s" % self.txid)
        print("Date: %s" % self.date)
        print("Network: %s" % self.network.name)
        if self.locktime and self.locktime != 0xffffffff:
            if self.locktime < 500000000:
                print("Locktime: Until block %d" % self.locktime)
            else:
                print("Locktime: Until %s UTC" % datetime.utcfromtimestamp(self.locktime))
        print("Version: %d" % self.version_int)
        print("Witness type: %s" % self.witness_type)
        print("Status: %s" % self.status)
        print("Verified: %s" % self.verified)
        print("Inputs")
        replace_by_fee = False
        for ti in self.inputs:
            print("-", ti.address, Value.from_satoshi(ti.value, network=self.network).str(1), ti.prev_txid.hex(),
                  ti.output_n_int)
            validstr = "not validated"
            if ti.valid:
                validstr = "valid"
            elif ti.valid is False:
                validstr = "invalid"
            print("  %s %s; sigs: %d (%d-of-%d) %s" %
                  (ti.witness_type, ti.script_type, len(ti.signatures), ti.sigs_required or 0, len(ti.keys), validstr))
            if ti.sequence <= SEQUENCE_REPLACE_BY_FEE:
                replace_by_fee = True
            if ti.sequence <= SEQUENCE_LOCKTIME_DISABLE_FLAG:
                if ti.sequence & SEQUENCE_LOCKTIME_TYPE_FLAG:
                    print("  Relative timelock for %d seconds" % (512 * (ti.sequence - SEQUENCE_LOCKTIME_TYPE_FLAG)))
                else:
                    print("  Relative timelock for %d blocks" % ti.sequence)
            if ti.locktime_cltv:
                if ti.locktime_cltv & SEQUENCE_LOCKTIME_TYPE_FLAG:
                    print("  Check Locktime Verify (CLTV) for %d seconds" %
                          (512 * (ti.locktime_cltv - SEQUENCE_LOCKTIME_TYPE_FLAG)))
                else:
                    print("  Check Locktime Verify (CLTV) for %d blocks" % ti.locktime_cltv)
            if ti.locktime_csv:
                if ti.locktime_csv & SEQUENCE_LOCKTIME_TYPE_FLAG:
                    print("  Check Sequence Verify Timelock (CSV) for %d seconds" %
                          (512 * (ti.locktime_csv - SEQUENCE_LOCKTIME_TYPE_FLAG)))
                else:
                    print("  Check Sequence Verify Timelock (CSV) for %d blocks" % ti.locktime_csv)

        print("Outputs")
        for to in self.outputs:
            if to.script_type == 'nulldata':
                print("- NULLDATA ", to.lock_script[2:])
            else:
                spent_str = ''
                if to.spent:
                    spent_str = 'S'
                elif to.spent is False:
                    spent_str = 'U'
                print("-", to.address, Value.from_satoshi(to.value, network=self.network).str(1), to.script_type,
                      spent_str)
        if replace_by_fee:
            print("Replace by fee: Enabled")
        print("Size: %s" % self.size)
        print("Vsize: %s" % self.vsize)
        print("Fee: %s" % self.fee)
        print("Confirmations: %s" % self.confirmations)
        print("Block: %s" % self.block_height)

    def set_locktime_relative_blocks(self, blocks, input_index_n=0):
        """
        Set nSequence relative locktime for this transaction. The transaction will only be valid if the specified number of blocks has been mined since the previous UTXO is confirmed.

        Maximum number of blocks is 65535 as defined in BIP-0068, which is around 455 days.

        When setting an relative timelock, the transaction version must be at least 2. The transaction will be updated so existing signatures for this input will be removed.

        :param blocks: The blocks value is the number of blocks since the previous transaction output has been confirmed.
        :type blocks: int
        :param input_index_n: Index number of input for nSequence locktime
        :type input_index_n: int

        :return:
        """
        if blocks == 0 or blocks == 0xffffffff:
            self.inputs[input_index_n].sequence = 0xffffffff
            self.sign(index_n=input_index_n, replace_signatures=True)
            return
        if blocks > SEQUENCE_LOCKTIME_MASK:
            raise TransactionError("Number of nSequence timelock blocks exceeds %d" % SEQUENCE_LOCKTIME_MASK)
        self.inputs[input_index_n].sequence = blocks
        self.version_int = 2
        self.sign_and_update(index_n=input_index_n)

    def set_locktime_relative_time(self, seconds, input_index_n=0):
        """
        Set nSequence relative locktime for this transaction. The transaction will only be valid if the specified amount of seconds have been passed since the previous UTXO is confirmed.

        Number of seconds will be rounded to the nearest 512 seconds. Any value below 512 will be interpreted as 512 seconds.

        Maximum number of seconds is 33553920 (512 * 65535), which equals 384 days. See BIP-0068 definition.

        When setting an relative timelock, the transaction version must be at least 2. The transaction will be updated so existing signatures for this input will be removed.

        :param seconds: Number of seconds since the related previous transaction output has been confirmed.
        :type seconds: int
        :param input_index_n: Index number of input for nSequence locktime
        :type input_index_n: int

        :return:
        """
        if seconds == 0 or seconds == 0xffffffff:
            self.inputs[input_index_n].sequence = 0xffffffff
            self.sign(index_n=input_index_n, replace_signatures=True)
            return
        elif seconds < 512:
            seconds = 512
        elif (seconds // 512) > SEQUENCE_LOCKTIME_MASK:
            raise TransactionError("Number of relative nSeqence timelock seconds exceeds %d" % SEQUENCE_LOCKTIME_MASK)
        self.inputs[input_index_n].sequence = seconds // 512 + SEQUENCE_LOCKTIME_TYPE_FLAG
        self.version_int = 2
        self.sign_and_update(index_n=input_index_n)

    def set_locktime_blocks(self, blocks):
        """
        Set nLocktime, a transaction level absolute lock time in blocks using the transaction sequence field.

        So for example if you set this value to 600000 the transaction will only be valid after block 600000.

        :param blocks: Transaction is valid after supplied block number. Value must be between 0 and 500000000. Zero means no locktime.
        :type blocks: int

        :return:
        """
        if blocks == 0 or blocks == 0xffffffff:
            self.locktime = 0xffffffff
            self.sign(replace_signatures=True)
            self.verify()
            return
        elif blocks > 500000000:
            raise TransactionError("Number of locktime blocks must be below %d" % 500000000)
        self.locktime = blocks
        if blocks != 0 and blocks != 0xffffffff:
            for i in self.inputs:
                if i.sequence == 0xffffffff:
                    i.sequence = 0xfffffffd
        self.sign_and_update()

    def set_locktime_time(self, timestamp):
        """
        Set nLocktime, a transaction level absolute lock time in timestamp using the transaction sequence field.

        :param timestamp: Transaction is valid after the given timestamp. Value must be between 500000000 and 0xfffffffe
        :return:
        """
        if timestamp == 0 or timestamp == 0xffffffff:
            self.locktime = 0xffffffff
            self.sign(replace_signatures=True)
            self.verify()
            return

        if timestamp <= 500000000:
            raise TransactionError("Timestamp must have a value higher then %d" % 500000000)
        if timestamp > 0xfffffffe:
            raise TransactionError("Timestamp must have a value lower then %d" % 0xfffffffe)
        self.locktime = timestamp

        # Input sequence value must be below 0xffffffff
        for i in self.inputs:
            if i.sequence == 0xffffffff:
                i.sequence = 0xfffffffd
        self.sign_and_update()

    def signature_hash(self, sign_id=None, hash_type=SIGHASH_ALL, witness_type=None, as_hex=False):
        """
        Double SHA256 Hash of Transaction signature

        :param sign_id: Index of input to sign
        :type sign_id: int
        :param hash_type: Specific hash type, default is SIGHASH_ALL
        :type hash_type: int
        :param witness_type: Legacy or Segwit witness type? Leave empty to use Transaction witness type
        :type witness_type: str
        :param as_hex: Return value as hexadecimal string. Default is False
        :type as_hex: bool

        :return bytes: Transaction signature hash
        """
        return double_sha256(self.signature(sign_id, hash_type, witness_type), as_hex=as_hex)

    def signature(self, sign_id=None, hash_type=SIGHASH_ALL, witness_type=None):
        """
        Serializes transaction and calculates signature for Legacy or Segwit transactions

        :param sign_id: Index of input to sign
        :type sign_id: int
        :param hash_type: Specific hash type, default is SIGHASH_ALL
        :type hash_type: int
        :param witness_type: Legacy or Segwit witness type? Leave empty to use Transaction witness type
        :type witness_type: str

        :return bytes: Transaction signature
        """

        if witness_type is None:
            witness_type = self.witness_type
        if witness_type == 'legacy' or sign_id is None:
            return self.raw(sign_id, hash_type, 'legacy')
        elif witness_type in ['segwit', 'p2sh-segwit']:
            return self.signature_segwit(sign_id, hash_type)
        else:
            raise TransactionError("Witness_type %s not supported" % self.witness_type)

    def signature_segwit(self, sign_id, hash_type=SIGHASH_ALL):
        """
        Serialize transaction signature for segregated witness transaction

        :param sign_id: Index of input to sign
        :type sign_id: int
        :param hash_type: Specific hash type, default is SIGHASH_ALL
        :type hash_type: int

        :return bytes: Segwit transaction signature
        """
        assert (self.witness_type == 'segwit')
        prevouts_serialized = b''
        sequence_serialized = b''
        outputs_serialized = b''
        hash_prevouts = b'\0' * 32
        hash_sequence = b'\0' * 32
        hash_outputs = b'\0' * 32

        for i in self.inputs:
            prevouts_serialized += i.prev_txid[::-1] + i.output_n[::-1]
            sequence_serialized += i.sequence.to_bytes(4, 'little')
        if not hash_type & SIGHASH_ANYONECANPAY:
            hash_prevouts = double_sha256(prevouts_serialized)
            if (hash_type & 0x1f) != SIGHASH_SINGLE and (hash_type & 0x1f) != SIGHASH_NONE:
                hash_sequence = double_sha256(sequence_serialized)
        if (hash_type & 0x1f) != SIGHASH_SINGLE and (hash_type & 0x1f) != SIGHASH_NONE:
            for o in self.outputs:
                outputs_serialized += int(o.value).to_bytes(8, 'little')
                outputs_serialized += varstr(o.lock_script)
            hash_outputs = double_sha256(outputs_serialized)
        elif (hash_type & 0x1f) != SIGHASH_SINGLE and sign_id < len(self.outputs):
            outputs_serialized += int(self.outputs[sign_id].value).to_bytes(8, 'little')
            outputs_serialized += varstr(self.outputs[sign_id].lock_script)
            hash_outputs = double_sha256(outputs_serialized)

        if not self.inputs[sign_id].value:
            raise TransactionError("Need value of input %d to create transaction signature, value can not be 0" %
                                   sign_id)

        if not self.inputs[sign_id].redeemscript:
            self.inputs[sign_id].redeemscript = self.inputs[sign_id].script_code

        if (not self.inputs[sign_id].redeemscript or self.inputs[sign_id].redeemscript == b'\0') and \
                self.inputs[sign_id].redeemscript != 'unknown':
            raise TransactionError("Redeem script missing")

        ser_tx = \
            self.version[::-1] + hash_prevouts + hash_sequence + self.inputs[sign_id].prev_txid[::-1] + \
            self.inputs[sign_id].output_n[::-1] + \
            varstr(self.inputs[sign_id].redeemscript) + int(self.inputs[sign_id].value).to_bytes(8, 'little') + \
            self.inputs[sign_id].sequence.to_bytes(4, 'little') + \
            hash_outputs + self.locktime.to_bytes(4, 'little') + hash_type.to_bytes(4, 'little')
        return ser_tx

    def raw(self, sign_id=None, hash_type=SIGHASH_ALL, witness_type=None):
        """
        Serialize raw transaction

        Return transaction with signed inputs if signatures are available

        :param sign_id: Create raw transaction which can be signed by transaction with this input ID
        :type sign_id: int, None
        :param hash_type: Specific hash type, default is SIGHASH_ALL
        :type hash_type: int
        :param witness_type: Serialize transaction with other witness type then default. Use to create legacy raw transaction for segwit transaction to create transaction signature ID's
        :type witness_type: str

        :return bytes:
        """

        if witness_type is None:
            witness_type = self.witness_type

        r = self.version[::-1]
        if sign_id is None and witness_type == 'segwit':
            r += b'\x00'  # marker (BIP 141)
            r += b'\x01'  # flag (BIP 141)

        r += int_to_varbyteint(len(self.inputs))
        r_witness = b''
        for i in self.inputs:
            r += i.prev_txid[::-1] + i.output_n[::-1]
            if i.witnesses and i.witness_type != 'legacy':
                r_witness += int_to_varbyteint(len(i.witnesses)) + b''.join([bytes(varstr(w)) for w in i.witnesses])
            else:
                r_witness += b'\0'
            if sign_id is None:
                r += varstr(i.unlocking_script)
            elif sign_id == i.index_n:
                r += varstr(i.unlocking_script_unsigned)
            else:
                r += b'\0'
            r += i.sequence.to_bytes(4, 'little')

        r += int_to_varbyteint(len(self.outputs))
        for o in self.outputs:
            if o.value < 0:
                raise TransactionError("Output value < 0 not allowed")
            r += int(o.value).to_bytes(8, 'little')
            r += varstr(o.lock_script)

        if sign_id is None and witness_type == 'segwit':
            r += r_witness

        r += self.locktime.to_bytes(4, 'little')
        if sign_id is not None:
            r += hash_type.to_bytes(4, 'little')
        else:
            if not self.size and b'' not in [i.unlocking_script for i in self.inputs]:
                self.size = len(r)
                self.calc_weight_units()
        return r

    def raw_hex(self, sign_id=None, hash_type=SIGHASH_ALL, witness_type=None):
        """
        Wrapper for raw() method. Return current raw transaction hex

        :param sign_id: Create raw transaction which can be signed by transaction with this input ID
        :type sign_id: int
        :param hash_type: Specific hash type, default is SIGHASH_ALL
        :type hash_type: int
        :param witness_type: Serialize transaction with other witness type then default. Use to create legacy raw transaction for segwit transaction to create transaction signature ID's
        :type witness_type: str

        :return hexstring:
        """

        return self.raw(sign_id, hash_type=hash_type, witness_type=witness_type).hex()

    def witness_data(self):
        """
        Get witness data for all inputs of this transaction

        :return bytes:
        """
        witness_data = b''
        for i in self.inputs:
            witness_data += int_to_varbyteint(len(i.witnesses)) + b''.join([bytes(varstr(w)) for w in i.witnesses])
        return witness_data

    def verify(self):
        """
        Verify all inputs of a transaction, check if signatures match public key.

        Does not check if UTXO is valid or has already been spent

        :return bool: True if enough signatures provided and if all signatures are valid
        """

        self.verified = False
        for inp in self.inputs:
            try:
                transaction_hash = self.signature_hash(inp.index_n, witness_type=inp.witness_type)
            except TransactionError as e:
                _logger.info("Could not create transaction hash. Error: %s" % e)
                return False
            if not transaction_hash:
                _logger.info("Need at least 1 key to create segwit transaction signature")
                return False
            self.verified = inp.verify(transaction_hash)
            if not self.verified:
                return False

        self.verified = True
        return True

    def sign(self, keys=None, index_n=None, multisig_key_n=None, hash_type=SIGHASH_ALL, fail_on_unknown_key=True,
             replace_signatures=False):
        """
        Sign the transaction input with provided private key

        :param keys: A private key or list of private keys
        :type keys: HDKey, Key, bytes, list
        :param index_n: Index of transaction input. Leave empty to sign all inputs
        :type index_n: int
        :param multisig_key_n: Index number of key for multisig input for segwit transactions. Leave empty if not known. If not specified all possibilities will be checked
        :type multisig_key_n: int
        :param hash_type: Specific hash type, default is SIGHASH_ALL
        :type hash_type: int
        :param fail_on_unknown_key: Method fails if public key from signature is not found in public key list
        :type fail_on_unknown_key: bool
        :param replace_signatures: Replace signature with new one if already signed.
        :type replace_signatures: bool

        :return None:
        """

        if index_n is None:
            tids = range(len(self.inputs))
        else:
            tids = [index_n]

        if keys is None:
            keys = []
        elif not isinstance(keys, list):
            keys = [keys]

        for tid in tids:
            n_signs = 0
            tid_keys = [k if isinstance(k, (HDKey, Key)) else Key(k, compressed=self.inputs[tid].compressed)
                        for k in keys]
            for k in self.inputs[tid].keys:
                if k.is_private and k not in tid_keys:
                    tid_keys.append(k)
            # If input does not contain any keys, try using provided keys
            if not self.inputs[tid].keys:
                self.inputs[tid].keys = tid_keys
                self.inputs[tid].update_scripts(hash_type=hash_type)
            if self.inputs[tid].script_type == 'coinbase':
                raise TransactionError("Can not sign coinbase transactions")
            pub_key_list = [k.public_byte for k in self.inputs[tid].keys]
            n_total_sigs = len(self.inputs[tid].keys)
            sig_domain = [''] * n_total_sigs

            txid = self.signature_hash(tid, witness_type=self.inputs[tid].witness_type)
            for key in tid_keys:
                # Check if signature signs known key and is not already in list
                if key.public_byte not in pub_key_list:
                    if fail_on_unknown_key:
                        raise TransactionError("This key does not sign any known key: %s" % key.public_hex)
                    else:
                        _logger.info("This key does not sign any known key: %s" % key.public_hex)
                        continue
                if not replace_signatures and key in [x.public_key for x in self.inputs[tid].signatures]:
                    _logger.info("Key %s already signed" % key.public_hex)
                    break

                if not key.private_byte:
                    raise TransactionError("Please provide a valid private key to sign the transaction")
                sig = sign(txid, key)
                newsig_pos = pub_key_list.index(key.public_byte)
                sig_domain[newsig_pos] = sig
                n_signs += 1

            if not n_signs:
                break

            # Add already known signatures on correct position
            n_sigs_to_insert = len(self.inputs[tid].signatures)
            for sig in self.inputs[tid].signatures:
                if not sig.public_key:
                    break
                newsig_pos = pub_key_list.index(sig.public_key.public_byte)
                if sig_domain[newsig_pos] == '':
                    sig_domain[newsig_pos] = sig
                    n_sigs_to_insert -= 1
            if n_sigs_to_insert:
                for sig in self.inputs[tid].signatures:
                    free_positions = [i for i, s in enumerate(sig_domain) if s == '']
                    for pos in free_positions:
                        sig_domain[pos] = sig
                        n_sigs_to_insert -= 1
                        break
            if n_sigs_to_insert:
                _logger.info("Some signatures are replaced with the signatures of the provided keys")
            self.inputs[tid].signatures = [s for s in sig_domain if s != '']
            self.inputs[tid].update_scripts(hash_type)

    def sign_and_update(self, index_n=None):
        """
        Update transaction ID and resign. Use if some properties of the transaction changed

        :param index_n: Index of transaction input. Leave empty to sign all inputs
        :type index_n: int

        :return:
        """

        self.version = self.version_int.to_bytes(4, 'big')
        self.sign(index_n=index_n, replace_signatures=True)
        self.txid = self.signature_hash()[::-1].hex()
        self.size = len(self.raw())
        self.calc_weight_units()
        self.update_totals()
        if self.fee:
            self.fee_per_kb = int((self.fee / float(self.vsize)) * 1000)

    def add_input(self, prev_txid, output_n, keys=None, signatures=None, public_hash=b'', unlocking_script=b'',
                  unlocking_script_unsigned=None, script_type=None, address='',
                  sequence=0xffffffff, compressed=True, sigs_required=None, sort=False, index_n=None,
                  value=None, double_spend=False, locktime_cltv=None, locktime_csv=None,
                  key_path='', witness_type=None, witnesses=None, encoding=None, strict=True):
        """
        Add input to this transaction

        Wrapper for append method of Input class.

        :param prev_txid: Transaction hash of the UTXO (previous output) which will be spent.
        :type prev_txid: bytes, hexstring
        :param output_n: Output number in previous transaction.
        :type output_n: bytes, int
        :param keys: Public keys can be provided to construct an Unlocking script. Optional
        :type keys: bytes, str
        :param signatures: Add signatures to input if already known
        :type signatures: bytes, str
        :param public_hash: Specify public hash from key or redeemscript if key is not available
        :type public_hash: bytes
        :param unlocking_script: Unlocking script (scriptSig) to prove ownership. Optional
        :type unlocking_script: bytes, hexstring
        :param unlocking_script_unsigned: TODO: find better name...
        :type unlocking_script_unsigned: bytes, str
        :param script_type: Type of unlocking script used, i.e. p2pkh or p2sh_multisig. Default is p2pkh
        :type script_type: str
        :param address: Specify address of input if known, default is to derive from key or scripts
        :type address: str, Address
        :param sequence: Sequence part of input, used for timelocked transactions
        :type sequence: int, bytes
        :param compressed: Use compressed or uncompressed public keys. Default is compressed
        :type compressed: bool
        :param sigs_required: Number of signatures required for a p2sh_multisig unlocking script
        :param sigs_required: int
        :param sort: Sort public keys according to BIP0045 standard. Default is False to avoid unexpected change of key order.
        :type sort: boolean
        :param index_n: Index number of position in transaction, leave empty to add input to end of inputs list
        :type index_n: int
        :param value: Value of input
        :type value: int
        :param double_spend: True if double spend is detected, depends on which service provider is selected
        :type double_spend: bool
        :param locktime_cltv: Check Lock Time Verify value. Script level absolute time lock for this input
        :type locktime_cltv: int
        :param locktime_csv: Check Sequency Verify value.
        :type locktime_csv: int
        :param key_path: Key path of input key as BIP32 string or list
        :type key_path: str, list
        :param witness_type: Specify witness/signature position: 'segwit' or 'legacy'. Determine from script, address or encoding if not specified.
        :type witness_type: str
        :param witnesses: List of witnesses for inputs, used for segwit transactions for instance.
        :type witnesses: list of bytes, list of str
        :param encoding: Address encoding used. For example bech32/base32 or base58. Leave empty to derive from script or script type
        :type encoding: str
        :param strict: Raise exception when input is malformed or incomplete
        :type strict: bool

        :return int: Transaction index number (index_n)
        """

        if index_n is None:
            index_n = len(self.inputs)
        sequence_int = sequence
        if isinstance(sequence, bytes):
            sequence_int = int.from_bytes(sequence, 'little')
        if self.version == b'\x00\x00\x00\x01' and 0 < sequence_int < SEQUENCE_LOCKTIME_DISABLE_FLAG:
            self.version = b'\x00\x00\x00\x02'
            self.version_int = 2
        self.inputs.append(
            Input(prev_txid=prev_txid, output_n=output_n, keys=keys, signatures=signatures, public_hash=public_hash,
                  unlocking_script=unlocking_script, unlocking_script_unsigned=unlocking_script_unsigned,
                  script_type=script_type, address=address, sequence=sequence, compressed=compressed,
                  sigs_required=sigs_required, sort=sort, index_n=index_n, value=value, double_spend=double_spend,
                  locktime_cltv=locktime_cltv, locktime_csv=locktime_csv, key_path=key_path, witness_type=witness_type,
                  witnesses=witnesses, encoding=encoding, strict=strict, network=self.network.name))
        return index_n

    def add_output(self, value, address='', public_hash=b'', public_key=b'', lock_script=b'', spent=False,
                   output_n=None, encoding=None, spending_txid=None, spending_index_n=None, strict=True):
        """
        Add an output to this transaction

        Wrapper for the append method of the Output class.

        :param value: Value of output in smallest denominator of currency, for example satoshi's for bitcoins
        :type value: int
        :param address: Destination address of output. Leave empty to derive from other attributes you provide.
        :type address: str, Address
        :param public_hash: Hash of public key or script
        :type public_hash: bytes, str
        :param public_key: Destination public key
        :type public_key: bytes, str
        :param lock_script: Locking script of output. If not provided a default unlocking script will be provided with a public key hash.
        :type lock_script: bytes, str
        :param spent: Has output been spent in new transaction?
        :type spent: bool, None
        :param output_n: Index number of output in transaction
        :type output_n: int
        :param encoding: Address encoding used. For example bech32/base32 or base58. Leave empty for to derive from script or script type
        :type encoding: str
        :param spending_txid: Transaction hash of input spending this transaction output
        :type spending_txid: str
        :param spending_index_n: Index number of input spending this transaction output
        :type spending_index_n: int
        :param strict: Raise exception when output is malformed or incomplete
        :type strict: bool

        :return int: Transaction output number (output_n)
        """

        lock_script = to_bytes(lock_script)
        if output_n is None:
            output_n = len(self.outputs)
        if not float(value).is_integer():
            raise TransactionError("Output must be of type integer and contain no decimals")
        if lock_script.startswith(b'\x6a'):
            if value != 0:
                raise TransactionError("Output value for OP_RETURN script must be 0")
        self.outputs.append(Output(value=int(value), address=address, public_hash=public_hash,
                                   public_key=public_key, lock_script=lock_script, spent=spent, output_n=output_n,
                                   encoding=encoding, spending_txid=spending_txid, spending_index_n=spending_index_n,
                                   strict=strict, network=self.network.name))
        return output_n

    def merge_transaction(self, transaction):
        """
        Merge this transaction with provided Transaction object.

        Add all inputs and outputs of a transaction to this Transaction object. Because the transaction signature
        changes with this operation, the transaction inputs need to be signed again.

        Can be used to implement CoinJoin. Where two or more unrelated Transactions are merged into 1 transaction
        to safe fees and increase privacy.

        :param transaction: The transaction to be merged
        :type transaction: Transaction

        """
        self.inputs += transaction.inputs
        self.outputs += transaction.outputs
        self.shuffle()
        self.update_totals()
        self.sign_and_update()

    def estimate_size(self, number_of_change_outputs=0):
        """
        Get estimated vsize in for current transaction based on transaction type and number of inputs and outputs.

        For old-style legacy transaction the vsize is the length of the transaction. In segwit transaction the
        witness data has less weight. The formula used is: math.ceil(((est_size-witness_size) * 3 + est_size) / 4)

        :param number_of_change_outputs: Number of change outputs, default is 0
        :type number_of_change_outputs: int

        :return int: Estimated transaction size
        """

        # if self.input_total and self.output_total + self.fee == self.input_total:
        #     add_change_output = False
        est_size = 10
        witness_size = 2
        if self.witness_type != 'legacy':
            est_size += 2
        # TODO: if no inputs assume 1 input
        if not self.inputs:
            est_size += 125
            witness_size += 72
        for inp in self.inputs:
            est_size += 40
            scr_size = 0
            if inp.witness_type != 'legacy':
                est_size += 1
            if inp.unlocking_script and len(inp.signatures) >= inp.sigs_required:
                scr_size += len(varstr(inp.unlocking_script))
                if inp.witness_type == 'p2sh-segwit':
                    scr_size += sum([1 + len(w) for w in inp.witnesses])
            else:
                if inp.script_type == 'sig_pubkey':
                    scr_size += 107
                    if not inp.compressed:
                        scr_size += 33
                    if inp.witness_type == 'p2sh-segwit':
                        scr_size += 24
                # elif inp.script_type in ['p2sh_multisig', 'p2sh_p2wpkh', 'p2sh_p2wsh']:
                elif inp.script_type == 'p2sh_multisig':
                    scr_size += 9 + (len(inp.keys) * 34) + (inp.sigs_required * 72)
                    if inp.witness_type == 'p2sh-segwit':
                        scr_size += 17 * inp.sigs_required
                elif inp.script_type == 'signature':
                    scr_size += 9 + 72
                else:
                    raise TransactionError("Unknown input script type %s cannot estimate transaction size" %
                                           inp.script_type)
            est_size += scr_size
            witness_size += scr_size
        for outp in self.outputs:
            est_size += 8
            if outp.lock_script:
                est_size += len(varstr(outp.lock_script))
            else:
                raise TransactionError("Need locking script for output %d to estimate size" % outp.output_n)
        if number_of_change_outputs:
            is_multisig = True if self.inputs and self.inputs[0].script_type == 'p2sh_multisig' else False
            co_size = 8
            if not self.inputs or self.inputs[0].witness_type == 'legacy':
                co_size += 24 if is_multisig else 26
            elif self.inputs[0].witness_type == 'p2sh-segwit':
                co_size += 24
            else:
                co_size += 33 if is_multisig else 23
            est_size += (number_of_change_outputs * co_size)
        self.size = est_size
        self.vsize = est_size
        if self.witness_type == 'legacy':
            return est_size
        else:
            self.vsize = math.ceil((((est_size - witness_size) * 3 + est_size) / 4) - 1.5)
            return self.vsize

    def calc_weight_units(self):
        """
        Calculate weight units and vsize for this Transaction. Weight units are used to determine fee.

        :return int:
        """
        if not self.size:
            return None
        witness_data_size = len(self.witness_data())
        wu = self.size * 4
        if self.witness_type == 'segwit' and witness_data_size > 1:
            wu = wu - 6  # for segwit marker and flag
            wu = wu - witness_data_size * 3
        self.vsize = math.ceil(wu / 4)
        return wu

    @property
    def weight_units(self):
        return self.calc_weight_units()

    def calculate_fee(self):
        """
        Get fee for this transaction in the smallest denominator (i.e. Satoshi) based on its size and the
        transaction.fee_per_kb value

        :return int: Estimated transaction fee
        """

        if not self.fee_per_kb:
            raise TransactionError("Cannot calculate transaction fees: transaction.fee_per_kb is not set")
        if self.fee_per_kb < self.network.fee_min:
            self.fee_per_kb = self.network.fee_min
        elif self.fee_per_kb > self.network.fee_max:
            self.fee_per_kb = self.network.fee_max
        if not self.vsize:
            self.estimate_size()
        fee = int(self.vsize / 1000.0 * self.fee_per_kb)
        return fee

    def update_totals(self):
        """
        Update input_total, output_total and fee according to inputs and outputs of this transaction

        :return int:
        """

        self.input_total = sum([i.value for i in self.inputs if i.value])
        self.output_total = sum([o.value for o in self.outputs if o.value])
        if self.coinbase:
            self.input_total = self.output_total
        # self.fee = 0
        if self.input_total:
            self.fee = self.input_total - self.output_total
            if self.vsize:
                self.fee_per_kb = int((self.fee / float(self.vsize)) * 1000)

    def save(self, filename=None):
        """
        Store transaction object as file so it can be imported in bitcoinlib later with the :func:`load` method.

        :param filename: Location and name of file, leave empty to store transaction in bitcoinlib data directory: .bitcoinlib/<transaction_id.tx)
        :type filename: str

        :return:
        """
        if not filename:
            p = Path(BCL_DATA_DIR, '%s.tx' % self.txid)
        else:
            p = Path(filename)
            if not p.parent or str(p.parent) == '.':
                p = Path(BCL_DATA_DIR, filename)
        f = p.open('wb')
        pickle.dump(self, f)
        f.close()

    def shuffle_inputs(self):
        """
        Shuffle transaction inputs in random order.

        :return:
        """
        random.shuffle(self.inputs)
        for idx, o in enumerate(self.inputs):
            o.index_n = idx

    def shuffle_outputs(self):
        """
        Shuffle transaction outputs in random order.

        :return:
        """
        random.shuffle(self.outputs)
        for idx, o in enumerate(self.outputs):
            o.output_n = idx

    def shuffle(self):
        """
        Shuffle transaction inputs and outputs in random order.

        :return:
        """
        self.shuffle_inputs()
        self.shuffle_outputs() def get_unlocking_script_type(locking_script_type, witness_type='legacy', multisig=False):
    """
    Specify locking script type and get corresponding script type for unlocking script

    >>> get_unlocking_script_type('p2wsh')
    'p2sh_multisig'

    :param locking_script_type: Locking script type. I.e.: p2pkh, p2sh, p2wpkh, p2wsh
    :type locking_script_type: str
    :param witness_type: Type of witness: legacy or segwit. Default is legacy
    :type witness_type: str
    :param multisig: Is multisig script or not? Default is False
    :type multisig: bool

    :return str: Unlocking script type such as sig_pubkey or p2sh_multisig
    """

    if locking_script_type in ['p2pkh', 'p2wpkh']:
        return 'sig_pubkey'
    elif locking_script_type == 'p2wsh' or (witness_type == 'legacy' and multisig):
        return 'p2sh_multisig'
    elif locking_script_type == 'p2sh':
        if not multisig:
            return 'sig_pubkey'
        else:
            return 'p2sh_multisig'
    elif locking_script_type == 'p2pk':
        return 'signature'
    else:
        raise TransactionError("Unknown locking script type %s" % locking_script_type) 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
import json
import random
import numpy as np
import pickle
from itertools import groupby
from operator import itemgetter
from bitcoinlib.db import *
from bitcoinlib.encoding import *
from bitcoinlib.keys import Address, BKeyError, HDKey, check_network_and_key, path_expand
from bitcoinlib.mnemonic import Mnemonic
from bitcoinlib.networks import Network
from bitcoinlib.values import Value, value_to_satoshi
from bitcoinlib.services.services import Service
from bitcoinlib.transactions import Input, Output, Transaction, get_unlocking_script_type
from bitcoinlib.scripts import Script
from sqlalchemy import func, or_
    session.close()

    _logger.info("All keys and transactions from wallet '%s' deleted" % wallet)

    return True


def wallet_delete_if_exists(wallet, db_uri=None, force=False):
    """
    Delete wallet and associated keys from the database. If wallet has unspent outputs it raises a WalletError exception
    unless 'force=True' is specified. If wallet wallet does not exist return False

    :param wallet: Wallet ID as integer or Wallet Name as string
    :type wallet: int, str
    :param db_uri: URI of the database
    :type db_uri: str
    :param force: If set to True wallet will be deleted even if unspent outputs are found. Default is False
    :type force: bool

    :return int: Number of rows deleted, so 1 if successful
    """

    if wallet_exists(wallet, db_uri):
        return wallet_delete(wallet, db_uri, force)
    return False


def normalize_path(path):
    """
    Normalize BIP0044 key path for HD keys. Using single quotes for hardened keys

    >>> normalize_path("m/44h/2p/1'/0/100")
    "m/44'/2'/1'/0/100"

    :param path: BIP0044 key path
    :type path: str

    :return str: Normalized BIP0044 key path with single quotes
    """

    levels = path.split("/")
    npath = ""
    for level in levels:
        if not level:
            raise WalletError("Could not parse path. Index is empty.")
        nlevel = level
        if level[-1] in "'HhPp":
            nlevel = level[:-1] + "'"
        npath += nlevel + "/"
    if npath[-1] == "/":
        npath = npath[:-1]
    return npath


class WalletKey(object):
    """
    Used as attribute of Wallet class. Contains HDKey class, and adds extra wallet related information such as
    key ID, name, path and balance.

    All WalletKeys are stored in a database
    """

    @staticmethod
    def from_key(name, wallet_id, session, key, account_id=0, network=None, change=0, purpose=44, parent_id=0,
                 path='m', key_type=None, encoding=None, witness_type=DEFAULT_WITNESS_TYPE, multisig=False,
                 cosigner_id=None):
        """
        Create WalletKey from a HDKey object or key.

        Normally you don't need to call this method directly. Key creation is handled by the Wallet class.

        >>> w = wallet_create_or_open('hdwalletkey_test')
        >>> wif = 'xprv9s21ZrQH143K2mcs9jcK4EjALbu2z1N9qsMTUG1frmnXM3NNCSGR57yLhwTccfNCwdSQEDftgjCGm96P29wGGcbBsPqZH85iqpoHA7LrqVy'
        >>> wk = WalletKey.from_key('import_key', w.wallet_id, w._session, wif)
        >>> wk.address
        '1MwVEhGq6gg1eeSrEdZom5bHyPqXtJSnPg'
        >>> wk # doctest:+ELLIPSIS
        <WalletKey(key_id=..., name=import_key, wif=xprv9s21ZrQH143K2mcs9jcK4EjALbu2z1N9qsMTUG1frmnXM3NNCSGR57yLhwTccfNCwdSQEDftgjCGm96P29wGGcbBsPqZH85iqpoHA7LrqVy, path=m)>

        :param name: New key name
        :type name: str
        :param wallet_id: ID of wallet where to store key
        :type wallet_id: int
        :param session: Required Sqlalchemy Session object
        :type session: sqlalchemy.orm.session.Session
        :param key: Optional key in any format accepted by the HDKey class
        :type key: str, int, byte, HDKey
        :param account_id: Account ID for specified key, default is 0
        :type account_id: int
        :param network: Network of specified key
        :type network: str
        :param change: Use 0 for normal key, and 1 for change key (for returned payments)
        :type change: int
        :param purpose: BIP0044 purpose field, default is 44
        :type purpose: int
        :param parent_id: Key ID of parent, default is 0 (no parent)
        :type parent_id: int
        :param path: BIP0044 path of given key, default is 'm' (masterkey)
        :type path: str
        :param key_type: Type of key, single or BIP44 type
        :type key_type: str
        :param encoding: Encoding used for address, i.e.: base58 or bech32. Default is base58
        :type encoding: str
        :param witness_type: Witness type used when creating transaction script: legacy, p2sh-segwit or segwit.
        :type witness_type: str
        :param multisig: Specify if key is part of multisig wallet, used for create keys and key representations such as WIF and addreses
        :type multisig: bool
        :param cosigner_id: Set this if you would like to create keys for other cosigners.
        :type cosigner_id: int

        :return WalletKey: WalletKey object
        """

        key_is_address = False
        if isinstance(key, HDKey):
            k = key
            if network is None:
                network = k.network.name
            elif network != k.network.name:
                raise WalletError("Specified network and key network should be the same")

        elif isinstance(key, Address):