# -*- coding: utf-8 -*-
"""
Contains :class:`.Base58` and various helper functions for working with Base58 public/private keys used on
blockchains such as GOLOS and STEEM.
"""
import hashlib
import logging
import string
from binascii import hexlify, unhexlify
log = logging.getLogger(__name__)
PREFIX = "GLS"
""" Default Prefix """
known_prefixes = [
PREFIX,
"STM",
"TST",
]
"""
A list of known public key prefixes, used by :class:`.Base58` for alerting the user if an invalid prefix may have
been used.
"""
[docs]class Base58(object):
"""
**Base58 base class.** This class serves as an abstraction layer to deal with base58 encoded
strings and their corresponding hex and binary representation throughout the library.
:param data: Data to initialize object, e.g. pubkey data, address data,
...
:type data: hex, wif, bip38 encrypted wif, base58 string
:param str prefix: Prefix to use for Address/PubKey strings (defaults
to ``GPH``)
:return: Base58 object initialized with ``data``
:rtype: Base58
:raises ValueError: if data cannot be decoded
* ``bytes(Base58)``: Returns the raw data
* ``str(Base58)``: Returns the readable ``Base58CheckEncoded`` data.
* ``repr(Base58)``: Gives the hex representation of the data.
* ``format(Base58,_format)`` Formats the instance according to
``_format``:
* ``"btc"``: prefixed with ``0x80``. Yields a valid btc address
* ``"wif"``: prefixed with ``0x00``. Yields a valid wif key
* ``"bts"``: prefixed with ``BTS``
* etc.
"""
[docs] def __init__(self, data, prefix=PREFIX):
self._prefix = prefix
if all(c in string.hexdigits for c in data):
self._hex = data
elif data[0] == "5" or data[0] == "6":
self._hex = base58CheckDecode(data)
elif data[0] == "K" or data[0] == "L":
self._hex = base58CheckDecode(data)[:-2]
elif data[:len(self._prefix)] == self._prefix:
self._hex = gphBase58CheckDecode(data[len(self._prefix):])
else:
raise ValueError("Error loading Base58 object")
def __format__(self, _format):
""" Format output according to argument _format (wif,btc,...)
:param str _format: Format to use
:return: formatted data according to _format
:rtype: str
"""
if _format.upper() == "WIF":
return base58CheckEncode(0x80, self._hex)
elif _format.upper() == "ENCWIF":
return base58encode(self._hex)
elif _format.upper() == "BTC":
return base58CheckEncode(0x00, self._hex)
elif _format.upper() in known_prefixes:
return _format.upper() + str(self)
else:
log.warn("Format %s unkown. You've been warned!\n" % _format)
return _format.upper() + str(self)
def __repr__(self):
""" Returns hex value of object
:return: Hex string of instance's data
:rtype: hex string
"""
return self._hex
def __str__(self):
""" Return graphene-base58CheckEncoded string of data
:return: Base58 encoded data
:rtype: str
"""
return gphBase58CheckEncode(self._hex)
def __bytes__(self):
""" Return raw bytes
:return: Raw bytes of instance
:rtype: bytes
"""
return unhexlify(self._hex)
# https://github.com/tochev/python3-cryptocoins/raw/master/cryptocoins/base58.py
BASE58_ALPHABET = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
[docs]def base58decode(base58_str):
base58_text = bytes(base58_str, "ascii")
n = 0
leading_zeroes_count = 0
for b in base58_text:
n = n * 58 + BASE58_ALPHABET.find(b)
if n == 0:
leading_zeroes_count += 1
res = bytearray()
while n >= 256:
div, mod = divmod(n, 256)
res.insert(0, mod)
n = div
else:
res.insert(0, n)
return hexlify(bytearray(1) * leading_zeroes_count + res).decode('ascii')
[docs]def base58encode(hexstring):
byteseq = bytes(unhexlify(bytes(hexstring, 'ascii')))
n = 0
leading_zeroes_count = 0
for c in byteseq:
n = n * 256 + c
if n == 0:
leading_zeroes_count += 1
res = bytearray()
while n >= 58:
div, mod = divmod(n, 58)
res.insert(0, BASE58_ALPHABET[mod])
n = div
else:
res.insert(0, BASE58_ALPHABET[n])
return (BASE58_ALPHABET[0:1] * leading_zeroes_count + res).decode('ascii')
[docs]def ripemd160(s):
ripemd160 = hashlib.new('ripemd160')
ripemd160.update(unhexlify(s))
return ripemd160.digest()
[docs]def doublesha256(s):
return hashlib.sha256(hashlib.sha256(unhexlify(s)).digest()).digest()
[docs]def b58encode(v):
return base58encode(v)
[docs]def b58decode(v):
return base58decode(v)
[docs]def base58CheckEncode(version, payload):
s = ('%.2x' % version) + payload
checksum = doublesha256(s)[:4]
result = s + hexlify(checksum).decode('ascii')
return base58encode(result)
[docs]def base58CheckDecode(s):
s = unhexlify(base58decode(s))
dec = hexlify(s[:-4]).decode('ascii')
checksum = doublesha256(dec)[:4]
assert (s[-4:] == checksum)
return dec[2:]
[docs]def gphBase58CheckEncode(s):
checksum = ripemd160(s)[:4]
result = s + hexlify(checksum).decode('ascii')
return base58encode(result)
[docs]def gphBase58CheckDecode(s):
s = unhexlify(base58decode(s))
dec = hexlify(s[:-4]).decode('ascii')
checksum = ripemd160(dec)[:4]
assert (s[-4:] == checksum)
return dec