From f1e7cbb7a0380783fd0e9da0f2fab7a0942d199a Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 29 Oct 2019 13:40:07 +0000 Subject: [PATCH] baseconv: minor cleanups & fixes (import, init_mn) --- mmgen/baseconv.py | 45 +++++++++++++++++++++++++++------------------ mmgen/bip39.py | 5 ++++- mmgen/protocol.py | 3 ++- mmgen/seed.py | 8 +++----- mmgen/tx.py | 3 ++- mmgen/util.py | 2 -- 6 files changed, 38 insertions(+), 28 deletions(-) diff --git a/mmgen/baseconv.py b/mmgen/baseconv.py index d78ea650..a7dbb740 100755 --- a/mmgen/baseconv.py +++ b/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))) diff --git a/mmgen/bip39.py b/mmgen/bip39.py index 321c8da5..ec973d91 100755 --- a/mmgen/bip39.py +++ b/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'" diff --git a/mmgen/protocol.py b/mmgen/protocol.py index 242e505c..2ea7889c 100755 --- a/mmgen/protocol.py +++ b/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)]) diff --git a/mmgen/seed.py b/mmgen/seed.py index b52d0a4d..a08e82b2 100755 --- a/mmgen/seed.py +++ b/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) diff --git a/mmgen/tx.py b/mmgen/tx.py index c4952022..6dfca918 100755 --- a/mmgen/tx.py +++ b/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' diff --git a/mmgen/util.py b/mmgen/util.py index a1f813bb..c974b8f9 100755 --- a/mmgen/util.py +++ b/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()))