Browse Source

baseconv: minor cleanups & fixes (import, init_mn)

The MMGen Project 5 years ago
parent
commit
f1e7cbb7a0
6 changed files with 38 additions and 28 deletions
  1. 27 18
      mmgen/baseconv.py
  2. 4 1
      mmgen/bip39.py
  3. 2 1
      mmgen/protocol.py
  4. 3 5
      mmgen/seed.py
  5. 2 1
      mmgen/tx.py
  6. 0 2
      mmgen/util.py

+ 27 - 18
mmgen/baseconv.py

@@ -29,50 +29,53 @@ def is_b32_str(s): return set(list(s)) <= set(baseconv.digits['b32'])
 class baseconv(object):
 
 	desc = {
-		'b58':   ('base58',               'base58-encoded data'),
-		'b32':   ('MMGen base32',         'MMGen base32-encoded data created using simple base conversion'),
-		'b16':   ('hexadecimal string',   'base16 (hexadecimal) string data'),
-		'b10':   ('base10 string',        'base10 (decimal) string data'),
-		'b8':    ('base8 string',         'base8 (octal) string data'),
-		'b6d':   ('base6d (die roll)',    'base6 data using the digits from one to six'),
+		'b58':   ('base58',            'base58-encoded data'),
+		'b32':   ('MMGen base32',      'MMGen base32-encoded data created using simple base conversion'),
+		'b16':   ('hexadecimal string','base16 (hexadecimal) string data'),
+		'b10':   ('base10 string',     'base10 (decimal) string data'),
+		'b8':    ('base8 string',      'base8 (octal) string data'),
+		'b6d':   ('base6d (die roll)', 'base6 data using the digits from one to six'),
+		'tirosh':('Tirosh mnemonic',   'base1626 mnemonic using truncated Tirosh wordlist'), # not used by wallet
 		'mmgen': ('MMGen native mnemonic',
 		'MMGen native mnemonic seed phrase data created using old Electrum wordlist and simple base conversion'),
 	}
