|
8 | 8 | # file COPYING or http://www.opensource.org/licenses/mit-license.php. |
9 | 9 | # |
10 | 10 |
|
11 | | -from bitcoin.serialize import Hash |
| 11 | +from bitcoin.serialize import Hash, ser_uint256 |
12 | 12 |
|
13 | 13 | b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' |
14 | 14 |
|
15 | | -def encode (n): |
16 | | - l = [] |
| 15 | +from binascii import hexlify, unhexlify |
| 16 | + |
| 17 | +class Base58Error(Exception): |
| 18 | + pass |
| 19 | + |
| 20 | +class InvalidBase58Error(Base58Error): |
| 21 | + pass |
| 22 | + |
| 23 | +def encode(b): |
| 24 | + """Encode bytes to a base58-encoded string""" |
| 25 | + |
| 26 | + # Convert big-endian bytes to integer |
| 27 | + n = int('0x0' + hexlify(b), 16) |
| 28 | + |
| 29 | + # Divide that integer into bas58 |
| 30 | + res = [] |
17 | 31 | while n > 0: |
18 | 32 | n, r = divmod (n, 58) |
19 | | - l.insert (0, (b58_digits[r])) |
20 | | - return ''.join (l) |
21 | | - |
22 | | -def decode (s): |
23 | | - n = 0 |
24 | | - for ch in s: |
25 | | - n *= 58 |
26 | | - digit = b58_digits.index (ch) |
27 | | - n += digit |
28 | | - return n |
| 33 | + res.append(b58_digits[r]) |
| 34 | + res = ''.join(res[::-1]) |
29 | 35 |
|
30 | | -def encode_padded (s): |
31 | | - res = base58_encode (int ('0x' + s.encode ('hex'), 16)) |
| 36 | + # Encode leading zeros as base58 zeros |
32 | 37 | pad = 0 |
33 | | - for c in s: |
| 38 | + for c in b: |
34 | 39 | if c == chr(0): pad += 1 |
35 | 40 | else: break |
36 | 41 | return b58_digits[0] * pad + res |
37 | 42 |
|
38 | | -def decode_padded (s): |
39 | | - pad = 0 |
| 43 | +def decode(s): |
| 44 | + """Decode a base58-encoding string, returning bytes""" |
| 45 | + if not s: |
| 46 | + return b'' |
| 47 | + |
| 48 | + # Convert the string to an integer |
| 49 | + n = 0 |
40 | 50 | for c in s: |
41 | | - if c == b58_digits[0]: pad += 1 |
42 | | - else: break |
43 | | - h = '%x' % base58_decode (s) |
| 51 | + n *= 58 |
| 52 | + if c not in b58_digits: |
| 53 | + raise InvalidBase58Error('Character %r is not a valid base58 character' % c) |
| 54 | + digit = b58_digits.index(c) |
| 55 | + n += digit |
| 56 | + |
| 57 | + # Convert the integer to bytes |
| 58 | + h = '%x' % n |
44 | 59 | if len(h) % 2: |
45 | 60 | h = '0' + h |
46 | | - res = h.decode ('hex') |
47 | | - return chr(0) * pad + res |
48 | | - |
49 | | -def key_to_address (s): |
50 | | - vs = chr (addrtype) + s |
51 | | - check = Hash(vs)[:4] |
52 | | - return base58_encode_padded (vs + check) |
53 | | - |
54 | | -def address_to_key (s): |
55 | | - k = base58_decode_padded (s) |
56 | | - hash160, check0 = k[1:-4], k[-4:] |
57 | | - check1 = Hash (chr (addrtype) + hash160)[:4] |
58 | | - if check0 != check1: |
59 | | - return None |
60 | | - return hash160 |
| 61 | + res = unhexlify(h) |
| 62 | + |
| 63 | + # Add padding back. |
| 64 | + pad = 0 |
| 65 | + for c in s[:-1]: |
| 66 | + if c == b58_digits[0]: pad += 1 |
| 67 | + else: break |
| 68 | + return b'\x00' * pad + res |
| 69 | + |
| 70 | + |
| 71 | +class Base58ChecksumError(Base58Error): |
| 72 | + pass |
| 73 | + |
| 74 | +class CBase58Data(bytes): |
| 75 | + def __new__(cls, data, nVersion): |
| 76 | + self = super(CBase58Data, cls).__new__(cls, data) |
| 77 | + self.nVersion = nVersion |
| 78 | + return self |
| 79 | + |
| 80 | + def __str__(self): |
| 81 | + vs = chr(self.nVersion) + self |
| 82 | + check = ser_uint256(Hash(vs))[0:4] |
| 83 | + return encode(vs + check) |
| 84 | + |
| 85 | + @classmethod |
| 86 | + def from_str(cls, s): |
| 87 | + k = decode(s) |
| 88 | + addrbyte, data, check0 = k[0], k[1:-4], k[-4:] |
| 89 | + check1 = ser_uint256(Hash(addrbyte + data))[:4] |
| 90 | + if check0 != check1: |
| 91 | + raise Base58ChecksumError('Checksum mismatch: expected %r, calculated %r' % (check0, check1)) |
| 92 | + return cls(data, ord(addrbyte)) |
| 93 | + |
61 | 94 |
|
| 95 | +class CBitcoinAddress(CBase58Data): |
| 96 | + PUBKEY_ADDRESS = 0 |
| 97 | + SCRIPT_ADDRESS = 5 |
| 98 | + PUBKEY_ADDRESS_TEST = 111 |
| 99 | + SCRIPT_ADDRESS_TEST = 196 |
0 commit comments