From 33673cbb1b9d878e5726d06407916717154a5329 Mon Sep 17 00:00:00 2001 From: philemon Date: Tue, 8 Aug 2017 16:46:39 +0300 Subject: [PATCH] reimplement base58 routines, move from bitcoin module to class baseconv --- mmgen/bitcoin.py | 85 ++++++++++-------------------------------------- mmgen/seed.py | 13 ++++---- mmgen/tool.py | 4 +-- mmgen/tx.py | 8 ++--- mmgen/util.py | 24 ++++++++++++++ 5 files changed, 52 insertions(+), 82 deletions(-) diff --git a/mmgen/bitcoin.py b/mmgen/bitcoin.py index 65235461..e05f6ef7 100755 --- a/mmgen/bitcoin.py +++ b/mmgen/bitcoin.py @@ -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] diff --git a/mmgen/seed.py b/mmgen/seed.py index f0adb512..375d90c9 100755 --- a/mmgen/seed.py +++ b/mmgen/seed.py @@ -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 diff --git a/mmgen/tool.py b/mmgen/tool.py index e3f0fa79..ad65f13c 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -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'): diff --git a/mmgen/tx.py b/mmgen/tx.py index 36c1271d..48296add 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -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 diff --git a/mmgen/util.py b/mmgen/util.py index 7167eba5..49e6f9d9 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -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):