Skip to content

Instantly share code, notes, and snippets.

@gitzhou
Last active October 10, 2024 03:21
Show Gist options
  • Save gitzhou/c21f64af3f2980c4fbd6748bc326675d to your computer and use it in GitHub Desktop.
Save gitzhou/c21f64af3f2980c4fbd6748bc326675d to your computer and use it in GitHub Desktop.
base58check demo
from base_x import BaseX
class Base58:
b58 = BaseX('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz')
@staticmethod
def encode(payload: bytes) -> str:
leading_zeros = 0
for byte in payload:
if byte != 0:
break
leading_zeros += 1
encoded = ''
if payload[leading_zeros:]:
num = int.from_bytes(payload, 'big')
encoded = Base58.b58.from_dec(num)
return '1' * leading_zeros + encoded
@staticmethod
def decode(encoded: str) -> bytes:
leading_zeros = 0
for char in encoded:
if char != '1':
break
leading_zeros += 1
num_b = b''
if encoded[leading_zeros:]:
num = Base58.b58.to_dec(encoded)
num_b = num.to_bytes((num.bit_length() + 7) // 8, 'big')
return b'\x00' * leading_zeros + num_b
if __name__ == '__main__':
base58_cases = [
(b'', ''),
(b'\x00', '1'),
(b'\x00\x00', '11'),
(b'1234567890', '3mJr7AoUCHxNqd'),
(b'hello world', 'StV1DL6CwTryKyV'),
(b'\x00\x00hello world', '11StV1DL6CwTryKyV'),
(b'\x01\x02\x03\x04\x05', '7bWpTW'),
]
for _payload, _encoded in base58_cases:
assert Base58.encode(_payload) == _encoded
assert Base58.decode(_encoded) == _payload
from base58 import Base58
from hash import hash256
class Base58Check:
CHECKSUM_BYTES = 4
@staticmethod
def checksum(payload: bytes) -> bytes:
return hash256(payload)[:Base58Check.CHECKSUM_BYTES]
@staticmethod
def encode(payload: bytes) -> str:
checksum = Base58Check.checksum(payload)
return Base58.encode(payload + checksum)
@staticmethod
def decode(encoded: str) -> bytes:
decoded = Base58.decode(encoded)
if len(decoded) < Base58Check.CHECKSUM_BYTES:
raise ValueError('invalid base58check encoded string')
payload = decoded[:-Base58Check.CHECKSUM_BYTES]
checksum = decoded[-Base58Check.CHECKSUM_BYTES:]
if Base58Check.checksum(payload) != checksum:
raise ValueError('invalid checksum')
return payload
if __name__ == '__main__':
base58check_cases = [
(b'', '3QJmnh'),
(b'\x00', '1Wh4bh'),
(b'\x00\x00', '112edB6q'),
(b'1234567890', 'K5zqBMZXFNFYSLJZTdx'),
(b'hello world', '3vQB7B6MrGQZaxCuFg4oh'),
(b'\x00\x00hello world', '113vQB7B6MrGQZaxCpfiiBh'),
(b'\x01\x02\x03\x04\x05', 'kA3B33GVuqT'),
]
for _payload, _encoded in base58check_cases:
assert Base58Check.encode(_payload) == _encoded
assert Base58Check.decode(_encoded) == _payload
class BaseX:
def __init__(self, alphabet: str):
if len(alphabet) < 2:
raise ValueError('alphabet must have at least 2 characters')
self.alphabet = alphabet
def from_dec(self, num: int) -> str:
"""
Converts a decimal unsigned integer to base X of this instance
:param num: decimal number
:return: encoded number in base X of this instance
"""
if num < 0:
raise ValueError('num must be non-negative')
encoded = ''
while num > 0:
num, remaining = divmod(num, len(self.alphabet))
encoded = self.alphabet[remaining] + encoded
return encoded or self.alphabet[0]
def to_dec(self, encoded: str) -> int:
"""
Convert an encoded number in base X of this instance
to the decimal unsigned integer
:param encoded: encoded number in base X of this instance
:return: decimal number
"""
if not encoded:
raise ValueError('encoded number must not be empty')
num = 0
try:
for char in encoded:
num = num * len(self.alphabet) + self.alphabet.index(char)
except Exception:
raise ValueError(f'invalid base{len(self.alphabet)} encoded number')
return num
def from_x(self, num_s: str, base_x: 'BaseX') -> str:
"""
Convert an encoded number in base X of passed-in instance
to the encoded number in base X of this instance
:param num_s: encoded number in base X of passed-in instance
:param base_x: BaseX instance
"""
num = base_x.to_dec(num_s)
return self.from_dec(num)
def to_x(self, num_s: str, base_x: 'BaseX') -> str:
"""
Convert an encoded number in base X of this instance
to the encoded number in base X of passed-in instance
:param num_s: encoded number in base X of this instance
:param base_x: BaseX instance
:return:
"""
num = self.to_dec(num_s)
return base_x.from_dec(num)
import unittest
class TestBaseX(unittest.TestCase):
def test(self):
base5 = BaseX('01234')
base7 = BaseX('0123456')
base8 = BaseX('01234567')
base9 = BaseX('012345678')
# base8('123') to dec
assert base8.to_dec('123') == 83
# dec 83 to base7
assert base7.from_dec(83) == '146'
# base5('1324') to dec
assert base5.to_dec('1324') == 214
# dec 214 to base9
assert base9.from_dec(214) == '257'
# base5('1324') to base9
assert base9.from_x('1324', base5) == '257'
assert base5.to_x('1324', base9) == '257'
# custom base10
my_base10 = BaseX(')!@#$%^&*(')
assert my_base10.to_dec('!@#$%^&*()') == 1234567890
def test_corner_cases(self):
base5 = BaseX('01234')
assert base5.from_dec(0) == '0'
assert base5.to_dec('0') == 0
with self.assertRaises(ValueError) as context:
base5.from_dec(-1)
assert str(context.exception) == 'num must be non-negative'
with self.assertRaises(ValueError) as context:
base5.to_dec('')
assert str(context.exception) == 'encoded number must not be empty'
with self.assertRaises(ValueError) as context:
base5.to_dec('5')
assert str(context.exception) == 'invalid base5 encoded number'
if __name__ == '__main__':
unittest.main()
import hashlib
def sha256(payload: bytes) -> bytes:
return hashlib.sha256(payload).digest()
def hash256(payload: bytes) -> bytes:
return sha256(sha256(payload))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment