diff --git a/NEWS b/NEWS index 8eb60b3a..d5ca41b7 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,9 @@ +changes from 2.2 +---------------- + +* Fix Base58 decoding of a Bitcoin address made only of the digit 1. That digit is a zero byte, so a string of them was one byte too long. + + changes from 2.1 to 2.2 ----------------------- diff --git a/stdnum/bitcoin.py b/stdnum/bitcoin.py index 4cf2bd90..6eac7470 100644 --- a/stdnum/bitcoin.py +++ b/stdnum/bitcoin.py @@ -68,10 +68,11 @@ def b58decode(s: str) -> bytes: """Decode a Base58 encoded string to a bytestring.""" value = reduce(lambda a, c: a * 58 + _base58_alphabet.index(c), s, 0) result = b'' - while value >= 256: + # A zero value has no payload bytes. Each leading 1 is already a zero + # byte, so packing the leftover 0 here would add one extra. + while value: value, mod = divmod(value, 256) result = struct.pack('B', mod) + result - result = struct.pack('B', value) + result return struct.pack('B', 0) * (len(s) - len(s.lstrip('1'))) + result diff --git a/tests/test_bitcoin.doctest b/tests/test_bitcoin.doctest index 022bd846..1038619c 100644 --- a/tests/test_bitcoin.doctest +++ b/tests/test_bitcoin.doctest @@ -23,6 +23,19 @@ useful as module documentation. >>> from stdnum import bitcoin +The digit 1 is zero. A string of them is that many zero bytes, and an empty +string is empty. A leftover zero must not add another byte. + +>>> bitcoin.b58decode('1') +b'\x00' +>>> bitcoin.b58decode('11') +b'\x00\x00' +>>> bitcoin.b58decode('2') +b'\x01' +>>> bitcoin.b58decode('') +b'' + + These are found and constructed P2PKH addresses (P2SH addresses are basically the same because they follow the same validation, except that the first digit is a 3).