Browse Source

terminal-related fixes and cleanups

The MMGen Project 5 years ago
parent
commit
656bb69587
6 changed files with 64 additions and 45 deletions
  1. 18 9
      mmgen/crypto.py
  2. 1 0
      mmgen/globalvars.py
  3. 4 2
      mmgen/tw.py
  4. 14 5
      mmgen/tx.py
  5. 1 11
      mmgen/util.py
  6. 26 18
      test/misc/term.py

+ 18 - 9
mmgen/crypto.py

@@ -152,8 +152,8 @@ def make_key(passwd,salt,hash_preset,desc='encryption key',from_what='passphrase
 	dmsg('Key: {}'.format(key.hex()))
 	return key
 
-def _get_random_data_from_user(uchars,desc):
-	m = 'Enter {r} random symbols' if opt.quiet else crmsg['usr_rand_notice']
+def _get_random_data_from_user(uchars,desc,test_suite=False):
+	m = 'Enter {r} random symbols' if opt.quiet or test_suite else crmsg['usr_rand_notice']
 	msg(m.format(r=uchars,d=desc))
 	prompt = 'You may begin typing.  {} symbols left: '
 
@@ -165,15 +165,24 @@ def _get_random_data_from_user(uchars,desc):
 		key_data += get_char_raw('\r'+prompt.format(uchars-i))
 		time_data.append(time.time())
 
-	if opt.quiet: msg_r('\r')
-	else: msg_r("\rThank you.  That's enough.{}\n\n".format(' '*18))
+	msg_r('\r' if opt.quiet else "\rThank you.  That's enough.{}\n\n".format(' '*18))
 
-	fmt_time_data = list(map('{:.22f}'.format,time_data))
-	dmsg('\nUser input:\n{!r}\nKeystroke time values:\n{}\n'.format(key_data,'\n'.join(fmt_time_data)))
-	prompt = 'User random data successfully acquired.  Press ENTER to continue'
-	prompt_and_get_char(prompt,'',enter_ok=True)
+	time_data = ['{:.22f}'.format(t).rstrip('0') for t in time_data]
 
-	return key_data.encode() + ''.join(fmt_time_data).encode()
+	avg_prec = sum(len(t.split('.')[1]) for t in time_data) // len(time_data)
+	if avg_prec < g.min_time_precision:
+		m = 'WARNING: Avg. time precision of only {} decimal points.  User entropy quality is degraded!'
+		ymsg(m.format(avg_prec))
+
+	ret = key_data + '\n' + '\n'.join(time_data)
+
+	if g.debug:
+		msg('USER ENTROPY (user input + keystroke timings):\n{}'.format(ret))
+
+	if not test_suite:
+		my_raw_input('User random data successfully acquired.  Press ENTER to continue: ')
+
+	return ret.encode()
 
 def get_random(length):
 	return add_user_random(os.urandom(length),'OS random data')

+ 1 - 0
mmgen/globalvars.py

@@ -58,6 +58,7 @@ class g(object):
 	http_timeout = 60
 	err_disp_timeout = 0.7
 	short_disp_timeout = 0.3
+	min_time_precision = 18
 
 	# Variables - these might be altered at runtime:
 

+ 4 - 2
mmgen/tw.py

@@ -344,8 +344,10 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
 		no_output,oneshot_msg = False,None
 		while True:
 			msg_r('' if no_output else '\n\n' if opt.no_blank else CUR_HOME+ERASE_ALL)
-			reply = get_char('' if no_output else self.format_for_display()+'\n'+(oneshot_msg or '')+prompt,
-								immed_chars=''.join(self.key_mappings.keys()))
+			reply = get_char(
+				'' if no_output else self.format_for_display()+'\n'+(oneshot_msg or '')+prompt,
+				immed_chars=''.join(self.key_mappings.keys())
+			)
 			no_output = False
 			oneshot_msg = '' if oneshot_msg else None # tristate, saves previous state
 			if reply not in self.key_mappings:

+ 14 - 5
mmgen/tx.py

@@ -1030,11 +1030,20 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
 			ask_tty=ask_tty,
 			ask_write_default_yes=ask_write_default_yes)
 
-	def view_with_prompt(self,prompt=''):
-		prompt += ' (y)es, (N)o, pager (v)iew, (t)erse view'
-		reply = prompt_and_get_char(prompt,'YyNnVvTt',enter_ok=True)
-		if reply and reply in 'YyVvTt':
-			self.view(pager=reply in 'Vv',terse=reply in 'Tt')
+	def view_with_prompt(self,prompt='',pause=True):
+		prompt += ' (y)es, (N)o, pager (v)iew, (t)erse view: '
+		from mmgen.term import get_char
+		ok_chars = 'YyNnVvTt'
+		while True:
+			reply = get_char(prompt,immed_chars=ok_chars).strip('\n\r')
+			msg('')
+			if reply == '' or reply in 'Nn':
+				break
+			elif reply in 'YyVvTt':
+				self.view(pager=reply in 'Vv',terse=reply in 'Tt',pause=pause)
+				break
+			else:
+				msg('Invalid reply')
 
 	def view(self,pager=False,pause=True,terse=False):
 		o = self.format_view(terse=terse)