+	# https://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet
+	# https://tools.ietf.org/html/rfc4648
 	digits = {
 		'b58': tuple('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'),
-		'b32': tuple('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'),
+		'b32': tuple('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), # RFC 4648 alphabet
 		'b16': tuple('0123456789abcdef'),
 		'b10': tuple('0123456789'),
 		'b8':  tuple('01234567'),
 		'b6d': tuple('123456'),
 	}
 	mn_base = 1626 # tirosh list is 1633 words long!
-	mn_ids = ('mmgen','tirosh')
 	wl_chksums = {
 		'mmgen':  '5ca31424',
 		'tirosh': '48f05e1f', # tirosh truncated to mn_base (1626)
 		# 'tirosh1633': '1a5faeff'
 	}
-	seed_pad_lens = {
+	seedlen_map = {
 		'b58': { 16:22, 24:33, 32:44 },
 		'b6d': { 16:50, 24:75, 32:100 },
 	}
-	seed_pad_lens_rev = {
+	seedlen_map_rev = {
 		'b58': { 22:16, 33:24, 44:32 },
 		'b6d': { 50:16, 75:24, 100:32 },
 	}
 
 	@classmethod
 	def init_mn(cls,mn_id):
-		assert mn_id in cls.mn_ids
+		if mn_id in cls.digits:
+			return
 		if mn_id == 'mmgen':
 			from mmgen.mn_electrum import words
 			cls.digits[mn_id] = words
 		elif mn_id == 'tirosh':
 			from mmgen.mn_tirosh import words
 			cls.digits[mn_id] = words[:cls.mn_base]
-		else: # bip39
-			cls.digits[mn_id] = cls.words
+		else:
+			raise ValueError('{}: unrecognized mnemonic ID'.format(mn_id))
 
 	@classmethod
 	def get_wordlist(cls,wl_id):
@@ -109,7 +112,7 @@ class baseconv(object):
 		"""
 		'pad' argument to baseconv conversion methods must be either None, 'seed' or an integer.
 		If None, output of minimum (but never zero) length will be produced.
-		If 'seed', output length will be mapped from input length using data in seed_pad_lens.
+		If 'seed', output length will be mapped from input length using data in seedlen_map.
 		If an integer, the string, hex string or byte output will be padded to this length.
 		"""
 		if pad == None:
@@ -131,6 +134,9 @@ class baseconv(object):
 	def tobytes(cls,words_arg,wl_id,pad=None):
 		"convert string or list data of base 'wl_id' to byte string"
 
+		if wl_id not in cls.digits:
+			cls.init_mn(wl_id)
+
 		words = words_arg if isinstance(words_arg,(list,tuple)) else tuple(words_arg.strip())
 		desc = cls.desc[wl_id][0]
 
@@ -138,8 +144,8 @@ class baseconv(object):
 			raise BaseConversionError('empty {} data'.format(desc))
 
 		def get_seed_pad():
-			assert wl_id in cls.seed_pad_lens_rev,'seed padding not supported for base {!r}'.format(wl_id)
-			d = cls.seed_pad_lens_rev[wl_id]
+			assert wl_id in cls.seedlen_map_rev,'seed padding not supported for base {!r}'.format(wl_id)
+			d = cls.seedlen_map_rev[wl_id]
 			if not len(words) in d:
 				m = '{}: invalid length for seed-padded {} data in base conversion'
 				raise BaseConversionError(m.format(len(words),desc))
@@ -172,12 +178,15 @@ class baseconv(object):
 	def frombytes(cls,bytestr,wl_id,pad=None,tostr=False):
 		"convert byte string to list or string data of base 'wl_id'"
 
+		if wl_id not in cls.digits:
+			cls.init_mn(wl_id)
+
 		if not bytestr:
 			raise BaseConversionError('empty data not allowed in base conversion')
 
 		def get_seed_pad():
-			assert wl_id in cls.seed_pad_lens,'seed padding not supported for base {!r}'.format(wl_id)
-			d = cls.seed_pad_lens[wl_id]
+			assert wl_id in cls.seedlen_map,'seed padding not supported for base {!r}'.format(wl_id)
+			d = cls.seedlen_map[wl_id]
 			if not len(bytestr) in d:
 				m = '{}: invalid byte length for seed data in seed-padded base conversion'
 				raise SeedLengthError(m.format(len(bytestr)))

+ 4 - 1
mmgen/bip39.py

@@ -2081,7 +2081,6 @@ zoo
 """.split())
 
 	mn_base = 2048
-	mn_ids = ('bip39',)
 	wl_chksums = { 'bip39': 'f18b9a84' }
 	#    ENT   CS  MS
 	constants = {
@@ -2170,3 +2169,7 @@ zoo
 		res = seed_bin + chk_bin
 
 		return tuple(wl[int(res[i*11:(i+1)*11],2)] for i in range(mn_len))
+
+	@classmethod
+	def init_mn(cls,mn_id):
+		assert mn_id == 'bip39', "'mn_id' must be 'bip39'"

+ 2 - 1
mmgen/protocol.py

@@ -25,7 +25,6 @@ from mmgen.util import msg,ymsg,Msg,ydie
 from mmgen.obj import MMGenObject,BTCAmt,LTCAmt,BCHAmt,B2XAmt,ETHAmt
 from mmgen.globalvars import g
 import mmgen.bech32 as bech32
-from mmgen.baseconv import baseconv,is_b58_str
 
 def hash160(hexnum): # take hex, return hex - OP_HASH160
 	return hashlib.new('ripemd160',hashlib.sha256(bytes.fromhex(hexnum)).digest()).hexdigest()
@@ -407,6 +406,8 @@ class MoneroProtocol(DummyWIF,BitcoinProtocolAddrgen):
 	@classmethod
 	def verify_addr(cls,addr,hex_width,return_dict=False):
 
+		from mmgen.baseconv import baseconv,is_b58_str
+
 		def b58dec(addr_str):
 			l = len(addr_str)
 			a = ''.join([baseconv.tohex(addr_str[i*11:i*11+11],'b58',pad=16) for i in range(l//11)])

+ 3 - 5
mmgen/seed.py

@@ -825,10 +825,6 @@ class MMGenMnemonic(SeedSourceUnenc):
 	wl_id = 'mmgen'
 	conv_cls = baseconv
 
-	def __init__(self,*args,**kwargs):
-		self.conv_cls.init_mn(self.wl_id)
-		super().__init__(*args,**kwargs)
-
 	def _get_data_from_user(self,desc):
 
 		if not g.stdin_tty:
@@ -853,6 +849,7 @@ class MMGenMnemonic(SeedSourceUnenc):
 			if keypress_confirm(prompt,default_yes=True,no_nl=not g.test_suite):
 				break
 
+		self.conv_cls.init_mn(self.wl_id)
 		wl = self.conv_cls.digits[self.wl_id]
 		longest_word = max(len(w) for w in wl)
 		from string import ascii_lowercase
@@ -923,6 +920,7 @@ class MMGenMnemonic(SeedSourceUnenc):
 
 	def _deformat(self):
 
+		self.conv_cls.init_mn(self.wl_id)
 		mn = self.fmt_data.split()
 
 		if len(mn) not in self.mn_lens:
@@ -1227,7 +1225,7 @@ class Wallet (SeedSourceEnc):
 			msg("Hash parameters '{}' don't match hash preset '{}'".format(' '.join(hash_params),d.hash_preset))
 			return False
 
-		lmin,foo,lmax = sorted(baseconv.seed_pad_lens_rev['b58']) # 22,33,44
+		lmin,foo,lmax = sorted(baseconv.seedlen_map_rev['b58']) # 22,33,44
 		for i,key in (4,'salt'),(5,'enc_seed'):
 			l = lines[i].split(' ')
 			chk = l.pop(0)

+ 2 - 1
mmgen/tx.py

@@ -24,7 +24,6 @@ import sys,os,json
 from stat import *
 from mmgen.common import *
 from mmgen.obj import *
-from mmgen.baseconv import *
 
 wmsg = lambda k: {
 	'addr_in_addrfile_only': """
@@ -699,6 +698,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
 			repr([amt_to_str(e.__dict__) for e in self.outputs])
 		]
 		if self.label:
+			from mmgen.baseconv import baseconv
 			lines.append(baseconv.frombytes(self.label.encode(),'b58',tostr=True))
 		if self.coin_txid:
 			if not self.label: lines.append('-') # keep old tx files backwards compatible
@@ -1215,6 +1215,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
 				c = tx_data.pop(-1)
 				if c != '-':
 					desc = 'encoded comment (not base58)'
+					from mmgen.baseconv import baseconv
 					comment = baseconv.tobytes(c,'b58').decode()
 					assert comment != False,'invalid comment'
 					desc = 'comment'

+ 0 - 2
mmgen/util.py

@@ -264,8 +264,6 @@ def is_int(s):
 	except:
 		return False
 
-# https://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet
-# https://tools.ietf.org/html/rfc4648
 def is_hex_str(s):    return set(list(s.lower())) <= set(list(hexdigits.lower()))
 def is_hex_str_lc(s): return set(list(s))         <= set(list(hexdigits.lower()))
 def is_hex_str_uc(s): return set(list(s))         <= set(list(hexdigits.upper()))