Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions bitcoin/base58.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
_bord = lambda x: x

import binascii
from hashlib import sha256

import bitcoin.core

Expand Down Expand Up @@ -91,6 +92,30 @@ def decode(s):
else: break
return b'\x00' * pad + res

class InvalidMinikeyError(Base58Error):
"""Raised for invalid minikeys"""
pass

def decode_minikey(minikey):
"""Decode minikey in str or bytes to standard base58 bytes

Minikeys are an old key format, for details see
https://en.bitcoin.it/wiki/Mini_private_key_format.
"""
if isinstance(minikey, str):
minikey = minikey.encode('ascii')
length = len(minikey)
if length not in [22, 30]:
raise InvalidMinikeyError('Minikey length %d is not 22 or 30' % length)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest using str("length is {} yo").format(length) instead of using the % operator.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you please explain why?

  • % is not deprecated, and I find it even more readable
  • % is used throughout the whole project
  • for this use case the extra power of format provides no benefit as far as I can see

h = sha256(minikey)
h_cs = h.copy()
h_cs.update(b'?')
checksum = _bord(h_cs.digest()[0])
if checksum != 0:
raise InvalidMinikeyError('Minikey checksum %s is not 0' % checksum)
versioned = b'\x80' + h.digest()
checked = versioned + sha256(sha256(versioned).digest()).digest()[:4]
return encode(checked)

class Base58ChecksumError(Base58Error):
"""Raised on Base58 checksum errors"""
Expand Down Expand Up @@ -151,6 +176,8 @@ def __repr__(self):
'InvalidBase58Error',
'encode',
'decode',
'InvalidMinikeyError',
'decode_minikey',
'Base58ChecksumError',
'CBase58Data',
)
31 changes: 31 additions & 0 deletions bitcoin/tests/test_base58.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,37 @@ def test_encode_decode(self):
self.assertEqual(act_base58, exp_base58)
self.assertEqual(act_bin, exp_bin)

class Test_minikey(unittest.TestCase):

valid_minikeys = [
('S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy', '5JPy8Zg7z4P7RSLsiqcqyeAF1935zjNUdMxcDeVrtU1oarrgnB7'),
('SVY4eSFCF4tMtMohEkpXkoN9FHxDV7', '5JSyovgwfVcuFZBAp8LAta2tMsmscxXv3FvzvJWeKBfycLAmjuZ'),
('S6c56bnXQiBjk9mqSYEa30', '5KM4V1haDBMEcgzPuAWdHSBAVAEJNp4he2meirV3JNvZz9aWBNH')
]
invalid_minikeys = [
('', 'Minikey length 0 is not 22 or 30'),
('S6c56bnXQiBjk9mqSYE7ykVQ7NzrR', 'Minikey length 29 is not 22 or 30'),
('S6c56bnXQiBjk9mqSYE7ykVQ7NzrRyz', 'Minikey length 31 is not 22 or 30'),
('S6c56bnXQiBjk9mqSYE7ykVQ7NzrRz', 'Minikey checksum 213 is not 0'),
('S6c56bnXQiBjk9mqSYE7yk', 'Minikey checksum 46 is not 0')
]

def test_decode_minikey_bytes(self):
for minikey, exp_base58_key in self.valid_minikeys:
base58_key2 = decode_minikey(minikey.encode('ascii'))
self.assertEqual(base58_key2, exp_base58_key)

def test_decode_minikey_str(self):
for minikey, exp_base58_key in self.valid_minikeys:
base58_key = decode_minikey(minikey)
self.assertEqual(base58_key, exp_base58_key)

def test_invalid(self):
for minikey, msg in self.invalid_minikeys:
with self.assertRaises(InvalidMinikeyError) as cm:
decode_minikey(minikey)
self.assertEqual(str(cm.exception), msg)

class Test_CBase58Data(unittest.TestCase):
def test_from_data(self):
b = CBase58Data.from_bytes(b"b\xe9\x07\xb1\\\xbf'\xd5BS\x99\xeb\xf6\xf0\xfbP\xeb\xb8\x8f\x18", 0)
Expand Down
36 changes: 36 additions & 0 deletions examples/minikey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
#
# Copyright (C) 2013-2015 The python-bitcoinlib developers
#
# This file is part of python-bitcoinlib.
#
# It is subject to the license terms in the LICENSE file found in the top-level
# directory of this distribution.
#
# No part of python-bitcoinlib, including this file, may be copied, modified,
# propagated, or distributed except according to the terms contained in the
# LICENSE file.

from __future__ import absolute_import, division, print_function, unicode_literals

from bitcoin import base58

def parser():
import argparse
parser = argparse.ArgumentParser(
description='Decode a minikey to base58 format.',
epilog='Security warning: arguments may be visible to other users on the same host.')
parser.add_argument(
'minikey',
help='the minikey')
return parser

if __name__ == '__main__':
args = parser().parse_args()
try:
base58_key = base58.decode_minikey(args.minikey)
except Exception as error:
print('%s: %s' % (error.__class__.__name__, str(error)))
exit(1)
else:
print(base58_key)