reimplement base58 routines, move from bitcoin module to class baseconv

This commit is contained in:
The MMGen Project 2017-08-08 16:46:39 +03:00
commit 33673cbb1b
Signed by: mmgen
GPG key ID: 62DBE9E5212F05BE
5 changed files with 52 additions and 82 deletions

View file

@ -39,17 +39,26 @@ _generator_secp256k1 = ecdsa.ellipticcurve.Point(_curve_secp256k1,_Gx,_Gy,_r)
_oid_secp256k1 = (1,3,132,0,10)
_secp256k1 = ecdsa.curves.Curve('secp256k1',_curve_secp256k1,_generator_secp256k1,_oid_secp256k1)
b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
# From en.bitcoin.it:
# The Base58 encoding used is home made, and has some differences.
# Especially, leading zeroes are kept as single zeroes when conversion
# happens.
#
# The Base58 encoding used is home made, and has some differences.
# Especially, leading zeroes are kept as single zeroes when conversion happens.
# Test: 5JbQQTs3cnoYN9vDYaGY6nhQ1DggVsY4FJNBUfEfpSQqrEp3srk
#
# The 'zero address':
# 1111111111111111111114oLvT2 (use step2 = ('0' * 40) to generate)
# 1111111111111111111114oLvT2 (pubkeyhash = '\0'*20)
_b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def _numtob58(num):
ret = []
while num:
ret.append(_b58a[num % 58])
num /= 58
return ''.join(ret)[::-1]
def _b58tonum(b58num):
b58num = b58num.strip()
for i in b58num:
if not i in _b58a: return False
return sum(_b58a.index(n) * (58**i) for i,n in enumerate(list(b58num[::-1])))
from mmgen.globalvars import g
@ -91,66 +100,6 @@ def verify_addr(addr,verbose=False,return_hex=False,return_type=False):
if verbose: Msg("Invalid address '%s'" % addr)
return False
# Reworked code from here:
def _numtob58(num):
ret = []
while num:
ret.append(b58a[num % 58])
num /= 58
return ''.join(ret)[::-1]
def _b58tonum(b58num):
b58num = b58num.strip()
for i in b58num:
if not i in b58a: return False
return sum(b58a.index(n) * (58**i) for i,n in enumerate(list(b58num[::-1])))
# The following are MMGen internal (non-Bitcoin) b58 functions
# Drop-in replacements for b64encode() and b64decode():
# (well, not exactly: they yield numeric but not bytewise equivalence)
def b58encode(s):
if s == '': return ''
num = int(hexlify(s),16)
return _numtob58(num)
def b58decode(b58num):
b58num = b58num.strip()
if b58num == '': return ''
# Zap all spaces:
# Use translate() only with str, not unicode
num = _b58tonum(str(b58num).translate(None,' \t\n\r'))
if num == False: return False
out = u'{:x}'.format(num)
return unhexlify(u'0'*(len(out)%2) + out)
# These yield bytewise equivalence in our special cases:
bin_lens = 16,24,32
b58_lens = 22,33,44
def _b58_pad(s,a,b,pad,f,w):
try:
outlen = b[a.index(len(s))]
except:
Msg('_b58_pad() accepts only %s %s bytes long '\
'(input was %s bytes)' % (w,','.join([str(i) for i in a]),len(s)))
return False
out = f(s)
if out == False: return False
return '%s%s' % (pad * (outlen - len(out)), out)
def b58encode_pad(s):
return _b58_pad(s,
a=bin_lens,b=b58_lens,pad='1',f=b58encode,w='binary strings')
def b58decode_pad(s):
return _b58_pad(s,
a=b58_lens,b=bin_lens,pad='\0',f=b58decode,w='base 58 numbers')
# Compressed address support:
def wif_is_compressed(wif): return wif[0] != ('5','9')[g.testnet]

View file

