Browse Source

`mmgen-tool usage`: various fixes and cleanups

The MMGen Project 2 years ago
parent
commit
642b45b2e3

+ 2 - 2
mmgen/baseconv.py

@@ -132,7 +132,7 @@ class baseconv(object):
 			die('BaseConversionPadError',f"{pad!r}: illegal value for 'pad' (must be None,'seed' or int)")
 
 	def tohex(self,words_arg,pad=None):
-		"convert string or list data of instance base to hex string"
+		"convert string or list data of instance base to a hexadecimal string"
 		return self.tobytes(words_arg,pad//2 if type(pad)==int else pad).hex()
 
 	def tobytes(self,words_arg,pad=None):
@@ -166,7 +166,7 @@ class baseconv(object):
 		return ret.to_bytes(max(pad_val,bl//8+bool(bl%8)),'big')
 
 	def fromhex(self,hexstr,pad=None,tostr=False):
-		"convert hex string to list or string data of instance base"
+		"convert a hexadecimal string to a list or string data of instance base"
 
 		from .util import is_hex_str
 		if not is_hex_str(hexstr):

+ 16 - 19
mmgen/main_tool.py

@@ -42,7 +42,7 @@ opts_data = {
 -q, --quiet            Produce quieter output
 -r, --usr-randchars=n  Get 'n' characters of additional randomness from
                        user (min={g.min_urandchars}, max={g.max_urandchars})
--t, --type=t           Specify address type (valid options: 'legacy',
+-t, --type=t           Specify address type (valid choices: 'legacy',
                        'compressed', 'segwit', 'bech32', 'zcash_z')
 -v, --verbose          Produce more verbose output
 -X, --cached-balances  Use cached balances (Ethereum only)
@@ -53,7 +53,7 @@ opts_data = {
                                COMMANDS
 
 {ch}
-Type '{pn} help <command>' for help on a particular command
+Type ‘{pn} help <command>’ for help on a particular command
 """
 	},
 	'code': {
@@ -171,7 +171,7 @@ mods = {
 	),
 }
 
-def create_call_sig(cmd,cls,parsed=False):
+def create_call_sig(cmd,cls,as_string=False):
 
 	m = getattr(cls,cmd)
 
@@ -190,29 +190,26 @@ def create_call_sig(cmd,cls,parsed=False):
 		ann[a] if a in ann and isinstance(ann[a],type) else type(dfls[i])
 			for i,a in enumerate(args[nargs:]) )
 
-	def get_type_from_ann(arg):
-		return (
-			('str' + ('' if parsed else ' or STDIN')) if ann[arg] == 'sstr' else
-			ann[arg].__name__ )
-
-	if parsed:
+	if as_string:
+		get_type_from_ann = lambda x: 'str or STDIN' if ann[x] == 'sstr' else ann[x].__name__
+		return ' '.join(
+			[f'{a} [{get_type_from_ann(a)}]' for a in args[:nargs]] +
+			['{a} [{b}={c!r}{d}]'.format(
+				a = a,
+				b = dfl_types[n].__name__,
+				c = dfls[n],
+				d = (' ' + ann[a] if a in ann and isinstance(ann[a],str) else ''))
+					for n,a in enumerate(args[nargs:])] )
+	else:
+		get_type_from_ann = lambda x: 'str' if ann[x] == 'sstr' else ann[x].__name__
 		return (
 			[(a,get_type_from_ann(a)) for a in args[:nargs]],            # c_args
 			dict([(a,dfls[n]) for n,a in enumerate(args[nargs:])]),      # c_kwargs
 			dict([(a,dfl_types[n]) for n,a in enumerate(args[nargs:])]), # c_kwargs_types
 			('STDIN_OK' if nargs and ann[args[0]] == 'sstr' else flag) ) # flag
-	else:
-		c_args = [f'{a} [{get_type_from_ann(a)}]' for a in args[:nargs]]
-		c_kwargs = ['{a} [{b}={c!r}{d}]'.format(
-			a = a,
-			b = dfl_types[n].__name__,
-			c = dfls[n],
-			d = (' ' + ann[a] if a in ann and isinstance(ann[a],str) else '') )
-				for n,a in enumerate(args[nargs:])]
-		return ' '.join(c_args + c_kwargs)
 
 def process_args(cmd,cmd_args,cls):
-	c_args,c_kwargs,c_kwargs_types,flag = create_call_sig(cmd,cls,parsed=True)
+	c_args,c_kwargs,c_kwargs_types,flag = create_call_sig(cmd,cls)
 	have_stdin_input = False
 
 	def usage_die(s):

+ 2 - 2
mmgen/mn_entry.py

@@ -392,7 +392,7 @@ class MnemonicEntry(object):
 		}
 		wl = wl.lower()
 		if wl not in d:
-			raise ValueError(f'wordlist {wl!r} not recognized (valid options: {fmt_list(list(d))})')
+			raise ValueError(f'wordlist {wl!r} not recognized (valid choices: {fmt_list(list(d))})')
 		return d[wl]
 
 	@classmethod
@@ -402,7 +402,7 @@ class MnemonicEntry(object):
 			if v not in tcls.entry_modes:
 				raise ValueError(
 					f'entry mode {v!r} not recognized for wordlist {k!r}:' +
-					f'\n    (valid options: {fmt_list(tcls.entry_modes)})' )
+					f'\n    (valid choices: {fmt_list(tcls.entry_modes)})' )
 			tcls.usr_dfl_entry_mode = v
 
 class MnemonicEntryMMGen(MnemonicEntry):

+ 20 - 17
mmgen/tool/coin.py

@@ -32,11 +32,11 @@ class tool_cmd(tool_cmd_base):
 	"""
 	cryptocoin key/address utilities
 
-		May require use of the '--coin', '--type' and/or '--testnet' options
+	May require use of the '--coin', '--type' and/or '--testnet' options
 
-		Examples:
-			mmgen-tool --coin=ltc --type=bech32 wif2addr <wif key>
-			mmgen-tool --coin=zec --type=zcash_z randpair
+	Examples:
+	  mmgen-tool --coin=ltc --type=bech32 wif2addr <wif key>
+	  mmgen-tool --coin=zec --type=zcash_z randpair
 	"""
 
 	need_proto = True
@@ -71,13 +71,13 @@ class tool_cmd(tool_cmd_base):
 			gd.ag.to_addr( gd.kg.gen_data(privkey) ))
 
 	def wif2hex(self,wifkey:'sstr'):
-		"convert a private key from WIF to hex format"
+		"convert a private key from WIF to hexadecimal format"
 		return PrivKey(
 			self.proto,
 			wif = wifkey ).hex()
 
 	def hex2wif(self,privhex:'sstr'):
-		"convert a private key from hex to WIF format"
+		"convert a private key from hexadecimal to WIF format"
 		return PrivKey(
 			self.proto,
 			bytes.fromhex(privhex),
@@ -102,7 +102,7 @@ class tool_cmd(tool_cmd_base):
 		return gd.ag.to_segwit_redeem_script( gd.kg.gen_data(privkey) )
 
 	def wif2segwit_pair(self,wifkey:'sstr'):
-		"generate both a Segwit P2SH-P2WPKH redeem script and address from WIF"
+		"generate a Segwit P2SH-P2WPKH redeem script and address from a WIF private key"
 		assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
 		gd = self._init_generators()
 		data = gd.kg.gen_data(PrivKey(
@@ -112,8 +112,7 @@ class tool_cmd(tool_cmd_base):
 			gd.ag.to_segwit_redeem_script(data),
 			gd.ag.to_addr(data) )
 
-	def privhex2addr(self,privhex:'sstr',output_pubhex=False):
-		"generate coin address from raw private key data in hexadecimal format"
+	def _privhex2out(self,privhex:'sstr',output_pubhex=False):
 		gd = self._init_generators()
 		pk = PrivKey(
 			self.proto,
@@ -123,12 +122,16 @@ class tool_cmd(tool_cmd_base):
 		data = gd.kg.gen_data(pk)
 		return data.pubkey.hex() if output_pubhex else gd.ag.to_addr(data)
 
+	def privhex2addr(self,privhex:'sstr'):
+		"generate a coin address from raw hexadecimal private key data"
+		return self._privhex2out(privhex)
+
 	def privhex2pubhex(self,privhex:'sstr'): # new
-		"generate a hex public key from a hex private key"
-		return self.privhex2addr(privhex,output_pubhex=True)
+		"generate a hexadecimal public key from raw hexadecimal private key data"
+		return self._privhex2out(privhex,output_pubhex=True)
 
 	def pubhex2addr(self,pubkeyhex:'sstr'):
-		"convert a hex pubkey to an address"
+		"convert a hexadecimal pubkey to an address"
 		if self.proto.base_proto == 'Ethereum' and len(pubkeyhex) == 128: # support raw ETH pubkeys
 			pubkeyhex = '04' + pubkeyhex
 		from ..keygen import keygen_public_data
@@ -141,19 +144,19 @@ class tool_cmd(tool_cmd_base):
 		))
 
 	def pubhex2redeem_script(self,pubkeyhex:'sstr'): # new
-		"convert a hex pubkey to a Segwit P2SH-P2WPKH redeem script"
+		"convert a hexadecimal pubkey to a Segwit P2SH-P2WPKH redeem script"
 		assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
 		from ..proto.common import hash160
 		return self.proto.pubhash2redeem_script( hash160(bytes.fromhex(pubkeyhex)) ).hex()
 
-	def redeem_script2addr(self,redeem_scripthex:'sstr'): # new
+	def redeem_script2addr(self,redeem_script_hex:'sstr'): # new
 		"convert a Segwit P2SH-P2WPKH redeem script to an address"
 		assert self.mmtype.name == 'segwit', 'This command is meaningful only for --type=segwit'
-		assert redeem_scripthex[:4] == '0014', f'{redeem_scripthex!r}: invalid redeem script'
-		assert len(redeem_scripthex) == 44, f'{len(redeem_scripthex)//2} bytes: invalid redeem script length'
+		assert redeem_script_hex[:4] == '0014', f'{redeem_script_hex!r}: invalid redeem script'
+		assert len(redeem_script_hex) == 44, f'{len(redeem_script_hex)//2} bytes: invalid redeem script length'
 		from ..proto.common import hash160
 		return self.proto.pubhash2addr(
-			hash160( bytes.fromhex(redeem_scripthex) ),
+			hash160( bytes.fromhex(redeem_script_hex) ),
 			p2sh = True )
 
 	def pubhash2addr(self,pubhashhex:'sstr'):

+ 1 - 1
mmgen/tool/common.py

@@ -23,7 +23,7 @@ tool/common.py: Base class and shared routines for the 'mmgen-tool' utility
 from ..objmethods import MMGenObject
 
 def options_annot_str(l):
-	return "(valid options: '{}')".format( "','".join(l) )
+	return "(valid choices: '{}')".format( "','".join(l) )
 
 class tool_cmd_base(MMGenObject):
 

+ 2 - 2
mmgen/tool/file.py

@@ -78,13 +78,13 @@ class tool_cmd(tool_cmd_base):
 				'dfls': ( False, False, 'addr', 'mtime' ),
 				'annots': {
 					'mmgen_tx_file(s)': str,
-					'sort': options_annot_str(['addr','raw']),
+					'sort':     options_annot_str(['addr','raw']),
 					'filesort': options_annot_str(['mtime','ctime','atime']),
 				}
 			},
 			*infiles,
 			**kwargs ):
-		"show raw/signed MMGen transaction in human-readable form"
+		"display specified raw or signed MMGen transaction files in human-readable form"
 
 		terse = bool(kwargs.get('terse'))
 		tx_sort = kwargs.get('sort') or 'addr'

+ 4 - 4
mmgen/tool/filecrypt.py

@@ -30,10 +30,10 @@ class tool_cmd(tool_cmd_base):
 	"""
 	file encryption and decryption
 
-		MMGen encryption suite:
-		* Key: Scrypt (user-configurable hash parameters, 32-byte salt)
-		* Enc: AES256_CTR, 16-byte rand IV, sha256 hash + 32-byte nonce + data
-		* The encrypted file is indistinguishable from random data
+	MMGen encryption suite:
+	* Key: Scrypt (user-configurable hash parameters, 32-byte salt)
+	* Enc: AES256_CTR, 16-byte rand IV, sha256 hash + 32-byte nonce + data
+	* The encrypted file is indistinguishable from random data
 	"""
 	def encrypt(self,infile:str,outfile='',hash_preset=''):
 		"encrypt a file"

+ 3 - 1
mmgen/tool/fileutil.py

@@ -65,7 +65,9 @@ class tool_cmd(tool_cmd_base):
 		return True
 
 	def rand2file(self,outfile:str,nbytes:str,threads=4,silent=False):
-		"write 'n' bytes of random data to specified file"
+		"""
+		write ‘nbytes’ bytes of random data to specified file (dd-style byte specifiers supported)
+		"""
 		from threading import Thread
 		from queue import Queue
 		from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes

+ 57 - 24
mmgen/tool/help.py

@@ -59,7 +59,9 @@ def main_help():
 
 	return '\n'.join(do())
 
-def usage(cmdname=None,exit_val=1):
+def gen_tool_usage():
+
+	from ..util import capfirst
 
 	m1 = """
 		USAGE INFORMATION FOR MMGEN-TOOL COMMANDS:
@@ -68,12 +70,16 @@ def usage(cmdname=None,exit_val=1):
 
 		  Arguments with both type and default value specified in square brackets are
 		  optional and must be specified in the form ‘name=value’
+
+		  For more detailed usage information for a particular tool command, type
+		  ‘mmgen-tool help <command name>’
 		"""
 
 	m2 = """
 		  To force a command to read from STDIN instead of file (for commands taking
 		  a filename as their first argument), substitute "-" for the filename.
 
+
 		EXAMPLES:
 
 		  Generate a random LTC Bech32 public/private keypair:
@@ -97,39 +103,66 @@ def usage(cmdname=None,exit_val=1):
 		  Reverse a hex string:
 		  $ mmgen-tool hexreverse "deadbeefcafe"
 
-		  Same as above, but supply input via stdin:
+		  Same as above, but supply input via STDIN:
 		  $ echo "deadbeefcafe" | mmgen-tool hexreverse -
 		"""
 
-	from ..util import Msg,Msg_r,fmt,die,capfirst
+	for line in m1.lstrip().split('\n'):
+		yield line.lstrip('\t')
+
+	for clsname,cmdlist in main_tool.mods.items():
+		cls = main_tool.get_mod_cls(clsname)
+		cls_docstr = cls.__doc__.strip()
+		yield ''
+		yield '  {}:'.format( capfirst(cls_docstr.split('\n')[0].strip()) )
+		yield ''
+
+		if '\n' in cls_docstr:
+			for line in cls_docstr.split('\n')[2:]:
+				yield '    ' + line.lstrip('\t')
+			yield ''
+
+		max_w = max(map(len,cmdlist))
+		for cmdname in cmdlist:
+			yield '    {a:{w}} {b}'.format(
+				a = cmdname,
+				b = main_tool.create_call_sig(cmdname,cls,as_string=True),
+				w = max_w )
+		yield ''
+
+	for line in m2.rstrip().split('\n'):
+		yield line.lstrip('\t')
+
+def gen_tool_cmd_usage(mod,cmdname):
+
+	from ..globalvars import g
+	from ..util import capfirst
+
+	cls = main_tool.get_mod_cls(mod)
+	docstr = getattr(cls,cmdname).__doc__.strip()
+	args,kwargs,kwargs_types,flag = main_tool.create_call_sig(cmdname,cls)
+
+	yield '{a}\n\nUSAGE: {b} {c} {d}{e}'.format(
+		a = capfirst( docstr.split('\n')[0].strip() ),
+		b = g.prog_name,
+		c = cmdname,
+		d = main_tool.create_call_sig(cmdname,cls,as_string=True),
+		e = '\n\n' + fmt('\n'.join(docstr.split('\n')[1:]),strip_char='\t').rstrip()
+			if '\n' in docstr else '' )
+
+def usage(cmdname=None,exit_val=1):
+
+	from ..util import Msg,die,do_pager
 
 	if cmdname:
-		from ..globalvars import g
 		for mod,cmdlist in main_tool.mods.items():
 			if cmdname in cmdlist:
-				cls = main_tool.get_mod_cls(mod)
-				docstr = getattr(cls,cmdname).__doc__.strip()
-				Msg('{a}\n\nUSAGE: {b} {c} {d}{e}'.format(
-					a = capfirst( docstr.split('\n')[0].strip() ),
-					b = g.prog_name,
-					c = cmdname,
-					d = main_tool.create_call_sig(cmdname,cls),
-					e = '\n\n' + fmt('\n'.join(docstr.split('\n')[1:]),strip_char='\t').rstrip()
-						if '\n' in docstr else '' ))
+				Msg('\n'.join(gen_tool_cmd_usage(mod,cmdname)))
 				break
 		else:
 			die(1,f'{cmdname!r}: no such tool command')
 	else:
-		Msg(fmt(m1,strip_char='\t'))
-		for clsname,cmdlist in main_tool.mods.items():
-			cls = main_tool.get_mod_cls(clsname)
-			cls_info = cls.__doc__.strip().split('\n')[0]
-			Msg('  {}{}:\n'.format( cls_info[0].upper(), cls_info[1:] ))
-			max_w = max(map(len,cmdlist))
-			for cmdname in cmdlist:
-				Msg(f'    {cmdname:{max_w}} {main_tool.create_call_sig(cmdname,cls)}')
-			Msg('')
-		Msg_r('  ' + fmt(m2,strip_char='\t'))
+		do_pager('\n'.join(gen_tool_usage()))
 
 	import sys
 	sys.exit(exit_val)
@@ -142,5 +175,5 @@ class tool_cmd(tool_cmd_base):
 		usage(command_name,exit_val=0)
 
 	def usage(self,command_name=''):
-		"display usage information for a single command"
+		"display usage information for a single command or all commands"
 		usage(command_name,exit_val=0)

+ 27 - 25
mmgen/tool/mnemonic.py

@@ -39,23 +39,25 @@ mn_opts_disp = options_annot_str(mnemonic_fmts)
 
 class tool_cmd(tool_cmd_base):
 	"""
-	seed phrase utilities (valid formats: 'mmgen' (default), 'bip39', 'xmrseed')
-
-		IMPORTANT NOTE: MMGen's default seed phrase format uses the Electrum
-		wordlist, however seed phrases are computed using a different algorithm
-		and are NOT Electrum-compatible!
-
-		BIP39 support is fully compatible with the standard, allowing users to
-		import and export seed entropy from BIP39-compatible wallets.  However,
-		users should be aware that BIP39 support does not imply BIP32 support!
-		MMGen uses its own key derivation scheme differing from the one described
-		by the BIP32 protocol.
-
-		For Monero ('xmrseed') seed phrases, input data is reduced to a spendkey
-		before conversion so that a canonical seed phrase is produced.  This is
-		required because Monero seeds, unlike ordinary wallet seeds, are tied
-		to a concrete key/address pair.  To manually generate a Monero spendkey,
-		use the 'hex2wif' command.
+	seed phrase utilities
+
+	Supported seed phrase formats: 'mmgen' (default), 'bip39', 'xmrseed'
+
+	IMPORTANT NOTE: MMGen’s default seed phrase format uses the Electrum
+	wordlist, however seed phrases are computed using a different algorithm
+	and are NOT Electrum-compatible!
+
+	BIP39 support is fully compatible with the standard, allowing users to
+	import and export seed entropy from BIP39-compatible wallets.  However,
+	users should be aware that BIP39 support does not imply BIP32 support!
+	MMGen uses its own key derivation scheme differing from the one described
+	by the BIP32 protocol.
+
+	For Monero (‘xmrseed’) seed phrases, input data is reduced to a spendkey
+	before conversion so that a canonical seed phrase is produced.  This is
+	required because Monero seeds, unlike ordinary wallet seeds, are tied
+	to a concrete key/address pair.  To manually generate a Monero spendkey,
+	use the ‘hex2wif’ command.
 	"""
 
 	@staticmethod
@@ -81,31 +83,31 @@ class tool_cmd(tool_cmd_base):
 		return self.hex2mn(randbytes.hex(),fmt=fmt)
 
 	def mn_rand128(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
-		"generate random 128-bit mnemonic seed phrase"
+		"generate a random 128-bit mnemonic seed phrase"
 		return self._do_random_mn(16,fmt)
 
 	def mn_rand192(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
-		"generate random 192-bit mnemonic seed phrase"
+		"generate a random 192-bit mnemonic seed phrase"
 		return self._do_random_mn(24,fmt)
 
 	def mn_rand256(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
-		"generate random 256-bit mnemonic seed phrase"
+		"generate a random 256-bit mnemonic seed phrase"
 		return self._do_random_mn(32,fmt)
 
 	def hex2mn( self, hexstr:'sstr', fmt:mn_opts_disp = dfl_mnemonic_fmt ):
-		"convert a 16, 24 or 32-byte hexadecimal number to a mnemonic seed phrase"
+		"convert a 16, 24 or 32-byte hexadecimal string to a mnemonic seed phrase"
 		if fmt == 'xmrseed':
 			hexstr = self._xmr_reduce(bytes.fromhex(hexstr)).hex()
 		f = mnemonic_fmts[fmt]
 		return ' '.join( f.conv_cls(fmt).fromhex(hexstr,f.pad) )
 
 	def mn2hex( self, seed_mnemonic:'sstr', fmt:mn_opts_disp = dfl_mnemonic_fmt ):
-		"convert a mnemonic seed phrase to a hexadecimal number"
+		"convert a mnemonic seed phrase to a hexadecimal string"
 		f = mnemonic_fmts[fmt]
 		return f.conv_cls(fmt).tohex( seed_mnemonic.split(), f.pad )
 
 	def mn2hex_interactive( self, fmt:mn_opts_disp = dfl_mnemonic_fmt, mn_len=24, print_mn=False ):
-		"convert an interactively supplied mnemonic seed phrase to a hexadecimal number"
+		"convert an interactively supplied mnemonic seed phrase to a hexadecimal string"
 		from ..mn_entry import mn_entry
 		mn = mn_entry(fmt).get_mnemonic_from_user(25 if fmt == 'xmrseed' else mn_len,validate=False)
 		if print_mn:
@@ -114,11 +116,11 @@ class tool_cmd(tool_cmd_base):
 		return self.mn2hex(seed_mnemonic=mn,fmt=fmt)
 
 	def mn_stats(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
-		"show stats for mnemonic wordlist"
+		"show stats for a mnemonic wordlist"
 		return mnemonic_fmts[fmt].conv_cls(fmt).check_wordlist()
 
 	def mn_printlist( self, fmt:mn_opts_disp = dfl_mnemonic_fmt, enum=False, pager=False ):
-		"print mnemonic wordlist"
+		"print a mnemonic wordlist"
 		ret = mnemonic_fmts[fmt].conv_cls(fmt).get_wordlist()
 		if enum:
 			ret = [f'{n:>4} {e}' for n,e in enumerate(ret)]

+ 3 - 7
mmgen/tool/rpc.py

@@ -24,7 +24,7 @@ from .common import tool_cmd_base,options_annot_str
 from ..tw.common import TwCommon
 
 class tool_cmd(tool_cmd_base):
-	"tracking wallet commands using the JSON-RPC interface"
+	"tracking-wallet commands using the JSON-RPC interface"
 
 	need_proto = True
 	need_amt = True
@@ -43,17 +43,13 @@ class tool_cmd(tool_cmd_base):
 	async def listaddress(self,
 			mmgen_addr:str,
 			minconf     = 1,
-			pager       = False,
-			showempty   = True,
 			showbtcaddr = True,
 			age_fmt: options_annot_str(TwCommon.age_fmts) = 'confs' ):
-		"list the specified MMGen address and its balance"
+		"list the specified MMGen address in the tracking wallet and its balance"
 
 		return await self.listaddresses(
 			mmgen_addrs  = mmgen_addr,
 			minconf      = minconf,
-			pager        = pager,
-			showempty    = showempty,
 			showbtcaddrs = showbtcaddr,
 			age_fmt      = age_fmt )
 
@@ -140,7 +136,7 @@ class tool_cmd(tool_cmd_base):
 			sort            = 'age',
 			age_fmt: options_annot_str(TwCommon.age_fmts) = 'confs',
 			interactive     = False ):
-		"view transaction history"
+		"view transaction history of tracking wallet"
 
 		from ..tw.txhistory import TwTxHistory
 		obj = await TwTxHistory(self.proto,sinceblock=sinceblock)

+ 27 - 27
mmgen/tool/util.py

@@ -39,10 +39,10 @@ class tool_cmd(tool_cmd_base):
 		from ..util import int2bytespec
 		return int2bytespec( n, dd_style_byte_specifier, fmt, print_sym )
 
-	def randhex(self,nbytes='32'):
+	def randhex(self,nbytes=32):
 		"print 'n' bytes (default 32) of random data in hex format"
 		from ..crypto import get_random
-		return get_random( int(nbytes) ).hex()
+		return get_random( nbytes ).hex()
 
 	def hexreverse(self,hexstr:'sstr'):
 		"reverse bytes of a hexadecimal string"
@@ -55,7 +55,7 @@ class tool_cmd(tool_cmd_base):
 		return data.hex()
 
 	def unhexlify(self,hexstr:'sstr'):
-		"convert hexadecimal value to bytes (warning: outputs binary data)"
+		"convert a hexadecimal string to bytes (warning: outputs binary data)"
 		return bytes.fromhex(hexstr)
 
 	def hexdump(self,infile:str,cols=8,line_nums='hex'):
@@ -81,17 +81,17 @@ class tool_cmd(tool_cmd_base):
 		from ..proto.common import hash160
 		return hash160( bytes.fromhex(hexstr) ).hex()
 
-	def hash256(self,string_or_bytes:str,file_input=False,hex_input=False): # TODO: handle stdin
+	def hash256(self,data:str,file_input=False,hex_input=False): # TODO: handle stdin
 		"compute sha256(sha256(data)) (double sha256)"
 		from hashlib import sha256
 		if file_input:
 			from ..fileutil import get_data_from_file
-			b = get_data_from_file( string_or_bytes, binary=True )
+			b = get_data_from_file( data, binary=True )
 		elif hex_input:
 			from ..util import decode_pretty_hexdump
-			b = decode_pretty_hexdump(string_or_bytes)
+			b = decode_pretty_hexdump(data)
 		else:
-			b = string_or_bytes
+			b = data
 		return sha256(sha256(b.encode()).digest()).hexdigest()
 
 	def id6(self,infile:str):
@@ -102,7 +102,7 @@ class tool_cmd(tool_cmd_base):
 			get_data_from_file( infile, dash=True, quiet=True, binary=True ))
 
 	def str2id6(self,string:'sstr'): # retain ignoring of space for backwards compat
-		"generate 6-character MMGen ID for a string, ignoring spaces"
+		"generate 6-character MMGen ID for a string, ignoring spaces in string"
 		from ..util import make_chksum_6
 		return make_chksum_6( ''.join(string.split()) )
 
@@ -126,50 +126,50 @@ class tool_cmd(tool_cmd_base):
 		data = get_data_from_file( infile, dash=True, quiet=True, binary=True )
 		return baseconv('b58').frombytes( data, pad=pad, tostr=True )
 
-	def b58tobytes(self,b58num:'sstr',pad=0):
-		"convert a base 58 number to bytes (warning: outputs binary data)"
+	def b58tobytes(self,b58_str:'sstr',pad=0):
+		"convert a base 58 string to bytes (warning: outputs binary data)"
 		from ..baseconv import baseconv
-		return baseconv('b58').tobytes( b58num, pad=pad )
+		return baseconv('b58').tobytes( b58_str, pad=pad )
 
 	def hextob58(self,hexstr:'sstr',pad=0):
-		"convert a hexadecimal number to base 58"
+		"convert a hexadecimal string to base 58"
 		from ..baseconv import baseconv
 		return baseconv('b58').fromhex( hexstr, pad=pad, tostr=True )
 
-	def b58tohex(self,b58num:'sstr',pad=0):
-		"convert a base 58 number to hexadecimal"
+	def b58tohex(self,b58_str:'sstr',pad=0):
+		"convert a base 58 string to hexadecimal"
 		from ..baseconv import baseconv
-		return baseconv('b58').tohex( b58num, pad=pad )
+		return baseconv('b58').tohex( b58_str, pad=pad )
 
 	def hextob58chk(self,hexstr:'sstr'):
-		"convert a hexadecimal number to base58-check encoding"
+		"convert a hexadecimal string to base58-check encoding"
 		from ..proto.common import b58chk_encode
 		return b58chk_encode( bytes.fromhex(hexstr) )
 
-	def b58chktohex(self,b58chk_num:'sstr'):
-		"convert a base58-check encoded number to hexadecimal"
+	def b58chktohex(self,b58chk_str:'sstr'):
+		"convert a base58-check encoded string to hexadecimal"
 		from ..proto.common import b58chk_decode
-		return b58chk_decode(b58chk_num).hex()
+		return b58chk_decode(b58chk_str).hex()
 
 	def hextob32(self,hexstr:'sstr',pad=0):
-		"convert a hexadecimal number to MMGen's flavor of base 32"
+		"convert a hexadecimal string to an MMGen-flavor base 32 string"
 		from ..baseconv import baseconv
 		return baseconv('b32').fromhex( hexstr, pad, tostr=True )
 
-	def b32tohex(self,b32num:'sstr',pad=0):
-		"convert an MMGen-flavor base 32 number to hexadecimal"
+	def b32tohex(self,b32_str:'sstr',pad=0):
+		"convert an MMGen-flavor base 32 string to hexadecimal"
 		from ..baseconv import baseconv
-		return baseconv('b32').tohex( b32num.upper(), pad )
+		return baseconv('b32').tohex( b32_str.upper(), pad )
 
 	def hextob6d(self,hexstr:'sstr',pad=0,add_spaces=True):
-		"convert a hexadecimal number to die roll base6 (base6d)"
+		"convert a hexadecimal string to die roll base6 (base6d)"
 		from ..baseconv import baseconv
 		from ..util import block_format
 		ret = baseconv('b6d').fromhex(hexstr,pad,tostr=True)
 		return block_format( ret, gw=5, cols=None ).strip() if add_spaces else ret
 
-	def b6dtohex(self,b6d_num:'sstr',pad=0):
-		"convert a die roll base6 (base6d) number to hexadecimal"
+	def b6dtohex(self,b6d_str:'sstr',pad=0):
+		"convert a die roll base6 (base6d) string to hexadecimal"
 		from ..baseconv import baseconv
 		from ..util import remove_whitespace
-		return baseconv('b6d').tohex( remove_whitespace(b6d_num), pad )
+		return baseconv('b6d').tohex( remove_whitespace(b6d_str), pad )

+ 11 - 4
mmgen/tool/wallet.py

@@ -67,23 +67,30 @@ class tool_cmd(tool_cmd_base):
 		return Wallet(sf).seed.split( share_count, id_str, master_share ).format()
 
 	def gen_key(self,mmgen_addr:str,wallet=''):
-		"generate a single MMGen WIF key from default or specified wallet"
-		return self.gen_addr( mmgen_addr, wallet, target='wif' )
+		"generate a single WIF key for specified MMGen address from default or specified wallet"
+		return self._gen_keyaddr( mmgen_addr, 'wif', wallet )
 
-	def gen_addr(self,mmgen_addr:str,wallet='',target='addr'):
+	def gen_addr(self,mmgen_addr:str,wallet=''):
 		"generate a single MMGen address from default or specified wallet"
+		return self._gen_keyaddr( mmgen_addr, 'addr', wallet )
+
+	def _gen_keyaddr(self,mmgen_addr,target,wallet=''):
 		from ..addr import MMGenID
 		from ..addrlist import AddrList,AddrIdxList
+
 		addr = MMGenID( self.proto, mmgen_addr )
 		opt.quiet = True
 		sf = get_seed_file([wallet] if wallet else [],1)
 		ss = Wallet(sf)
+
 		if ss.seed.sid != addr.sid:
 			from ..util import die
 			die(1,f'Seed ID of requested address ({addr.sid}) does not match wallet ({ss.seed.sid})')
+
 		d = AddrList(
 			proto     = self.proto,
 			seed      = ss.seed,
 			addr_idxs = AddrIdxList(str(addr.idx)),
 			mmtype    = addr.mmtype ).data[0]
-		return d.sec.wif if target == 'wif' else d.addr
+
+		return { 'wif': d.sec.wif, 'addr': d.addr }[target]

+ 1 - 1
test/tooltest.py

@@ -43,7 +43,7 @@ opts_data = {
 -L, --list-names    List the names of all tested 'mmgen-tool' commands
 -s, --system        Test scripts and modules installed on system rather than
                     those in the repo root
--t, --type=t        Specify address type (valid options: 'zcash_z')
+-t, --type=t        Specify address type (valid choices: 'zcash_z')
 -v, --verbose       Produce more verbose output
 """,
 	'notes': """