From c82c9c5bd7d1eea5478a5a9a01ad2456a54c365e Mon Sep 17 00:00:00 2001 From: MMGen Date: Sun, 7 Jul 2019 13:24:05 +0000 Subject: [PATCH] minor changes and fixes --- mmgen/addr.py | 6 ++++-- mmgen/altcoins/eth/tw.py | 1 - mmgen/crypto.py | 8 ++++++-- mmgen/seed.py | 11 +++++------ mmgen/term.py | 6 ------ mmgen/util.py | 24 ++++++++++++++---------- test/scrambletest.py | 8 ++++++-- test/test_py_d/common.py | 14 +++++++++----- test/test_py_d/ts_autosign.py | 2 +- test/test_py_d/ts_misc.py | 14 ++++++++------ test/unit_tests_d/ut_subseed.py | 2 +- 11 files changed, 54 insertions(+), 42 deletions(-) diff --git a/mmgen/addr.py b/mmgen/addr.py index 53718fb7..d4d0d325 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -473,7 +473,8 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file dmsg('Key {:>03}: {}'.format(pos,e.passwd)) out.append(e) - if g.debug_addrlist: Msg('generate():\n{}'.format(e.pformat())) + if g.debug_addrlist: + Msg('generate():\n{}'.format(e.ppformat())) qmsg('\r{}: {} {}{} generated{}'.format( self.al_id.hl(),t_addrs,self.gen_desc,suf(t_addrs,self.gen_desc_pl),' '*15)) @@ -577,7 +578,8 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file for n,e in enumerate(d,1): qmsg_r('\rGenerating addresses from keylist: {}/{}'.format(n,len(d))) e.addr = ag.to_addr(kg.to_pubhex(e.sec)) - if g.debug_addrlist: Msg('generate_addrs_from_keys():\n{}'.format(e.pformat())) + if g.debug_addrlist: + Msg('generate_addrs_from_keys():\n{}'.format(e.ppformat())) qmsg('\rGenerated addresses from keylist: {}/{} '.format(n,len(d))) def format(self,enable_comments=False): diff --git a/mmgen/altcoins/eth/tw.py b/mmgen/altcoins/eth/tw.py index 92ce37f9..818a58c9 100755 --- a/mmgen/altcoins/eth/tw.py +++ b/mmgen/altcoins/eth/tw.py @@ -303,7 +303,6 @@ class EthereumTokenTwUnspentOutputs(EthereumTwUnspentOutputs): def get_display_precision(self): return 10 # truncate precision for narrow display - # NB: two wallet instances open simultaneously on the same data: def get_unspent_data(self): super().get_unspent_data() for e in self.unspent: diff --git a/mmgen/crypto.py b/mmgen/crypto.py index 63d80c59..f3151650 100755 --- a/mmgen/crypto.py +++ b/mmgen/crypto.py @@ -118,14 +118,18 @@ def scrypt_hash_passphrase(passwd,salt,hash_preset,buflen=32): import scrypt return scrypt.hash(passwd,salt,2**N,r,p,buflen=buflen) - msg_r('Hashing passphrase...') + if int(hash_preset) > 3: + msg_r('Hashing passphrase, please wait...') + # hashlib.scrypt doesn't support N > 14 (hash preset 3) if N > 14 or g.force_standalone_scrypt_module: ret = do_standalone_scrypt() else: try: ret = do_hashlib_scrypt() except: ret = do_standalone_scrypt() - msg('done') + + if int(hash_preset) > 3: + msg_r('\b'*34 + ' '*34 + '\b'*34) return ret diff --git a/mmgen/seed.py b/mmgen/seed.py index 2678aeae..87d3d99e 100755 --- a/mmgen/seed.py +++ b/mmgen/seed.py @@ -765,28 +765,27 @@ class Mnemonic (SeedSourceUnenc): while True: r = get_char('\r'+prompt).decode() if r in urange: break - msg_r('\r' + ' '*len(prompt) + '\r') + msg_r(('\r','\n')[g.test_suite] + ' '*len(prompt) + '\r') return self.mn_lens[int(r)-1] while True: mn_len = choose_mn_len() prompt = 'Mnemonic length of {} words chosen. OK?'.format(mn_len) - if keypress_confirm(prompt,default_yes=True,no_nl=True): break - + if keypress_confirm(prompt,default_yes=True,no_nl=not g.test_suite): + break wl = baseconv.digits[self.wl_id] longest_word = max(len(w) for w in wl) from string import ascii_lowercase - m = 'Enter your {ml}-word mnemonic, hitting ENTER or SPACE after each word.\n' + m = 'Enter your {ml}-word seed phrase, hitting ENTER or SPACE after each word.\n' m += "Optionally, you may use pad characters. Anything you type that's not a\n" m += 'lowercase letter will be treated as a {lq}pad character{rq}, i.e. it will simply\n' m += 'be discarded. Pad characters may be typed before, after, or in the middle\n' m += "of words. For each word, once you've typed {lw} characters total (including\n" - m += 'pad characters) a pad character will enter the word.' + m += 'pad characters) any pad character will enter the word.' # pexpect chokes on these utf8 chars under MSYS2 lq,rq = (('“','”'),('"','"'))[g.test_suite and g.platform=='win'] - msg(m.format(ml=mn_len,lw=longest_word,lq=lq,rq=rq)) def get_word(): diff --git a/mmgen/term.py b/mmgen/term.py index bd8be269..c51a7c2e 100755 --- a/mmgen/term.py +++ b/mmgen/term.py @@ -58,7 +58,6 @@ def _kb_hold_protect_unix(): # as well as UTF8 chars (4 bytes max). def _get_keypress_unix(prompt='',immed_chars='',prehold_protect=True,num_chars=5): msg_r(prompt) - sys.stderr.flush() timeout = float(0.3) fd = sys.stdin.fileno() old = termios.tcgetattr(fd) @@ -81,7 +80,6 @@ def _get_keypress_unix(prompt='',immed_chars='',prehold_protect=True,num_chars=5 def _get_keypress_unix_raw(prompt='',immed_chars='',prehold_protect=None,num_chars=5): msg_r(prompt) - sys.stderr.flush() fd = sys.stdin.fileno() old = termios.tcgetattr(fd) tty.setcbreak(fd) @@ -91,7 +89,6 @@ def _get_keypress_unix_raw(prompt='',immed_chars='',prehold_protect=None,num_cha def _get_keypress_unix_stub(prompt='',immed_chars='',prehold_protect=None,num_chars=None): msg_r(prompt) - sys.stderr.flush() return sys.stdin.read(1).encode() #_get_keypress_unix_stub = _get_keypress_unix @@ -112,7 +109,6 @@ def _kb_hold_protect_mswin(): def _get_keypress_mswin(prompt='',immed_chars='',prehold_protect=True,num_chars=None): msg_r(prompt) - sys.stderr.flush() timeout = float(0.5) while True: @@ -135,14 +131,12 @@ def _get_keypress_mswin(prompt='',immed_chars='',prehold_protect=True,num_chars= def _get_keypress_mswin_raw(prompt='',immed_chars='',prehold_protect=None,num_chars=None): msg_r(prompt) - sys.stderr.flush() ch = msvcrt.getch() if ch == b'\x03': raise KeyboardInterrupt return ch def _get_keypress_mswin_stub(prompt='',immed_chars='',prehold_protect=None,num_chars=None): msg_r(prompt) - sys.stderr.flush() return os.read(0,1) def _get_terminal_size_linux(): diff --git a/mmgen/util.py b/mmgen/util.py index 5b3462a9..3cb8f1aa 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -31,11 +31,13 @@ if g.platform == 'win': def msg_r(s): try: g.stderr.write(s) + g.stderr.flush() except: os.write(2,s.encode()) def Msg_r(s): try: g.stdout.write(s) + g.stdout.flush() except: os.write(1,s.encode()) def msg(s): msg_r(s + '\n') @@ -300,16 +302,16 @@ class baseconv(object): @classmethod def b58encode(cls,s,pad=None): - pad = cls.get_pad(s,pad,'b58encode',cls.b58pad_lens,(bytes,)) + pad = cls._get_pad(s,pad,'b58encode',cls.b58pad_lens,(bytes,)) return cls.fromhex(s.hex(),'b58',pad=pad,tostr=True) @classmethod def b58decode(cls,s,pad=None): - pad = cls.get_pad(s,pad,'b58decode',cls.b58pad_lens_rev,(bytes,str)) + pad = cls._get_pad(s,pad,'b58decode',cls.b58pad_lens_rev,(bytes,str)) return bytes.fromhex(cls.tohex(s,'b58',pad=pad*2 if pad else None)) @staticmethod - def get_pad(s,pad,op_desc,pad_map,ok_types): + def _get_pad(s,pad,op_desc,pad_map,ok_types): if not isinstance(s,ok_types): m = "{}() input must be one of {}, not '{}'" raise ValueError(m.format(op_desc,repr([t.__name__ for t in ok_types]),type(s).__name__)) @@ -329,21 +331,21 @@ class baseconv(object): @classmethod def check_wordlists(cls): - for k,v in list(cls.wl_chksums.items()): assert cls.get_wordlist_chksum(k) == v + for k,v in list(cls.wl_chksums.items()): + res = cls.get_wordlist_chksum(k) + assert res == v,'{}: checksum mismatch for {} (should be {})'.format(res,k,v) @classmethod def check_wordlist(cls,wl_id): - wl = baseconv.digits[wl_id] - Msg('Wordlist: {}\nLength: {} words'.format(capfirst(wl_id),len(wl))) + wl = cls.digits[wl_id] + qmsg('Wordlist: {}\nLength: {} words'.format(wl_id,len(wl))) new_chksum = cls.get_wordlist_chksum(wl_id) - a,b = 'generated checksum','saved checksum' + a,b = 'generated','saved' compare_chksums(new_chksum,a,cls.wl_chksums[wl_id],b,die_on_fail=True) - Msg('Checksum {} matches'.format(new_chksum)) - Msg('List is sorted') if tuple(sorted(wl)) == wl else die(3,'ERROR: List is not sorted!') - + qmsg('List is sorted') if tuple(sorted(wl)) == wl else die(3,'ERROR: List is not sorted!') @classmethod def tohex(cls,words_arg,wl_id,pad=None): @@ -362,6 +364,8 @@ class baseconv(object): @classmethod def fromhex(cls,hexnum,wl_id,pad=None,tostr=False): + if wl_id in ('electrum','tirosh'): + assert tostr == False,"'tostr' must be False for '{}'".format(wl_id) if not is_hex_str(hexnum): die(2,"{!r}: not a hexadecimal number".format(hexnum)) diff --git a/test/scrambletest.py b/test/scrambletest.py index 09942051..ef1f92b3 100755 --- a/test/scrambletest.py +++ b/test/scrambletest.py @@ -94,7 +94,11 @@ def run_tests(): msg_r('Testing: --coin {:4} {:22}'.format(coin.upper(),type_arg)) p = subprocess.Popen(cmd.split(),stdout=subprocess.PIPE,stderr=subprocess.PIPE) o = p.stdout.read().decode() - vmsg(o) + err = p.stderr.read().decode() + exit_val = p.wait() + if exit_val != 0: + ydie(2,'\nSpawned program exited with error code {}:\n{}'.format(exit_val,err)) + vmsg('\nCOMMAND OUTPUT:\n[{}]'.format(o)) o = o.splitlines() d = [e for e in o if len(e) > 4 and e[:9] == 'sc_debug_'] d.append('sc_debug_addr: ' + o[-2].split()[-1]) @@ -103,7 +107,7 @@ def run_tests(): a = test_data[test][n] b = [e for e in d if e[:len(kk)] == kk][0][len(kk)+2:] if b == a: - vmsg('sc_{}: {}'.format(k,a)) + vmsg('sc_debug_{}: {}'.format(k,a)) else: rdie(1,'\nError: sc_{} value {} does not match reference value {}'.format(k,b,a)) msg('OK') diff --git a/test/test_py_d/common.py b/test/test_py_d/common.py index 025d69b7..d8bab00b 100755 --- a/test/test_py_d/common.py +++ b/test/test_py_d/common.py @@ -46,6 +46,7 @@ non_mmgen_fn = 'coinkey' ref_dir = os.path.join('test','ref') dfl_words_file = os.path.join(ref_dir,'98831F3A.mmwords') +mn_words_mmgen = os.path.join(ref_dir,'FE3C6545.mmwords') from mmgen.obj import MMGenTXLabel,TwComment @@ -146,23 +147,26 @@ def get_label(do_shuffle=False): label_iter = iter(labels) return next(label_iter) -def stealth_mnemonic_entry(t,mn): +def stealth_mnemonic_entry(t,mn,fmt): wnum = 1 - max_wordlen = 12 + max_wordlen = { 'words': 12 }[fmt] def get_pad_chars(n): ret = '' for i in range(n): - m = int(os.urandom(1).hex(),16) % 32 + m = int.from_bytes(os.urandom(1),'big') % 32 ret += r'123579!@#$%^&*()_+-=[]{}"?/,.<>|'[m] return ret for i in range(len(mn)): w = mn[i] - if len(w) > 5: + if len(w) > (3,5)[max_wordlen==12]: w = w + '\n' else: - w = get_pad_chars(3 if randbool() else 0) + w[0] + get_pad_chars(3) + w[1:] + get_pad_chars(7) + w = ( + get_pad_chars(2 if randbool() else 0) + + w[0] + get_pad_chars(2) + w[1:] + + get_pad_chars(9) ) w = w[:max_wordlen+1] em,rm = 'Enter word #{}: ','Repeat word #{}: ' ret = t.expect((em.format(wnum),rm.format(wnum-1))) diff --git a/test/test_py_d/ts_autosign.py b/test/test_py_d/ts_autosign.py index 9412f83d..6b7fe7e4 100755 --- a/test/test_py_d/ts_autosign.py +++ b/test/test_py_d/ts_autosign.py @@ -67,7 +67,7 @@ class TestSuiteAutosign(TestSuiteBase): mn_file = dfl_words_file mn = read_from_file(mn_file).strip().split() mn = ['foo'] + mn[:5] + ['realiz','realized'] + mn[5:] - stealth_mnemonic_entry(t,mn) + stealth_mnemonic_entry(t,mn,fmt='words') wf = t.written_to_file('Autosign wallet') t.ok() diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index f88dac76..f1588e86 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -141,20 +141,22 @@ class TestSuiteInput(TestSuiteBase): return 'skip' # pexpect double-escapes utf8, so skip return self.password_entry('Enter passphrase (echoed): ',['--echo-passphrase']) - def mnemonic_entry(self): - mn = read_from_file(dfl_words_file).strip().split()[:12] - mn = ['foo'] + mn[:5] + ['realiz','realized'] + mn[5:] - t = self.spawn('mmgen-walletconv',['-S','-i','words','-o','words']) + def _mnemonic_entry(self,fmt,wf): + mn = read_from_file(wf).strip().split() + mn = ['foo'] + mn[:5] + ['grac','graceful'] + mn[5:] + t = self.spawn('mmgen-walletconv',['-S','-i',fmt,'-o',fmt]) t.expect('words: ','1') t.expect('(Y/n): ','y') - stealth_mnemonic_entry(t,mn) - sid_chk = '5F9BC42F' + stealth_mnemonic_entry(t,mn,fmt=fmt) + sid_chk = 'FE3C6545' sid = t.expect_getend('Valid mnemonic data for Seed ID ')[:8] assert sid == sid_chk,'Seed ID mismatch! {} != {}'.format(sid,sid_chk) t.expect('to confirm: ','YES\n') t.read() return t + def mnemonic_entry(self): return self._mnemonic_entry('words',mn_words_mmgen) + class TestSuiteTool(TestSuiteMain,TestSuiteBase): "tests for interactive 'mmgen-tool' commands" networks = ('btc',) diff --git a/test/unit_tests_d/ut_subseed.py b/test/unit_tests_d/ut_subseed.py index effd6444..67aabd28 100755 --- a/test/unit_tests_d/ut_subseed.py +++ b/test/unit_tests_d/ut_subseed.py @@ -53,7 +53,7 @@ class unit_test(object): seed2.subseeds._generate(10) assert len(ss2_list) == 10, len(ss2_list) - assert seed.pformat() == seed2.pformat() + assert seed.ppformat() == seed2.ppformat() s = seed.subseeds.format(1,g.subseeds) s_lines = s.strip().split('\n')