Browse Source

various minor fixes and cleanups

MMGen 5 years ago
parent
commit
3a09017804
16 changed files with 67 additions and 28 deletions
  1. 4 0
      MANIFEST.in
  2. 2 3
      mmgen/addr.py
  3. 2 0
      mmgen/common.py
  4. 3 2
      mmgen/crypto.py
  5. 1 0
      mmgen/globalvars.py
  6. 5 5
      mmgen/main.py
  7. 11 6
      mmgen/main_wallet.py
  8. 2 1
      mmgen/obj.py
  9. 1 0
      mmgen/opts.py
  10. 6 3
      mmgen/tool.py
  11. 6 2
      mmgen/util.py
  12. 0 2
      setup.py
  13. 2 0
      test/objtest.py
  14. 0 1
      test/test_py_d/ts_main.py
  15. 20 2
      test/tooltest2.py
  16. 2 1
      test/unit_tests.py

+ 4 - 0
MANIFEST.in

@@ -1,5 +1,6 @@
 include README.md SIGNING_KEYS.pub LICENSE INSTALL
 include README.md SIGNING_KEYS.pub LICENSE INSTALL
 include doc/wiki/using-mmgen/*
 include doc/wiki/using-mmgen/*
+
 include test/*.py
 include test/*.py
 include test/test_py_d/*.py
 include test/test_py_d/*.py
 include test/ref/*
 include test/ref/*
@@ -10,6 +11,9 @@ include test/ref/dash/*
 include test/ref/zcash/*
 include test/ref/zcash/*
 include test/ref/monero/*
 include test/ref/monero/*
 
 
+include mmgen/altcoins/eth/rlp/LICENSE
+include mmgen/altcoins/eth/pyethereum/LICENSE
+
 include scripts/compute-file-chksum.py
 include scripts/compute-file-chksum.py
 include scripts/create-token.py
 include scripts/create-token.py
 include scripts/test-release.sh
 include scripts/test-release.sh

+ 2 - 3
mmgen/addr.py

@@ -375,7 +375,6 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
 	gen_keys = False
 	gen_keys = False
 	has_keys = False
 	has_keys = False
 	ext      = 'addrs'
 	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)
 	chksum_rec_f = lambda foo,e: (str(e.idx), e.addr)
 
 
 	def __init__(self,addrfile='',al_id='',adata=[],seed='',addr_idxs='',src='',
 	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():
 		if g.proto.is_testnet():
 			scramble_key += ':testnet'
 			scramble_key += ':testnet'
 		dmsg_sc('str',scramble_key)
 		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'):
 	def encrypt(self,desc='new key list'):
 		from mmgen.crypto import mmgen_encrypt
 		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'
 		# NB: In original implementation, pw_id_str was 'baseN', not 'bN'
 		scramble_key = '{}:{}:{}'.format(self.pw_fmt,self.pw_len,self.pw_id_str)
 		scramble_key = '{}:{}:{}'.format(self.pw_fmt,self.pw_len,self.pw_id_str)
 		from mmgen.crypto import scramble_seed
 		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):
 class AddrData(MMGenObject):
 	msgs = {
 	msgs = {

+ 2 - 0
mmgen/common.py

@@ -41,6 +41,8 @@ def help_notes(k):
 		'rel_fee_desc': MMGenTX().rel_fee_desc,
 		'rel_fee_desc': MMGenTX().rel_fee_desc,
 		'fee_spec_letters': fee_spec_letters(),
 		'fee_spec_letters': fee_spec_letters(),
 		'passwd': """
 		'passwd': """
+PASSPHRASE NOTE:
+
 For passphrases all combinations of whitespace are equal, and leading and
 For passphrases all combinations of whitespace are equal, and leading and
 trailing space are ignored.  This permits reading passphrase or brainwallet
 trailing space are ignored.  This permits reading passphrase or brainwallet
 data from a multi-line file with free spacing and indentation.
 data from a multi-line file with free spacing and indentation.

+ 3 - 2
mmgen/crypto.py

@@ -45,8 +45,9 @@ def sha256_rounds(s,n):
 def scramble_seed(seed,scramble_key,hash_rounds):
 def scramble_seed(seed,scramble_key,hash_rounds):
 	import hmac
 	import hmac
 	scr_seed = hmac.new(seed,scramble_key,sha256).digest()
 	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)
 	return sha256_rounds(scr_seed,hash_rounds)
 
 
 def encrypt_seed(seed,key):
 def encrypt_seed(seed,key):

+ 1 - 0
mmgen/globalvars.py

@@ -200,6 +200,7 @@ class g(object):
 	min_urandchars = 10
 	min_urandchars = 10
 
 
 	seed_lens = 128,192,256
 	seed_lens = 128,192,256
+	scramble_hash_rounds = 10
 
 
 	mmenc_ext      = 'mmenc'
 	mmenc_ext      = 'mmenc'
 	salt_len       = 16
 	salt_len       = 16

+ 5 - 5
mmgen/main.py

@@ -20,11 +20,11 @@
 main.py - Script launcher for the MMGen suite
 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
 	import sys,os
 	from mmgen.globalvars import g
 	from mmgen.globalvars import g
@@ -36,7 +36,7 @@ def launch(what):
 		atexit.register(lambda: termios.tcsetattr(fd,termios.TCSADRAIN,old))
 		atexit.register(lambda: termios.tcsetattr(fd,termios.TCSADRAIN,old))
 
 
 	try:
 	try:
-		__import__('mmgen.main_' + what)
+		__import__('mmgen.main_' + mod)
 	except KeyboardInterrupt:
 	except KeyboardInterrupt:
 		sys.stderr.write('\nUser interrupt\n')
 		sys.stderr.write('\nUser interrupt\n')
 	except EOFError:
 	except EOFError:

+ 11 - 6
mmgen/main_wallet.py

@@ -30,8 +30,14 @@ usage = '[opts] [infile]'
 nargs = 1
 nargs = 1
 iaction = 'convert'
 iaction = 'convert'
 oaction = '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-
 # full: defhHiJkKlLmoOpPqrSvz-
 if invoked_as == 'gen':
 if invoked_as == 'gen':
@@ -51,9 +57,7 @@ elif invoked_as == 'passchg':
 	desc = 'Change the passphrase, hash preset or label of an {pnm} wallet'
 	desc = 'Change the passphrase, hash preset or label of an {pnm} wallet'
 	opt_filter = 'efhdiHkKOlLmpPqrSvz-'
 	opt_filter = 'efhdiHkKOlLmpPqrSvz-'
 	iaction = 'input'
 	iaction = 'input'
-	bw_note = False
-else:
-	die(1,"'{}': unrecognized invocation".format(g.prog_name))
+	do_bw_note = False
 
 
 opts_data = {
 opts_data = {
 	'text': {
 	'text': {
@@ -108,7 +112,7 @@ FMT CODES:
 		'notes': lambda s: s.format(
 		'notes': lambda s: s.format(
 			f='\n  '.join(SeedSource.format_fmt_codes().splitlines()),
 			f='\n  '.join(SeedSource.format_fmt_codes().splitlines()),
 			n_pw=help_notes('passwd'),
 			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)
 	msg(m1+m2)
 
 
 ss_in = None if invoked_as == 'gen' else SeedSource(sf,passchg=(invoked_as=='passchg'))
 ss_in = None if invoked_as == 'gen' else SeedSource(sf,passchg=(invoked_as=='passchg'))
+
 if invoked_as == 'chk':
 if invoked_as == 'chk':
 	lbl = ss_in.ssdata.label.hl() if hasattr(ss_in.ssdata,'label') else 'NONE'
 	lbl = ss_in.ssdata.label.hl() if hasattr(ss_in.ssdata,'label') else 'NONE'
 	vmsg('Wallet label: {}'.format(lbl))
 	vmsg('Wallet label: {}'.format(lbl))

+ 2 - 1
mmgen/obj.py

@@ -118,7 +118,8 @@ class InitErrors(object):
 
 
 	@staticmethod
 	@staticmethod
 	def arg_chk(cls,on_fail):
 	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
 	@staticmethod
 	def init_fail(m,on_fail):
 	def init_fail(m,on_fail):

+ 1 - 0
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'):
 	if hasattr(g,'cfg_options_changed'):
 		ymsg("Warning: config file options have changed! See '{}' for details".format(g.cfg_file+'.sample'))
 		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: ')
 		my_raw_input('Hit ENTER to continue: ')
 
 
 	if g.debug and g.prog_name != 'test.py':
 	if g.debug and g.prog_name != 'test.py':

+ 6 - 3
mmgen/tool.py

@@ -94,6 +94,8 @@ def _usage(cmd=None,exit_val=1):
 			Msg('')
 			Msg('')
 		Msg(m2)
 		Msg(m2)
 	elif cmd in MMGenToolCmd._user_commands():
 	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)))
 		msg('USAGE: {} {} {}'.format(g.prog_name,cmd,_create_call_sig(cmd)))
 	else:
 	else:
 		die(1,"'{}': no such tool command".format(cmd))
 		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'):
 	def gen_addr(self,mmgen_addr:str,wallet='',target='addr'):
 		"generate a single MMGen address from default or specified wallet"
 		"generate a single MMGen address from default or specified wallet"
 		addr = MMGenID(mmgen_addr)
 		addr = MMGenID(mmgen_addr)
-		sf = get_seed_file([wallet] if wallet else [],1)
 		opt.quiet = True
 		opt.quiet = True
+		sf = get_seed_file([wallet] if wallet else [],1)
 		from mmgen.seed import SeedSource
 		from mmgen.seed import SeedSource
 		ss = SeedSource(sf)
 		ss = SeedSource(sf)
 		if ss.seed.sid != addr.sid:
 		if ss.seed.sid != addr.sid:
@@ -885,8 +887,9 @@ class MMGenToolCmdMonero(MMGenToolCmdBase):
 			while True:
 			while True:
 				ret = p.expect([r' / .*',r'\[wallet.*:.*'])
 				ret = p.expect([r' / .*',r'\[wallet.*:.*'])
 				if ret == 0: # TODO: coverage
 				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:
 				elif ret == 1:
 					if height:
 					if height:
 						height = height.split()[-1]
 						height = height.split()[-1]

+ 6 - 2
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')
 	def Msg(s): Msg_r(s + '\n')
 	def Msg(s): Msg_r(s + '\n')
 else:
 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.stderr.write(s + '\n')
 	def Msg(s):   g.stdout.write(s + '\n')
 	def Msg(s):   g.stdout.write(s + '\n')
 
 

+ 0 - 2
setup.py

@@ -117,12 +117,10 @@ setup(
 			'mmgen.altcoins.eth.tx',
 			'mmgen.altcoins.eth.tx',
 			'mmgen.altcoins.eth.tw',
 			'mmgen.altcoins.eth.tw',
 
 
-			'mmgen/altcoins/eth/pyethereum/LICENSE',
 			'mmgen.altcoins.eth.pyethereum.__init__',
 			'mmgen.altcoins.eth.pyethereum.__init__',
 			'mmgen.altcoins.eth.pyethereum.transactions',
 			'mmgen.altcoins.eth.pyethereum.transactions',
 			'mmgen.altcoins.eth.pyethereum.utils',
 			'mmgen.altcoins.eth.pyethereum.utils',
 
 
-			'mmgen/altcoins/eth/rlp/LICENSE',
 			'mmgen/altcoins/eth/rlp/__init__',
 			'mmgen/altcoins/eth/rlp/__init__',
 			'mmgen/altcoins/eth/rlp/atomic',
 			'mmgen/altcoins/eth/rlp/atomic',
 			'mmgen/altcoins/eth/rlp/codec',
 			'mmgen/altcoins/eth/rlp/codec',

+ 2 - 0
test/objtest.py

@@ -67,6 +67,8 @@ def run_test(test,arg,input_data):
 			del arg['ret']
 			del arg['ret']
 			del arg_copy['ret']
 			del arg_copy['ret']
 		kwargs.update(arg)
 		kwargs.update(arg)
+	elif type(arg) == tuple:
+		args = arg
 	else:
 	else:
 		args = [arg]
 		args = [arg]
 	try:
 	try:

+ 0 - 1
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')
 			t.expect('move it to the data directory? (Y/n): ','y')
 			self.have_dfl_wallet = True
 			self.have_dfl_wallet = True
 		t.written_to_file('MMGen wallet')
 		t.written_to_file('MMGen wallet')
-		t.req_exit_val = 0
 		return t
 		return t
 
 
 	def passchg(self,wf,pf,label_action='cmdline'):
 	def passchg(self,wf,pf,label_action='cmdline'):

+ 20 - 2
test/tooltest2.py

@@ -40,6 +40,15 @@ from mmgen.seed import is_mnemonic
 
 
 def is_str(s): return type(s) == str
 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 = {
 opts_data = {
 	'text': {
 	'text': {
 		'desc': "Simple test suite for the 'mmgen-tool' utility",
 		'desc': "Simple test suite for the 'mmgen-tool' utility",
@@ -592,7 +601,12 @@ def run_test(gid,cmd_name):
 		cmd_err = p.stderr.read()
 		cmd_err = p.stderr.read()
 		if cmd_err: vmsg(cmd_err.strip().decode())
 		if cmd_err: vmsg(cmd_err.strip().decode())
 		if p.wait() != 0:
 		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
 		return cmd_out
 
 
@@ -655,7 +669,11 @@ def run_test(gid,cmd_name):
 			elif out is not None:
 			elif out is not None:
 				assert cmd_out == out,"Output ({!r}) doesn't match expected output ({!r})".format(cmd_out,out)
 				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):
 			for co,o in zip(cmd_out.split('\n') if opt.fork else cmd_out,out):
 				check_output(co,o)
 				check_output(co,o)
 		else:
 		else:

+ 2 - 1
test/unit_tests.py

@@ -117,7 +117,8 @@ class UnitTests(object):
 					extra_desc,
 					extra_desc,
 					'' if opt.quiet else '\n'))
 					'' if opt.quiet else '\n'))
 			else:
 			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():
 		def test_core_vectors():
 			self._get_core_repo_root()
 			self._get_core_repo_root()