From fc87dcf0ba9447ecaeb13d7bb9bf3338de9ab1f4 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Mon, 7 Feb 2022 21:08:08 +0000 Subject: [PATCH] fixes and cleanups throughout --- mmgen/baseconv.py | 7 +++++++ mmgen/bip39.py | 2 +- mmgen/main_wallet.py | 19 ++++++++++--------- mmgen/opts.py | 2 ++ mmgen/passwdlist.py | 2 +- mmgen/txsign.py | 11 ++++++----- mmgen/util.py | 21 --------------------- mmgen/wallet.py | 17 +---------------- scripts/exec_wrapper.py | 10 ++++++---- test/test.py | 6 ++++-- test/test_py_d/ts_main.py | 22 ++++++++++++---------- test/test_py_d/ts_ref_3seed.py | 6 +++--- test/test_py_d/ts_seedsplit.py | 3 +-- test/test_py_d/ts_shared.py | 9 ++++----- test/test_py_d/ts_wallet.py | 14 ++++++++------ test/tooltest2.py | 3 ++- test/unit_tests_d/ut_dep.py | 1 + test/unit_tests_d/ut_testdep.py | 1 + test/unit_tests_d/ut_tx_deserialize.py | 6 +++--- 19 files changed, 73 insertions(+), 89 deletions(-) diff --git a/mmgen/baseconv.py b/mmgen/baseconv.py index ffdf5d75..926b411d 100755 --- a/mmgen/baseconv.py +++ b/mmgen/baseconv.py @@ -30,6 +30,13 @@ def is_b58_str(s): def is_b32_str(s): return set(list(s)) <= set(baseconv('b32').digits) +def is_mmgen_mnemonic(s): + try: + baseconv('mmgen').tobytes(s.split(),pad='seed') + return True + except: + return False + class baseconv(object): mn_base = 1626 dt = namedtuple('desc_tuple',['short','long']) diff --git a/mmgen/bip39.py b/mmgen/bip39.py index 984ec68b..576f9dc3 100755 --- a/mmgen/bip39.py +++ b/mmgen/bip39.py @@ -25,7 +25,7 @@ from hashlib import sha256 from .baseconv import baseconv from .util import is_hex_str,die -def is_bip39_str(s): +def is_bip39_mnemonic(s): return bool( bip39().tohex(s.split()) ) # implements a subset of the baseconv API diff --git a/mmgen/main_wallet.py b/mmgen/main_wallet.py index 2ec8ff18..c13b2168 100755 --- a/mmgen/main_wallet.py +++ b/mmgen/main_wallet.py @@ -231,15 +231,16 @@ if invoked_as == 'passchg' and ss_in.infile.dirname == g.data_dir: shred_file( ss_in.infile.name, verbose = opt.verbose ) -elif ( - invoked_as == 'gen' - and not opt.outdir - and not opt.stdout - and not find_file_in_dir( MMGenWallet, g.data_dir ) - and keypress_confirm( - 'Make this wallet your default and move it to the data directory?', - default_yes = True ) ): - ss_out.write_to_file(outdir=g.data_dir) +elif invoked_as == 'gen' and not opt.outdir and not opt.stdout: + from .filename import find_file_in_dir + if ( + not find_file_in_dir( MMGenWallet, g.data_dir ) + and keypress_confirm( + 'Make this wallet your default and move it to the data directory?', + default_yes = True ) ): + ss_out.write_to_file(outdir=g.data_dir) + else: + ss_out.write_to_file() else: ss_out.write_to_file() diff --git a/mmgen/opts.py b/mmgen/opts.py index 495b835d..01e5af1e 100755 --- a/mmgen/opts.py +++ b/mmgen/opts.py @@ -475,10 +475,12 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails opt_unrecognized(key,val) if key == 'out_fmt': p = 'hidden_incog_output_params' + if sstype == IncogWalletHidden and not getattr(opt,p): die( 'UserOptError', 'Hidden incog format output requested. ' + f'You must supply a file and offset with the {fmt_opt(p)!r} option' ) + if issubclass(sstype,IncogWallet) and opt.old_incog_fmt: opt_display(key,val,beg='Selected',end=' ') opt_display('old_incog_fmt',beg='conflicts with',end=':\n') diff --git a/mmgen/passwdlist.py b/mmgen/passwdlist.py index 0aba2211..95bb5f5b 100755 --- a/mmgen/passwdlist.py +++ b/mmgen/passwdlist.py @@ -55,7 +55,7 @@ class PasswordList(AddrList): pw_info = { 'b32': pwinfo(10, 42 ,24, None, 'base32 password', 'baseconv.is_b32_str'), # 32**24 < 2**128 'b58': pwinfo(8, 36 ,20, None, 'base58 password', 'baseconv.is_b58_str'), # 58**20 < 2**128 - 'bip39': pwinfo(12, 24 ,24, [12,18,24],'BIP39 mnemonic', 'bip39.is_bip39_str'), + 'bip39': pwinfo(12, 24 ,24, [12,18,24],'BIP39 mnemonic', 'bip39.is_bip39_mnemonic'), 'xmrseed': pwinfo(25, 25, 25, [25], 'Monero new-style mnemonic','xmrseed.is_xmrseed'), 'hex': pwinfo(32, 64 ,64, [32,48,64],'hexadecimal password', 'util.is_hex_str'), } diff --git a/mmgen/txsign.py b/mmgen/txsign.py index d12d27ac..c262a12c 100755 --- a/mmgen/txsign.py +++ b/mmgen/txsign.py @@ -105,12 +105,12 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None): vmsg(f'Added {len(new_keys)} wif key{suf(new_keys)} from {desc}') return new_keys -def _pop_and_return(args,cmplist): # strips found args +def _pop_matching_fns(args,cmplist): # strips found args return list(reversed([args.pop(args.index(a)) for a in reversed(args) if get_extension(a) in cmplist])) def get_tx_files(opt,args): from .tx.unsigned import Unsigned - ret = _pop_and_return(args,[Unsigned.ext]) + ret = _pop_matching_fns(args,[Unsigned.ext]) if not ret: die(1,'You must specify a raw transaction file!') return ret @@ -118,11 +118,12 @@ def get_tx_files(opt,args): def get_seed_files(opt,args): # favor unencrypted seed sources first, as they don't require passwords u,e = WalletUnenc,WalletEnc - ret = _pop_and_return(args,u.get_extensions()) + ret = _pop_matching_fns(args,u.get_extensions()) from .filename import find_file_in_dir wf = find_file_in_dir(MMGenWallet,g.data_dir) # Make this the first encrypted ss in the list - if wf: ret.append(wf) - ret += _pop_and_return(args,e.get_extensions()) + if wf: + ret.append(wf) + ret += _pop_matching_fns(args,e.get_extensions()) if not (ret or opt.mmgen_keys_from_file or opt.keys_from_file): # or opt.use_wallet_dat die(1,'You must specify a seed or key source!') return ret diff --git a/mmgen/util.py b/mmgen/util.py index 207adf7f..cf8d155c 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -332,16 +332,6 @@ def make_iv_chksum(s): from hashlib import sha256 return sha256(s).hexdigest()[:8].upper() -def splitN(s,n,sep=None): # always return an n-element list - ret = s.split(sep,n-1) - return ret + ['' for i in range(n-len(ret))] - -def split2(s,sep=None): - return splitN(s,2,sep) # always return a 2-element list - -def split3(s,sep=None): - return splitN(s,3,sep) # always return a 3-element list - def split_into_cols(col_wid,s): return ' '.join([s[col_wid*i:col_wid*(i+1)] for i in range(len(s)//col_wid+1)]).rstrip() @@ -646,17 +636,6 @@ def do_license_msg(immed=False): msg_r('\r') msg('') -def format_par(s,indent=0,width=80,as_list=False): - words,lines = s.split(),[] - assert width >= indent + 4,'width must be >= indent + 4' - while words: - line = '' - while len(line) <= (width-indent) and words: - if line and len(line) + len(words[0]) + 1 > width-indent: break - line += ('',' ')[bool(line)] + words.pop(0) - lines.append(' '*indent + line) - return lines if as_list else '\n'.join(lines) + '\n' - def get_subclasses(cls,names=False): def gen(cls): for i in cls.__subclasses__(): diff --git a/mmgen/wallet.py b/mmgen/wallet.py index a795fd29..224961e2 100755 --- a/mmgen/wallet.py +++ b/mmgen/wallet.py @@ -32,21 +32,6 @@ def check_usr_seed_len(seed_len): if opt.seed_len and opt.seed_len != seed_len: die(1,f'ERROR: requested seed length ({opt.seed_len}) doesn’t match seed length of source ({seed_len})') -def _is_mnemonic(s,fmt): - oq_save = bool(opt.quiet) - opt.quiet = True - try: - Wallet(in_data=s,in_fmt=fmt) - ret = True - except: - ret = False - finally: - opt.quiet = oq_save - return ret - -def is_bip39_mnemonic(s): return _is_mnemonic(s,fmt='bip39') -def is_mmgen_mnemonic(s): return _is_mnemonic(s,fmt='words') - class WalletMeta(type): wallet_classes = set() # one-instance class, so store data in class attr def __init__(cls,name,bases,namespace): @@ -841,7 +826,7 @@ class MMGenWallet(WalletEnc): class Brainwallet(WalletEnc): stdin_ok = True - fmt_codes = ('mmbrain','brainwallet','brain','bw','b') + fmt_codes = ('mmbrain','brainwallet','brain','bw') desc = 'brainwallet' ext = 'mmbrain' # brainwallet warning message? TODO diff --git a/scripts/exec_wrapper.py b/scripts/exec_wrapper.py index ef325541..599ae0e0 100755 --- a/scripts/exec_wrapper.py +++ b/scripts/exec_wrapper.py @@ -28,7 +28,7 @@ def exec_wrapper_init(): # don't change: name is used to test if script is runni if not os.getenv('EXEC_WRAPPER_NO_TRACEBACK'): try: - os.unlink('my.err') + os.unlink('test.py.err') except: pass @@ -55,8 +55,9 @@ def exec_wrapper_write_traceback(e): c.red(message) ) + '\n' ) - with open('my.err','w') as fp: - fp.write(''.join(lines+[exc])) + if not os.getenv('EXEC_WRAPPER_NO_TRACEBACK'): + with open('test.py.err','w') as fp: + fp.write(''.join(lines+[exc])) def exec_wrapper_end_msg(): if os.getenv('EXEC_WRAPPER_SPAWN') and not os.getenv('MMGEN_TEST_SUITE_DETERMINISTIC'): @@ -110,7 +111,8 @@ except SystemExit as e: exec_wrapper_end_msg() sys.exit(e.code) except Exception as e: - exec_wrapper_write_traceback(e) + if not os.getenv('EXEC_WRAPPER_NO_TRACEBACK'): + exec_wrapper_write_traceback(e) retval = e.mmcode if hasattr(e,'mmcode') else e.code if hasattr(e,'code') else 1 sys.exit(retval) diff --git a/test/test.py b/test/test.py index 8b2bf847..de9353cc 100755 --- a/test/test.py +++ b/test/test.py @@ -84,8 +84,10 @@ if not (len(sys.argv) == 2 and sys.argv[1] == 'clean'): from mmgen.common import * -try: os.unlink(os.path.join(repo_root,'my.err')) -except: pass +try: + os.unlink(os.path.join(repo_root,'test.py.err')) +except: + pass g.quiet = False # if 'quiet' was set in config file, disable here os.environ['MMGEN_QUIET'] = '0' # for this script and spawned scripts diff --git a/test/test_py_d/ts_main.py b/test/test_py_d/ts_main.py index 0a070148..a889d01c 100755 --- a/test/test_py_d/ts_main.py +++ b/test/test_py_d/ts_main.py @@ -583,8 +583,8 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): t.passphrase(icls.desc,self.wpasswd) ocls = Wallet.fmt_code_to_type(out_fmt) - out_pw = issubclass(ocls,WalletEnc) and ocls != Brainwallet - if out_pw: + + if issubclass(ocls,WalletEnc) and ocls != Brainwallet: t.passphrase_new('new '+ocls.desc,self.wpasswd) t.usr_rand(self.usr_rand_chars) @@ -594,6 +594,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): t.expect(m) incog_id = t.expect_getend('New Incog Wallet ID: ') t.expect(m) + if ocls == IncogWalletHidden: self.write_to_tmpfile(incog_id_fn,incog_id) t.hincog_create(hincog_bytes) @@ -637,17 +638,18 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): def addrgen_seed(self,wf,foo,in_fmt='seed'): wcls = Wallet.fmt_code_to_type(in_fmt) stdout = wcls == MMGenSeedFile # capture output to screen once - add_args = ([],['-S'])[bool(stdout)] + self.segwit_arg - t = self.spawn('mmgen-addrgen', add_args + - ['-i'+in_fmt,'-d',self.tmpdir,wf,self.addr_idx_list]) + t = self.spawn( + 'mmgen-addrgen', + (['-S'] if stdout else []) + + self.segwit_arg + + [ '-i' + in_fmt, '-d', self.tmpdir, wf, self.addr_idx_list ] ) t.license() t.expect_getend(f'Valid {wcls.desc} for Seed ID ') vmsg('Comparing generated checksum with checksum from previous address file') - chk = t.expect_getend(r'Checksum for address data .*?: ',regex=True) - if stdout: - t.read() - verify_checksum_or_exit(self._get_addrfile_checksum(),chk) - if in_fmt != 'seed': + verify_checksum_or_exit( + self._get_addrfile_checksum(), + t.expect_getend(r'Checksum for address data .*?: ',regex=True) ) + if not stdout: t.no_overwrite() t.req_exit_val = 1 return t diff --git a/test/test_py_d/ts_ref_3seed.py b/test/test_py_d/ts_ref_3seed.py index 50e6f377..808717a8 100755 --- a/test/test_py_d/ts_ref_3seed.py +++ b/test/test_py_d/ts_ref_3seed.py @@ -67,7 +67,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared): ('ref_walletconv_plainhexseed',([],'wallet filename (plain hex seed)')), ('ref_walletconv_dieroll', ([],'wallet filename (dieroll (b6d) seed)')), ('ref_walletconv_incog', ([],'wallet filename (incog)')), - ('ref_walletconv_xincog', ([],'wallet filename (hex incog)')), + ('ref_walletconv_hexincog', ([],'wallet filename (hex incog)')), ) def __init__(self,trunner,cfgs,spawn): @@ -135,7 +135,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared): hp_arg = f'-p{ref_wallet_hash_preset}' label = f'test.py ref. wallet (pw {ref_wallet_brainpass!r}, seed len {self.seed_len}) α' bf = 'ref.mmbrain' - args = ['-d',self.tmpdir,hp_arg,sl_arg,'-ib','-L',label] + args = ['-d',self.tmpdir,hp_arg,sl_arg,'-ibw','-L',label] self.write_to_tmpfile(bf,ref_wallet_brainpass) self.write_to_tmpfile(pwfile,self.wpasswd) t = self.spawn('mmgen-walletconv', args + [self.usr_rand_arg]) @@ -190,7 +190,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared): pat = r'{}-[0-9A-F]{{8}}-[0-9A-F]{{8}}\[{},1\]' + ('-α' if g.debug_utf8 else '') + '.' + ext return self.ref_walletconv(ofmt=ofmt,extra_args=args,re_pat=pat) - def ref_walletconv_xincog(self): + def ref_walletconv_hexincog(self): return self.ref_walletconv_incog(ofmt='incog_hex',ext='mmincox') class TestSuiteRef3Addr(TestSuiteRef3Seed): diff --git a/test/test_py_d/ts_seedsplit.py b/test/test_py_d/ts_seedsplit.py index 6f42b553..5028e9a3 100755 --- a/test/test_py_d/ts_seedsplit.py +++ b/test/test_py_d/ts_seedsplit.py @@ -112,8 +112,7 @@ class TestSuiteSeedSplit(TestSuiteBase): pat = f'master share #{master}' t.expect(pat,regex=True) ocls = Wallet.fmt_code_to_type(ofmt) - pw = issubclass(ocls,WalletEnc) - if pw: + if issubclass(ocls,WalletEnc): t.hash_preset('new '+ocls.desc,'1') t.passphrase_new('new '+ocls.desc,sh1_passwd) if ocls == IncogWalletHidden: diff --git a/test/test_py_d/ts_shared.py b/test/test_py_d/ts_shared.py index fe99682d..1e5e6690 100755 --- a/test/test_py_d/ts_shared.py +++ b/test/test_py_d/ts_shared.py @@ -168,8 +168,7 @@ class TestSuiteShared(object): t.license() t.view_tx(view) wcls = MMGenWallet if dfl_wallet else Wallet.ext_to_type(get_extension(wf)) - pw = issubclass(wcls,WalletEnc) and wcls != Brainwallet - if pw: + if issubclass(wcls,WalletEnc) and wcls != Brainwallet: t.passphrase(wcls.desc,self.wpasswd) if save: self.txsign_end(t,has_label=has_label) @@ -194,12 +193,12 @@ class TestSuiteShared(object): extra_desc=extra_desc) if wcls != IncogWalletHidden: t.expect(f"Getting {wcls.desc} from file '") - pw = issubclass(wcls,WalletEnc) and wcls != Brainwallet - if pw: + if issubclass(wcls,WalletEnc) and wcls != Brainwallet: t.passphrase(wcls.desc,self.wpasswd) t.expect(['Passphrase is OK', 'Passphrase.* are correct'],regex=True) chk = t.expect_getend(f'Valid {wcls.desc} for Seed ID ')[:8] - if sid: cmp_or_die(chk,sid) + if sid: + cmp_or_die(chk,sid) return t def addrgen(self,wf, diff --git a/test/test_py_d/ts_wallet.py b/test/test_py_d/ts_wallet.py index 5da5be5e..35811851 100755 --- a/test/test_py_d/ts_wallet.py +++ b/test/test_py_d/ts_wallet.py @@ -106,7 +106,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared): def ref_dieroll_conv(self): return self.ref_mn_conv(ext='b6d') def ref_brain_conv(self): - uopts = ['-i','b','-p','1','-l',str(self.seed_len)] + uopts = ['-i','bw','-p','1','-l',str(self.seed_len)] return self.walletconv_in(None,uopts,oo=True,icls=Brainwallet) def ref_incog_conv(self,wfk='ic_wallet',in_fmt='i'): @@ -121,7 +121,11 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared): ic_f = joinpath(ref_dir,self.sources[str(self.seed_len)][wfk]) uopts = ['-i','hi','-p','1','-l',str(self.seed_len)] + add_uopts hi_opt = ['-H',f'{ic_f},{ref_wallet_incog_offset}'] - return self.walletconv_in(None,uopts+hi_opt,oo=True,icls=IncogWalletHidden) + return self.walletconv_in( + None, + uopts + hi_opt, + oo = True, + icls = IncogWalletHidden ) def ref_hincog_conv_old(self): return self.ref_hincog_conv(wfk='hic_wallet_old',add_uopts=['-O']) @@ -178,8 +182,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared): icls = icls or Wallet.ext_to_type(get_extension(infile)) if icls == Brainwallet: t.expect('Enter brainwallet: ',ref_wallet_brainpass+'\n') - pw = issubclass(icls,WalletEnc) and icls != Brainwallet - if pw: + if issubclass(icls,WalletEnc) and icls != Brainwallet: t.passphrase(icls.desc,self.wpasswd) if self.test_name[:19] == 'ref_hincog_conv_old': t.expect('Is the Seed ID correct? (Y/n): ','\n') @@ -202,8 +205,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared): add_args = [f'-l{self.seed_len}'] t.license() - pw = issubclass(wcls,WalletEnc) and wcls != Brainwallet - if pw: + if issubclass(wcls,WalletEnc) and wcls != Brainwallet: t.passphrase_new('new '+wcls.desc,self.wpasswd) t.usr_rand(self.usr_rand_chars) if wcls in (IncogWallet,IncogWalletHex,IncogWalletHidden): diff --git a/test/tooltest2.py b/test/tooltest2.py index c15b83ad..86ee416c 100755 --- a/test/tooltest2.py +++ b/test/tooltest2.py @@ -33,7 +33,8 @@ sys.path.insert(0,overlay_setup(repo_root)) from mmgen.common import * from test.include.common import * -from mmgen.wallet import is_bip39_mnemonic,is_mmgen_mnemonic +from mmgen.bip39 import is_bip39_mnemonic +from mmgen.baseconv import is_mmgen_mnemonic from mmgen.xmrseed import is_xmrseed from mmgen.baseconv import * diff --git a/test/unit_tests_d/ut_dep.py b/test/unit_tests_d/ut_dep.py index 94072f03..6f185fe4 100755 --- a/test/unit_tests_d/ut_dep.py +++ b/test/unit_tests_d/ut_dep.py @@ -7,6 +7,7 @@ test.unit_tests_d.ut_dep: dependency unit tests for the MMGen suite """ from mmgen.common import * +from mmgen.exception import NoLEDSupport class unit_tests: diff --git a/test/unit_tests_d/ut_testdep.py b/test/unit_tests_d/ut_testdep.py index 0082ffc3..ed01d6ff 100755 --- a/test/unit_tests_d/ut_testdep.py +++ b/test/unit_tests_d/ut_testdep.py @@ -4,6 +4,7 @@ test.unit_tests_d.ut_testdep: test dependency unit tests for the MMGen suite """ from mmgen.common import * +from subprocess import run,PIPE sec = 'deadbeef' * 8 diff --git a/test/unit_tests_d/ut_tx_deserialize.py b/test/unit_tests_d/ut_tx_deserialize.py index 883a8e56..dbe3d2f7 100755 --- a/test/unit_tests_d/ut_tx_deserialize.py +++ b/test/unit_tests_d/ut_tx_deserialize.py @@ -41,12 +41,12 @@ async def test_tx(tx_proto,tx_hex,desc,n): dt = DeserializeTX(tx_proto,tx_hex) if opt.verbose: - Msg('\n====================================================') + Msg('\n\n================================ Core vector: ==================================') Msg_r('.' if opt.quiet else f'{n:>3}) {desc}\n') if opt.verbose: Pmsg(d) - Msg('----------------------------------------------------') - Pmsg(dt) + Msg('\n------------------------------ MMGen deserialized: -----------------------------') + Pmsg(dt._asdict()) # metadata assert dt.txid == d['txid'],'TXID does not match'