-
Notifications
You must be signed in to change notification settings - Fork 0
/
base58.py
183 lines (142 loc) · 5.08 KB
/
base58.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# -*- coding: utf-8 -*-
import hashlib
import string
import logging
from binascii import hexlify, unhexlify
from .utils import _bytes
from .prefix import Prefix
log = logging.getLogger(__name__)
class Base58(Prefix):
"""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``:
* ``"wif"``: prefixed with ``0x00``. Yields a valid wif key
* ``"bts"``: prefixed with ``X4T``
* etc.
"""
def __init__(self, data, prefix=None):
self.set_prefix(prefix)
if isinstance(data, Base58):
data = repr(data)
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": # pragma: no cover
raise NotImplementedError(
"Private Keys starting with L or K are not supported!"
)
elif data[: len(self.prefix)] == self.prefix:
self._hex = gphBase58CheckDecode(data[len(self.prefix) :])
else:
raise ValueError("Error loading Base58 object: {}".format(data))
def __format__(self, _format):
""" Format output according to argument _format (wif,...)
: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)
else:
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"
def base58decode(base58_str):
base58_text = _bytes(base58_str)
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")
def base58encode(hexstring):
byteseq = unhexlify(_bytes(hexstring))
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")
def ripemd160(s):
ripemd160 = hashlib.new("ripemd160")
ripemd160.update(unhexlify(s))
return ripemd160.digest()
def doublesha256(s):
return hashlib.sha256(hashlib.sha256(unhexlify(s)).digest()).digest()
def b58encode(v):
return base58encode(v)
def b58decode(v):
return base58decode(v)
def base58CheckEncode(version, payload):
s = ("%.2x" % version) + payload
checksum = doublesha256(s)[:4]
result = s + hexlify(checksum).decode("ascii")
return base58encode(result)
def base58CheckDecode(s):
s = unhexlify(base58decode(s))
dec = hexlify(s[:-4]).decode("ascii")
checksum = doublesha256(dec)[:4]
assert s[-4:] == checksum
return dec[2:]
def gphBase58CheckEncode(s):
checksum = ripemd160(s)[:4]
result = s + hexlify(checksum).decode("ascii")
return base58encode(result)
def gphBase58CheckDecode(s):
s = unhexlify(base58decode(s))
dec = hexlify(s[:-4]).decode("ascii")
checksum = ripemd160(dec)[:4]
assert s[-4:] == checksum
return dec