baseconv.py: convert class methods to instance methods

This commit is contained in:
The MMGen Project 2022-01-18 09:10:58 +00:00
commit 7b890a2c95
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
13 changed files with 170 additions and 188 deletions

View file

@ -97,10 +97,10 @@ class addr_generator:
def b58enc(self,addr_bytes):
from .baseconv import baseconv
enc = baseconv.frombytes
enc = baseconv('b58').frombytes
l = len(addr_bytes)
a = ''.join([enc( addr_bytes[i*8:i*8+8], 'b58', pad=11, tostr=True ) for i in range(l//8)])
b = enc( addr_bytes[l-l%8:], 'b58', pad=7, tostr=True )
a = ''.join([enc( addr_bytes[i*8:i*8+8], pad=11, tostr=True ) for i in range(l//8)])
b = enc( addr_bytes[l-l%8:], pad=7, tostr=True )
return a + b
@check_data

View file

@ -31,7 +31,7 @@ def is_b32_str(s):
return set(list(s)) <= set(baseconv.digits['b32'])
def is_xmrseed(s):
return bool(baseconv.tobytes(s.split(),wl_id='xmrseed'))
return bool( baseconv('xmrseed').tobytes(s.split()) )
class baseconv(object):
@ -77,58 +77,49 @@ class baseconv(object):
'xmrseed': { 25:32 },
}
@classmethod
def init_mn(cls,mn_id):
if mn_id in cls.digits:
return
if mn_id == 'mmgen':
def __init__(self,wl_id):
if wl_id == 'mmgen':
from .mn_electrum import words
cls.digits[mn_id] = words
elif mn_id == 'xmrseed':
self.digits[wl_id] = words
elif wl_id == 'xmrseed':
from .mn_monero import words
cls.digits[mn_id] = words
elif mn_id == 'tirosh':
self.digits[wl_id] = words
elif wl_id == 'tirosh':
from .mn_tirosh import words
cls.digits[mn_id] = words[:cls.mn_base]
else:
raise ValueError(f'{mn_id}: unrecognized mnemonic ID')
self.digits[wl_id] = words[:self.mn_base]
elif wl_id not in self.digits:
raise ValueError(f'{wl_id}: unrecognized mnemonic ID')
@classmethod
def get_wordlist(cls,wl_id):
cls.init_mn(wl_id)
return cls.digits[wl_id]
self.wl_id = wl_id
@classmethod
def get_wordlist_chksum(cls,wl_id):
cls.init_mn(wl_id)
return sha256(' '.join(cls.digits[wl_id]).encode()).hexdigest()[:8]
def get_wordlist(self):
return self.digits[self.wl_id]
@classmethod
def check_wordlists(cls):
for k,v in list(cls.wl_chksums.items()):
res = cls.get_wordlist_chksum(k)
assert res == v,f'{res}: checksum mismatch for {k} (should be {v})'
return True
def get_wordlist_chksum(self):
return sha256(' '.join(self.digits[self.wl_id]).encode()).hexdigest()[:8]
@classmethod
def check_wordlist(cls,wl_id):
cls.init_mn(wl_id)
def check_wordlist(self):
wl = cls.digits[wl_id]
wl = self.digits[self.wl_id]
from .util import qmsg,compare_chksums
ret = f'Wordlist: {wl_id}\nLength: {len(wl)} words'
new_chksum = cls.get_wordlist_chksum(wl_id)
ret = f'Wordlist: {self.wl_id}\nLength: {len(wl)} words'
new_chksum = self.get_wordlist_chksum()
a,b = 'generated','saved'
compare_chksums(new_chksum,a,cls.wl_chksums[wl_id],b,die_on_fail=True)
compare_chksums(
new_chksum,
'generated',
self.wl_chksums[self.wl_id],
'saved',
die_on_fail = True )
if tuple(sorted(wl)) == wl:
return ret + '\nList is sorted'
else:
die(3,'ERROR: List is not sorted!')
@classmethod
def get_pad(cls,pad,seed_pad_func):
@staticmethod
def get_pad(pad,seed_pad_func):
"""
'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.
@ -150,34 +141,29 @@ class baseconv(object):
wstr = ''.join(word[:3] for word in words)
return words[crc32(wstr.encode()) % len(words)]
@classmethod
def tohex(cls,words_arg,wl_id,pad=None):
"convert string or list data of base 'wl_id' to hex string"
return cls.tobytes(words_arg,wl_id,pad//2 if type(pad)==int else pad).hex()
def tohex(self,words_arg,pad=None):
"convert string or list data of instance base to hex string"
return self.tobytes(words_arg,pad//2 if type(pad)==int else pad).hex()
@classmethod
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)
def tobytes(self,words_arg,pad=None):
"convert string or list data of instance base to byte string"
words = words_arg if isinstance(words_arg,(list,tuple)) else tuple(words_arg.strip())
desc = cls.desc[wl_id][0]
desc = self.desc[self.wl_id][0]
if len(words) == 0:
raise BaseConversionError(f'empty {desc} data')
def get_seed_pad():
assert wl_id in cls.seedlen_map_rev,f'seed padding not supported for base {wl_id!r}'
d = cls.seedlen_map_rev[wl_id]
assert self.wl_id in self.seedlen_map_rev, f'seed padding not supported for base {self.wl_id!r}'
d = self.seedlen_map_rev[self.wl_id]
if not len(words) in d:
raise BaseConversionError(
f'{len(words)}: invalid length for seed-padded {desc} data in base conversion' )
return d[len(words)]
pad_val = max(cls.get_pad(pad,get_seed_pad),1)
wl = cls.digits[wl_id]
pad_val = max(self.get_pad(pad,get_seed_pad),1)
wl = self.digits[self.wl_id]
base = len(wl)
if not set(words) <= set(wl):
@ -185,11 +171,11 @@ class baseconv(object):
( 'seed data' if pad == 'seed' else f'{words_arg!r}:' ) +
f' not in {desc} format' )
if wl_id == 'xmrseed':
if len(words) not in cls.seedlen_map_rev['xmrseed']:
if self.wl_id == 'xmrseed':
if len(words) not in self.seedlen_map_rev['xmrseed']:
die(2,f'{len(words)}: invalid length for Monero mnemonic')
z = cls.monero_mn_checksum(words[:-1])
z = self.monero_mn_checksum(words[:-1])
assert z == words[-1],'invalid Monero mnemonic checksum'
words = tuple(words[:-1])
@ -204,9 +190,8 @@ class baseconv(object):
bl = ret.bit_length()
return ret.to_bytes(max(pad_val,bl//8+bool(bl%8)),'big')
@classmethod
def fromhex(cls,hexstr,wl_id,pad=None,tostr=False):
"convert hex string to list or string data of base 'wl_id'"
def fromhex(self,hexstr,pad=None,tostr=False):
"convert hex string to list or string data of instance base"
from .util import is_hex_str
if not is_hex_str(hexstr):
@ -214,32 +199,28 @@ class baseconv(object):
( 'seed data' if pad == 'seed' else f'{hexstr!r}:' ) +
' not a hexadecimal string' )
return cls.frombytes(bytes.fromhex(hexstr),wl_id,pad,tostr)
return self.frombytes( bytes.fromhex(hexstr), pad, tostr )
@classmethod
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)
def frombytes(self,bytestr,pad=None,tostr=False):
"convert byte string to list or string data of instance base"
if not bytestr:
raise BaseConversionError('empty data not allowed in base conversion')
def get_seed_pad():
assert wl_id in cls.seedlen_map, f'seed padding not supported for base {wl_id!r}'
d = cls.seedlen_map[wl_id]
assert self.wl_id in self.seedlen_map, f'seed padding not supported for base {self.wl_id!r}'
d = self.seedlen_map[self.wl_id]
if not len(bytestr) in d:
raise SeedLengthError(
f'{len(bytestr)}: invalid byte length for seed data in seed-padded base conversion' )
return d[len(bytestr)]
pad = max(cls.get_pad(pad,get_seed_pad),1)
wl = cls.digits[wl_id]
pad = max(self.get_pad(pad,get_seed_pad),1)
wl = self.digits[self.wl_id]
base = len(wl)
if wl_id == 'xmrseed':
if len(bytestr) not in cls.seedlen_map['xmrseed']:
if self.wl_id == 'xmrseed':
if len(bytestr) not in self.seedlen_map['xmrseed']:
die(2, f'{len(bytestr)}: invalid seed byte length for Monero mnemonic')
def num2base_monero(num):
@ -251,7 +232,7 @@ class baseconv(object):
o = []
for i in range(len(bytestr)//4):
o += num2base_monero(int.from_bytes(bytestr[i*4:i*4+4][::-1],'big'))
o.append(cls.monero_mn_checksum(o))
o.append(self.monero_mn_checksum(o))
else:
num = int.from_bytes(bytestr,'big')
ret = []
@ -260,4 +241,4 @@ class baseconv(object):
num //= base
o = [wl[n] for n in [0] * (pad-len(ret)) + ret[::-1]]
return (' ' if wl_id in ('mmgen','xmrseed') else '').join(o) if tostr else o
return (' ' if self.wl_id in ('mmgen','xmrseed') else '').join(o) if tostr else o

View file

@ -27,7 +27,7 @@ from .baseconv import baseconv
from .util import is_hex_str
def is_bip39_str(s):
return bool( bip39.tohex(s.split(), wl_id='bip39') )
return bool( bip39().tohex(s.split()) )
# implements a subset of the baseconv API
class bip39(baseconv):
@ -47,8 +47,12 @@ class bip39(baseconv):
224: bc(7, 21),
256: bc(8, 24),
}
from .mn_bip39 import words
digits = { 'bip39': words }
def __init__(self,wl_id='bip39'):
assert wl_id == 'bip39', "initialize with 'bip39' for compatibility with baseconv API"
from .mn_bip39 import words
self.digits = { 'bip39': words }
self.wl_id = 'bip39'
@classmethod
def nwords2seedlen(cls,nwords,in_bytes=False,in_hex=False):
@ -65,21 +69,17 @@ class bip39(baseconv):
except:
raise ValueError(f'{seed_bits!r}: invalid seed length for BIP39 mnemonic')
@classmethod
def tobytes(cls,*args,**kwargs):
def tobytes(self,*args,**kwargs):
raise NotImplementedError('Method not supported')
@classmethod
def frombytes(cls,*args,**kwargs):
def frombytes(self,*args,**kwargs):
raise NotImplementedError('Method not supported')
@classmethod
def tohex(cls,words,wl_id,pad=None):
def tohex(self,words,pad=None):
assert isinstance(words,(list,tuple)),'words must be list or tuple'
assert wl_id == 'bip39',"'wl_id' must be 'bip39'"
assert pad == None, f"{pad}: invalid 'pad' argument (must be None)"
wl = cls.digits[wl_id]
wl = self.digits['bip39']
for n,w in enumerate(words):
if w not in wl:
@ -87,7 +87,7 @@ class bip39(baseconv):
res = ''.join(['{:011b}'.format(wl.index(w)) for w in words])
for k,v in cls.constants.items():
for k,v in self.constants.items():
if len(words) == v.mn_len:
bitlen = k
break
@ -100,7 +100,7 @@ class bip39(baseconv):
seed_hex = '{:0{w}x}'.format(int(seed_bin,2),w=bitlen//4)
seed_bytes = bytes.fromhex(seed_hex)
chk_len = cls.constants[bitlen].chk_len
chk_len = self.constants[bitlen].chk_len
chk_hex_chk = sha256(seed_bytes).hexdigest()
chk_bin_chk = '{:0{w}b}'.format(int(chk_hex_chk,16),w=256)[:chk_len]
@ -109,19 +109,17 @@ class bip39(baseconv):
return seed_hex
@classmethod
def fromhex(cls,seed_hex,wl_id,pad=None,tostr=False):
def fromhex(self,seed_hex,pad=None,tostr=False):
assert is_hex_str(seed_hex),'seed data not a hexadecimal string'
assert wl_id == 'bip39',"'wl_id' must be 'bip39'"
assert tostr == False,"'tostr' must be False for 'bip39'"
assert pad == None, f"{pad}: invalid 'pad' argument (must be None)"
wl = cls.digits[wl_id]
wl = self.digits['bip39']
seed_bytes = bytes.fromhex(seed_hex)
bitlen = len(seed_bytes) * 8
assert bitlen in cls.constants, f'{bitlen}: invalid seed bit length'
c = cls.constants[bitlen]
assert bitlen in self.constants, f'{bitlen}: invalid seed bit length'
c = self.constants[bitlen]
chk_hex = sha256(seed_bytes).hexdigest()
@ -131,7 +129,3 @@ class bip39(baseconv):
res = seed_bin + chk_bin
return tuple(wl[int(res[i*11:(i+1)*11],2)] for i in range(c.mn_len))
@classmethod
def init_mn(cls,mn_id):
assert mn_id == 'bip39', "'mn_id' must be 'bip39'"

View file

@ -219,9 +219,8 @@ def mn_entry(wl_id,entry_mode=None):
wl_id = 'mmgen'
me = MnemonicEntry.get_cls_by_wordlist(wl_id)
import importlib
me.conv_cls = getattr(importlib.import_module(f'mmgen.{me.modname}'),me.modname)
me.conv_cls.init_mn(wl_id)
me.wl = me.conv_cls.digits[wl_id]
me.bconv = getattr(importlib.import_module(f'mmgen.{me.modname}'),me.modname)(wl_id)
me.wl = me.bconv.digits[wl_id]
obj = me()
if entry_mode:
obj.em = globals()['MnEntryMode'+capfirst(entry_mode)](obj)
@ -335,7 +334,7 @@ class MnemonicEntry(object):
msg_r(erase)
def get_mnemonic_from_user(self,mn_len,validate=True):
mll = list(self.conv_cls.seedlen_map_rev[self.wl_id])
mll = list(self.bconv.seedlen_map_rev[self.wl_id])
assert mn_len in mll, f'{mn_len}: invalid mnemonic length (must be one of {mll})'
if self.usr_dfl_entry_mode:
@ -375,7 +374,7 @@ class MnemonicEntry(object):
words = [self.wl[i] for i in idxs]
if validate:
self.conv_cls.tohex(words,self.wl_id)
self.bconv.tohex(words)
if self.has_chksum:
qmsg('Mnemonic is valid')
else:

View file

@ -166,7 +166,7 @@ class PasswordList(AddrList):
elif pf in ('b32','b58'):
pw_int = (32 if pf == 'b32' else 58) ** self.pw_len
pw_bytes = pw_int.bit_length() // 8
good_pw_len = len(baseconv.frombytes(b'\xff'*seed.byte_len,wl_id=pf))
good_pw_len = len( baseconv(pf).frombytes(b'\xff'*seed.byte_len) )
else:
raise NotImplementedError(f'{pf!r}: unknown password format')
@ -194,19 +194,18 @@ class PasswordList(AddrList):
from .bip39 import bip39
pw_len_bytes = bip39.nwords2seedlen( self.pw_len, in_bytes=True )
# take most significant part
return ' '.join( bip39.fromhex( secbytes[:pw_len_bytes].hex(), wl_id='bip39' ) )
return ' '.join( bip39().fromhex(secbytes[:pw_len_bytes].hex()) )
elif self.pw_fmt == 'xmrseed':
pw_len_bytes = baseconv.seedlen_map_rev['xmrseed'][self.pw_len]
from .protocol import init_proto
bytes_preproc = init_proto('xmr').preprocess_key(
secbytes[:pw_len_bytes], # take most significant part
None )
return ' '.join( baseconv.frombytes( bytes_preproc, wl_id='xmrseed' ) )
return ' '.join( baseconv('xmrseed').frombytes(bytes_preproc) )
else:
# take least significant part
return baseconv.frombytes(
return baseconv(self.pw_fmt).frombytes(
secbytes,
self.pw_fmt,
pad = self.pw_len,
tostr = True )[-self.pw_len:]

View file

@ -512,9 +512,10 @@ class CoinProtocol(MMGenObject):
from .baseconv import baseconv,is_b58_str
def b58dec(addr_str):
bc = baseconv('b58')
l = len(addr_str)
a = b''.join([baseconv.tobytes(addr_str[i*11:i*11+11],'b58',pad=8) for i in range(l//11)])
b = baseconv.tobytes(addr_str[-(l%11):],'b58',pad=5)
a = b''.join([bc.tobytes( addr_str[i*11:i*11+11], pad=8 ) for i in range(l//11)])
b = bc.tobytes( addr_str[-(l%11):], pad=5 )
return a + b
ret = b58dec(addr)

View file

@ -390,24 +390,24 @@ class MMGenToolCmdUtil(MMGenToolCmds):
def randb58(self,nbytes=32,pad=0):
"generate random data (default: 32 bytes) and convert it to base 58"
return baseconv.frombytes(get_random(nbytes),'b58',pad=pad,tostr=True)
return baseconv('b58').frombytes(get_random(nbytes),pad=pad,tostr=True)
def bytestob58(self,infile:str,pad=0):
"convert bytes to base 58 (supply data via STDIN)"
data = get_data_from_file(infile,dash=True,quiet=True,binary=True)
return baseconv.frombytes(data,'b58',pad=pad,tostr=True)
return baseconv('b58').frombytes(data,pad=pad,tostr=True)
def b58tobytes(self,b58num:'sstr',pad=0):
"convert a base 58 number to bytes (warning: outputs binary data)"
return baseconv.tobytes(b58num,'b58',pad=pad)
return baseconv('b58').tobytes(b58num,pad=pad)
def hextob58(self,hexstr:'sstr',pad=0):
"convert a hexadecimal number to base 58"
return baseconv.fromhex(hexstr,'b58',pad=pad,tostr=True)
return baseconv('b58').fromhex(hexstr,pad=pad,tostr=True)
def b58tohex(self,b58num:'sstr',pad=0):
"convert a base 58 number to hexadecimal"
return baseconv.tohex(b58num,'b58',pad=pad)
return baseconv('b58').tohex(b58num,pad=pad)
def hextob58chk(self,hexstr:'sstr'):
"convert a hexadecimal number to base58-check encoding"
@ -421,20 +421,20 @@ class MMGenToolCmdUtil(MMGenToolCmds):
def hextob32(self,hexstr:'sstr',pad=0):
"convert a hexadecimal number to MMGen's flavor of base 32"
return baseconv.fromhex(hexstr,'b32',pad,tostr=True)
return baseconv('b32').fromhex(hexstr,pad,tostr=True)
def b32tohex(self,b32num:'sstr',pad=0):
"convert an MMGen-flavor base 32 number to hexadecimal"
return baseconv.tohex(b32num.upper(),'b32',pad)
return baseconv('b32').tohex(b32num.upper(),pad)
def hextob6d(self,hexstr:'sstr',pad=0,add_spaces=True):
"convert a hexadecimal number to die roll base6 (base6d)"
ret = baseconv.fromhex(hexstr,'b6d',pad,tostr=True)
ret = baseconv('b6d').fromhex(hexstr,pad,tostr=True)
return block_format(ret,gw=5,cols=None).strip() if add_spaces else ret
def b6dtohex(self,b6d_num:'sstr',pad=0):
"convert a die roll base6 (base6d) number to hexadecimal"
return baseconv.tohex(remove_whitespace(b6d_num),'b6d',pad)
return baseconv('b6d').tohex(remove_whitespace(b6d_num),pad)
class MMGenToolCmdCoin(MMGenToolCmds):
"""
@ -625,20 +625,20 @@ class MMGenToolCmdMnemonic(MMGenToolCmds):
"convert a 16, 24 or 32-byte hexadecimal number to a mnemonic seed phrase"
if fmt == 'bip39':
from .bip39 import bip39
return ' '.join(bip39.fromhex(hexstr,fmt))
return ' '.join(bip39(fmt).fromhex(hexstr))
else:
bytestr = bytes.fromhex(hexstr)
if fmt == 'xmrseed':
bytestr = self._xmr_reduce(bytestr)
return baseconv.frombytes(bytestr,fmt,'seed',tostr=True)
return baseconv(fmt).frombytes(bytestr,'seed',tostr=True)
def mn2hex( self, seed_mnemonic:'sstr', fmt:mn_opts_disp = dfl_mnemonic_fmt ):
"convert a mnemonic seed phrase to a hexadecimal number"
if fmt == 'bip39':
from .bip39 import bip39
return bip39.tohex(seed_mnemonic.split(),fmt)
return bip39(fmt).tohex(seed_mnemonic.split())
else:
return baseconv.tohex(seed_mnemonic.split(),fmt,'seed')
return baseconv(fmt).tohex(seed_mnemonic.split(),'seed')
def mn2hex_interactive( self, fmt:mn_opts_disp = dfl_mnemonic_fmt, mn_len=24, print_mn=False ):
"convert an interactively supplied mnemonic seed phrase to a hexadecimal number"
@ -651,12 +651,12 @@ class MMGenToolCmdMnemonic(MMGenToolCmds):
def mn_stats(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
"show stats for mnemonic wordlist"
conv_cls = mnemonic_fmts[fmt]['conv_cls']()
return conv_cls.check_wordlist(fmt)
return conv_cls(fmt).check_wordlist()
def mn_printlist( self, fmt:mn_opts_disp = dfl_mnemonic_fmt, enum=False, pager=False ):
"print mnemonic wordlist"
conv_cls = mnemonic_fmts[fmt]['conv_cls']()
ret = conv_cls.get_wordlist(fmt)
ret = conv_cls(fmt).get_wordlist()
if enum:
ret = [f'{n:>4} {e}' for n,e in enumerate(ret)]
return '\n'.join(ret)

View file

@ -81,7 +81,7 @@ class MMGenTxFile:
if c != '-':
desc = 'encoded comment (not base58)'
from .baseconv import baseconv
comment = baseconv.tobytes(c,'b58').decode()
comment = baseconv('b58').tobytes(c).decode()
assert comment != False,'invalid comment'
desc = 'comment'
tx.label = MMGenTxLabel(comment)
@ -177,7 +177,7 @@ class MMGenTxFile:
if tx.label:
from .baseconv import baseconv
lines.append(baseconv.frombytes(tx.label.encode(),'b58',tostr=True))
lines.append(baseconv('b58').frombytes(tx.label.encode(),tostr=True))
if tx.coin_txid:
if not tx.label:

View file

@ -425,8 +425,9 @@ class Mnemonic(WalletUnenc):
hexseed = self.seed.hexdata
mn = self.conv_cls.fromhex(hexseed,self.wl_id,self._hex2mn_pad(hexseed))
ret = self.conv_cls.tohex(mn,self.wl_id,self._mn2hex_pad(mn))
bc = self.conv_cls(self.wl_id)
mn = bc.fromhex( hexseed, self._hex2mn_pad(hexseed) )
ret = bc.tohex( mn, self._mn2hex_pad(mn) )
# Internal error, so just die on fail
compare_or_die(ret,'recomputed seed',hexseed,'original',e='Internal error')
@ -436,7 +437,7 @@ class Mnemonic(WalletUnenc):
def _deformat(self):
self.conv_cls.init_mn(self.wl_id)
bc = self.conv_cls(self.wl_id)
mn = self.fmt_data.split()
if len(mn) not in self.mn_lens:
@ -446,12 +447,12 @@ class Mnemonic(WalletUnenc):
return False
for n,w in enumerate(mn,1):
if w not in self.conv_cls.digits[self.wl_id]:
if w not in bc.digits[self.wl_id]:
msg(f'Invalid mnemonic: word #{n} is not in the {self.wl_id.upper()} wordlist')
return False
hexseed = self.conv_cls.tohex(mn,self.wl_id,self._mn2hex_pad(mn))
ret = self.conv_cls.fromhex(hexseed,self.wl_id,self._hex2mn_pad(hexseed))
hexseed = bc.tohex( mn, self._mn2hex_pad(mn) )
ret = bc.fromhex( hexseed, self._hex2mn_pad(hexseed) )
if len(hexseed) * 4 not in g.seed_lens:
msg('Invalid mnemonic (produces too large a number)')
@ -502,7 +503,7 @@ class MMGenSeedFile(WalletUnenc):
ext = 'mmseed'
def _format(self):
b58seed = baseconv.frombytes(self.seed.data,'b58',pad='seed',tostr=True)
b58seed = baseconv('b58').frombytes(self.seed.data,pad='seed',tostr=True)
self.ssdata.chksum = make_chksum_6(b58seed)
self.ssdata.b58seed = b58seed
self.fmt_data = '{} {}\n'.format(
@ -532,7 +533,7 @@ class MMGenSeedFile(WalletUnenc):
if not compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True):
return False
ret = baseconv.tobytes(b,'b58',pad='seed')
ret = baseconv('b58').tobytes(b,pad='seed')
if ret == False:
msg(f'Invalid base-58 encoded seed: {val}')
@ -562,7 +563,7 @@ class DieRollSeedFile(WalletUnenc):
interactive_input = False
def _format(self):
d = baseconv.frombytes(self.seed.data,'b6d',pad='seed',tostr=True) + '\n'
d = baseconv('b6d').frombytes(self.seed.data,pad='seed',tostr=True) + '\n'
self.fmt_data = block_format(d,gw=5,cols=5)
def _deformat(self):
@ -578,7 +579,7 @@ class DieRollSeedFile(WalletUnenc):
# truncate seed to correct length, discarding high bits
seed_len = rmap[len(d)]
seed_bytes = baseconv.tobytes(d,'b6d',pad='seed')[-seed_len:]
seed_bytes = baseconv('b6d').tobytes(d,pad='seed')[-seed_len:]
if self.interactive_input and opt.usr_randchars:
if keypress_confirm(self.user_entropy_prompt):
@ -781,8 +782,9 @@ class MMGenWallet(WalletEnc):
def _format(self):
d = self.ssdata
s = self.seed
slt_fmt = baseconv.frombytes(d.salt,'b58',pad='seed',tostr=True)
es_fmt = baseconv.frombytes(d.enc_seed,'b58',pad='seed',tostr=True)
bc = baseconv('b58')
slt_fmt = bc.frombytes(d.salt,pad='seed',tostr=True)
es_fmt = bc.frombytes(d.enc_seed,pad='seed',tostr=True)
lines = (
d.label,
'{} {} {} {} {}'.format( s.sid.lower(), d.key_id.lower(), s.bitlen, d.pw_status, d.timestamp ),
@ -852,7 +854,7 @@ class MMGenWallet(WalletEnc):
make_chksum_6(b58_val),'computed checksum',verbose=True):
return False
val = baseconv.tobytes(b58_val,'b58',pad='seed')
val = baseconv('b58').tobytes(b58_val,pad='seed')
if val == False:
msg(f'Invalid base 58 number: {b58_val}')
return False

View file

@ -584,7 +584,7 @@ class MoneroWalletOps:
'restore_deterministic_wallet',
filename = os.path.basename(fn),
password = d.wallet_passwd,
seed = baseconv.fromhex(d.sec.wif,'xmrseed',tostr=True),
seed = baseconv('xmrseed').fromhex(d.sec.wif,tostr=True),
restore_height = uopt.restore_height,
language = 'English' )

View file

@ -162,7 +162,7 @@ class unit_test(object):
vmsg(f'\nBase: {base}')
vmsg(fs.format(h='Input',p='Pad',r='Output'))
for (hexstr,pad),ret_chk in data:
ret = baseconv.fromhex(hexstr,wl_id=base,pad=pad,tostr=True)
ret = baseconv(base).fromhex(hexstr,pad=pad,tostr=True)
if pad != 'seed':
assert len(ret) >= (pad or 0), perr.format(ret,pad or 0)
assert ret == ret_chk, rerr.format(ret,ret_chk)
@ -180,9 +180,8 @@ class unit_test(object):
for (hexstr,pad),ret_chk in data:
if type(pad) == int:
pad = len(hexstr)
ret = baseconv.tohex(
ret = baseconv(base).tohex(
ret_chk.split() if base == 'xmrseed' else ret_chk,
wl_id=base,
pad=pad)
if pad == None:
assert int(ret,16) == int(hexstr,16), rerr.format(int(ret,16),int(hexstr,16))
@ -191,34 +190,43 @@ class unit_test(object):
vmsg(fs.format(h=ret_chk,r=ret,p=str(pad)))
# msg("(('{h}',{p}),'{r}'),".format(h=hexstr,r=ret_chk,c=ret_chk,p=pad))
qmsg_r('\nChecking wordlist checksums:')
vmsg('')
for wl_id in baseconv.wl_chksums:
vmsg_r(f' {wl_id+":":9}')
baseconv(wl_id).check_wordlist()
qmsg('')
vmsg('')
qmsg('Checking error handling:')
bad_b58 = 'I'*22
bad_b58len = 'a'*23
th = baseconv.tohex
fh = baseconv.fromhex
fr58 = baseconv('b58').fromhex
to58 = baseconv('b58').tohex
to32 = baseconv('b32').tohex
to8 = baseconv('b8').tohex
bad_data = (
('hexstr', 'HexadecimalStringError', ': not a hexadecimal str', lambda:fh('x','b58')),
('hexstr (seed)', 'HexadecimalStringError', 'seed data not a hexadec', lambda:fh('x','b58',pad='seed')),
('hexstr (empty)', 'BaseConversionError', 'empty data not allowed', lambda:fh('','b58')),
('b58 data', 'BaseConversionError', ': not in base58', lambda:th('IfFzZ','b58')),
('b58 data (seed)', 'BaseConversionError', 'seed data not in base58', lambda:th(bad_b58,'b58',pad='seed')),
('b58 len (seed)', 'BaseConversionError', 'invalid length for', lambda:th(bad_b58len,'b58',pad='seed')),
('b58 data (empty)','BaseConversionError', 'empty base58 data', lambda:th('','b58')),
('b8 data (empty)' ,'BaseConversionError', 'empty base8 string data', lambda:th('','b8')),
('b32 data', 'BaseConversionError', 'not in MMGen base32', lambda:th('1az','b32')),
('pad arg (in)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:fh('ff','b58',pad='foo')),
('pad arg (in)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:fh('ff','b58',pad=False)),
('pad arg (in)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:fh('ff','b58',pad=True)),
('seedlen (in)', 'SeedLengthError', 'invalid byte length', lambda:fh('ff','b58',pad='seed')),
('pad arg (out)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:th('Z','b58',pad='foo')),
('pad arg (out)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:th('Z','b58',pad=False)),
('pad arg (out)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:th('Z','b58',pad=True)),
('seedlen (out)', 'BaseConversionError', 'invalid length for seed', lambda:th('Z','b58',pad='seed')),
('hexstr', 'HexadecimalStringError', ': not a hexadecimal str', lambda:fr58('x')),
('hexstr (seed)', 'HexadecimalStringError', 'seed data not a hexadec', lambda:fr58('x',pad='seed')),
('hexstr (empty)', 'BaseConversionError', 'empty data not allowed', lambda:fr58('')),
('b58 data', 'BaseConversionError', ': not in base58', lambda:to58('IfFzZ')),
('b58 data (seed)', 'BaseConversionError', 'seed data not in base58', lambda:to58(bad_b58,pad='seed')),
('b58 len (seed)', 'BaseConversionError', 'invalid length for', lambda:to58(bad_b58len,pad='seed')),
('b58 data (empty)','BaseConversionError', 'empty base58 data', lambda:to58('')),
('b8 data (empty)' ,'BaseConversionError', 'empty base8 string data', lambda:to8('')),
('b32 data', 'BaseConversionError', 'not in MMGen base32', lambda:to32('1az')),
('pad arg (in)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:fr58('ff',pad='foo')),
('pad arg (in)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:fr58('ff',pad=False)),
('pad arg (in)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:fr58('ff',pad=True)),
('seedlen (in)', 'SeedLengthError', 'invalid byte length', lambda:fr58('ff',pad='seed')),
('pad arg (out)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:to58('Z',pad='foo')),
('pad arg (out)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:to58('Z',pad=False)),
('pad arg (out)', 'BaseConversionPadError', "illegal value for 'pad'", lambda:to58('Z',pad=True)),
('seedlen (out)', 'BaseConversionError', 'invalid length for seed', lambda:to58('Z',pad='seed')),
)
ut.process_bad_data(bad_data)

View file

@ -90,15 +90,15 @@ class unit_test(object):
from mmgen.bip39 import bip39
bip39.check_wordlists()
bip39.check_wordlist('bip39')
b = bip39()
b.check_wordlist()
vmsg('')
qmsg('Checking seed to mnemonic conversion:')
for v in self.vectors:
chk = tuple(v[1].split())
vmsg(' '+v[1])
res = bip39.fromhex(v[0],'bip39')
res = b.fromhex( v[0] )
assert res == chk, f'mismatch:\nres: {res}\nchk: {chk}'
vmsg('')
@ -106,7 +106,7 @@ class unit_test(object):
for v in self.vectors:
chk = v[0]
vmsg(' '+chk)
res = bip39.tohex(v[1].split(),'bip39')
res = b.tohex( v[1].split() )
assert res == chk, f'mismatch:\nres: {res}\nchk: {chk}'
vmsg('')
@ -119,20 +119,18 @@ class unit_test(object):
bad_seed = 'deadbeef'
good_seed = 'deadbeef' * 4
th = bip39.tohex
fh = bip39.fromhex
th = b.tohex
fh = b.fromhex
bad_data = (
('hex', 'AssertionError', 'not a hexadecimal',lambda:fh('xx','bip39')),
('id (tohex)', 'AssertionError', "must be 'bip39'", lambda:fh(good_seed,'foo')),
('seed len', 'AssertionError', 'invalid seed bit', lambda:fh(bad_seed,'bip39')),
('mnemonic type', 'AssertionError', 'must be list', lambda:th('string','bip39')),
('id (fromhex)', 'AssertionError', "must be 'bip39'", lambda:th(good_mn,'foo')),
('arg (tostr=True)', 'AssertionError', "'tostr' must be", lambda:fh(good_seed,'bip39',tostr=True)),
('pad len (fromhex)','AssertionError', "invalid 'pad' arg",lambda:fh(good_seed,'bip39',pad=23)),
('pad len (tohex)', 'AssertionError', "invalid 'pad' arg",lambda:th(good_mn,'bip39',pad=23)),
('word', 'MnemonicError', "not in the BIP39", lambda:th(bad_word_mn,'bip39')),
('checksum', 'MnemonicError', "checksum", lambda:th(bad_chksum_mn,'bip39')),
('seed phrase len', 'MnemonicError', "phrase len", lambda:th(bad_len_mn,'bip39')),
('hex', 'AssertionError', 'not a hexadecimal',lambda:fh('xx')),
('seed len', 'AssertionError', 'invalid seed bit', lambda:fh(bad_seed)),
('mnemonic type', 'AssertionError', 'must be list', lambda:th('string')),
('arg (tostr=True)', 'AssertionError', "'tostr' must be", lambda:fh(good_seed,tostr=True)),
('pad len (fromhex)','AssertionError', "invalid 'pad' arg",lambda:fh(good_seed,pad=23)),
('pad len (tohex)', 'AssertionError', "invalid 'pad' arg",lambda:th(good_mn,pad=23)),
('word', 'MnemonicError', "not in the BIP39", lambda:th(bad_word_mn)),
('checksum', 'MnemonicError', "checksum", lambda:th(bad_chksum_mn)),
('seed phrase len', 'MnemonicError', "phrase len", lambda:th(bad_len_mn)),
)
ut.process_bad_data(bad_data)

View file

@ -139,7 +139,7 @@ class unit_tests:
'restore_deterministic_wallet',
filename = fn,
password = 'foo',
seed = baseconv.fromhex('beadface'*8,'xmrseed',tostr=True) )
seed = baseconv('xmrseed').fromhex('beadface'*8,tostr=True) )
qmsg(f'Opening {wd.network} wallet')
await c.call( 'open_wallet', filename=fn, password='foo' )