Source code for multihash.multihash

# -*- coding: utf-8 -*-
from binascii import hexlify
from collections import namedtuple
from io import BytesIO

from morphys import ensure_bytes, ensure_unicode
import base58
import varint

import multihash.constants as constants


Multihash = namedtuple('Multihash', 'code,name,length,digest')


[docs]def to_hex_string(multihash): """ Convert the given multihash to a hex encoded string :param bytes hash: the multihash to be converted to hex string :return: input multihash in str :rtype: str :raises: `TypeError`, if the `multihash` has incorrect type """ if not isinstance(multihash, bytes): raise TypeError('multihash should be bytes, not {}'.format(type(multihash))) return hexlify(multihash).decode()
[docs]def from_hex_string(multihash): """ Convert the given hex encoded string to a multihash :param str multihash: hex multihash encoded string :return: input multihash in bytes :rtype: bytes :raises: `TypeError`, if the `multihash` has incorrect type """ if not isinstance(multihash, str): raise TypeError('multihash should be str, not {}'.format(type(multihash))) return bytes.fromhex(multihash)
[docs]def to_b58_string(multihash): """ Convert the given multihash to a base58 encoded string :param bytes multihash: multihash to base58 encode :return: base58 encoded multihash string :rtype: str :raises: `TypeError`, if the `multihash` has incorrect type """ if not isinstance(multihash, bytes): raise TypeError('multihash should be bytes, not {}'.format(type(multihash))) return base58.b58encode(multihash).decode()
[docs]def from_b58_string(multihash): """ Convert the given base58 encoded string to a multihash :param str multihash: base58 encoded multihash string :return: decoded multihash :rtype: bytes :raises: `TypeError`, if the `multihash` has incorrect type """ if not isinstance(multihash, str): raise TypeError('multihash should be str, not {}'.format(type(multihash))) return base58.b58decode(multihash)
[docs]def is_app_code(code): """ Checks whether a code is part of the app range :param int code: input code :return: if `code` is in the app range or not :rtype: bool """ return 0 < code < 0x10
[docs]def coerce_code(hash_fn): """ Converts a hash function name into its code If passed a number it will return the number if it's a valid code :param hash_fn: The input hash function can be - str, the name of the hash function - int, the code of the hash function :return: hash function code :rtype: int :raises ValueError: if the hash function is not supported :raises ValueError: if the hash code is not supported :raises ValueError: if the hash type is not a string or an int """ if isinstance(hash_fn, str): try: return constants.HASH_CODES[hash_fn] except KeyError: raise ValueError('Unsupported hash function {}'.format(hash_fn)) elif isinstance(hash_fn, int): if hash_fn in constants.CODE_HASHES or is_app_code(hash_fn): return hash_fn raise ValueError('Unsupported hash code {}'.format(hash_fn)) raise TypeError('hash code should be either an integer or a string')
[docs]def is_valid_code(code): """ Checks whether a multihash code is valid or not :param int code: input code :return: if the code valid or not :rtype: bool """ return is_app_code(code) or code in constants.CODE_HASHES
[docs]def decode(multihash): """ Decode a hash from the given multihash :param bytes multihash: multihash :return: decoded :py:class:`multihash.Multihash` object :rtype: :py:class:`multihash.Multihash` :raises TypeError: if `multihash` is not of type `bytes` :raises ValueError: if the length of multihash is less than 3 characters :raises ValueError: if the code is invalid :raises ValueError: if the length is invalid :raises ValueError: if the length is not same as the digest """ if not isinstance(multihash, bytes): raise TypeError('multihash should be bytes, not {}', type(multihash)) if len(multihash) < 3: raise ValueError('multihash must be greater than 3 bytes.') buffer = BytesIO(multihash) code = varint.decode_stream(buffer) if not is_valid_code(code): raise ValueError('Unsupported hash code {}'.format(code)) try: length = varint.decode_stream(buffer) except TypeError: raise ValueError('Invalid length provided') buf = buffer.read() if len(buf) != length: raise ValueError('Inconsistent multihash length {} != {}'.format(len(buf), length)) return Multihash(code=code, name=constants.CODE_HASHES[code], length=length, digest=buf)
[docs]def encode(digest, code, length=None): """ Encode a hash digest along with the specified function code :param bytes digest: hash digest :param (int or str) code: hash function code :param int length: hash digest length :return: encoded multihash :rtype: bytes :raises TypeError: when the digest is not a bytes object :raises ValueError: when the digest length is not correct """ hash_code = coerce_code(code) if not isinstance(digest, bytes): raise TypeError('digest must be a bytes object, not {}'.format(type(digest))) if length is None: length = len(digest) elif length != len(digest): raise ValueError('digest length should be equal to specified length') return varint.encode(hash_code) + varint.encode(length) + digest
[docs]def is_valid(multihash): """ Check if the given buffer is a valid multihash :param bytes multihash: input multihash :return: if the input is a valid multihash or not :rtype: bool """ try: decode(multihash) return True except ValueError: return False
[docs]def get_prefix(multihash): """ Return the prefix from the multihash :param bytes multihash: input multihash :return: multihash prefix :rtype: bytes :raises ValueError: when the multihash is invalid """ if is_valid(multihash): return multihash[:2] raise ValueError('invalid multihash')