text: # Copyright (c) 2016-2017, Neil Booth
  #
  # All rights reserved.
  #
  # The MIT License (MIT)
  #
  # Permission is hereby granted, free of charge, to any person obtaining
  # a copy of this software and associated documentation files (the
  # "Software"), to deal in the Software without restriction, including
  # without limitation the rights to use, copy, modify, merge, publish,
  # distribute, sublicense, and/or sell copies of the Software, and to
  # permit persons to whom the Software is furnished to do so, subject to
  # the following conditions:
  #
  # The above copyright notice and this permission notice shall be
  # included in all copies or substantial portions of the Software.
  #
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  """Cryptograph hash functions and related classes."""
  
  
  import hashlib
  import hmac
  
  from lbry.wallet.server.util import bytes_to_int, int_to_bytes, hex_to_bytes
  
  _sha256 = hashlib.sha256
  _sha512 = hashlib.sha512
  _new_hash = hashlib.new
  _new_hmac = hmac.new
  HASHX_LEN = 11
  
  
  def sha256(x):
      """Simple wrapper of hashlib sha256."""
      return _sha256(x).digest()
  
  
  def ripemd160(x):
      """Simple wrapper of hashlib ripemd160."""
      h = _new_hash('ripemd160')
      h.update(x)
      return h.digest()
  
  
  def double_sha256(x):
      """SHA-256 of SHA-256, as used extensively in bitcoin."""
      return sha256(sha256(x))
  
  
  def hmac_sha512(key, msg):
      """Use SHA-512 to provide an HMAC."""
      return _new_hmac(key, msg, _sha512).digest()
  
  
  def hash160(x):
      """RIPEMD-160 of SHA-256.
  
      Used to make bitcoin addresses from pubkeys."""
      return ripemd160(sha256(x))
  
  
  def hash_to_hex_str(x: bytes) -> str:
      """Convert a big-endian binary hash to displayed hex string.
  
      Display form of a binary hash is reversed and converted to hex.
      """
      return x[::-1].hex()
  
  
  def hex_str_to_hash(x: str) -> bytes:
      """Convert a displayed hex string to a binary hash."""
      return hex_to_bytes(x)[::-1]
  
  
  class Base58Error(Exception):
      """Exception used for Base58 errors."""
  
  
  class Base58:
      """Class providing base 58 functionality."""
  
      chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
      assert len(chars) == 58
      cmap = {c: n for n, c in enumerate(chars)}
  
      @staticmethod
      def char_value(c):
          val = Base58.cmap.get(c)
          if val is None:
              raise Base58Error(f'invalid base 58 character "{c}"')
          return val
  
      @staticmethod
      def decode(txt):
          """Decodes txt into a big-endian bytearray."""
          if not isinstance(txt, str):
              raise TypeError('a string is required')
  
          if not txt:
              raise Base58Error('string cannot be empty')
  
          value = 0
          for c in txt:
              value = value * 58 + Base58.char_value(c)
  
          result = int_to_bytes(value)
  
          # Prepend leading zero bytes if necessary
          count = 0
          for c in txt:
              if c != '1':
                  break
              count += 1
          if count:
              result = bytes(count) + result
  
          return result
  
      @staticmethod
      def encode(be_bytes):
          """Converts a big-endian bytearray into a base58 string."""
          value = bytes_to_int(be_bytes)
  
          txt = ''
          while value:
              value, mod = divmod(value, 58)
              txt += Base58.chars[mod]
  
          for byte in be_bytes:
              if byte != 0:
                  break
              txt += '1'
  
          return txt[::-1]
  
      @staticmethod
      def decode_check(txt, *, hash_fn=double_sha256):
          """Decodes a Base58Check-encoded string to a payload.  The version
          prefixes it."""
          be_bytes = Base58.decode(txt)
          result, check = be_bytes[:-4], be_bytes[-4:]
          if check != hash_fn(result)[:4]:
              raise Base58Error(f'invalid base 58 checksum for {txt}')
          return result
  
      @staticmethod
      def encode_check(payload, *, hash_fn=double_sha256):
          """Encodes a payload bytearray (which includes the version byte(s))
          into a Base58Check string."""
          be_bytes = payload + hash_fn(payload)[:4]
          return Base58.encode(be_bytes)
  