@ -24,7 +24,6 @@ import os
from binascii import hexlify,unhexlify
from mmgen.common import *
from mmgen.bitcoin import b58encode_pad,b58decode_pad,b58_lens
from mmgen.obj import *
from mmgen.filename import *
from mmgen.crypto import *
@ -478,7 +477,7 @@ class SeedFile (SeedSourceUnenc):
ext = 'mmseed'
def _format(self):
b58seed = b58encode_pad(self.seed.data)
b58seed = baseconv.b58encode(self.seed.data,pad=True)
self.ssdata.chksum = make_chksum_6(b58seed)
self.ssdata.b58seed = b58seed
self.fmt_data = '%s %s\n' % (
@ -509,7 +508,7 @@ class SeedFile (SeedSourceUnenc):
if not compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True):
return False
ret = b58decode_pad(b)
ret = baseconv.b58decode(b,pad=True)
if ret == False:
msg('Invalid base-58 encoded seed: %s' % val)
@ -626,8 +625,8 @@ class Wallet (SeedSourceEnc):
def _format(self):
d = self.ssdata
s = self.seed
slt_fmt = b58encode_pad(d.salt)
es_fmt = b58encode_pad(d.enc_seed)
slt_fmt = baseconv.b58encode(d.salt,pad=True)
es_fmt = baseconv.b58encode(d.enc_seed,pad=True)
lines = (
d.label,
'{} {} {} {} {}'.format(s.sid.lower(), d.key_id.lower(),
@ -688,7 +687,7 @@ class Wallet (SeedSourceEnc):
(' '.join(hash_params), d.hash_preset))
return False
lmin,lmax = b58_lens[0],b58_lens[-1] # 22,33,44
lmin,foo,lmax = [v for k,v in baseconv.b58pad_lens] # 22,33,44
for i,key in (4,'salt'),(5,'enc_seed'):
l = lines[i].split(' ')
chk = l.pop(0)
@ -702,7 +701,7 @@ class Wallet (SeedSourceEnc):
make_chksum_6(b58_val),'computed checksum',verbose=True):
return False
val = b58decode_pad(b58_val)
val = baseconv.b58decode(b58_val,pad=True)
if val == False:
msg('Invalid base 58 number: %s' % b58_val)
return False

View file

@ -322,8 +322,8 @@ def Unhexdump(infile):
def B58randenc():
r = get_random(32)
enc = mmb.b58encode(r)
dec = mmb.b58decode(enc)
enc = baseconv.b58encode(r,pad=True)
dec = baseconv.b58decode(enc,pad=True)
print_convert_results(r,enc,dec,'str')
def Randhex(nbytes='32'):

View file

@ -406,7 +406,6 @@ class MMGenTX(MMGenObject):
self.blockcount = int(c.getblockcount())
def format(self):
from mmgen.bitcoin import b58encode
lines = [
'{} {} {} {} {}'.format(
self.chain.upper() if self.chain else 'Unknown',
@ -420,7 +419,7 @@ class MMGenTX(MMGenObject):
repr([e.__dict__ for e in self.outputs])
]
if self.label:
lines.append(b58encode(self.label.encode('utf8')))
lines.append(baseconv.b58encode(self.label.encode('utf8')))
if self.btc_txid:
if not self.label: lines.append('-') # keep old tx files backwards compatible
lines.append(self.btc_txid)
@ -761,8 +760,7 @@ class MMGenTX(MMGenObject):
if len(tx_data) == 5:
c = tx_data.pop(-1)
if c != '-':
from mmgen.bitcoin import b58decode
comment = b58decode(c)
comment = baseconv.b58decode(c).decode('utf8')
if comment == False:
do_err('encoded comment (not base58)')
else:
@ -770,7 +768,7 @@ class MMGenTX(MMGenObject):
if not self.label:
do_err('comment')
else:
comment = ''
comment = u''
if len(tx_data) == 4:
metadata,self.hex,inputs_data,outputs_data = tx_data

View file

@ -253,6 +253,30 @@ class baseconv(object):
'tirosh': '48f05e1f', # tirosh truncated to mn_base (1626)
# 'tirosh1633': '1a5faeff'
}
b58pad_lens = [(16,22), (24,33), (32,44)]
b58pad_lens_rev = [(v,k) for k,v in b58pad_lens]
@classmethod
def b58encode(cls,s,pad=None):
pad = cls.get_pad(s,pad,'en',cls.b58pad_lens,[bytes])
return ''.join(cls.fromhex(hexlify(s),'b58',pad=pad))
@classmethod
def b58decode(cls,s,pad=None):
pad = cls.get_pad(s,pad,'de',cls.b58pad_lens_rev,[bytes,unicode])
return unhexlify(cls.tohex(s,'b58',pad=pad*2 if pad else None))
@staticmethod
def get_pad(s,pad,op,pad_map,ok_types):
m = "b58{}code() input must be one of {}, not '{}'"
assert type(s) in ok_types, m.format(op,repr([t.__name__ for t in ok_types]),type(s).__name__)
if pad:
assert type(pad) == bool, "'pad' must be boolean type"
d = dict(pad_map)
assert len(s) in d, 'Invalid data length for b58{}code(pad=True)'.format(op)
return d[len(s)]
else:
return None
@classmethod
def get_wordlist_chksum(cls,wl_id):