baseconv.py: convert class methods to instance methods
This commit is contained in:
parent
4496f795d7
commit
7b890a2c95
13 changed files with 170 additions and 188 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'"
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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' )
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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' )
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue