From 3a09017804bc4c577954b89f0ce35d66cd82cca1 Mon Sep 17 00:00:00 2001 From: MMGen Date: Sun, 12 May 2019 09:24:00 +0000 Subject: [PATCH] various minor fixes and cleanups --- MANIFEST.in | 4 ++++ mmgen/addr.py | 5 ++--- mmgen/common.py | 2 ++ mmgen/crypto.py | 5 +++-- mmgen/globalvars.py | 1 + mmgen/main.py | 10 +++++----- mmgen/main_wallet.py | 17 +++++++++++------ mmgen/obj.py | 3 ++- mmgen/opts.py | 1 + mmgen/tool.py | 9 ++++++--- mmgen/util.py | 8 ++++++-- setup.py | 2 -- test/objtest.py | 2 ++ test/test_py_d/ts_main.py | 1 - test/tooltest2.py | 22 ++++++++++++++++++++-- test/unit_tests.py | 3 ++- 16 files changed, 67 insertions(+), 28 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 5040d9ad..719bd3b1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,6 @@ include README.md SIGNING_KEYS.pub LICENSE INSTALL include doc/wiki/using-mmgen/* + include test/*.py include test/test_py_d/*.py include test/ref/* @@ -10,6 +11,9 @@ include test/ref/dash/* include test/ref/zcash/* include test/ref/monero/* +include mmgen/altcoins/eth/rlp/LICENSE +include mmgen/altcoins/eth/pyethereum/LICENSE + include scripts/compute-file-chksum.py include scripts/create-token.py include scripts/test-release.sh diff --git a/mmgen/addr.py b/mmgen/addr.py index 69b6d691..56e36b87 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -375,7 +375,6 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file gen_keys = False has_keys = False ext = 'addrs' - scramble_hash_rounds = 10 # not too many rounds, so hand decoding can still be feasible chksum_rec_f = lambda foo,e: (str(e.idx), e.addr) def __init__(self,addrfile='',al_id='',adata=[],seed='',addr_idxs='',src='', @@ -497,7 +496,7 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file if g.proto.is_testnet(): scramble_key += ':testnet' dmsg_sc('str',scramble_key) - return scramble_seed(seed,scramble_key.encode(),self.scramble_hash_rounds) + return scramble_seed(seed,scramble_key.encode(),g.scramble_hash_rounds) def encrypt(self,desc='new key list'): from mmgen.crypto import mmgen_encrypt @@ -886,7 +885,7 @@ Record this checksum: it will be used to verify the password file in the future # NB: In original implementation, pw_id_str was 'baseN', not 'bN' scramble_key = '{}:{}:{}'.format(self.pw_fmt,self.pw_len,self.pw_id_str) from mmgen.crypto import scramble_seed - return scramble_seed(seed,scramble_key.encode(),self.scramble_hash_rounds) + return scramble_seed(seed,scramble_key.encode(),g.scramble_hash_rounds) class AddrData(MMGenObject): msgs = { diff --git a/mmgen/common.py b/mmgen/common.py index e2969c7a..2d96939a 100755 --- a/mmgen/common.py +++ b/mmgen/common.py @@ -41,6 +41,8 @@ def help_notes(k): 'rel_fee_desc': MMGenTX().rel_fee_desc, 'fee_spec_letters': fee_spec_letters(), 'passwd': """ +PASSPHRASE NOTE: + For passphrases all combinations of whitespace are equal, and leading and trailing space are ignored. This permits reading passphrase or brainwallet data from a multi-line file with free spacing and indentation. diff --git a/mmgen/crypto.py b/mmgen/crypto.py index 279931b4..2c3c461c 100755 --- a/mmgen/crypto.py +++ b/mmgen/crypto.py @@ -45,8 +45,9 @@ def sha256_rounds(s,n): def scramble_seed(seed,scramble_key,hash_rounds): import hmac scr_seed = hmac.new(seed,scramble_key,sha256).digest() - fs = 'Seed: {}\nScramble key: {}\nScrambled seed: {}' - dmsg(fs.format(seed.hex(),scramble_key.decode(),scr_seed.hex())) + if g.debug: + fs = 'Seed: {!r}\nScramble key: {}\nScrambled seed: {}\n' + msg(fs.format(seed.hex(),scramble_key,scr_seed.hex())) return sha256_rounds(scr_seed,hash_rounds) def encrypt_seed(seed,key): diff --git a/mmgen/globalvars.py b/mmgen/globalvars.py index 848aff17..7ee56480 100755 --- a/mmgen/globalvars.py +++ b/mmgen/globalvars.py @@ -200,6 +200,7 @@ class g(object): min_urandchars = 10 seed_lens = 128,192,256 + scramble_hash_rounds = 10 mmenc_ext = 'mmenc' salt_len = 16 diff --git a/mmgen/main.py b/mmgen/main.py index 5497a1b9..0606546f 100755 --- a/mmgen/main.py +++ b/mmgen/main.py @@ -20,11 +20,11 @@ main.py - Script launcher for the MMGen suite """ -def launch(what): +def launch(mod): - if what in ('walletgen','walletchk','walletconv','passchg'): - what = 'wallet' - if what == 'keygen': what = 'addrgen' + if mod in ('walletgen','walletchk','walletconv','passchg'): + mod = 'wallet' + if mod == 'keygen': mod = 'addrgen' import sys,os from mmgen.globalvars import g @@ -36,7 +36,7 @@ def launch(what): atexit.register(lambda: termios.tcsetattr(fd,termios.TCSADRAIN,old)) try: - __import__('mmgen.main_' + what) + __import__('mmgen.main_' + mod) except KeyboardInterrupt: sys.stderr.write('\nUser interrupt\n') except EOFError: diff --git a/mmgen/main_wallet.py b/mmgen/main_wallet.py index 8aa6daef..5877df65 100755 --- a/mmgen/main_wallet.py +++ b/mmgen/main_wallet.py @@ -30,8 +30,14 @@ usage = '[opts] [infile]' nargs = 1 iaction = 'convert' oaction = 'convert' -invoked_as = 'passchg' if g.prog_name == 'mmgen-passchg' else g.prog_name.partition('-wallet')[2] -bw_note = True +do_bw_note = True + +invoked_as = { + 'mmgen-walletgen': 'gen', + 'mmgen-walletconv': 'conv', + 'mmgen-walletchk': 'chk', + 'mmgen-passchg': 'passchg', +}[g.prog_name] # full: defhHiJkKlLmoOpPqrSvz- if invoked_as == 'gen': @@ -51,9 +57,7 @@ elif invoked_as == 'passchg': desc = 'Change the passphrase, hash preset or label of an {pnm} wallet' opt_filter = 'efhdiHkKOlLmpPqrSvz-' iaction = 'input' - bw_note = False -else: - die(1,"'{}': unrecognized invocation".format(g.prog_name)) + do_bw_note = False opts_data = { 'text': { @@ -108,7 +112,7 @@ FMT CODES: 'notes': lambda s: s.format( f='\n '.join(SeedSource.format_fmt_codes().splitlines()), n_pw=help_notes('passwd'), - n_bw=('','\n\n' + help_notes('brainwallet'))[bw_note] + n_bw=('','\n\n'+help_notes('brainwallet'))[do_bw_note] ) } } @@ -128,6 +132,7 @@ if invoked_as in ('conv','passchg'): msg(m1+m2) ss_in = None if invoked_as == 'gen' else SeedSource(sf,passchg=(invoked_as=='passchg')) + if invoked_as == 'chk': lbl = ss_in.ssdata.label.hl() if hasattr(ss_in.ssdata,'label') else 'NONE' vmsg('Wallet label: {}'.format(lbl)) diff --git a/mmgen/obj.py b/mmgen/obj.py index 338af9db..5e1d1b37 100755 --- a/mmgen/obj.py +++ b/mmgen/obj.py @@ -118,7 +118,8 @@ class InitErrors(object): @staticmethod def arg_chk(cls,on_fail): - assert on_fail in ('die','return','silent','raise'),'arg_chk in class {}'.format(cls.__name__) + assert on_fail in ('die','return','silent','raise'),( + "'{}': invalid value for 'on_fail' in class {}".format(on_fail,cls.__name__) ) @staticmethod def init_fail(m,on_fail): diff --git a/mmgen/opts.py b/mmgen/opts.py index 3de922c7..65216e0d 100755 --- a/mmgen/opts.py +++ b/mmgen/opts.py @@ -331,6 +331,7 @@ def init(opts_data,add_opts=[],opt_filter=None,parse_only=False): if hasattr(g,'cfg_options_changed'): ymsg("Warning: config file options have changed! See '{}' for details".format(g.cfg_file+'.sample')) + from mmgen.util import my_raw_input my_raw_input('Hit ENTER to continue: ') if g.debug and g.prog_name != 'test.py': diff --git a/mmgen/tool.py b/mmgen/tool.py index aca68549..42eee1dc 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -94,6 +94,8 @@ def _usage(cmd=None,exit_val=1): Msg('') Msg(m2) elif cmd in MMGenToolCmd._user_commands(): + docstr = getattr(MMGenToolCmd,cmd).__doc__.strip() + msg('{}\n'.format(capfirst(docstr))) msg('USAGE: {} {} {}'.format(g.prog_name,cmd,_create_call_sig(cmd))) else: die(1,"'{}': no such tool command".format(cmd)) @@ -678,8 +680,8 @@ class MMGenToolCmdWallet(MMGenToolCmdBase): def gen_addr(self,mmgen_addr:str,wallet='',target='addr'): "generate a single MMGen address from default or specified wallet" addr = MMGenID(mmgen_addr) - sf = get_seed_file([wallet] if wallet else [],1) opt.quiet = True + sf = get_seed_file([wallet] if wallet else [],1) from mmgen.seed import SeedSource ss = SeedSource(sf) if ss.seed.sid != addr.sid: @@ -885,8 +887,9 @@ class MMGenToolCmdMonero(MMGenToolCmdBase): while True: ret = p.expect([r' / .*',r'\[wallet.*:.*']) if ret == 0: # TODO: coverage - height = p.after - msg_r('\r Block {}{}'.format(p.before.split()[-1],height)) + cur_block = p.before.decode().split()[-1] + height = p.after.decode() + msg_r('\r Block {}{}'.format(cur_block,height)) elif ret == 1: if height: height = height.split()[-1] diff --git a/mmgen/util.py b/mmgen/util.py index d153d66e..a3b07aa5 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -41,8 +41,12 @@ if g.platform == 'win': def msg(s): msg_r(s + '\n') def Msg(s): Msg_r(s + '\n') else: - def msg_r(s): g.stderr.write(s) - def Msg_r(s): g.stdout.write(s) + def msg_r(s): + g.stderr.write(s) + g.stderr.flush() + def Msg_r(s): + g.stdout.write(s) + g.stdout.flush() def msg(s): g.stderr.write(s + '\n') def Msg(s): g.stdout.write(s + '\n') diff --git a/setup.py b/setup.py index 17c8a632..61dccb3c 100755 --- a/setup.py +++ b/setup.py @@ -117,12 +117,10 @@ setup( 'mmgen.altcoins.eth.tx', 'mmgen.altcoins.eth.tw', - 'mmgen/altcoins/eth/pyethereum/LICENSE', 'mmgen.altcoins.eth.pyethereum.__init__', 'mmgen.altcoins.eth.pyethereum.transactions', 'mmgen.altcoins.eth.pyethereum.utils', - 'mmgen/altcoins/eth/rlp/LICENSE', 'mmgen/altcoins/eth/rlp/__init__', 'mmgen/altcoins/eth/rlp/atomic', 'mmgen/altcoins/eth/rlp/codec', diff --git a/test/objtest.py b/test/objtest.py index 39c8996d..f2f9594b 100755 --- a/test/objtest.py +++ b/test/objtest.py @@ -67,6 +67,8 @@ def run_test(test,arg,input_data): del arg['ret'] del arg_copy['ret'] kwargs.update(arg) + elif type(arg) == tuple: + args = arg else: args = [arg] try: diff --git a/test/test_py_d/ts_main.py b/test/test_py_d/ts_main.py index d1992f72..f7bc9a0d 100755 --- a/test/test_py_d/ts_main.py +++ b/test/test_py_d/ts_main.py @@ -199,7 +199,6 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): t.expect('move it to the data directory? (Y/n): ','y') self.have_dfl_wallet = True t.written_to_file('MMGen wallet') - t.req_exit_val = 0 return t def passchg(self,wf,pf,label_action='cmdline'): diff --git a/test/tooltest2.py b/test/tooltest2.py index 97037ffe..15eeedd4 100755 --- a/test/tooltest2.py +++ b/test/tooltest2.py @@ -40,6 +40,15 @@ from mmgen.seed import is_mnemonic def is_str(s): return type(s) == str +def md5_hash(s): + from hashlib import md5 + return md5(s.encode()).hexdigest() + +def md5_hash_strip(s): + import re + s = re.sub('\x1b\[[;0-9]+?m','',s) # strip ANSI color sequences + return md5_hash(s.strip()) + opts_data = { 'text': { 'desc': "Simple test suite for the 'mmgen-tool' utility", @@ -592,7 +601,12 @@ def run_test(gid,cmd_name): cmd_err = p.stderr.read() if cmd_err: vmsg(cmd_err.strip().decode()) if p.wait() != 0: - die(1,'Spawned program exited with error') + import re + m = re.match(b"tool command returned '(None|False)'\n",cmd_err) + if m: + return { b'None': None, b'False': False }[m.group(1)] + else: + ydie(1,'Spawned program exited with error: {}'.format(cmd_err)) return cmd_out @@ -655,7 +669,11 @@ def run_test(gid,cmd_name): elif out is not None: assert cmd_out == out,"Output ({!r}) doesn't match expected output ({!r})".format(cmd_out,out) - if type(out) in (list,tuple): + if type(out) == tuple and type(out[0]).__name__ == 'function': + func_out = out[0](cmd_out) + assert func_out == out[1],( + "{}({}) == {} failed!\nOutput: {}".format(out[0].__name__,cmd_out,out[1],func_out)) + elif type(out) in (list,tuple): for co,o in zip(cmd_out.split('\n') if opt.fork else cmd_out,out): check_output(co,o) else: diff --git a/test/unit_tests.py b/test/unit_tests.py index 432e35b3..a9a5d149 100755 --- a/test/unit_tests.py +++ b/test/unit_tests.py @@ -117,7 +117,8 @@ class UnitTests(object): extra_desc, '' if opt.quiet else '\n')) else: - Msg('Testing transactions from {!r}'.format(fn)) + Msg_r('Testing transactions from {!r}'.format(fn)) + if not opt.quiet: Msg('') def test_core_vectors(): self._get_core_repo_root()