52 lines
1.6 KiB
Python
52 lines
1.6 KiB
Python
from __future__ import annotations
|
|
|
|
try:
|
|
# Prefer external package if available
|
|
from base58 import b58encode, b58decode # type: ignore
|
|
except Exception:
|
|
# Minimal fallback (compatible subset)
|
|
ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
|
ALPHABET_INDEX = {c: i for i, c in enumerate(ALPHABET)}
|
|
|
|
def _to_bytes(value: bytes | bytearray | str) -> bytes:
|
|
if isinstance(value, (bytes, bytearray)):
|
|
return bytes(value)
|
|
if isinstance(value, str):
|
|
return value.encode()
|
|
raise TypeError("value must be bytes or str")
|
|
|
|
def b58encode(data: bytes | bytearray | str) -> bytes:
|
|
data = _to_bytes(data)
|
|
if not data:
|
|
return b""
|
|
n = int.from_bytes(data, "big")
|
|
out = []
|
|
while n > 0:
|
|
n, rem = divmod(n, 58)
|
|
out.append(ALPHABET[rem])
|
|
enc = "".join(reversed(out))
|
|
leading = 0
|
|
for b in data:
|
|
if b == 0:
|
|
leading += 1
|
|
else:
|
|
break
|
|
return ("1" * leading + enc).encode()
|
|
|
|
def b58decode(data: bytes | bytearray | str) -> bytes:
|
|
data_b = _to_bytes(data)
|
|
if not data_b:
|
|
return b""
|
|
num = 0
|
|
for ch in data_b.decode():
|
|
num = num * 58 + ALPHABET_INDEX[ch]
|
|
full = num.to_bytes((num.bit_length() + 7) // 8, "big")
|
|
leading = 0
|
|
for ch in data_b:
|
|
if ch == ord('1'):
|
|
leading += 1
|
|
else:
|
|
break
|
|
return b"\x00" * leading + full
|
|
|