|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# Copyright (c) 2022 tecnovert
|
|
|
|
# Distributed under the MIT software license, see the accompanying
|
|
|
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
|
|
|
import hashlib
|
|
|
|
from basicswap.contrib.segwit_addr import bech32_decode, convertbits, bech32_encode
|
|
|
|
from basicswap.util.crypto import ripemd160
|
|
|
|
|
|
|
|
__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
|
|
|
|
|
|
|
|
|
|
|
def b58decode(v, length=None):
|
|
|
|
long_value = 0
|
|
|
|
for (i, c) in enumerate(v[::-1]):
|
|
|
|
ofs = __b58chars.find(c)
|
|
|
|
if ofs < 0:
|
|
|
|
return None
|
|
|
|
long_value += ofs * (58**i)
|
|
|
|
result = bytes()
|
|
|
|
while long_value >= 256:
|
|
|
|
div, mod = divmod(long_value, 256)
|
|
|
|
result = bytes((mod,)) + result
|
|
|
|
long_value = div
|
|
|
|
result = bytes((long_value,)) + result
|
|
|
|
nPad = 0
|
|
|
|
for c in v:
|
|
|
|
if c == __b58chars[0]:
|
|
|
|
nPad += 1
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
pad = bytes((0,)) * nPad
|
|
|
|
result = pad + result
|
|
|
|
if length is not None and len(result) != length:
|
|
|
|
return None
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def b58encode(v):
|
|
|
|
long_value = 0
|
|
|
|
for (i, c) in enumerate(v[::-1]):
|
|
|
|
long_value += (256**i) * c
|
|
|
|
|
|
|
|
result = ''
|
|
|
|
while long_value >= 58:
|
|
|
|
div, mod = divmod(long_value, 58)
|
|
|
|
result = __b58chars[mod] + result
|
|
|
|
long_value = div
|
|
|
|
result = __b58chars[long_value] + result
|
|
|
|
|
|
|
|
# leading 0-bytes in the input become leading-1s
|
|
|
|
nPad = 0
|
|
|
|
for c in v:
|
|
|
|
if c == 0:
|
|
|
|
nPad += 1
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
return (__b58chars[0] * nPad) + result
|
|
|
|
|
|
|
|
|
|
|
|
def encodeStealthAddress(prefix_byte, scan_pubkey, spend_pubkey):
|
|
|
|
data = bytes((0x00,))
|
|
|
|
data += scan_pubkey
|
|
|
|
data += bytes((0x01,))
|
|
|
|
data += spend_pubkey
|
|
|
|
data += bytes((0x00,)) # number_signatures - unused
|
|
|
|
data += bytes((0x00,)) # num prefix bits
|
|
|
|
|
|
|
|
b = bytes((prefix_byte,)) + data
|
|
|
|
b += hashlib.sha256(hashlib.sha256(b).digest()).digest()[:4]
|
|
|
|
return b58encode(b)
|
|
|
|
|
|
|
|
|
|
|
|
def decodeWif(encoded_key):
|
|
|
|
key = b58decode(encoded_key)[1:-4]
|
|
|
|
if len(key) == 33:
|
|
|
|
return key[:-1]
|
|
|
|
return key
|
|
|
|
|
|
|
|
|
|
|
|
def toWIF(prefix_byte, b, compressed=True):
|
|
|
|
b = bytes((prefix_byte,)) + b
|
|
|
|
if compressed:
|
|
|
|
b += bytes((0x01,))
|
|
|
|
b += hashlib.sha256(hashlib.sha256(b).digest()).digest()[:4]
|
|
|
|
return b58encode(b)
|
|
|
|
|
|
|
|
|
|
|
|
def getKeyID(bytes):
|
|
|
|
data = hashlib.sha256(bytes).digest()
|
|
|
|
return ripemd160(data)
|
|
|
|
|
|
|
|
|
|
|
|
def bech32Decode(hrp, addr):
|
|
|
|
hrpgot, data = bech32_decode(addr)
|
|
|
|
if hrpgot != hrp:
|
|
|
|
return None
|
|
|
|
decoded = convertbits(data, 5, 8, False)
|
|
|
|
if decoded is None or len(decoded) < 2 or len(decoded) > 40:
|
|
|
|
return None
|
|
|
|
return bytes(decoded)
|
|
|
|
|
|
|
|
|
|
|
|
def bech32Encode(hrp, data):
|
|
|
|
ret = bech32_encode(hrp, convertbits(data, 8, 5))
|
|
|
|
if bech32Decode(hrp, ret) is None:
|
|
|
|
return None
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
def decodeAddress(address_str):
|
|
|
|
b58_addr = b58decode(address_str)
|
|
|
|
if b58_addr is not None:
|
|
|
|
address = b58_addr[:-4]
|
|
|
|
checksum = b58_addr[-4:]
|
|
|
|
assert(hashlib.sha256(hashlib.sha256(address).digest()).digest()[:4] == checksum), 'Checksum mismatch'
|
|
|
|
return b58_addr[:-4]
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def encodeAddress(address):
|
|
|
|
checksum = hashlib.sha256(hashlib.sha256(address).digest()).digest()
|
|
|
|
return b58encode(address + checksum[0:4])
|
|
|
|
|
|
|
|
|
|
|
|
def pubkeyToAddress(prefix, pubkey):
|
|
|
|
return encodeAddress(bytes((prefix,)) + getKeyID(pubkey))
|