From 7c1aaefcd3c17063f1e2df0b8897a5f80d9f3050 Mon Sep 17 00:00:00 2001 From: sumanjeet0012 Date: Sat, 11 Jul 2026 17:27:19 +0530 Subject: [PATCH] Fix base58 encoding losing leading zero bytes --- multibase/converters.py | 37 ++++++++++++++++++++++++++++--------- tests/test_multibase.py | 11 +++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/multibase/converters.py b/multibase/converters.py index 4202658..7ad9d22 100644 --- a/multibase/converters.py +++ b/multibase/converters.py @@ -7,8 +7,20 @@ class BaseStringConverter(BaseConverter): def encode(self, bytes): + # Count leading zero bytes + leading_zeros = 0 + for b in bytes: + if b == 0: + leading_zeros += 1 + else: + break + number = int.from_bytes(bytes, byteorder="big", signed=False) - return ensure_bytes(super().encode(number)) + encoded = ensure_bytes(super().encode(number)) + + # Prepend the zero-character for each leading zero byte + zero_char = ensure_bytes(self.digits[0]) + return zero_char * leading_zeros + encoded def bytes_to_int(self, bytes): length = len(bytes) @@ -20,11 +32,23 @@ def bytes_to_int(self, bytes): return value def decode(self, bytes): + # Count leading zero characters + zero_char = self.digits[0] + leading_zeros = 0 + + decoded_str = bytes.decode("utf-8") if hasattr(bytes, "decode") else bytes + for ch in decoded_str: + if ch == zero_char: + leading_zeros += 1 + else: + break + decoded_int = self.bytes_to_int(bytes) # See https://docs.python.org/3.5/library/stdtypes.html#int.to_bytes for more about the magical expression # below decoded_data = decoded_int.to_bytes((decoded_int.bit_length() + 7) // 8, byteorder="big") - return decoded_data + + return b"\x00" * leading_zeros + decoded_data class Base16StringConverter(BaseStringConverter): @@ -39,17 +63,12 @@ def encode(self, bytes): return ensure_bytes(result) def decode(self, data): - # Base16 decode is case-insensitive, normalize to our digits case + # Base16 decode is case-insensitive if isinstance(data, bytes): data_str = data.decode("utf-8") else: data_str = data - # Convert to match our digits case - if self.uppercase: - data_str = data_str.upper() - else: - data_str = data_str.lower() - return super().decode(data_str.encode("utf-8")) + return bytes.fromhex(data_str) class BaseByteStringConverter: diff --git a/tests/test_multibase.py b/tests/test_multibase.py index 77bad99..c3bf1f7 100644 --- a/tests/test_multibase.py +++ b/tests/test_multibase.py @@ -104,6 +104,17 @@ ("base64urlpad", "foob", "UZm9vYg=="), ("base64urlpad", "fooba", "UZm9vYmE="), ("base64urlpad", "foobar", "UZm9vYmFy"), + ( + "base2", + "\x00\x00yes mani !", + "0001111001011001010111001100100000011011010110000101101110011010010010000000100001", + ), + ("base8", "\x00\x00yes mani !", "700171312714403326055632220041"), + ("base10", "\x00\x00yes mani !", "900573277761329450583662625"), + ("base36", "\x00\x00yes mani !", "k002lcpzo5yikidynfl"), + ("base32z", "\x00\x00yes mani !", "hyyxf1zgedpcfzg1ebb"), + ("base58btc", "\x00\x00yes mani !", "z117paNL19xttacUY"), + ("base58flickr", "\x00\x00yes mani !", "Z117Pznk19XTTzBtx"), )