+ 1 - 11
mmgen/util.py

@@ -760,7 +760,7 @@ def keypress_confirm(prompt,default_yes=False,verbose=False,no_nl=False,complete
 
 	from mmgen.term import get_char
 	while True:
-		reply = get_char(p).strip('\n\r')
+		reply = get_char(p,immed_chars='yYnN').strip('\n\r')
 		if not reply:
 			msg_r(nl)
 			return True if default_yes else False
@@ -770,16 +770,6 @@ def keypress_confirm(prompt,default_yes=False,verbose=False,no_nl=False,complete
 		else:
 			msg_r('\nInvalid reply\n' if verbose else '\r')
 
-def prompt_and_get_char(prompt,chars,enter_ok=False,verbose=False):
-
-	from mmgen.term import get_char
-	while True:
-		reply = get_char('{}: '.format(prompt)).strip('\n\r')
-		if reply in chars or (enter_ok and not reply):
-			msg('')
-			return reply
-		msg_r('\nInvalid reply\n' if verbose else '\r')
-
 def do_pager(text):
 
 	pagers = ['less','more']

+ 26 - 18
test/misc/term.py

@@ -67,22 +67,6 @@ def tt_my_raw_input():
 	reply = my_raw_input('\nEnter text: ')
 	confirm('Did you enter the text {!r}?'.format(reply))
 
-def tt_prompt_and_get_char():
-	cmsg('Testing prompt_and_get_char():')
-	m = 'Type some letters besides "x" or "z", then "x" or "z"'
-	reply = prompt_and_get_char(m,'xz')
-	confirm('Did you enter the letter {!r}?'.format(reply))
-
-def tt_prompt_and_get_char_enter_ok():
-	cmsg('Testing prompt_and_get_char() with blank choices and enter_ok=True:')
-	for m in (
-		'Type ENTER',
-		'Type any letter followed by a pause, followed by ENTER',
-	):
-		reply = prompt_and_get_char(m,'',enter_ok=True)
-		assert reply == ''
-		msg('OK')
-
 def tt_get_char(raw=False,one_char=False,sleep=0,immed_chars=''):
 	fname = ('get_char','get_char_raw')[raw]
 	fs = fmt("""
@@ -130,6 +114,30 @@ def tt_get_char(raw=False,one_char=False,sleep=0,immed_chars=''):
 	except KeyboardInterrupt:
 		msg('\nDone')
 
+def tt_urand():
+	cmsg('Testing _get_random_data_from_user():')
+	from mmgen.crypto import _get_random_data_from_user
+	ret = _get_random_data_from_user(10,desc='data',test_suite=True).decode()
+	msg('USER ENTROPY (user input + keystroke timings):\n\n{}'.format(fmt(ret,'  ')))
+	times = ret.splitlines()[1:]
+	avg_prec = sum(len(t.split('.')[1]) for t in times) // len(times)
+	if avg_prec < g.min_time_precision:
+		m = 'WARNING: Avg. time precision of only {} decimal points.  User entropy quality is degraded!'
+		ymsg(m.format(avg_prec))
+	else:
+		msg('Average time precision: {} decimal points - OK'.format(avg_prec))
+	my_raw_input('Press ENTER to continue: ')
+
+def tt_txview():
+	cmsg('Testing tx.view_with_prompt() (try each viewing option)')
+	from mmgen.tx import MMGenTX
+	fn = 'test/ref/0B8D5A[15.31789,14,tl=1320969600].rawtx'
+	tx = MMGenTX(fn,offline=True)
+	while True:
+		tx.view_with_prompt('View data for transaction?',pause=False)
+		if not keypress_confirm('Continue testing transaction view?',default_yes=True):
+			break
+
 if g.platform == 'linux':
 	import termios,atexit
 	fd = sys.stdin.fileno()
@@ -142,8 +150,8 @@ tt_get_terminal_size()
 tt_color()
 tt_license()
 tt_my_raw_input()
-tt_prompt_and_get_char()
-tt_prompt_and_get_char_enter_ok()
+tt_urand()
+tt_txview()
 
 tt_get_char(one_char=True)
 tt_get_char(one_char=True,sleep=1)